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),并由独立的寄存器组控制。
SoC 内部 GPIO 模块框图 CPU Core ARM / RISC-V 系统总线 AHB / APB GPIO 控制器 寄存器组 + 控制逻辑 端口 A PA0 ~ PA15 端口 B PB0 ~ PB15 端口 C PC0 ~ PC15 中断控制器 (GIC) NVIC / GIC 说明 每个端口拥有独立的寄存器基地址,通过基地址 + 偏移量访问控制寄存器。

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 定义电平极性。

物理引脚 → 软件 GPIO 编号映射关系 物理引脚 PA0 PA1 ... PA15 PB0 PB1 ... 映射逻辑 GPIO number = (port_index × 16) + pin_offset port_index: A=0, B=1, C=2 pin_offset: 0 ~ 15 软件 GPIO 编号 0 (PA0) 1 (PA1) ... 15 (PA15) 16 (PB0) 17 (PB1) ... 不同 SoC 的端口引脚数可能不同(如 16、32),但映射原理一致:端口索引 × 每端口引脚数 + 引脚偏移。

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 寄存器选择。