GPIO是通用输入/输出的缩写,是嵌入式Linux系统中最常用的外设之一。
在内部,Linux 内核对 GPIO 的访问方法可以参考如下内容
GPIO Driver Interface — The Linux Kernel documentation
GPIO Descriptor Consumer Interface — The Linux Kernel documentation
为了管理 GPIO 注册和分配,Linux 内核中有一个名为?gpiolib?的框架。此框架为在内核空间和用户空间应用程序中运行的设备驱动程序提供 API。
在 Linux 版本 4.7 之前,在用户空间中管理 GPIO 行的接口始终通过导出在 /sys/class/gpio?中的文件在 sysfs 中。例如,如果我想设置GPIO,我必须:
举个实际的例子,要从用户空间设置 GPIO 504,我们必须执行以下命令:
# echo 504 > /sys/class/gpio/export
# echo out > /sys/class/gpio/gpio504/direction
# echo 1 > /sys/class/gpio/gpio504/value
这个界面非常简单,运行良好,但有一些不足:
从 Linux 版本 4.8 开始,GPIO sysfs 接口被弃用,如果想要在4.8版本以后仍然使用旧的版本,可以在Linux config中将如下开关配置为Y
?CONFIG_GPIO_SYSFS =Y
新的方法基于字符设备的新 API,可以从用户空间访问 GPIO 。
每个 GPIO 控制器 (gpiochip) 在?/dev?中都有一个字符设备,我们可以使用文件操作 (open(), read(), write(),?ioctl(), poll(),?close())?来管理 GPIO 行并与之交互:
# ls /dev/gpiochip*
/dev/gpiochip0 /dev/gpiochip2 /dev/gpiochip4 /dev/gpiochip6
/dev/gpiochip1 /dev/gpiochip3 /dev/gpiochip5 /dev/gpiochip7
尽管这个新的 char 设备接口可以防止使用?echo?和?cat?等标准命令行工具操作 GPIO,但与 sysfs 接口相比,它具有一些优势,包括:
示例C代码:具体参照??libgpiod?项目
#include <stdio.h>
#include <gpiod.h>
int main() {
// 打开 GPIO 控制器
struct gpiod_chip *chip;
chip = gpiod_chip_open("/dev/gpiochip0");
// 设置要控制的 GPIO 线路编号
int gpioLine = 17;
// 获取指定线路的 GPIO 行对象
struct gpiod_line *line;
line = gpiod_chip_get_line(chip, gpioLine);
// 设置 GPIO 行为输出模式
int ret = gpiod_line_request_output(line, "example", 0);
if (ret < 0) {
printf("无法设置 GPIO 行为输出模式\n");
return 1;
}
// 将 GPIO 置高
gpiod_line_set_value(line, 1);
// 延时一段时间
usleep(1000000);
// 将 GPIO 置低
gpiod_line_set_value(line, 0);
// 释放 GPIO 行对象
gpiod_line_release(line);
// 关闭 GPIO 控制器
gpiod_chip_close(chip);
return 0;
}
为了使用这个新的 char 设备接口,libgpiod?项目提供了一个库和一组工具。
例如,以下 C 语言程序使用?libgpiod?读取 GPIO 行:
int main() {
struct gpiod_chip *chip;
struct gpiod_line *line;
int req, value;
chip = gpiod_chip_open("/dev/gpiochip0");
if (!chip)
return -1;
line = gpiod_chip_get_line(chip, 3);
if (!line) {
gpiod_chip_close(chip);
return -1;
}
req = gpiod_line_request_input(line, "gpio_state");
if (req) {
gpiod_chip_close(chip);
return -1;
}
value = gpiod_line_get_value(line);
printf("GPIO value is: %d\n", value);
gpiod_chip_close(chip);
}
该库是 C 语言,但有其他语言(如?C++?和?Python)的绑定。
如果要从终端管理 GPIO 行并与之交互,可以使用 libgpiod 提供的命令行工具。
gpiodetect?命令将列出所有 gpiochip、labels和 GPIO? lines:
# gpiodetect
gpiochip0 [209c000.gpio] (32 lines)
gpiochip1 [20a0000.gpio] (32 lines)
gpiochip2 [20a4000.gpio] (32 lines)
gpiochip3 [20a8000.gpio] (32 lines)
gpiochip4 [20ac000.gpio] (32 lines)
gpiochip5 [20b0000.gpio] (32 lines)
gpiochip6 [20b4000.gpio] (32 lines)
gpiochip7 [mcp23s08.0] (8 lines)
gpioinfo?命令
# gpioinfo 0
gpiochip0 - 32 lines:
line 0: unnamed unused input active-high
line 1: unnamed unused input active-high
line 2: unnamed unused input active-high
line 3: unnamed "scl" output active-high [used open-drain]
line 4: unnamed unused input active-high
line 5: unnamed unused input active-high
line 6: unnamed "sda" output active-high [used open-drain]
line 7: unnamed "Push Button" input active-low [used]
line 8: unnamed unused input active-high
line 9: unnamed "?" output active-high [used]
line 10: unnamed unused input active-high
line 11: unnamed unused input active-high
[...]
?gpioset?命令? 将gpiochip7组的第0个引脚电平设置为1
# gpioset gpiochip7 0=1
gpioget?命令? 读取gpiochip7组的第0个引脚电平,取得值为1
# gpioget gpiochip7 0
1
gpiomon?命令 可以检测端子变化状态
# gpiomon 0 7
event: FALLING EDGE offset: 7 timestamp: [ 4564.943435210]
event: RISING EDGE offset: 7 timestamp: [ 4565.072156210]
event: FALLING EDGE offset: 7 timestamp: [ 4566.113641877]
event: RISING EDGE offset: 7 timestamp: [ 4566.366691877]
所有这些命令的源代码都可以在?libgpiod 存储库中找到。?