Linux系统编程-通过sys子系统控制gpio
重要提示:sysfs GPIO 接口自 Linux 4.8 起已被标记为已弃用,并在 Linux 5.3 之后开始逐步移除。
基本原理
sysfs 是一个虚拟文件系统,它将内核中的设备、驱动等信息映射到用户空间,以文件和目录的形式呈现。
对于 GPIO,其控制接口位于 /sys/class/gpio/
。
将 gpio 写入 /sys/class/gpio/export
,就可将此 gpio 从内核空间导出到用户空间。例如:
1 | echo 12 > /sys/class/gpio/export |
导出后,在 /sys/class/gpio/
目录下就会生成一个 gpio12
目录。其目录结构如下:
gpio12
├── active_low
├── device -> ../../../gpiochip0
├── direction
├── edge
├── power
│ ├── autosuspend_delay_ms
│ ├── control
│ ├── runtime_active_time
│ ├── runtime_status
│ └── runtime_suspended_time
├── subsystem -> ../../../../../../../class/gpio
├── uevent
└── value
GPIO Sysfs 接口文件功能详解:
value (最重要的文件)
功能:读取或设置GPIO引脚的电平状态。
读取 (
cat value
):
- 返回引脚的当前电平状态。
- 0: 代表低电平。
- 1: 代表高电平。
写入 (
echo 1 > value
或echo 0 > value
):
- 当引脚被设置为输出模式时,向此文件写入可以控制引脚输出高电平或低电平。
- 注意:在写入之前,必须先将
direction
设置为out
。direction
功能:设置GPIO引脚的方向(输入或输出)。
读取 (
cat direction
):
- 返回引脚当前的方向,通常是
in
或out
。写入:
echo in > direction
: 将引脚设置为输入模式。echo out > direction
: 将引脚设置为输出模式。- 注意:出于安全考虑,将方向从
in
改为out
时,引脚的初始输出电平是未定义的。有些系统允许使用echo low > direction
或echo high > direction
来同时设置方向为输出并指定初始输出电平(low
为低,high
为高)。edge
功能:设置引脚的中断触发模式。这个文件仅当引脚设置为输入模式时有效。
读取 (
cat edge
): 返回当前设置的中断触发模式。写入:
echo none > edge
: 默认值。禁止中断触发。表示忽略该引脚上的边沿变化。echo rising > edge
: 设置为上升沿触发。当引脚电平从低变高时,会产生中断事件。echo falling > edge
: 设置为下降沿触发。当引脚电平从高变低时,会产生中断事件。echo both > edge
: 设置为双边沿触发。只要引脚电平发生变化(无论是上升还是下降),都会产生中断事件。active_low
功能:反转引脚的逻辑电平。这是一个非常有用的功能,用于处理低电平有效的设备(例如,低电平触发的LED或按键)。
读取 (
cat active_low
):
- 0: 默认值。正常逻辑。
value
文件中的0
代表低电平,1
代表高电平。- 1: 反转逻辑。
value
文件中的0
现在代表高电平,1
代表低电平。写入:
echo 0 > active_low
: 使用正常逻辑。echo 1 > active_low
: 使用反转逻辑。device
功能:一个符号链接(Symbolic Link),指向这个GPIO引脚所属于的硬件设备(通常是GPIO控制器芯片)在
/sys/devices/
目录下的路径。作用: 主要用于在sysfs文件系统中维护设备之间的层次关系,对于普通用户控制GPIO来说,一般不需要关心这个文件。
subsystem
功能:一个符号链接,指向包含它的子系统目录,即
/sys/class/gpio
。作用: 同样用于维护sysfs的内部结构,表明这个
gpio12
目录属于gpio
子系统。用户通常无需操作。uevent
功能:内核事件接口。主要用于通知设备管理器(如udev)关于设备的热插拔事件。
作用: 当你向这个文件写入
add
,它会强制内核发送一个“新增设备”的事件,通知用户空间的设备管理工具。对于GPIO,这个文件通常由系统自动管理,用户一般不需要手动操作。power
功能:电源管理相关目录。它包含一些与设备电源状态管理相关的接口文件,如
async
,runtime_status
,control
等。作用: 用于高级的电源管理功能,例如挂起或恢复设备以节省功耗。对于简单的GPIO操作,完全可以忽略这个目录及其内容。
控制步骤
要控制一个GPIO,通常遵循以下步骤:
- 导出引脚:
echo 12 > /sys/class/gpio/export
- 设置方向:
- 输出:
echo out > /sys/class/gpio/gpio12/direction
- 输入:
echo in > /sys/class/gpio/gpio12/direction
- 操作引脚:
- 如果是输出:
- 设置高电平:
echo 1 > /sys/class/gpio/gpio12/value
- 设置低电平:
echo 0 > /sys/class/gpio/gpio12/value
- 如果是输入:
- 读取电平:
cat /sys/class/gpio/gpio12/value
- (可选)如果需要中断通知,设置中断模式:
echo rising > /sys/class/gpio/gpio12/edge
- (完成后)取消导出:
echo 12 > /sys/class/gpio/unexport
(这会移除gpio12
目录)
GPIO 输出
操作步骤
导出 GPIO
将 GPIO 控制从内核空间导出到用户空间
1 | echo 12 > /sys/class/gpio/export |
设置成输出模式
1 | echo out > /sys/class/gpio/gpio12/direction |
设置高低电平
1 | echo 1 > /sys/class/gpio/gpio12/value |
shell 编程示例
1 |
|
C 系统编程示例
1 |
|
GPIO 输入
操作步骤
导出 GPIO
将 GPIO 控制从内核空间导出到用户空间
1 | echo 12 > /sys/class/gpio/export |
设置成输入模式
1 | echo in > /sys/class/gpio/gpio12/direction |
读取电平
1 | cat /sys/class/gpio/gpio12/value |
C 系统编程示例
1 |
|
GPIO 中断
实现步骤
导出 GPIO
首先需要导出要使用的 GPIO 引脚:
1 | # 以 GPIO 13 为例 |
设置方向为输入
1 | echo "in" > /sys/class/gpio/gpio13/direction |
设置边沿触发模式
这是关键步骤,设置中断触发方式:
1 | # 设置为下降沿触发(从高到低电平变化) |
使用 poll 监测中断
poll 函数
函数原型和头文件
1 |
|
参数详解
struct pollfd *fds
指向 pollfd
结构体数组的指针,每个结构体描述一个要监视的文件描述符。
1 | struct pollfd { |
nfds_t nfds
fds
数组中的元素数量。
int timeout
等待的超时时间(毫秒):
-1
:无限期阻塞,直到有事件发生0
:立即返回,不阻塞(轮询)> 0
:等待指定的毫秒数
返回值
- > 0:返回就绪的文件描述符数量
- = 0:超时,没有文件描述符就绪
- -1:发生错误,并设置
errno
events 和 revents 标志
常用事件标志:
标志 | 描述 | 用途 |
---|---|---|
POLLIN |
有数据可读 | 普通文件、socket、管道 |
POLLPRI |
有紧急数据可读 | 带外数据、GPIO中断 |
POLLOUT |
可写,不会阻塞 | 普通文件、socket |
POLLERR |
发生错误 | 自动设置,无需请求 |
POLLHUP |
挂起 | 对端关闭连接 |
POLLNVAL |
文件描述符未打开 | 自动设置 |
C 编程
1 |
|