Linux GPIO子系统是内核提供的一套标准化接口,用于统一管理GPIO(通用输入输出)引脚。相比直接操作物理内存(如/dev/mem
),它更安全、可移植且支持高级功能(如中断、去抖动)。以下是详细说明:
一、核心概念
- GPIO控制器驱动
- 内核通过GPIO控制器驱动管理硬件,如
pinctrl
子系统负责引脚复用和配置。 - 控制器驱动需实现
gpiolib
接口,暴露GPIO编号和操作函数。
- 内核通过GPIO控制器驱动管理硬件,如
- GPIO编号
- 逻辑编号(如
GPIO17
)由控制器驱动分配,可能与物理引脚号不同。 - 使用
gpiochip
前缀表示控制器(如gpiochip0
)。
- 逻辑编号(如
二、用户空间操作
1. 通过Sysfs接口(传统方法)
# 导出GPIO到用户空间
echo 17 > /sys/class/gpio/export
# 设置方向(in/out)
echo out > /sys/class/gpio/gpio17/direction
# 读写值(0/1)
echo 1 > /sys/class/gpio/gpio17/value
cat /sys/class/gpio/gpio17/value
# 取消导出
echo 17 > /sys/class/gpio/unexport
2. 使用libgpiod(推荐)
- 安装库:
sudo apt-get install libgpiod-dev
- C语言示例:
#include <gpiod.h> int main() { struct gpiod_chip *chip = gpiod_chip_open_by_name("gpiochip0"); struct gpiod_line *line = gpiod_chip_get_line(chip, 17); gpiod_line_request_output(line, "my_gpio", 0); // 初始输出0 // 设置为高电平 gpiod_line_set_value(line, 1); gpiod_line_release(line); gpiod_chip_close(chip); return 0; }
三、内核空间操作
1. 内核模块示例
#include <linux/gpio.h>
#include <linux/module.h>
static int gpio_num = 17;
module_param(gpio_num, int, 0644);
static int __init my_gpio_init(void) {
int ret;
// 申请GPIO
ret = gpio_request(gpio_num, "my_gpio");
if (ret) {
printk(KERN_ERR "Failed to request GPIO%d\n", gpio_num);
return ret;
}
// 设置为输出模式
ret = gpio_direction_output(gpio_num, 0);
if (ret) {
printk(KERN_ERR "Failed to set direction\n");
gpio_free(gpio_num);
return ret;
}
// 输出高电平
gpio_set_value(gpio_num, 1);
return 0;
}
static void __exit my_gpio_exit(void) {
gpio_set_value(gpio_num, 0); // 设置为低电平
gpio_free(gpio_num); // 释放GPIO
}
module_init(my_gpio_init);
module_exit(my_gpio_exit);
MODULE_LICENSE("GPL");
2. 中断处理
static irqreturn_t gpio_isr(int irq, void *dev_id) {
printk(KERN_INFO "GPIO%d interrupt triggered\n", gpio_num);
return IRQ_HANDLED;
}
// 在初始化函数中:
ret = gpio_to_irq(gpio_num); // 获取中断号
ret = request_irq(ret, gpio_isr, IRQF_TRIGGER_RISING, "my_gpio_irq", NULL);
四、关键API
函数/宏 | 作用 |
---|---|
gpio_request() | 申请GPIO |
gpio_free() | 释放GPIO |
gpio_direction_input() | 设置为输入模式 |
gpio_direction_output() | 设置为输出模式 |
gpio_get_value() | 读取输入值 |
gpio_set_value() | 写入输出值 |
gpio_to_irq() | 获取GPIO对应的中断号 |
五、优势与注意事项
- 优势
- 安全性:避免直接操作物理地址导致的系统崩溃。
- 可移植性:逻辑GPIO编号由控制器驱动抽象,代码无需修改即可适配不同硬件。
- 高级功能:支持中断、去抖动、驱动强度配置等。
- 注意事项
- 资源释放:必须调用
gpio_free()
释放GPIO,避免泄漏。 - 并发访问:在中断上下文中避免使用可能睡眠的函数(如
printk
)。 - 权限管理:用户空间操作需
root
权限或配置udev
规则。
- 资源释放:必须调用
六、调试工具
gpiodetect
:列出所有GPIO控制器。gpioinfo
:查看GPIO详细信息(方向、值、中断等)。gpioset
:快速设置GPIO值(需libgpiod
工具)。
通过Linux GPIO子系统,开发者可以更高效、安全地操作GPIO,同时利用内核的抽象层实现跨平台兼容性。对于新项目,建议优先使用libgpiod
库进行用户空间开发,内核模块则使用gpiolib
API。