Linux 5.5+ 默认禁用 sysfs GPIO 接口,须改用 libgpiod:通过 gpiod_chip_open_by_name() 打开芯片,gpiod_chip_get_line() 获取引脚,gpiod_line_request_output() 请求后方可 set/get 值,并注意权限、抢占及事件监听限制。

为什么不能继续用 sysfs 接口操作 GPIO
Linux 5.5+ 内核已默认禁用 sysfs GPIO 接口(/sys/class/gpio/),很多嵌入式发行版(如 Yocto Kirkstone、Buildroot 2023.02+)在构建时直接剔除了 CONFIG_GPIO_SYSFS。你执行 echo 123 > /sys/class/gpio/export 会报错 bash: echo: write error: No such device,不是路径错了,是内核根本没开这个功能。
libgpiod 是官方推荐的现代替代方案,它通过 /dev/gpiochipN 字符设备与内核 gpiod 子系统通信,不依赖用户空间导出机制,权限控制更细、线程安全、支持事件监听 —— 但代价是 API 更底层,不能像以前那样“写个 echo 就完事”。
怎么用 libgpiod C++ 绑定正确打开并控制一个引脚
libgpiod 官方只提供 C API,C++ 项目需直接调用 C 函数(无官方 C++ 封装)。别找 gpiod::chip 这类不存在的类,那是第三方非主流 wrapper,稳定性差、版本适配混乱。
关键步骤:
立即学习“C++免费学习笔记(深入)”;
- 确认硬件编号:用
gpiodetect查芯片,gpioinfo查行号(line number),注意这不是物理引脚号,也不是旧 sysfs 的编号 - 用
gpiod_chip_open_by_name("gpiochip0")打开芯片设备(不要硬编码路径,/dev/gpiochip0可能不存在,名称更可靠) - 用
gpiod_chip_get_line(chip, line_offset)获取 line 对象,offset 来自gpioinfo输出的 “line 27” 这类数字 - 配置前必须先请求:调用
gpiod_line_request_output(line, "myapp", GPIOD_LINE_ACTIVE_STATE_HIGH),否则gpiod_line_set_value()直接失败返回 -1 - 设置值用
gpiod_line_set_value(line, 1),读值用gpiod_line_get_value(line),返回 0 或 1(不是布尔)
示例片段(省略错误检查):
struct gpiod_chip *chip = gpiod_chip_open_by_name("gpiochip0");
struct gpiod_line *line = gpiod_chip_get_line(chip, 27);
gpiod_line_request_output(line, "ledctl", GPIOD_LINE_ACTIVE_STATE_HIGH);
gpiod_line_set_value(line, 1); // 拉高
gpiod_line_release(line);
gpiod_chip_close(chip);
怎么避免 “Permission denied” 和 “Device or resource busy”
常见错误不是代码写错,而是权限或抢占问题:
-
Permission denied:libgpiod 默认要求gpio组权限,把运行用户加进该组:usermod -aG gpio myuser,然后重新登录;或者改 udev 规则(不推荐嵌入式环境滥用) -
Device or resource busy:同一行被内核驱动(如 LED 子系统、按钮驱动)或另一个进程占用了。用gpioinfo -s看 line 状态,带 “kernel” 标签说明被内核占用,需关掉对应驱动(如禁用leds-gpio或修改设备树删除相关节点) - 多个进程同时操作同一 chip:libgpiod 不阻止并发请求,但
request_output会失败,应用层需处理重试或加锁 - 忘记释放:每次
gpiod_line_request_*后必须配对gpiod_line_release(),否则下次请求失败且无法释放资源
事件监听比轮询更省资源,但要注意阻塞行为
嵌入式场景常需响应按键中断,用 gpiod_line_request_both_edges_events() 注册双边沿,再用 gpiod_line_event_read_fd() 配合 poll() 或 epoll() 监听,比死循环 get_value() 节省 CPU。
但注意:
- 事件队列有长度限制(通常 16),突发高频边沿可能丢事件,业务逻辑处理慢时要清空队列
-
gpiod_line_event_read()是阻塞调用,务必设好超时或搭配非阻塞 fd 使用 - 边沿类型由硬件支持决定:某些 SoC 的 GPIO 只支持单边沿中断,强行设
BOTH_EDGES会静默降级为上升沿,需查芯片手册确认 - 事件结构体
struct gpiod_line_event中的timestamp_ns是纳秒级,但嵌入式平台可能未校准,别拿它做精确定时
实际部署时,优先在设备树中把要用作中断的 GPIO 设为 interrupts = <...></...> 并绑定到你的驱动,仅当必须用户空间响应时才走 libgpiod 事件路径。


















