☰
pinctrl 子系统、GPIO 子系统、非阻塞 IO
2026/10/14 5:07:16 网站建设 项目流程

一、Linux 驱动底层核心思想

1. 面向对象:万物皆对象,完成从抽象到具体的描述。

2. 设备与驱动分离:硬件信息放在设备树,驱动代码独立。内核运行时由设备树向驱动传递硬件资源,降低移植难度,减少重复代码编写。

3. 驱动分层思想:内核采用面向对象设计,同类设备定义基类;子类可继承基类函数,也可重写覆盖同名函数,依靠多态提升代码复用。内核拆分为多个独立子系统协同工作,pinctrl、GPIO、I2C、SPI 等子系统各司其职。

二、pinctrl 子系统

1. 基本概念

  • Pin controller devices(引脚控制器):对应 IOMUX,管理引脚复用功能、电气特性配置,寄存器信息由芯片厂商预先写好。
  • Pinctrl client devices:pinctrl 的客户端,即使用 pinctrl 引脚配置的外设设备。

作用:在设备树中配置引脚,无需手动读写 IOMUX 寄存器。

2. 设备树 DTS 编写(i.MX6ULL)

(1)iomux 控制器节点iomuxc@020e0000,compatible 属性fsl,imx6ul-iomuxc,对应内核源码drivers/pinctrl/freescale/pinctrl-imx6ul.c,模块通过arch_initcall初始化。

(2)自定义引脚组:使用标签分组,如pinctrl_led;fsl,pins存放引脚宏与电气参数,引脚宏定义在imx6ul-pinfunc.h。每个引脚条目格式为<mux_reg conf_reg input_reg mux_mode input_val>,分别对应复用寄存器偏移、配置寄存器偏移、输入寄存器偏移、复用模式和输入值。

(3)外设节点引用:

  • pinctrl-names:引脚状态列表,默认default,还可定义sleep、idle等状态;
  • pinctrl-0:引用预先定义的引脚配置,对应 default 状态;
  • pinctrl-1:可引用另一组引脚配置,对应 sleep 等状态;
  • status:okay启用设备,disabled禁用设备。

(4)参考文档路径

内核中 pinctrl 设备树绑定规范文档:

  • Documentation/devicetree/bindings/pinctrl/fsl,imx-pinctrl.txt(飞思卡尔 i.MX 系列 pinctrl 绑定说明)
  • Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt(通用 pinctrl 设备树绑定规范)

示例:

