RK3568 GPIO子系统
文章目录
- 一、gpio子系统的概念
- **GPIO 子系统概念讲解**
- **1. GPIO 子系统的基本概念**
- **2. GPIO 子系统的组成**
- **2.1 GPIO 控制器(GPIO Controller)**
- **2.2 GPIO 设备(GPIO Device)**
- **2.3 GPIO 子系统 API**
- **3. GPIO 设备树(Device Tree)配置**
- **4. 用户空间访问 GPIO**
- **4.1 旧的 sysfs 方式(已弃用)**
- **4.2 使用 libgpiod(推荐)**
- **5. GPIO 中断(IRQ)处理**
- **6. GPIO 子系统的优势**
- **总结**
- 二、RK3568 GPIO命名规则
- **RK3568 GPIO 命名规则解析**
- **1. 命名格式**
- **2. GPIO 编号计算**
- **3. 实例解析**
- **4. GPIO 在设备树中的表示**
- **5. 复用模式(MUX 配置)**
- **6. 总结**
- 三、GPIO的两套控制函数
- **Linux GPIO 子系统的两套 API:`gpio` 和 `gpiod`**
- **1. `gpio` API(老版)**
- **1.1 `gpio` API 主要函数**
- **1.2 `sysfs` 方式(已废弃)**
- **2. `gpiod` API(新版,推荐)**
- **2.1 `gpiod` API 主要函数**
- **2.2 `libgpiod`(用户空间)**
- **3. `gpio` vs `gpiod` 对比**
- **4. 设备树(Device Tree) GPIO 描述**
- **5. 结论**
一、gpio子系统的概念
GPIO 子系统概念讲解
GPIO(General-Purpose Input/Output,通用输入/输出)是嵌入式系统和计算机架构中广泛使用的一种通用数字信号接口。Linux 内核提供了 GPIO 子系统,用于管理和控制 GPIO 资源,使驱动程序能够通过统一的接口访问 GPIO 设备。
1. GPIO 子系统的基本概念
GPIO 子系统的主要作用是提供一个标准化的接口,使不同的硬件平台可以以相同的方式访问 GPIO 资源。它主要用于:
- 输入(如读取按钮状态)
- 输出(如控制 LED、继电器等)
- 中断(GPIO 触发的事件,如按键按下时触发中断)
- 复用功能(GPIO 可作为 I2C、SPI、PWM 等功能引脚)
2. GPIO 子系统的组成
Linux 内核的 GPIO 子系统主要由以下几个部分组成:
2.1 GPIO 控制器(GPIO Controller)
GPIO 控制器是具体管理 GPIO 硬件的驱动程序,每个 SoC(如 Rockchip、Allwinner、NXP 等)都会有自己的 GPIO 控制器,负责:
- GPIO 的配置(输入/输出模式)
- GPIO 的电平控制(高/低)
- GPIO 的中断管理(上升沿、下降沿、中断屏蔽等)
在设备树(Device Tree,DT)中,GPIO 控制器通常被定义为一个 pinctrl
设备节点。例如:
gpio: gpio-controller@ff720000 {
compatible = "rockchip,gpio";
reg = <0xff720000 0x100>;
gpio-controller;
#gpio-cells = <2>;
};
compatible = "rockchip,gpio";
说明这是一个 Rockchip 平台的 GPIO 控制器。gpio-controller;
说明这个设备节点是一个 GPIO 控制器。#gpio-cells = <2>;
表示 GPIO 设备的参数格式。
2.2 GPIO 设备(GPIO Device)
每个 GPIO 控制器管理一组 GPIO 引脚,驱动程序可以通过 GPIO 子系统 API 访问这些 GPIO 设备。例如:
leds {
compatible = "gpio-leds";
led1 {
gpios = <&gpio 23 GPIO_ACTIVE_HIGH>;
label = "led1";
};
};
这表示 led1
连接到 gpio
控制器的 GPIO 23
,高电平点亮。
2.3 GPIO 子系统 API
Linux 提供了一套 API 供内核驱动访问 GPIO 设备,主要包括:
- 请求和释放 GPIO
int gpio_request(unsigned int gpio, const char *label); void gpio_free(unsigned int gpio);
- 配置 GPIO 方向
int gpio_direction_input(unsigned int gpio); int gpio_direction_output(unsigned int gpio, int value);
- 读取/设置 GPIO
int gpio_get_value(unsigned int gpio); void gpio_set_value(unsigned int gpio, int value);
- 中断处理
int gpio_to_irq(unsigned int gpio);
3. GPIO 设备树(Device Tree)配置
在 Linux 设备树中,GPIO 设备通常以 pinctrl
方式配置,示例如下:
&pinctrl {
leds_pins: leds-pins {
rockchip,pins = <1 RK_PA0 0 &pcfg_pull_up>;
};
};
leds {
compatible = "gpio-leds";
led {
gpios = <&gpio1 0 GPIO_ACTIVE_HIGH>;
label = "status-led";
};
};
rockchip,pins = <1 RK_PA0 0 &pcfg_pull_up>;
表示GPIO1_PA0
作为LED
设备引脚,并启用上拉电阻。compatible = "gpio-leds";
说明使用gpio-leds
驱动管理 LED。gpios = <&gpio1 0 GPIO_ACTIVE_HIGH>;
绑定 LED 到GPIO1
的第0
个引脚,并在高电平
时点亮。
4. 用户空间访问 GPIO
Linux 4.8 之前,用户态可以通过 /sys/class/gpio
访问 GPIO。但从 Linux 4.8 开始,推荐使用 libgpiod
。
4.1 旧的 sysfs 方式(已弃用)
echo 23 > /sys/class/gpio/export
echo out > /sys/class/gpio/gpio23/direction
echo 1 > /sys/class/gpio/gpio23/value # 设置高电平
echo 0 > /sys/class/gpio/gpio23/value # 设置低电平
4.2 使用 libgpiod(推荐)
安装 libgpiod
:
sudo apt install gpiod
查询 GPIO:
gpiodetect
控制 GPIO:
gpioset gpiochip0 23=1 # 设置 GPIO 23 为高
5. GPIO 中断(IRQ)处理
GPIO 除了用于输入和输出,还可以用于 中断(Interrupt)。驱动程序可以注册一个 GPIO 触发的中断,例如:
static irqreturn_t button_irq_handler(int irq, void *dev_id)
{
printk(KERN_INFO "Button Pressed!\n");
return IRQ_HANDLED;
}
static int __init my_gpio_init(void)
{
int irq_number;
irq_number = gpio_to_irq(23);
request_irq(irq_number, button_irq_handler, IRQF_TRIGGER_FALLING, "gpio_irq", NULL);
return 0;
}
gpio_to_irq(23);
获取GPIO 23
的中断号。request_irq
注册中断,IRQF_TRIGGER_FALLING
说明在下降沿触发中断。
6. GPIO 子系统的优势
- 抽象性:不同的硬件平台可以通过统一的 API 访问 GPIO。
- 灵活性:支持多种 GPIO 配置方式,如设备树、sysfs、
libgpiod
。 - 安全性:通过
GPIO 子系统
管理 GPIO 资源,避免多个驱动争用 GPIO。
总结
Linux 内核 GPIO 子系统的主要作用是 提供一个统一的接口 让用户可以访问 GPIO 设备。其核心组成包括:
- GPIO 控制器(管理所有 GPIO 引脚)
- GPIO 设备(具体的 GPIO 硬件资源)
- GPIO API(提供标准化的 GPIO 访问方式)
- 设备树配置(用于指定 GPIO 设备的连接方式)
- 用户空间访问方式(如
libgpiod
)
在嵌入式系统中,GPIO 常用于 控制 LED、按键检测、中断触发 等功能,是驱动开发中常见的模块之一。
二、RK3568 GPIO命名规则
RK3568 GPIO 命名规则解析
Rockchip RK3568 SoC 具有 五组 GPIO(GPIO0 ~ GPIO4),每组 GPIO 由多个 bank(A, B, C, D 等)组成,每个 bank 内包含 8 个 GPIO 引脚。
1. 命名格式
GPIOx_Yz
GPIOx
: GPIO 组,x 取值范围0~4
,共 5 组(GPIO0、GPIO1、GPIO2、GPIO3、GPIO4)。Y
: GPIO 组内的 Bank,A、B、C、D,对应每组中的 4 个 Bank。z
: GPIO 组内的引脚编号,取值0~7
(每个 Bank 有 8 个引脚)。
2. GPIO 编号计算
RK3568 的 GPIO 总共 160 个(5 组 × 32 个),其全局编号计算方式如下:
GPIO 编号 = (组号 × 32) + (Bank 号 × 8) + (引脚号)
其中:
组号
(x):GPIO0 = 0, GPIO1 = 1, GPIO2 = 2, GPIO3 = 3, GPIO4 = 4Bank 号
(Y):A = 0, B = 1, C = 2, D = 3引脚号
(z):0 ~ 7
3. 实例解析
GPIO 名称 | 组号 | Bank | 引脚编号 | 全局编号计算 |
---|---|---|---|---|
GPIO0_A0 | 0 | A (0) | 0 | (0×32) + (0×8) + 0 = 0 |
GPIO0_B3 | 0 | B (1) | 3 | (0×32) + (1×8) + 3 = 11 |
GPIO1_C7 | 1 | C (2) | 7 | (1×32) + (2×8) + 7 = 55 |
GPIO2_D4 | 2 | D (3) | 4 | (2×32) + (3×8) + 4 = 100 |
GPIO3_C3 | 3 | C (2) | 3 | (3×32) + (2×8) + 3 = 123 |
GPIO4_A5 | 4 | A (0) | 5 | (4×32) + (0×8) + 5 = 133 |
4. GPIO 在设备树中的表示
在 设备树(Device Tree) 中,Rockchip 的 GPIO 通过 <bank pin mux-config pull-config>
方式定义。例如:
&pinctrl {
gpio3c3_pins: gpio3c3 {
rockchip,pins = <3 RK_PC3 0 &pcfg_pull_none>;
};
};
解析:
3 RK_PC3
: GPIO 组 3,Bank C,Pin 30
: 引脚复用模式(mux)&pcfg_pull_none
: 无上拉/下拉
5. 复用模式(MUX 配置)
RK3568 的 GPIO 可用于 多种功能(如 GPIO、I2C、SPI、UART、PWM 等),其 复用模式(MUX) 取值如下:
值 | 模式 |
---|---|
0 | 纯 GPIO |
1 | 复用功能 1 |
2 | 复用功能 2 |
3 | 复用功能 3 |
4 | 复用功能 4 |
5 | 复用功能 5 |
6 | 复用功能 6 |
示例:
rockchip,pins = <3 RK_PC3 1 &pcfg_pull_none>;
表示 GPIO3_C3
复用功能 1
(具体功能取决于硬件手册)。
6. 总结
- GPIO 组:RK3568 共有 5 组(GPIO0 ~ GPIO4)。
- GPIO Bank:每组有 4 个 Bank(A ~ D),每个 Bank 8 个引脚。
- GPIO 命名:
GPIOx_Yz
,其中x
为 GPIO 组号,Y
为 Bank(A=0, B=1, C=2, D=3),z
为 Bank 内引脚号(0~7)。 - 全局 GPIO 编号 =
(组号 × 32) + (Bank 号 × 8) + (引脚号)
。 - 设备树定义:使用
<bank pin mux-config pull-config>
方式指定 GPIO 功能。
三、GPIO的两套控制函数
Linux GPIO 子系统的两套 API:gpio
和 gpiod
在 Linux 内核中,GPIO(通用输入输出)子系统提供了两种不同的 API 来操作 GPIO:
- 旧版
gpio
API(传统sysfs
接口 +gpio
内核 API) - 新版
gpiod
API(基于gpiolib
的gpiod
内核 API)
1. gpio
API(老版)
gpio
API 是 较早版本的 GPIO 控制接口,主要基于 sysfs
和 linux/gpio.h
头文件,主要用于 内核驱动开发。
1.1 gpio
API 主要函数
这些函数主要用于 直接控制 GPIO,适用于 早期的内核驱动:
#include <linux/gpio.h>
函数 | 作用 |
---|---|
gpio_request(gpio, label) | 申请一个 GPIO |
gpio_free(gpio) | 释放 GPIO |
gpio_direction_input(gpio) | 设置 GPIO 为输入模式 |
gpio_direction_output(gpio, value) | 设置 GPIO 为输出模式,并指定初始值 |
gpio_set_value(gpio, value) | 设置 GPIO 输出值 |
gpio_get_value(gpio) | 读取 GPIO 输入值 |
gpio_to_irq(gpio) | 将 GPIO 号转换为中断号 |
gpio_is_valid(gpio) | 检查 GPIO 是否可用 |
1.2 sysfs
方式(已废弃)
传统上,用户空间程序可以通过 sysfs 访问 GPIO:
# 导出 GPIO 23
echo 23 > /sys/class/gpio/export
# 设置 GPIO 方向
echo out > /sys/class/gpio/gpio23/direction
# 设置 GPIO 输出高电平
echo 1 > /sys/class/gpio/gpio23/value
# 释放 GPIO
echo 23 > /sys/class/gpio/unexport
⚠️ 注意:sysfs
方式在 Linux 5.10 之后被废弃,建议使用 gpiod
。
2. gpiod
API(新版,推荐)
gpiod
(GPIO descriptor API)是 Linux 4.8 及以后推荐的 GPIO 操作方式,它是基于 gpiolib
GPIO 子系统的,取代了 gpio
API 和 sysfs
。
2.1 gpiod
API 主要函数
#include <linux/gpio/consumer.h>
函数 | 作用 |
---|---|
gpiod_get(dev, con_id, flags) | 申请一个 GPIO |
gpiod_put(desc) | 释放 GPIO |
gpiod_direction_input(desc) | 设置 GPIO 为输入模式 |
gpiod_direction_output(desc, value) | 设置 GPIO 为输出模式 |
gpiod_set_value(desc, value) | 设置 GPIO 输出值 |
gpiod_get_value(desc) | 读取 GPIO 输入值 |
gpiod_to_irq(desc) | 将 GPIO 号转换为中断号 |
示例代码
struct gpio_desc *desc;
desc = gpiod_get(NULL, "my_gpio", GPIOD_OUT_LOW);
if (IS_ERR(desc)) {
pr_err("Failed to get GPIO descriptor\n");
return PTR_ERR(desc);
}
gpiod_set_value(desc, 1); // 设置 GPIO 高电平
msleep(1000);
gpiod_set_value(desc, 0); // 设置 GPIO 低电平
gpiod_put(desc); // 释放 GPIO
2.2 libgpiod
(用户空间)
在 用户空间,推荐使用 libgpiod
库,它提供了更强大的功能。
安装 libgpiod
:
sudo apt install libgpiod-dev gpiod
使用 gpiod
控制 GPIO:
# 列出所有 GPIO
gpiodetect
# 读取 GPIO 23 的值
gpioget gpiochip0 23
# 设置 GPIO 23 输出高电平
gpioset gpiochip0 23=1
用户空间 C 语言示例:
#include <gpiod.h>
#include <stdio.h>
int main() {
struct gpiod_chip *chip;
struct gpiod_line *line;
// 打开 GPIO 芯片(/dev/gpiochip0)
chip = gpiod_chip_open_by_name("gpiochip0");
line = gpiod_chip_get_line(chip, 23);
// 设置 GPIO 方向并输出高电平
gpiod_line_request_output(line, "example", 1);
// 释放资源
gpiod_chip_close(chip);
return 0;
}
3. gpio
vs gpiod
对比
对比项 | gpio API(旧) | gpiod API(新) |
---|---|---|
适用版本 | 旧内核(<4.8) | 新内核(≥4.8,推荐) |
内核驱动 | ✅ 可用 | ✅ 可用 |
用户空间 | ❌(sysfs 方式已废弃) | ✅ libgpiod |
GPIO 表示方式 | 整数编号(GPIO 号) | struct gpio_desc * |
接口 | gpio_request 、gpio_set_value | gpiod_get 、gpiod_set_value |
安全性 | ❌ 直接使用 GPIO 号,容易冲突 | ✅ 使用 GPIO descriptor ,更安全 |
4. 设备树(Device Tree) GPIO 描述
使用 gpiod
时,GPIO 在 设备树 中的定义:
leds {
compatible = "gpio-leds";
led1 {
gpios = <&gpio2 5 GPIO_ACTIVE_HIGH>;
label = "led1";
};
};
设备树中 gpios
语法:
gpios = <&gpioX Y FLAGS>;
&gpioX
:GPIO 控制器Y
:GPIO 号(相对编号)FLAGS
:GPIO_ACTIVE_HIGH
(高有效)GPIO_ACTIVE_LOW
(低有效)
5. 结论
情况 | 推荐使用 |
---|---|
内核驱动开发(新内核) | gpiod API |
内核驱动开发(旧内核) | gpio API |
用户空间程序 | libgpiod |
设备树 GPIO 定义 | gpios = <&gpioX Y GPIO_ACTIVE_HIGH>; |
✅ gpiod
是目前 Linux 推荐的 GPIO 操作方式,它比 gpio
API 更安全、易用,并且支持 用户空间操作。建议在 新项目 中 避免使用旧的 gpio
API。