3. GPIO 寄存器分类:数据寄存器、方向寄存器、功能选择寄存器、上拉/下拉寄存器详解

在嵌入式 Linux 驱动开发中,GPIO 的硬件操作本质是对一组特定内存映射寄存器的读写。不同厂商的 SoC(如 STM32、i.MX、Allwinner、Rockchip)虽然寄存器命名略有差异,但功能分类高度一致。本章将深入剖析 GPIO 的四大核心寄存器类别:数据寄存器、方向寄存器、功能选择寄存器和上拉/下拉寄存器,并给出典型配置流程。

核心认知:GPIO 寄存器是 CPU 与引脚之间的“翻译官”。驱动开发者通过修改寄存器比特位,控制引脚的电平状态、输入输出方向、复用功能以及内部电阻网络。

3.1 寄存器分类总览

下表对比了四类寄存器的核心功能、位宽与典型操作模式,帮助建立全局认知。

寄存器类别 英文缩写 位宽 核心功能 典型操作
数据寄存器 DR / IDR / ODR 32 位(每引脚 1 位) 读取引脚电平或设置输出电平 读 IDR 获取输入状态;写 ODR 控制输出高/低
方向寄存器 DIR / DDR / GPIO_OE 32 位(每引脚 1 位) 配置引脚为输入或输出模式 写 1 为输出,写 0 为输入
功能选择寄存器 AFR / MUX / ALS 32 位(每引脚 2~4 位) 选择 GPIO 或外设复用功能(如 UART、I2C) 写特定值切换至对应功能模式
上拉/下拉寄存器 PUR / PDR / PULL 32 位(每引脚 2 位) 控制内部上拉或下拉电阻的使能与类型 00=浮空,01=上拉,10=下拉,11=保留
驱动开发要点:在 Linux 内核中,GPIO 子系统通过 struct gpio_chip 抽象这些寄存器操作。驱动开发者通常调用 gpiod_direction_output() 等 API,底层会映射到方向寄存器和数据寄存器的写操作。

3.2 数据寄存器(Data Register)

数据寄存器是 GPIO 与 CPU 数据交换的直接通道。根据方向不同,分为输入数据寄存器(IDR)和输出数据寄存器(ODR)。部分 SoC 将两者合并为一个寄存器,通过读写方向区分。

  • 输入数据寄存器(IDR):只读。读取对应位返回引脚当前电平(0 或 1)。即使引脚配置为输出模式,IDR 仍可反映实际引脚状态(用于回读验证)。
  • 输出数据寄存器(ODR):可读可写。写入值决定输出模式下的引脚电平。写入 1 输出高电平,写入 0 输出低电平。
  • 置位/清除寄存器(BSRR):部分高级 SoC(如 STM32)提供 BSRR,通过写特定位实现原子级置位或清零,避免读-改-写竞争。
常见陷阱:在输出模式下,直接修改 ODR 的某一位时,必须使用读-改-写序列,否则会误改其他引脚状态。推荐使用 BSRR 或内核提供的 gpiod_set_value() 等原子操作 API。
数据寄存器读写路径示意图 数据寄存器读写路径 CPU 核心 写 ODR 读 IDR ODR IDR 驱动输出级 输入采样 GPIO 引脚 (物理电平) 说明: ODR 决定输出电平,IDR 反映实际引脚状态。输出模式下 IDR 可用于回读验证。

3.3 方向寄存器(Direction Register)

方向寄存器决定 GPIO 引脚的数据流向。每个引脚对应 1 位:1 表示输出模式,0 表示输入模式。复位后默认值通常为输入模式(0),以降低功耗和避免引脚冲突。

典型配置流程如下:

  1. 读取方向寄存器当前值(读-改-写保护)。
  2. 修改目标引脚对应位:输出模式置 1,输入模式清 0。
  3. 写回方向寄存器。
  4. (可选)读取回验,确认配置生效。
/* 伪代码:配置 GPIO 引脚为输出模式 */
uint32_t reg_val;
reg_val = readl(GPIO_DIR_REG);          // 读取当前方向寄存器
reg_val |= (1 << PIN_NUMBER);          // 设置对应位为 1(输出)
writel(reg_val, GPIO_DIR_REG);          // 写回寄存器
/* 配置为输入模式 */
reg_val = readl(GPIO_DIR_REG);
reg_val &= ~(1 << PIN_NUMBER);         // 清除对应位(输入)
writel(reg_val, GPIO_DIR_REG);
性能提示:在 Linux 内核中,gpiod_direction_output() 和 gpiod_direction_input() 封装了上述操作。批量配置多个引脚时,建议一次性修改整个寄存器值,避免多次寄存器访问。

3.4 功能选择寄存器(Function Select Register)

现代 SoC 的 GPIO 引脚通常支持多种复用功能(Alternate Function)。功能选择寄存器用于在普通 GPIO 模式和外设复用模式之间切换。每个引脚占用 2~4 位,具体编码由芯片手册定义。