/* 引脚分组节点:定义LED引脚复用与电气属性 */ pinctrl_led: ledgrp { fsl,pins = < MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 0x10B0 /* LED0,电气特性的配置 */ >; }; /* LED外设设备节点:引用上面pinctrl引脚配置 */ gpioleds { #address-cells = <1>; #size-cells = <1>; compatible = "pute-gpioleds"; pinctrl-names = "default"; // 定义引脚的状态名称列表,引脚默认状态 pinctrl-0 = <&pinctrl_led>; // 引用pinctrl_led引脚配置节点 led-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>; // 自定义属性,低电平有效 status = "okay"; // okay启用、disabled禁用、reserved保留 };

3. pinctrl 开发流程

(1)在 iomuxc 节点内添加引脚分组,配置引脚复用、电气属性;

(2)在设备节点引用 pinctrl 配置;

(3)编译生成 dtb 设备树二进制文件,下载至开发板;

(4)修改驱动代码,删除手动 ioremap 映射 IOMUX 寄存器的旧代码;

(5)调试验证:查看/sys/firmware/devicetree确认设备树节点加载,加载驱动模块后查看 sysfs 属性,上层应用测试。

三、GPIO 子系统

1. 设备树 DTS 编写

(1)GPIO 控制器节点,compatible 属性匹配芯片 GPIO 驱动,包含寄存器基地址、中断相关属性,添加gpio-controller标识。

(2)外设节点通过gpios属性指定使用的 GPIO 编号与有效电平,GPIO_ACTIVE_LOW代表低电平有效。

2. GPIO 常用内核 API

函数功能返回值说明
of_get_named_gpio从设备树节点解析获取 GPIO 编号成功返回 GPIO 号,失败返回负数
gpio_request申请 GPIO 硬件资源成功返回 0,失败负数 (常见 - EBUSY)
devm_gpio_request申请 GPIO 资源,设备卸载自动释放资源成功 0,失败负数
gpio_free手动释放 GPIO 资源无返回
gpio_direction_output设置 GPIO 为输出模式成功 0,失败负数
gpio_direction_input设置 GPIO 为输入模式成功 0,失败负数
gpio_set_value设置 GPIO 输出高低电平无返回
gpio_get_value读取 GPIO 输入引脚电平0 低电平,1 高电平
gpio_to_irq由 GPIO 编号获取对应的中断号成功返回中断号,失败负数

说明:devm_gpio_request是gpio_request的资源管理版本,驱动卸载或 probe 失败时内核自动释放,无需手动调用gpio_free,推荐在 probe 函数中使用。此外,新内核推荐使用gpiod_系列 API(基于描述符),如devm_gpiod_get、gpiod_set_value,与设备树gpios属性配合更安全。

3. GPIO 开发步骤

  • 删除旧驱动中 ioremap 映射 GPIO 寄存器、手动配置 PDIR 等寄存器代码;
  • 使用of_get_named_gpio解析设备树拿到 GPIO 编号;
  • 调用gpio_request申请 GPIO 资源;
  • 设置 GPIO 输入 / 输出方向;输出可设置默认电平,输入可读取引脚状态;
  • 使用完毕调用gpio_free释放 GPIO 资源。

4. pinctrl 与 GPIO 子系统关系

  • pinctrl:第一步,配置引脚复用功能,把引脚设置为 GPIO 功能,配置引脚电气属性。
  • GPIO 子系统:第二步,引脚已经复用为 GPIO 后,控制引脚输入输出、读写高低电平。

顺序:pinctrl 配置引脚功能 → GPIO 子系统操作引脚。

四、非阻塞 IO(按键驱动案例)

1. 阻塞 IO 与非阻塞 IO 概念

  • 阻塞 IO:应用调用 read 等函数,若无数据,进程休眠等待,直到有数据返回后唤醒。
  • 非阻塞 IO:调用 read 时,如果没有数据,驱动不休眠,立刻返回错误,应用程序不会挂起。应用需要循环轮询,持续查询设备状态。

2. 非阻塞 IO 驱动开发要点

  • 应用打开设备时,传入O_NONBLOCK标记,驱动通过file->f_flags识别该标志。
  • read 函数内判断:如果没有按键数据,并且是非阻塞模式,直接返回 - EAGAIN,不进入休眠。
  • 阻塞模式下,无数据时调用wait_event_interruptible进入等待队列休眠。
  • 按键中断触发,采集按键状态,唤醒等待队列,应用读取按键值。
  • read 返回时,使用copy_to_user将按键数据拷贝到用户空间,并检查返回值,若拷贝失败返回 - EFAULT。

注意:非阻塞 IO 需要应用层不断轮询,会占用 CPU 资源,适合简单场景。若需更高效的事件通知机制,可配合poll/epoll接口使用,驱动实现poll操作函数,在等待队列上注册,应用通过select/epoll等待可读事件,避免忙轮询。

3. 开发整体流程(按键驱动)

  • 设备树:添加 pinctrl 引脚配置,在按键节点定义 gpios 属性;
  • 驱动代码:通过 of 接口解析设备树,申请 GPIO 资源,配置引脚为输入;
  • 注册 GPIO 中断,按键触发中断,记录按键状态;
  • 在 read 接口判断O_NONBLOCK标志,区分阻塞 / 非阻塞模式;
  • 应用 open 时传入 O_NONBLOCK,循环 read 轮询读取按键。

五、整体总结

基于设备树开发驱动,利用 pinctrl 完成引脚复用配置,再通过 GPIO 子系统操作引脚。pinctrl、GPIO 子系统依托 Linux 驱动分层、设备驱动分离思想,将硬件描述放到设备树,驱动只处理业务逻辑。在按键这类输入设备中,可结合非阻塞 IO,应用轮询查询设备状态,无数据时驱动不阻塞进程,实现非阻塞读取。

三个子系统协作关系清晰:pinctrl 负责引脚复用与电气属性配置,GPIO 子系统负责引脚电平读写,非阻塞 IO 机制负责应用与驱动之间的高效数据交互。理解这一链路,是掌握 Linux 字符设备驱动开发的关键基础。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询