sysfs LED 控制
概述
Linux 提供了 LED 子系统(LED Subsystem),驱动可以将 LED 注册到 sysfs:
/sys/class/leds/<led-name>/用户空间通过读写这些文件控制 LED,而不是直接操作 GPIO 寄存器。
整体关系:
用户程序
↓
sysfs
↓
LED 子系统
↓
LED 驱动
↓
GPIO / PWM
↓
LEDsysfs 中的文件是内核提供的虚拟接口,不是普通磁盘文件。
查看 LED
ls /sys/class/leds/例如:
sys-led进入目录:
cd /sys/class/leds/sys-led/
ls常见文件:
| 文件 | 作用 |
|---|---|
brightness | 设置或读取 LED 亮度 |
max_brightness | 最大亮度值 |
trigger | 设置 LED 自动触发模式 |
delay_on | timer 模式亮灯时间 |
delay_off | timer 模式灭灯时间 |
brightness
查看当前亮度:
cat brightness普通 GPIO LED 一般:
0 → 熄灭
1 → 点亮控制 LED:
echo 1 > brightness
echo 0 > brightness查看最大亮度:
cat max_brightnessGPIO LED 常见:
1支持多级亮度的 LED 可能为:
255此时可以:
echo 128 > brightnessbrightness 不是 GPIO 电平
brightness 表示的是 LED 的逻辑亮度,不是 GPIO 的物理电平。
从 LED 子系统的用户空间语义来看:
brightness = 0
↓
LED_OFF
↓
LED 熄灭非零值表示非零亮度:
brightness = 1 ~ max_brightness
↓
LED 具有对应亮度对于普通 GPIO LED:
max_brightness = 1
0 → 熄灭
1 → 点亮对于支持多级亮度的 LED:
max_brightness = 255
0 → 熄灭
1~254 → 不同亮度
255 → 最大亮度即使 LED 硬件是低电平点亮:
GPIO = 0 → LED 亮
GPIO = 1 → LED 灭用户空间仍然使用 LED 的逻辑亮度进行控制。
例如设备树中:
gpios = <&gpio1 3 GPIO_ACTIVE_LOW>;驱动会根据 GPIO_ACTIVE_LOW 自动处理逻辑状态和 GPIO 电平之间的转换。
例如:
brightness = 1
↓
LED 子系统:要求 LED 点亮
↓
驱动发现 GPIO_ACTIVE_LOW
↓
GPIO 输出低电平
↓
LED 点亮因此:
brightness表示 LED 的逻辑亮度,不应该直接把它理解成 GPIO 输出的高低电平。
trigger
查看当前支持的 trigger:
cat trigger例如:
none timer [heartbeat] default-on方括号表示当前模式:
[heartbeat]不同系统支持的 trigger 不完全相同,以实际输出为准。
手动控制
echo none > trigger然后:
echo 1 > brightness
echo 0 > brightness如果程序需要自己控制 LED,建议先关闭 trigger。
timer 闪烁
设置:
echo timer > trigger此时通常会出现:
delay_on
delay_off例如:
echo 500 > delay_on
echo 500 > delay_off效果:
亮 500 ms
灭 500 ms即约 1 Hz 周期闪烁。
heartbeat 心跳灯
echo heartbeat > triggerLED 会按照类似心跳的节奏闪烁,常用于表示系统正在运行。
常用操作
手动控制
LED=/sys/class/leds/sys-led
echo none > ${LED}/trigger
echo 1 > ${LED}/brightness
sleep 1
echo 0 > ${LED}/brightness定时闪烁
LED=/sys/class/leds/sys-led
echo timer > ${LED}/trigger
echo 500 > ${LED}/delay_on
echo 500 > ${LED}/delay_off心跳模式
echo heartbeat > /sys/class/leds/sys-led/triggerC 语言控制 LED
本质就是操作 sysfs 文件:
open → write → close示例:
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
/* 将 sysfs 路径提取为宏定义,方便针对不同硬件平台进行配置和移植 */
#define LED_TRIGGER_PATH "/sys/class/leds/rgb-led-r/trigger"
#define LED_BRIGHTNESS_PATH "/sys/class/leds/rgb-led-r/brightness"
/* 统一的底层文件写入辅助函数 */
static int write_file(const char *path, const char *val)
{
int fd = open(path, O_WRONLY);
if (fd < 0) {
fprintf(stderr, "Error opening %s: %s\n", path, strerror(errno));
return -1;
}
/* 写入时带上 '\0' 结束符,避免内核读取到脏数据 */
ssize_t ret = write(fd, val, strlen(val) + 1);
if (ret < 0) {
fprintf(stderr, "Error writing to %s: %s\n", path, strerror(errno));
close(fd);
return -1;
}
close(fd);
return 0;
}
/* 打印使用帮助信息 */
static void print_usage(const char *prog)
{
fprintf(stderr,
"Usage: %s [command] [args]\n\n"
"Commands:\n"
" on Turn on the LED manually\n"
" off Turn off the LED manually\n"
" trigger <type> Set LED trigger mode\n"
" Supported types: none, heartbeat, timer, mmc0\n",
prog
);
}
int main(int argc, char *argv[])
{
if (argc < 2) {
print_usage(argv[0]);
return EXIT_FAILURE;
}
if (strcmp(argv[1], "on") == 0) {
/* 先切回手动模式,再设置亮度,并增加错误判断 */
if (write_file(LED_TRIGGER_PATH, "none") < 0) return EXIT_FAILURE;
if (write_file(LED_BRIGHTNESS_PATH, "1") < 0) return EXIT_FAILURE;
} else if (strcmp(argv[1], "off") == 0) {
if (write_file(LED_TRIGGER_PATH, "none") < 0) return EXIT_FAILURE;
if (write_file(LED_BRIGHTNESS_PATH, "0") < 0) return EXIT_FAILURE;
} else if (strcmp(argv[1], "trigger") == 0) {
if (argc < 3) {
fprintf(stderr, "Error: 'trigger' command requires a type argument.\n");
print_usage(argv[0]);
return EXIT_FAILURE;
}
/* 设置触发类型(如 heartbeat, timer, mmc0 等) */
if (write_file(LED_TRIGGER_PATH, argv[2]) < 0) return EXIT_FAILURE;
} else {
fprintf(stderr, "Error: Unknown command '%s'\n", argv[1]);
print_usage(argv[0]);
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}权限问题
如果出现:
Permission denied通常是当前用户没有写权限。
下面这种写法可能仍然失败:
sudo echo 1 > brightness因为 > 重定向仍由当前 Shell 执行。
可以使用:
echo 1 | sudo tee brightness或者:
sudo sh -c 'echo 1 > brightness'嵌入式开发板如果直接以 root 登录,则通常不需要 sudo。
常见问题
为什么写 brightness 没反应?
先关闭 trigger:
echo none > trigger然后:
echo 1 > brightness如果仍然没有反应,需要继续检查:
设备树
GPIO 配置
GPIO 极性
pinctrl
LED 硬件为什么没有 delay_on?
需要先:
echo timer > trigger然后重新查看目录。
怎么知道系统支持哪些 trigger?
cat trigger实际输出什么,就代表当前内核支持什么。
LED 是低电平点亮,brightness 应该写 0 吗?
不是。
低电平有效描述的是:
GPIO 物理电平而 brightness 描述的是:
LED 逻辑亮度正常情况下:
brightness = 0 → LED 熄灭
brightness > 0 → LED 非零亮度GPIO 最终输出高电平还是低电平,由驱动处理。
注意:不是所有驱动都使用 sysfs
Linux 不同子系统的用户空间接口不同,例如:
LED → /sys/class/leds/
GPIO → /dev/gpiochipX
I²C → /dev/i2c-X
SPI → /dev/spidevX.Y
UART → /dev/ttyS*
Video → /dev/videoX因此不能简单理解为:
Linux 驱动 = 找 sysfs 文件而应该理解为:
不同 Linux 子系统
↓
提供不同用户空间 API
↓
用户程序操作设备小结
LED sysfs 最需要掌握三个文件:
brightness
max_brightness
trigger常用命令:
# 查看 LED
ls /sys/class/leds/
# 手动控制
echo none > trigger
# 点亮
echo 1 > brightness
# 熄灭
echo 0 > brightness
# 定时闪烁
echo timer > trigger
# 心跳模式
echo heartbeat > trigger核心关系:
用户空间
↓
sysfs
↓
LED 子系统
↓
LED 驱动
↓
硬件其中最容易混淆的一点是:
brightness ≠ GPIO 电平brightness 是 LED 的逻辑亮度,GPIO 的实际高低电平由驱动根据硬件极性进行转换。