功能编码(2位) 模式说明 典型应用场景
00 普通 GPIO 模式(输入/输出由方向寄存器控制) 通用数字 I/O、按键、LED
01 复用功能 1(如 UART TX) 串口通信
10 复用功能 2(如 I2C SCL) I2C 总线
11 复用功能 3(如 SPI MOSI) SPI 通信
关键约束:配置复用功能前,必须确保方向寄存器与功能选择寄存器不冲突。例如,将引脚设为 UART TX 时,方向寄存器应保持输入模式(由外设硬件自动控制输出),否则可能导致总线竞争。
功能选择寄存器配置流程 功能选择寄存器配置流程 1. 读取 AFR 寄存器 2. 修改对应位编码 3. 写回 AFR 寄存器 验证? OK 失败重试 注意:配置复用功能后,方向寄存器可能由外设硬件接管,驱动不应再手动修改方向位。

3.5 上拉/下拉寄存器(Pull-up/Pull-down Register)

上拉/下拉寄存器控制 GPIO 引脚内部的弱电阻网络,用于在引脚悬空时确定默认电平。每个引脚通常占用 2 位,编码如下:

  • 00:浮空(无内部电阻)——引脚电平由外部电路决定,适用于外部已接上拉/下拉的场景。
  • 01:上拉使能——内部连接上拉电阻(典型值 10kΩ~100kΩ),引脚默认高电平。
  • 10:下拉使能——内部连接下拉电阻,引脚默认低电平。
  • 11:保留——部分 SoC 用于特殊模式(如强驱动或保持器)。
驱动设计建议:输入模式下,若外部信号为开漏或高阻,务必使能内部上拉/下拉,避免引脚电平不确定导致误触发。输出模式下,上拉/下拉通常无效,但部分 SoC 在输出高阻态(Hi-Z)时仍受其影响。
上拉/下拉电阻内部结构 上拉/下拉电阻内部结构 VDD SW_PU R GPIO 引脚 R SW_PD GND 控制逻辑 寄存器编码对应: 00: SW_PU=OFF, SW_PD=OFF (浮空) 01: SW_PU=ON, SW_PD=OFF (上拉) 10: SW_PU=OFF, SW_PD=ON (下拉)

3.6 寄存器配置综合示例

以下代码展示如何通过寄存器操作,将一个 GPIO 引脚配置为输出高电平模式,并启用内部上拉电阻(部分 SoC 支持输出模式下上拉)。

/* 综合配置:GPIO 引脚 P1.2 输出高电平 + 上拉使能 */
#define GPIO_BASE      0x50006000
#define GPIO_DR        (GPIO_BASE + 0x00)
#define GPIO_DIR       (GPIO_BASE + 0x04)
#define GPIO_AFR       (GPIO_BASE + 0x08)
#define GPIO_PULL      (GPIO_BASE + 0x0C)

#define PIN_NUM        2

void gpio_output_high_with_pullup(void)
{
    uint32_t val;

    /* 1. 功能选择:设为普通 GPIO(AFR=00) */
    val = readl(GPIO_AFR);
    val &= ~(0x3 << (PIN_NUM * 2));   // 清除 2 位
    writel(val, GPIO_AFR);

    /* 2. 方向:设为输出 */
    val = readl(GPIO_DIR);
    val |= (1 << PIN_NUM);
    writel(val, GPIO_DIR);

    /* 3. 数据:输出高电平 */
    val = readl(GPIO_DR);
    val |= (1 << PIN_NUM);
    writel(val, GPIO_DR);

    /* 4. 上拉:使能上拉(01) */
    val = readl(GPIO_PULL);
    val &= ~(0x3 << (PIN_NUM * 2));   // 先清除
    val |= (0x1 << (PIN_NUM * 2));     // 设为 01
    writel(val, GPIO_PULL);
}
调试技巧:配置完成后,通过读取 GPIO_DR 和 GPIO_DIR 寄存器回验。若输出模式但引脚电平与预期不符,检查上拉/下拉寄存器是否意外使能,或外部电路存在冲突。

3.7 寄存器配置顺序与依赖关系

四类寄存器之间存在严格的配置依赖关系,错误的顺序可能导致引脚状态异常甚至硬件损坏。下图总结了推荐的配置顺序。

寄存器配置依赖关系 寄存器配置依赖关系 1. 功能选择 (AFR) 2. 方向 (DIR) 3. 数据 (DR) 上拉 可并行配置 配置原则: 先确定功能(GPIO 或复用),再设方向,最后写数据。上拉/下拉可在任意阶段配置,但建议在功能选择之后、方向设置之前完成。

3.8 本章小结

  • 数据寄存器是 GPIO 读写的直接接口,输出用 ODR,输入用 IDR,注意原子操作。
  • 方向寄存器控制引脚数据流向,输出置 1,输入清 0,复位默认输入。
  • 功能选择寄存器决定引脚是普通 GPIO 还是外设复用,编码由芯片手册定义。
  • 上拉/下拉寄存器管理内部电阻网络,避免引脚浮空,输入模式下尤为重要。
  • 配置顺序应遵循:功能选择 → 方向 → 数据,上拉/下拉可灵活穿插。
下章预告:第 4 章将基于本章的寄存器知识,实战演示如何通过 GPIO 子系统 API 实现 LED 驱动,并对比寄存器直接操作与内核 API 的优劣。