RK3568开发板gpio模拟LED呼吸灯

设备树

复制代码
		breathe_led: breathe-led {
        compatible = "breathe-led";
        led-gpios = <&gpio0 RK_PC0 GPIO_ACTIVE_HIGH>;  // 使用GPIO0_A0
        pwm-channel = <0>;  // 可选,如果使用PWM方式
        max-brightness = <255>;
				breathe-interval = <10>;  // 更小的间隔让呼吸更平滑
        #address-cells = <1>;
        #size-cells = <0>;
        status = "okay";
    };

代码文件

新建:/home/alientek/sdk/rk3568_linux_sdk/kernel/drivers/leds/leds-breathe-rk3568.c

复制代码
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/gpio/consumer.h>
#include <linux/gpio.h>
#include <linux/timer.h>
#include <linux/workqueue.h>
#include <linux/leds.h>
#include <linux/slab.h>
#include <linux/of.h>

#define DRIVER_NAME  "breathe-led"
#define BREATHE_INTERVAL_MS 10

// 呼吸曲线查找表(模拟正弦波,0-255范围)
static const u8 breathe_curve[] = {
    0,   0,   0,   0,   0,   1,   1,   2, 
    2,   3,   4,   5,   6,   7,   9,   10,
    12,  14,  16,  18,  20,  23,  25,  28,
    31,  34,  37,  41,  44,  48,  52,  56,
    60,  65,  69,  74,  79,  84,  89,  94,
    100, 105, 111, 117, 123, 129, 135, 141,
    148, 154, 161, 167, 174, 181, 188, 195,
    202, 209, 216, 223, 230, 237, 244, 251,
    255, 251, 244, 237, 230, 223, 216, 209,
    202, 195, 188, 181, 174, 167, 161, 154,
    148, 141, 135, 129, 123, 117, 111, 105,
    100, 94,  89,  84,  79,  74,  69,  65,
    60,  56,  52,  48,  44,  41,  37,  34,
    31,  28,  25,  23,  20,  18,  16,  14,
    12,  10,  9,   7,   6,   5,   4,   3,
    2,   2,   1,   1,   0,   0,   0,   0
};

#define BREATHE_CURVE_SIZE ARRAY_SIZE(breathe_curve)

struct breathe_led {
    struct device *dev;
    struct gpio_desc *gpiod;
    struct timer_list timer;
    struct work_struct work;

    int brightness;
    int direction;
    int max_brightness;
    int breathe_interval;

    int pwm_period; //pwm周期
    int pwm_duty;   //当前占空比
    int pwm_counter;    //pwm计数器

    int curve_index;  //呼吸曲线索引

    bool is_active;
};

static void breathe_led_pwm_control(struct breathe_led *led){
    if(!led->gpiod)
        return;

    led->pwm_counter++;
    if(led->pwm_counter >= led->pwm_period){
        led->pwm_counter = 0;
    }
    
    if(led->pwm_counter < led->pwm_duty){
        gpiod_set_value(led->gpiod,1);
    }else{
        gpiod_set_value(led->gpiod,0);
    }
}

static void update_breathe_brightness(struct breathe_led *led){
    //更新曲线索引
    led->curve_index++;
    if(led->curve_index >= BREATHE_CURVE_SIZE){
        led->curve_index = 0;
    }
    led->brightness = breathe_curve[led->curve_index];
    led->pwm_duty = (led->brightness * led->pwm_period) / 255;
}

static void breathe_led_work(struct work_struct *work){
    struct breathe_led *led = container_of(work,struct breathe_led,work);

    //使用查找表版本的呼吸曲线
    update_breathe_brightness(led);

    //应用pwm控制
    breathe_led_pwm_control(led);

    if(led->is_active){
        mod_timer(&led->timer,jiffies + msecs_to_jiffies(led->breathe_interval));
    }
}

static void breathe_led_timer(struct timer_list *t){
    struct breathe_led *led = from_timer(led,t,timer);
    schedule_work(&led->work);
}

static void breathe_led_start(struct breathe_led *led){
    if(led->is_active)
        return;

    led->is_active = true;
    led->brightness = 0;
    led->pwm_counter = 0;
    led->pwm_duty = 0;
    led->curve_index = 0;
    led->direction = 1;

    //启动定时器
    mod_timer(&led->timer,jiffies + msecs_to_jiffies(led->breathe_interval));
    dev_info(led->dev,"Breathe LED started, period: %d, interval: %dms\\n",led->pwm_period,led->breathe_interval);
}

