Linux 驱动之 GPIO 子系统

GPIO 概述

以前学习 stm32 的时候看到手册里有关于 gpio 的电路图,如下:

  • 保护二极体:
    IO 引脚上下两边两个二极体用于防止引脚外部过高、过低的电压输入。当引脚电压高于 VDD 时,上方的二极体导通;当引脚电压低于 VSS 时,下方的二极体导通,防止不正常电压引入晶片导致晶片烧毁

  • P-MOS 管和 N-MOS 管:
    由 P-MOS 管和 N-MOS 管组成的单元电路使得 GPIO 具有“推挽输出”和“开漏输出”的模式

  • TTL 肖特基触发器:
    信号经过触发器后,模拟信号转化为 0 和 1 的数字信号。但是,当 GPIO 引脚作为 ADC 采集电压的输入通道时,用其“模拟输入”功能,此时信号不再经过触发器进行 TTL 电平转换。ADC 外设要采集到的原始的模拟信号

STM32 的 GPIO 支持 4 种输入模式(浮空输入、上拉输入、下拉输入、模拟输入)和 4 种输出模式(开漏输出、开漏复用输出、推挽输出、推挽复用输出)

  • 浮空输入模式:

  • 上拉输入模式:

  • 下拉输入模式:

  • 模拟输入模式:

  • 开漏输出模式:

开漏输出模式下,通过设定位设定/清除寄存器或者输出寄存器的值,途经 N-MOS 管,最终输出到 I/O 口。这里要注意 N-MOS 管,当设定输出的值为高电平的时候,N-MOS 管处于关闭状态,此时 I/O 口的电平就不会由输出的高低电平决定,而是由 I/O 口外部的上拉或者下拉决定;当设定输出的值为低电平的时候,N-MOS 管处于开启状态,此时 I/O 口的电平就是低电平。同时,I/O 口的电平也可以通过输入电路进行读取;注意,I/O 口的电平不一定是输出的电平。

  • 开漏复用输出模式:

GPIO 驱动中几个概念

  • Active-High and Active-Low
    以 LED 为例,需要设置 GPIO 电平。但是有些电路可能是高电平点亮 LED,有些是低电平点亮 LED。

    可以使用如下代码:

    1
    2
    gpiod_set_value(gpio, 1);  // 输出高电平点亮 LED
    gpiod_set_value(gpio, 0); // 输出低电平点亮 LED

    对应同一个目标:点亮 LED,对于不同的 LED,就需要不同的代码,原因在于上面的代码中 1、0 表示的是"物理值"。

    如果能使用"逻辑值",同样的逻辑值在不同的配置下输出对应的物理值,就可以保持代码一致,比如:

    1
    2
    3
    gpiod_set_value(gpio, 1);  // 输出逻辑 1
    // 在 Active-High 的情况下它会输出高电平
    // 在 Active-Low 的情况下它会输出低电平
  • Open Drain and Open Source: 有多个 GPIO 同时驱动一个电路时,就需要设置 Open Drain 或 Open Source

    • Open Drain:引脚被设置为低电平时才会驱动电路,典型场景是 I2C 接口。
    • Open Source:引脚被设置为高电平时才会驱动电路。

GPIO 数据结构

要理解一个驱动框架的设计就一定要理清楚该框架下的数据结构组织关系,首先列出 gpio 子系统的数据结构关系图如下:原图

  • gpio_chip: gpio_chip 这个数据结构用于描述一组 gpio,soc 里的 gpio 一般都是分为几组,同一组内的 gpio 具有相同的操作方式

  • gpio_device: gpio_device 是具体的 device 它管理一个 gpio_chip 和该组下的所有 pin。gpio_device 通过链表连接起来就是 soc 内的多组 gpio

  • gpio_desc: gpio_desc 这个结构用于描述一个 pin,一组 gpio 里面有 32 个 pin 或者多个 pin,每一个 pin 由 gpio_desc 描述,同一组的 pin 挂载在 gpio_device 下

GPIO 子系统的层次和接口

硬件层之上是 soc 原厂提供的 gpio controller 驱动这部分提供了具体 soc 的 pin 脚的描述和控制接口,在这之上是 gpiolib 层为其他驱动程序提供统一的接口来配置和控制 gpio,再往上就是其他驱动程序使用 gpio 的接口完成他们想要的工作。当然在 gpiolib 之下还会使用 pinctrl 子系统来管理 pin 的功能复用与配置,这个在 pinctrl 一篇文章里再详细说明。

实现

gpio_chip 是描述一个 gpio 控制器的数据结构,将一个 gpio 控制器添加到系统中的流程如下:原图

对于 gpio 驱动一般由原厂写好,其他驱动工程师不需要编写具体驱动,需要的时候直接在 dts 里配置就可以直接使用。其中 gpio_chip 结构体的注册如上图所示,每个 gpio_chip 都需要一个 gpio_device 结构,要先分配一个 gpio_device 结构体,然后就是处理该组下的 pin,以及各种中断信息。

sysfs 接口

/sys/bus/gpio/devices目录下,列出了所有的 GPIO 控制器 /sys/class/gpio/gpiochipXXX下有每个 GPIO 控制器的详细信息

1
2
3
4
5
6
7
8
/sys/class/gpio/gpiochip508]# ls -1
base // 这个 GPIO 控制器的 GPIO 编号
device
label // 名字
ngpio // 引脚个数
power
subsystem
uevent

查看 gpio 使用情况:

1
cat /sys/kernel/debug/gpio

导出/设置方向/读写值:

1
2
3
4
5
6
7
8
9
echo 509 > /sys/class/gpio/export
echo out > /sys/class/gpio/gpio509/direction
echo 1 > /sys/class/gpio/gpio509/value
echo 509 > /sys/class/gpio/unexport

echo 509 > /sys/class/gpio/export
echo in > /sys/class/gpio/gpio509/direction
cat /sys/class/gpio/gpio509/value
echo 509 > /sys/class/gpio/unexport

参考文献

https://iter01.com/539041.html
《韦东山老师的书籍和课程》