文章目录
Linux 应用层 GPIO 编程指南
一、GPIO 基础概念与架构
1. 什么是 GPIO?
GPIO(General Purpose Input/Output)是通用输入输出端口,可以通过软件控制实现输入输出功能。
2. Linux GPIO 架构
应用层 API
↓
sysfs GPIO 接口
↓
GPIO 子系统
↓
GPIO 驱动
↓
GPIO 硬件
二、GPIO 访问方式
1. sysfs 接口
Linux 提供了 sysfs 文件系统接口,路径为 /sys/class/gpio/:
/sys/class/gpio/
├── export # 导出GPIO
├── unexport # 取消导出
└── gpioN/ # GPIO目录
├── direction # 方向控制
├── value # 值读写
└── edge # 中断触发
2. 字符设备接口
新版本 Linux 内核推荐使用字符设备接口 /dev/gpiochipN。
三、GPIO 编程实战
1. 基础 GPIO 操作
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
// GPIO 导出函数
int gpio_export(unsigned int gpio) {
int fd, len;
char buf[64];
fd = open("/sys/class/gpio/export", O_WRONLY);
if (fd < 0) {
perror("gpio/export");
return fd;
}
len = snprintf(buf, sizeof(buf), "%d", gpio);
write(fd, buf, len);
close(fd);
return 0;
}
// GPIO 方向设置
int gpio_set_dir(unsigned int gpio, const char* dir) {
int fd;
char buf[64];
snprintf(buf, sizeof(buf), "/sys/class/gpio/gpio%d/direction", gpio);
fd = open(buf, O_WRONLY);
if (fd < 0) {
perror("gpio/direction");
return fd;
}
write(fd, dir, strlen(dir));
close(fd);
return 0;
}
// GPIO 值读取
int gpio_get_value(unsigned int gpio) {
int fd;
char buf[64], ch;
snprintf(buf, sizeof(buf), "/sys/class/gpio/gpio%d/value", gpio);
fd = open(buf, O_RDONLY);
if (fd < 0) {
perror("gpio/value");
return fd;
}
read(fd, &ch, 1);
close(fd);
return ch == '0' ? 0 : 1;
}
// GPIO 值设置
int gpio_set_value(unsigned int gpio, int value) {
int fd;
char buf[64];
snprintf(buf, sizeof(buf), "/sys/class/gpio/gpio%d/value", gpio);
fd = open(buf, O_WRONLY);
if (fd < 0) {
perror("gpio/value");
return fd;
}
write(fd, value ? "1" : "0", 1);
close(fd);
return 0;
}
2. 中断处理示例
#include <poll.h>
int gpio_set_edge(unsigned int gpio, const char* edge) {
int fd;
char buf[64];
snprintf(buf, sizeof(buf), "/sys/class/gpio/gpio%d/edge", gpio);
fd = open(buf, O_WRONLY);
if (fd < 0) {
perror("gpio/edge");
return fd;
}
write(fd, edge, strlen(edge));
close(fd);
return 0;
}
// 等待 GPIO 中断
int gpio_wait_for_interrupt(unsigned int gpio, int timeout_ms) {
int fd, ret;
char buf[64];
struct pollfd pfd;
char ch;
snprintf(buf, sizeof(buf), "/sys/class/gpio/gpio%d/value", gpio);
fd = open(buf, O_RDONLY);
if (fd < 0) {
perror("gpio/value");
return fd;
}
// 清除初始状态
read(fd, &ch, 1);
pfd.fd = fd;
pfd.events = POLLPRI | POLLERR;
pfd.revents = 0;
ret = poll(&pfd, 1, timeout_ms);
close(fd);
return ret;
}
四、实际应用示例
1. LED 控制示例
int main() {
const int LED_GPIO = 18; // 使用GPIO18
// 导出GPIO
gpio_export(LED_GPIO);
// 设置为输出模式
gpio_set_dir(LED_GPIO, "out");
// LED闪烁5次
for (int i = 0; i < 5; i++) {
gpio_set_value(LED_GPIO, 1); // LED开
sleep(1);
gpio_set_value(LED_GPIO, 0); // LED关
sleep(1);
}
return 0;
}
2. 按键检测示例
int main() {
const int BUTTON_GPIO = 17; // 使用GPIO17
// 导出GPIO
gpio_export(BUTTON_GPIO);
// 设置为输入模式
gpio_set_dir(BUTTON_GPIO, "in");
// 设置边缘触发
gpio_set_edge(BUTTON_GPIO, "rising");
printf("等待按键按下...\n");
while (1) {
if (gpio_wait_for_interrupt(BUTTON_GPIO, -1) > 0) {
printf("检测到按键按下!\n");
}
}
return 0;
}
五、注意事项
-
权限问题
- GPIO 操作通常需要 root 权限
- 可以通过 udev 规则配置访问权限
-
性能考虑
- sysfs 接口性能较低
- 对于高速操作,建议使用字符设备接口
-
资源管理
- 及时关闭文件描述符
- 程序退出时取消导出 GPIO
-
错误处理
- 检查所有系统调用的返回值
- 实现适当的错误恢复机制
六、调试技巧
- LED控制示例
# 1. 导出GPIO
echo 18 > /sys/class/gpio/export
# 2. 设置为输出模式
echo out > /sys/class/gpio/gpio18/direction
# 3. 控制LED
echo 1 > /sys/class/gpio/gpio18/value # LED亮
echo 0 > /sys/class/gpio/gpio18/value # LED灭
# 4. 使用完后取消导出
echo 18 > /sys/class/gpio/unexport
- 按键输入示例
# 1. 导出GPIO
echo 17 > /sys/class/gpio/export
# 2. 设置为输入模式
echo in > /sys/class/gpio/gpio17/direction
# 3. 设置中断触发方式
echo rising > /sys/class/gpio/gpio17/edge
# 4. 读取按键状态
cat /sys/class/gpio/gpio17/value
# 5. 使用完后取消导出
echo 17 > /sys/class/gpio/unexport
- 使用工具
- multimeter(万用表)测量电平
- oscilloscope(示波器)观察波形
- logic analyzer(逻辑分析仪)分析时序
185

被折叠的 条评论
为什么被折叠?