static void breathe_led_stop(struct breathe_led *led){
    if(!led->is_active)
        return;

    led->is_active = false;
    del_timer_sync(&led->timer);
    cancel_work_sync(&led->work);

    //关闭LED
    gpiod_set_value(led->gpiod,0);
    dev_info(led->dev,"Breathe LED stopped\\n");
}

static ssize_t brightness_show(struct device *dev,struct device_attribute *attr,char *buf){
    struct breathe_led *led = dev_get_drvdata(dev);
    return sprintf(buf,"%d\\n",led->brightness);
}

static ssize_t brightness_store(struct device *dev,struct device_attribute *attr,const char *buf,size_t count){
    struct breathe_led *led = dev_get_drvdata(dev);
    int value;

    if(kstrtoint(buf,0,&value)){
        return -EINVAL;
    }

    if(value >= 0 && value <= led->max_brightness){
        led->brightness = value;
        led->pwm_duty = (value * led->pwm_period) / led->max_brightness;
        breathe_led_pwm_control(led);
    }
    return count;
}

static DEVICE_ATTR(brightness,0644,brightness_show,brightness_store);

static ssize_t breathe_show(struct device *dev,struct device_attribute *attr,char *buf){
    struct breathe_led *led = dev_get_drvdata(dev);
    return sprintf(buf,"%d\\n",led->is_active ? 1 : 0);
}

static ssize_t breathe_store(struct device *dev,struct device_attribute *attr,const char *buf,size_t count){
    struct breathe_led *led = dev_get_drvdata(dev);
    int value;
    
    if(kstrtoint(buf,0,&value))
        return -EINVAL;

    if(value){
        breathe_led_start(led);
    }else{
        breathe_led_stop(led);
    }
    return count;
}

static DEVICE_ATTR(breathe,0644,breathe_show,breathe_store);

static struct attribute *breathe_led_attrs[] = {
    &dev_attr_breathe.attr,
    &dev_attr_brightness.attr,//亮度控制
    NULL,
}; 

static struct attribute_group breathe_led_attr_group = {
    .attrs = breathe_led_attrs
};

static int breathe_led_probe(struct platform_device *pdev){
    struct device *dev = &pdev->dev;
    struct breathe_led *led;
    int ret;

    led = devm_kzalloc(dev,sizeof(*led),GFP_KERNEL);
    if(!led)
        return -ENOMEM;

    led->dev = dev;

    //获取GPIO
    led->gpiod = devm_gpiod_get(dev,"led",GPIOD_OUT_LOW);
    if(IS_ERR(led->gpiod)){
        dev_err(dev,"Failed to get GPIO\\n");
        return PTR_ERR(led->gpiod);
    }

    //从设备树获取参数
    device_property_read_u32(dev,"max-brightness",&led->max_brightness);
    if(!led->max_brightness)
        led->max_brightness = 255;

    device_property_read_u32(dev,"breathe-interval",&led->breathe_interval);
    if(!led->breathe_interval)
        led->breathe_interval = BREATHE_INTERVAL_MS;

    //设置pwm参数
    led->pwm_period = 50;
    led->pwm_counter = 0;
    led->pwm_duty = 0;
    led->curve_index = 0;

    //初始化定时器与工作队列
    timer_setup(&led->timer,breathe_led_timer,0);
    INIT_WORK(&led->work,breathe_led_work);

    platform_set_drvdata(pdev,led);

    ret = sysfs_create_group(&dev->kobj,&breathe_led_attr_group);
    if(ret){
        dev_err(dev,"Failed to create sysfs group\\n");
        return ret;
    }

    //启动呼吸灯效果
    breathe_led_start(led);

    dev_err(dev,"Breathe LED driver probed successfully\\n");
    dev_info(dev,"PWM period: %d,Max brightness: %d,Interval: %dms\\n",led->pwm_period,led->max_brightness,led->breathe_interval);
    return 0;
}

static int breathe_led_remove(struct platform_device *pdev){
    struct breathe_led *led = platform_get_drvdata(pdev);
    breathe_led_stop(led);
    sysfs_remove_group(&pdev->dev.kobj,&breathe_led_attr_group);

    dev_info(&pdev->dev,"Breathe LED driver removed\\n");
    return 0;
}

