【学习笔记】linux:GPIO子系统
一、概述
在 pinctrl 子系统中把 pin 脚初始化成了普通 GPIO 后,就可以使用 GPIO 子系统的接口去操作 IO口的电平、中断等。驱动开发者在设备树中添加 gpio 相关信息,然后就可以在驱动程序中使用gpio 子系统提供的 API 函数来操作 GPIO。
二、GPIO控制
2.1 使用GPIO sysfs接口控制IO
最常见的读写GPIO方式就是用GPIO sysfs interface, 是通过操作 /sys/class/gpio 目录下的 export 、 unexport 、gpio{N}/direction, gpio{N} /value (用实际引脚号替代{N})等文件实现。
引脚编号计算公式:n*32 +K*8 + x。其中 n 为 bank(0,1,2,3,4),K 为 group(A=0,B=1,C=2,D=3),x 为该 group 的第 x(0,1,2,...,7)个引脚。例如,引脚GPIO1_C4的引脚号为52 (32 x 1 + 8 x 2 + 4)。
相关操作如下。若遇到权限不足的问题,可以切换到 root 用户:sudo -i,或者,使用 sh 包裹:sudo sh -c 'echo 52 > /sys/class/gpio/export'。
#以下所有操作均需要打开管理者权限使用
#使能引脚GPIO1_C4
echo 52 > /sys/class/gpio/export
#设置引脚为输入模式
echo in > /sys/class/gpio/gpio52/direction
#读取引脚的值
cat /sys/class/gpio/gpio52/value
#设置引脚为输出模式
echo out > /sys/class/gpio/gpio52/direction
#设置引脚为低电平
echo 0 > /sys/class/gpio/gpio52/value
#设置引脚为高电平
echo 1 > /sys/class/gpio/gpio52/value
#复位引脚
echo 52 > /sys/class/gpio/unexport
2.2 使用libgpiod控制IO
libgpiod是一种字符设备接口,GPIO访问控制是通过操作字符设备文件(比如 /dev/gpiodchip0 )实现的, 并通过libgpiod提供一些命令工具、c库以及python封装。通过 sudo apt install gpiod 指令安装gpiod命令行工具。
|
命令 |
作用 |
使用举例 |
说明 |
|---|---|---|---|
|
gpiodetect |
列出所有的GPIO控制器 |
gpiodetect(无参数) |
列出所有的GPIO控制器 |
|
gpioinfo |
列出gpio控制器的引脚情况 |
gpioinfo 1 |
列出第一组控制器引脚组情况 |
|
gpioset |
设置gpio |
gpioset 1 20=0 |
设置第一组控制器编号20引脚为低电平 |
|
gpioget |
获取gpio引脚状态 |
gpioget 1 20 |
获取第一组控制器编号20的引脚状态 |
|
gpiomon |
监控gpio的状态 |
gpiomon 1 20 |
监控第一组控制器编号20的引脚状态 |
三、API函数(新版本)
新版本的 GPIO 子系统 API 函数使用以 "gpiod_"作为前缀的函数命名约定,而旧版本的 GPIO 子系统 API 函数使用以"gpio_"作为前缀的函数命名约定。
GPIO 子系统,主要的数据结构有 gpio_device、gpio_chip、gpio_desc 等,主要是为了描述 gpio 控制器,有引脚信息,中断信息,以及相关操作函数等。一个 gpio_device 用来表示一个 GPIO 控制器,GPIO Controller 中每一个引脚用 gpio_desc 表示,引脚的相关操作函数和中断相关在 gpio_chip 中。
-
gpio_chip(底层驱动): 由芯片原厂(如 Rockchip)实现。它封装了对硬件寄存器的实际读写操作。对于上层驱动开发者,它是透明的。 -
gpio_desc(引脚描述符) ——【核心重点】: 这是新版 GPIO API (gpiod_系列) 的核心操作对象。每一个物理引脚对应一个gpio_desc。它不仅代表引脚本身,还保存了该引脚的属性标志(如ACTIVE_LOW、开漏输出等)。我们在驱动代码中持有的句柄(Handle)就是指向该结构体的指针。 -
gpio_device(设备实例): 内核内部的管理容器。它一一对应物理上的 GPIO 控制器(Bank),负责管理该控制器下的chip和所有引脚的desc。它也对应了用户空间的/dev/gpiochipN设备节点。
3.1获取GPIO描述符
gpiod_get / devm_gpiod_get
功能:解析设备树,获取对应的 GPIO 描述符。
实际开发中建议使用 devm_gpiod_get。带 devm_ 前缀表示 Managed (托管),驱动卸载时内核会自动释放引脚,防止内存泄漏。
struct gpio_desc *devm_gpiod_get(struct device *dev, const char *con_id, enum gpiod_flags flags);
参数详解:
-
dev: 设备指针(通常是&pdev->dev)。 -
con_id: 字符串 ID。-
如果传
NULL,内核会找gpios属性。 -
如果传
"led",内核会找led-gpios属性(注意:DTS 里写led-gpios,这里只传"led",后缀-gpios会自动匹配)。
-
-
flags: 初始化标志(非常重要!)。常用值:-
GPIOD_ASIS: 不改变当前状态。 -
GPIOD_IN: 初始化为输入。 -
GPIOD_OUT_LOW: 初始化为输出,且逻辑值为 0(关闭)。 -
GPIOD_OUT_HIGH: 初始化为输出,且逻辑值为 1(开启)。
-
返回值:成功返回描述符指针;失败返回 ERR_PTR 错误码。
gpiod_get_index / gpiod_get_optional:
-
gpiod_get_index: 当设备树里一行写了多个 GPIO 时使用(如data-gpios = <&gpio0 1 ...>, <&gpio0 2 ...>;),通过index(0, 1, 2...) 来区分。 -
gpiod_get_optional: 非常常用。如果设备树里没有这个 GPIO,它不会报错,而是返回NULL。后续调用gpiod_set_value操作空指针也是安全的(内核会忽略),适合编写兼容性强的驱动。
3.2 设置方向
gpiod_direction_output
功能:将引脚配置为输出模式,并设置初始电平。
int gpiod_direction_output(struct gpio_desc *desc, int value);
#value: 初始逻辑值(0 或 1)。
gpiod_direction_input
功能:将引脚配置为输入模式
int gpiod_direction_input(struct gpio_desc *desc);
3.3 数值读写
gpiod_set_value
功能:设置 GPIO 的输出电平。
void gpiod_set_value(struct gpio_desc *desc, int value);
关键点:逻辑值 vs 物理值
-
这里的
value是逻辑值。 -
如果你在 DTS 里配置了
GPIO_ACTIVE_LOW(低电平有效):-
gpiod_set_value(desc, 1)-> 逻辑上开启 -> 物理输出 低电平 (0V)。 -
gpiod_set_value(desc, 0)-> 逻辑上关闭 -> 物理输出 高电平 (3.3V)。
-
-
优势:驱动代码不需要关心硬件是高触发还是低触发,只需操作逻辑状态。
gpiod_get_value
功能:读取 GPIO 的电平状态。
int gpiod_get_value(struct gpio_desc *desc);
返回逻辑值(1 或 0)。内核会自动根据 ACTIVE_LOW 标志进行翻转。
3.4 释放
gpiod_put
功能:释放 GPIO 描述符。
void gpiod_put(struct gpio_desc *desc);
注意:如果你使用的是 devm_gpiod_get,则不需要手动调用此函数!驱动卸载时内核会自动释放。只有使用非 devm_ 版本的 gpiod_get 时才需要配对使用 gpiod_put。
注:此文章参考野火linux驱动开发指南。
注:本人学习时使用lubanmao4(rk3588)











