Linux LED 子系统框架

Linux 驱动开发系列 —— LED 子系统框架全解析

一、前言

LED(Light Emitting Diode,发光二极管)作为嵌入式系统中最基础的外设之一,广泛应用于状态指示、调试、用户交互等场景。Linux 为此提供了专门的 LED 子系统,使得开发者可以以统一的方式操作不同平台、不同驱动的 LED 灯。

本文将从 LED 子系统的整体架构出发,深入分析其核心数据结构、工作机制、驱动开发流程以及设备树支持等内容,结合实际代码案例,帮助开发者全面掌握 LED 子系统的开发与调试方法。

二、LED 子系统概述

Linux 的 LED 子系统是通过 drivers/leds/ 目录下的一系列驱动实现的,提供 /sys/class/leds/ 接口供用户空间访问。它支持多种控制方式,如触发器(trigger)、亮度调节(brightness)、闪烁控制(blink)等。

主要组成部分包括:

  • 核心框架代码 drivers/leds/led-core.c
  • 各平台适配的 LED 驱动(如 gpio-led、pmic-led 等)
  • 触发器机制实现 drivers/leds/trigger/

三、LED 子系统核心数据结构

LED 子系统以 struct led_classdev 为核心,描述一个 LED 设备。

struct led_classdev {
    const char          *name;
    int                 brightness;
    int                 max_brightness;
    int (*brightness_set)(struct led_classdev *, enum led_brightness);
    int (*brightness_get)(struct led_classdev *);
    void (*blink_set)(struct led_classdev *, unsigned long *delay_on, unsigned long *delay_off);
    // 触发器相关
    const char          *default_trigger;
    struct device       *dev;
    struct list_head    node;
    // 省略其他字段
};

led_classdev 注册后会在 /sys/class/leds/ 下生成对应目录,例如 led0,其中包含:

  • brightness:当前亮度值
  • max_brightness:最大亮度
  • trigger:可用触发器列表及当前选中项

四、LED 注册与注销流程

开发者通过调用如下两个接口完成注册和注销:

int led_classdev_register(struct device *parent, struct led_classdev *led_cdev);
void led_classdev_unregister(struct led_classdev *led_cdev);

注册成功后,系统会为 LED 创建设备节点,用户可通过 echo 等方式控制 LED。

注册过程关键步骤包括:

  1. 创建 class 设备 device_create
  2. 创建 brightness、trigger 等属性文件 device_create_file
  3. 初始化默认 trigger led_trigger_set_default

五、GPIO-LED 驱动详解

GPIO 控制的 LED 驱动是最常见的实现,驱动源码位于 drivers/leds/leds-gpio.c

其核心是 gpio_ledgpio_led_data 两个结构体:

struct gpio_led {
    const char *name;
    unsigned gpio;
    enum led_brightness default_state;
    const char *default_trigger;
    bool retain_state_suspended;
};

struct gpio_led_data {
    struct led_classdev cdev;
    struct gpio_desc *gpiod;
    // 其他字段
};

每个 gpio_led 描述一个 LED,通过 platform_data 或设备树传入。

六、设备树支持

设备树是 ARM 平台上主流的设备描述方式。GPIO LED 驱动支持通过设备树方式配置 LED。

设备树节点示例:

leds {
    compatible = "gpio-leds";
    
    led0 {
        label = "status_led";
        gpios = <&gpio1 4 GPIO_ACTIVE_HIGH>;
        default-state = "on";
        linux,default-trigger = "heartbeat";
    };
};

设备树中 gpios 定义引脚,default-state 定义默认状态,linux,default-trigger 定义触发器。

驱动通过 of_get_named_gpio()gpiod_get_from_of_node() 解析节点,并注册为 led_classdev

七、LED Trigger 机制

Trigger 是 LED 子系统一大特色。其作用是为 LED 灯绑定系统事件(如定时器、硬盘活动、CPU 负载等),从而自动控制灯的亮灭。

典型 trigger 有:

  • timer:周期闪烁
  • heartbeat:模仿心跳
  • cpu0:显示 CPU 活动状态

触发器注册接口:

void led_trigger_register(struct led_trigger *trigger);
void led_trigger_unregister(struct led_trigger *trigger);

驱动注册 LED 时,可设置 default_trigger,触发器会绑定 LED,并控制亮度变化。

八、自定义 LED 驱动开发流程

  1. 定义 led_classdev 实例,指定 brightness_set 实现
  2. 实现亮灭控制逻辑(如操作寄存器或 GPIO)
  3. probe() 中注册 LED
  4. remove() 中注销 LED
  5. 支持触发器可选
  6. 添加设备树支持(可选)

代码示例:

static void my_led_set(struct led_classdev *cdev, enum led_brightness brightness) {
    gpio_set_value(LED_GPIO, brightness);
}

static int my_led_probe(struct platform_device *pdev) {
    my_led_cdev.name = "my_led";
    my_led_cdev.brightness_set = my_led_set;
    my_led_cdev.max_brightness = 1;
    return led_classdev_register(&pdev->dev, &my_led_cdev);
}

九、调试与测试方法

  • 查看节点:ls /sys/class/leds/
  • 控制亮度:echo 1 > /sys/class/leds/my_led/brightness
  • 设置触发器:echo heartbeat > /sys/class/leds/my_led/trigger
  • 查看内核 log:dmesg | grep led
  • 使用 strace 跟踪系统调用

十、应用场景拓展

LED 子系统不仅限于状态指示,还可用于:

  • 指示系统状态(电源、网络、故障)
  • 工业控制设备状态反馈
  • 自定义报警/信号系统
  • 与输入设备联动提示

十一、未来演进方向

随着设备复杂性提升,LED 子系统也在逐步扩展,例如:

  • 多色 LED 支持(RGB)
  • PWM 调光集成
  • 与 IIO、HWMON 等子系统协同
  • 低功耗状态下保持状态

十二、结语

LED 子系统作为 Linux 设备模型中的一员,结构清晰、易于扩展、使用统一接口。在嵌入式开发中合理利用它,不仅能提升用户交互体验,也能加速开发效率。

通过本文的讲解,相信你已掌握 LED 子系统的核心结构、驱动实现及设备树适配方式,能自主编写定制化 LED 驱动,并灵活运用于实际项目中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值