static const struct of_device_id breathe_led_of_match[] = {
    { .compatible = "breathe-led" },
    { }
};

MODULE_DEVICE_TABLE(of,breathe_led_of_match);

static struct platform_driver breathe_led_driver = {
    .probe = breathe_led_probe,
    .remove = breathe_led_remove,
    .driver = {
        .name = DRIVER_NAME,
        .of_match_table = breathe_led_of_match
    }
};

module_platform_driver(breathe_led_driver);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Liangrong.Ren");
MODULE_DESCRIPTION("RK3568 Breathe LED Driver");
MODULE_VERSION("1.0");

Kconfig配置

修改/home/alientek/sdk/rk3568_linux_sdk/kernel/drivers/leds/Kconfig,加入下面内容

复制代码
config LEDS_BREATHE_RK3568
    tristate "RK3568 Breathe LED support"
    depends on (ARCH_ROCKCHIP && LEDS_CLASS) || COMPILE_TEST
    help
      This option enables support for breathe LED on RK3568 SoCs.
      It creates a breathing effect using GPIO and timer.

Makefile配置

修改/home/alientek/sdk/rk3568_linux_sdk/kernel/drivers/leds/Makefile,加入下面内容

复制代码
obj-$(CONFIG_LEDS_BREATHE_RK3568) += leds-breathe-rk3568.o

编译内核

执行编译内核命令,进入内核kernel的目录

复制代码
1. 进入sdk的内核目录
cd /home/alientek/sdk/rk3568_linux_sdk/kernel

2. 执行下面命令
make ARCH=arm64 menuconfig
 
3. 进入页面按/进入搜索CONFIG_LEDS_BREATHE_RK3568:
 Symbol: LEDS_BREATHE_RK3568 [=y]                                                                                                                    
  │ Type  : tristate                                                                                                                
  │ Prompt: RK3568 Breathe LED support          
  │   Location:                                         
  │     -> Device Drivers                                  
  │ (1)   -> LED Support (NEW_LEDS [=y])                             
  │   Defined at drivers/leds/Kconfig:765                                                     
  │   Depends on: NEW_LEDS [=y] && (ARCH_ROCKCHIP [=y] && LEDS_CLASS [=y] || COMPILE_TEST [=n])  
  
  
 4. 然后按上面数字1进入页面,按空格选中: 
  
 <*>   RK3568 Breathe LED support     
 
 5. 然后保存退出
 
 6. make ARCH=arm64 savedefconfig
 7. cp .config arch/arm64/configs/rockchip_defconfig
 8. 退回到sdk根目录编译内核,重新烧写boot.img

启动日志

复制代码
[    1.968598] breathe-led breathe-led: Breathe LED started, period: 50, interval: 10ms
[    1.968615] breathe-led breathe-led: Breathe LED driver probed successfully
[    1.968625] breathe-led breathe-led: PWM period: 50,Max brightness: 255,Interval: 10ms

看到这个日志就成功了,开发板上的LED灯两快闪一短闪,目前模拟实现就只能这样了。

相关推荐
JNX_SEMI8 小时前
EG2226 全桥驱动芯片技术解析:600V/1A 耐压、SSOP16 封装,助力逆变器与无刷电机驱动设计
单片机·嵌入式硬件·物联网
大卡片8 小时前
PWM控制原理
嵌入式硬件
lolo大魔王8 小时前
Linux 文件系统超全面详解(原理、结构、挂载、分区、inode、日志、管理命令)
linux·运维·服务器
磊 子10 小时前
详细讲解一下epoll
linux·io·epoll·io多路复用
周周记笔记10 小时前
【元器件专题】用阻抗等效分析法来分析开关电路
单片机·嵌入式硬件
printfLILEI10 小时前
php中的类与对象以及反序列化
linux·开发语言·php
leoFY12311 小时前
STM32H750配置LAN PHY芯片LAN8742
网络·stm32·嵌入式硬件
iCxhust11 小时前
如何利用iret修改cs ip
汇编·单片机·嵌入式硬件·微机原理·8088单板机
叠叠乐12 小时前
redmi k90 pro max 强解BL,刷海外rom, 并刷入sukisu ultra
linux
m0_3771081412 小时前
stm32平衡车
stm32·单片机·嵌入式硬件