最近在学习 Luckfox Pico Mini 板子的设备树和 LED 驱动开发,记录一下整个探索和实践过程,希望对初学者有参考价值。


1. 理解设备树(DTS)和 LED 驱动

在 Linux 内核中,设备树(Device Tree, DTS)用来描述硬件信息,让内核在启动时能够正确绑定驱动。例如,我们要控制板载 LED,就需要在 DTS 中声明它。

Luckfox Pico Mini 的默认 DTS 文件中包含了 SPI、I2C、UART、PWM 等节点,但没有 LED 节点。


2. LED 节点配置

我们查询到板子上 LED 连接的是 GPIO1_A2,初步配置如下:

/ {
    leds {
        compatible = "gpio-leds";

        user_led {
            label = "user-led";
            gpios = <&gpio1 2 GPIO_ACTIVE_HIGH>;
            default-state = "off";
        };
    };
};
  • compatible = "gpio-leds":内核自带 GPIO LED 驱动
  • gpios = <&gpio1 2 GPIO_ACTIVE_HIGH>:使用 GPIO1 组第 2 号引脚,高电平点亮
  • default-state = "off":开机默认灭
注意,GPIO_ACTIVE_HIGHGPIO_ACTIVE_LOW 会影响开关逻辑。如果 LED 实际是高电平点亮,就应该用 GPIO_ACTIVE_HIGH

注释示例

// user led is connected to GPIO1_A2
// led on when gpio1_a2 output high
user_led {
    label = "user-led";
    gpios = <&gpio1 2 GPIO_ACTIVE_HIGH>;
    default-state = "off";
};

3. DTS 修改生效流程

  • 修改 DTS 后,必须重新编译内核并打包 boot.img
  • 对于 LED 这种内核驱动,只需 重编 boot.img,无需重新烧写 uboot 或 rootfs
  • 编译流程(简化):
./build.sh kernel      # 编译内核生成 zImage + dtb
./build.sh driver      # 编译内核驱动
./build.sh firmware    # 打包 boot.img
# 烧写 boot.img 到板子 boot 分区
dd if=output/images/boot.img of=/dev/mtdX bs=512K
updateimg 是生成整套系统升级包的命令,不必用于单独修改 LED。

4. LED 驱动与 /sys/class/leds 接口

内核将 LED 注册为 /sys/class/leds/user-led

ls /sys/class/leds/
cat /sys/class/leds/user-led/brightness
cat /sys/class/leds/user-led/max-brightness
  • brightness:当前亮度/状态,写 0/1 控制 GPIO LED
  • max-brightness:最大亮度,只读,GPIO LED 通常为 1
  • 对普通 GPIO LED,内核 gpio-leds 驱动只是通过 gpio_set_value() 高低电平开关 LED,并没有 PWM 调光功能

GPIO vs PWM LED

类型驱动brightness 行为
GPIO LEDgpio-leds写 0/1 → 高低电平开关
PWM LEDpwm-leds写 0~max → 内核 PWM 占空比输出

Luckfox Pico Mini 的 GPIO1_A2 也是 PWM0_M0 复用引脚,但我们当前只用作普通 GPIO LED。


5. 用户空间控制脚本

直接操作 /sys/class/leds/.../brightness 确实有点麻烦,于是写了一个简单的 Bash 脚本 ledctl.sh

#!/bin/bash
LED_PATH="/sys/class/leds/user-led/brightness"

case "$1" in
    on)
        echo 1 > $LED_PATH
        ;;
    off)
        echo 0 > $LED_PATH
        ;;
    blink)
        for i in $(seq 1 $2); do
            echo 1 > $LED_PATH
            sleep 0.5
            echo 0 > $LED_PATH
            sleep 0.5
        done
        ;;
    *)
        echo "Usage: $0 on|off|blink N"
        ;;
esac

使用示例:

./ledctl.sh on      # 点亮 LED
./ledctl.sh off     # 熄灭 LED
./ledctl.sh blink 5 # 闪烁 5 次

6. 集成脚本到镜像

为了每次开机都能使用该脚本,需要把它集成到 rootfs:

  1. 在 Buildroot overlay 中放置脚本,例如:
board/luckfox-pico-mini/rootfs-overlay/usr/bin/ledctl.sh
chmod +x board/luckfox-pico-mini/rootfs-overlay/usr/bin/ledctl.sh
  1. 如果希望开机自动执行,可用 rc.local 或 systemd service:

rc.local 示例

#!/bin/sh
/usr/bin/ledctl.sh off
exit 0

systemd service 示例

[Unit]
Description=User LED Control

[Service]
Type=oneshot
ExecStart=/usr/bin/ledctl.sh off

[Install]
WantedBy=multi-user.target
  1. 编译 rootfs 后,开机即可生效。

7. 总结与学习经验

  • 设备树 DTS 是硬件描述和驱动绑定的入口
  • gpio-leds 驱动提供了统一接口 /sys/class/leds/.../brightness
  • 普通 GPIO LED 只能 0/1 开关,高级调光需要 PWM LED 驱动
  • 用户空间可以通过脚本操作 /sys/class/leds/.../brightness 控制 LED
  • Buildroot overlay + init 脚本可将自定义工具集成到镜像,实现开机自动控制

通过这次实践,我理解了:

  1. DTS 的修改方式与位置选择
  2. LED 驱动的类型差异和 /sys/class/leds 接口意义
  3. 如何在不重建整套镜像的情况下修改 boot.img 生效
  4. 用户空间控制 LED 的方法及集成方案

下次可以尝试将 GPIO1_A2 改为 PWM LED,实现呼吸灯和亮度渐变。

系列文章

Last modification:October 27, 2025
If you think my article is useful to you, please feel free to appreciate