支持作者新书《Yocto项目实战教程:高效定制嵌入式Linux系统》,点击京东购买
GPIO 子系统全面解析:旧版 GPIO 编号 API 与新版 Descriptor API 对比与实战
一、GPIO 子系统的基本概念
1. 什么是 GPIO?
GPIO(General Purpose Input/Output)即“通用输入输出”,是一种可通过软件控制的引脚,可用于读取电平(输入)或设置电平(输出)。在嵌入式系统中,GPIO 是最常见的外设控制手段之一。
2. 为什么要有 GPIO 子系统?
Linux 为不同硬件平台提供统一的软件接口抽象,GPIO 子系统是 Linux 内核为控制 GPIO 而设计的一个驱动子系统,它使得上层驱动程序可以不关心底层硬件差异。
在驱动开发中,GPIO 通常用于:
- 控制 LED 状态
- 读取按键输入
- 控制设备的上电/复位
- EEPROM 写保护(如 wp-gpios)等。
二、传统 GPIO 编号 API 与 Descriptor API 的演进
1. 传统方式(GPIO 编号 API)
传统方式以 int gpio_num
表示某个 GPIO 引脚,通过编号进行操作。
int wp_gpio = of_get_named_gpio(np, "wp-gpios", 0);
devm_gpio_request_one(dev, wp_gpio, GPIOF_OUT_INIT_HIGH, "eeprom-wp");
缺点:
- 编号不具可移植性
- 不易支持动态命名和状态追踪
- 编号方式难以管理设备间的依赖
2. 现代方式(Descriptor API)
Descriptor API 使用 struct gpio_desc *
对 GPIO 引脚抽象,提供更清晰的语义和设备关系。
struct gpio_desc *wp_desc;
wp_desc = gpiod_get_optional(dev, "wp", GPIOD_OUT_HIGH);
优势:
- 与设备绑定,具备更强的设备树兼容能力
- 更安全,易于调试
- 自动管理资源(通过
devm_
系列函数)
三、两种 API 对比总结
对比维度 | 编号 API | Descriptor API |
---|---|---|
表示方式 | int 类型 GPIO 编号 | struct gpio_desc * 指针 |
与设备关系 | 松散 | 强绑定:绑定 struct device |
面向设备树 | 兼容性差,需要手动解析 | 完美支持 gpiod_get_optional() |
自动释放资源 | 需要手动释放 | 使用 devm_ 系列自动释放 |
API 示例 | gpio_request() / of_get_named_gpio() | gpiod_get() / gpiod_set_value() |
四、实战:配置 EEPROM 的写保护 GPIO(以 at24.c 为例)
设备树配置
&i2c3 {
eeprom@50 {
compatible = "atmel,24c02";
reg = <0x50>;
wp-gpios = <&gpio4 17 GPIO_ACTIVE_LOW>; // 写保护引脚
};
};
驱动代码(基于 Descriptor API)
struct gpio_desc *wp_desc;
wp_desc = gpiod_get_optional(dev, "wp", GPIOD_OUT_HIGH);
if (!IS_ERR_OR_NULL(wp_desc)) {
dev_info(dev, "EEPROM WP GPIO 配置成功\n");
gpiod_set_value(wp_desc, 0); // 解除写保护
} else {
dev_warn(dev, "未定义 WP 引脚\n");
}
五、关键概念讲解
1. gpiod_get_optional()
是做什么的?
- 从设备树中解析
<gpio-name>-gpios
属性(如 “wp-gpios”) - 返回一个 GPIO 描述符(
struct gpio_desc *
) - 如果未定义此属性,不报错,返回 NULL
适合对 GPIO 可选的场景(如 EEPROM 写保护引脚)
2. devm_
系列函数的作用?
- 自动释放资源,无需手动调用
gpio_free()
- 与设备生命周期绑定
比如:
devm_gpio_request_one(dev, gpio_num, GPIOF_OUT_INIT_HIGH, "eeprom-wp");
3. gpio_desc
是什么?
- 是 GPIO 的内核抽象封装结构,代替传统编号
- 包含 GPIO 控制器信息、状态、电平、方向等
六、常见面试问答示例
❓ 问:你怎么理解新版 GPIO Descriptor API 的设计?
✅ 答:新版 Descriptor API 引入 gpio_desc *
结构体,代替原来的数字编号,更贴近设备的抽象模型。它与设备树结合更紧密,能更清晰地表示设备和 GPIO 的绑定关系,资源管理更安全,调试更方便。
❓ 问:什么时候使用 gpiod_get_optional()?
✅ 答:当某个 GPIO 并非设备工作所必需,比如 EEPROM 写保护引脚、可选 LED 控制引脚,可以使用 gpiod_get_optional()
,避免在设备树未提供该属性时报错。
❓ 问:旧版
gpio_request()
和新版gpiod_get()
有什么根本区别?
✅ 答:旧版直接基于编号管理 GPIO,无设备绑定,需手动释放;新版使用描述符结构绑定设备,支持资源自动管理,更加符合现代设备驱动架构。
七、练习建议与参考资源
推荐练习
- 在已有驱动中查找
gpio_request()
,尝试用 Descriptor API 替换 - 编写一个简单平台设备,添加可选 GPIO 控制(如 LED、MUTE)
- 阅读
drivers/gpio/
目录下的核心 GPIO 控制器实现
推荐阅读
- Linux 内核源码文档:Documentation/gpio/
- LWN 文章《The new GPIO descriptor interface》
- EEPROM 示例驱动:
drivers/misc/eeprom/at24.c
📘。配套视频请见 B 站 “嵌入式 Jerry”。