2. GPIO 硬件架构:SoC 内部 GPIO 模块框图、控制器与引脚映射关系
在嵌入式 Linux 驱动开发中,理解 GPIO 的硬件架构是编写正确、高效驱动程序的基石。本章将深入剖析 SoC 内部 GPIO 模块的组成、控制器的功能划分以及物理引脚与软件编号之间的映射关系。
2.1 SoC 内部 GPIO 模块总体框图
一个典型的 SoC(如 STM32MP1、i.MX6ULL、全志 V3s)内部,GPIO 模块并非孤立存在,而是作为 APB/AHB 总线 上的一个外设挂载在系统总线上。下图展示了 GPIO 模块在 SoC 内部的宏观位置与核心组成。
架构要点: GPIO 模块通过总线桥接器与 CPU 互联,内部包含多个端口(Port),每个端口对应一组引脚(如 PA0~PA15),并由独立的寄存器组控制。
2.2 GPIO 控制器内部结构
每个 GPIO 端口(如 GPIOA)内部包含一组功能寄存器,用于配置引脚模式、输出数据、输入数据和上下拉电阻。下表列出了核心寄存器及其功能。
| 寄存器名称 | 偏移地址 | 位宽 | 功能描述 |
|---|---|---|---|
| GPIOx_MODER | 0x00 | 32 位 | 模式寄存器:配置引脚为输入、输出、复用功能或模拟模式 |
| GPIOx_OTYPER | 0x04 | 32 位 | 输出类型寄存器:推挽输出或开漏输出 |
| GPIOx_OSPEEDR | 0x08 | 32 位 | 输出速度寄存器:低速、中速、高速、极高速 |
| GPIOx_PUPDR | 0x0C | 32 位 | 上拉/下拉寄存器:无上下拉、上拉、下拉 |
| GPIOx_IDR | 0x10 | 32 位 | 输入数据寄存器:读取引脚当前电平 |
| GPIOx_ODR | 0x14 | 32 位 | 输出数据寄存器:设置引脚输出电平 |
| GPIOx_BSRR | 0x18 | 32 位 | 位设置/复位寄存器:原子操作置位或复位输出 |
| GPIOx_AFRL / AFRH | 0x20 / 0x24 | 32 位 | 复用功能寄存器:选择引脚复用功能编号(AF0~AF15) |
驱动开发提示: 在 Linux 内核中,GPIO 寄存器通常通过
ioremap 映射到虚拟地址空间,然后使用 readl / writel 进行访问。操作 BSRR 寄存器可以实现无中断的原子位操作,避免多线程竞争。
2.3 引脚映射关系:从物理引脚到软件 GPIO 编号
在 SoC 数据手册中,引脚通常以 端口 + 序号 的形式标识(如 PA0、PB5)。但在 Linux 内核中,GPIO 使用一个全局整数编号(GPIO number)来唯一标识。映射规则通常由芯片厂商的 BSP 或设备树定义。
2.3.1 典型映射公式
对于大多数 SoC,GPIO 编号的计算方式为:
GPIO number = (端口索引 × 端口引脚数) + 引脚序号
示例:
端口 A 索引 = 0,端口 B 索引 = 1,端口 C 索引 = 2 ...
每个端口 16 个引脚(0 ~ 15)
PA0 = 0 × 16 + 0 = 0
PA15 = 0 × 16 + 15 = 15
PB0 = 1 × 16 + 0 = 16
PC5 = 2 × 16 + 5 = 37
2.3.2 设备树中的引脚描述
在现代 ARM Linux 中,引脚映射通过设备树(Device Tree)描述。以下是一个典型的 GPIO 引脚配置节点:
&gpio1 {
/* 将 GPIO1_IO12 配置为输出模式,低电平有效 */
led-pin {
gpios = &gpio1 12 GPIO_ACTIVE_LOW;
line-name = "user-led";
};
};
其中 &gpio1 对应 GPIO 控制器实例,12 是控制器内部的引脚偏移,GPIO_ACTIVE_LOW 定义电平极性。
2.4 多路复用与引脚功能选择
现代 SoC 的 GPIO 引脚通常具有 多路复用(MUX) 功能,即一个物理引脚可以服务于 GPIO、UART、I2C、SPI 等多个外设。引脚功能的选择由 复用功能寄存器(AFR) 控制。
常见错误: 驱动开发中,如果忘记配置引脚复用功能,直接操作 GPIO 寄存器可能导致引脚无响应或外设功能异常。务必在初始化阶段通过 AFR 寄存器将引脚切换到 GPIO 模式(通常为 AF0)。
2.4.1 复用功能配置示例
以下代码展示了如何将 PA9 配置为 GPIO 输出模式(非复用):
/* 假设 GPIOA 基地址为 0x50002000 */
#define GPIOA_BASE 0x50002000
#define GPIOA_MODER (GPIOA_BASE + 0x00)
void gpio_pa9_output_init(void)
{
unsigned int *moder = (unsigned int *)ioremap(GPIOA_MODER, 4);
unsigned int val;
val = readl(moder);
/* 清除 PA9 对应的 2 位 (bit18, bit19) */
val &= ~(0x3 << 18);
/* 设置为输出模式: 0b01 */
val |= (0x1 << 18);
writel(val, moder);
iounmap(moder);
}
2.5 本章小结
- GPIO 模块架构: 由 CPU、总线、GPIO 控制器和多个端口组成,每个端口拥有独立的寄存器组。
- 核心寄存器: MODER(模式)、OTYPER(输出类型)、OSPEEDR(速度)、PUPDR(上下拉)、IDR(输入)、ODR(输出)、BSRR(原子置位/复位)、AFR(复用功能)。
- 引脚映射: 物理引脚(如 PA0)通过
端口索引 × 引脚数 + 偏移映射为全局 GPIO 编号。 - 复用功能: 每个引脚可配置为 GPIO 或特定外设功能,需通过 AFR 寄存器选择。