3. GPIO与基础外设的低功耗配置

在 nRF 系列 SoC 上,GPIO 本身并不耗电,真正让电流悄悄升高的,往往是引脚电平处于“中间态”、上下拉电阻持续导通、外设时钟一直开着,或者悬空引脚随环境电压漂移导致输入级反复翻转。本章围绕 GPIO Sense、引脚复用、上下拉配置、未使用引脚处理、外设时钟门控和低功耗 GPIO 唤醒六个方面,说明如何把基础外设的静态电流压到微安级,并保留可靠的唤醒能力。

一、先理解 GPIO 的静态电流来源

nRF52/nRF53 的 GPIO 由 P0、P1 两组端口组成,每个引脚可独立配置方向、输入缓冲、上下拉和驱动能力。引脚电流主要来自三处:

  • 输入缓冲翻转:引脚悬空且输入缓冲使能时,电压停在阈值附近,输入级会因噪声反复翻转,产生动态电流。
  • 上下拉电阻:内部上下拉约 13 kΩ,若引脚被外部电路强制拉到相反电平,会持续流过约 VDD/13 kΩ 的电流,3 V 下可达 200 µA 以上。
  • 外设时钟:GPIO 本身不需要时钟,但驱动引脚的 SPI、TWI、UART、PWM 等外设若时钟未门控,会持续消耗电流。

因此低功耗 GPIO 配置的核心思路是:让每个引脚都有确定的电平,且不形成持续电流通路;让不用的外设时钟彻底关闭。

二、GPIO Sense:把引脚变成唤醒源

GPIO Sense(DETECT 机制)允许在系统进入 System OFF 后,由指定引脚的指定电平触发复位唤醒。它与普通 GPIOTE 中断不同:GPIOTE 需要 CPU 和时钟运行,而 Sense 在 System OFF 下由 Always-On 域监测,功耗极低。

配置 Sense 的关键步骤:

  1. 将目标引脚设为输入,并根据外部电路选择上拉或下拉,确保空闲电平确定。
  2. 通过 nrf_gpio_cfg_sense_input(pin, pull, sense) 一次性完成方向、上下拉和 Sense 极性配置。
  3. Sense 极性可选 NRF_GPIO_PIN_SENSE_LOW 或 NRF_GPIO_PIN_SENSE_HIGH,必须与按键按下时的实际电平一致。
  4. 调用 nrf_pwr_mgmt_shutdown(NRF_PWR_MGMT_SHUTDOWN_GOTO_SYSOFF) 进入 System OFF。

例如按键一端接 P0.14、另一端接 GND,按下为低电平,则应配置上拉并选择低电平 Sense:

nrf_gpio_cfg_sense_input(14,
    NRF_GPIO_PIN_PULLUP,
    NRF_GPIO_PIN_SENSE_LOW);

唤醒后芯片会复位重启,程序从复位向量重新执行,因此唤醒后的状态恢复逻辑必须放在初始化流程中,而不能依赖掉电前的 RAM 内容。

三、引脚复用与上下拉配置

nRF 的引脚复用通过外设的 PSEL 寄存器完成,同一引脚同一时刻只能分配给一个外设。低功耗配置时要注意:

  • 外设未使用时,应将其 PSEL 设为 0xFFFFFFFF(断开),避免外设内部逻辑继续驱动引脚。
  • UART 的 TX 空闲时应保持高电平;若外部没有上拉,可配置内部上拉,避免浮空。
  • I²C 的 SDA/SCL 必须外部上拉,内部上拉阻值偏大,不适合作为 I²C 的唯一上拉。
  • SPI 的 SCK、MOSI 在空闲时建议配置为输出低或输入下拉,避免悬空翻转。

上下拉选择的原则是:让引脚在空闲状态下的电平与外部电路的自然电平一致,不产生持续电流。例如按键接 GND,用上拉;按键接 VDD,用下拉。若外部已有上拉,内部再开上拉只会略微降低阻值,通常可以关闭内部上下拉以省电。

四、未使用引脚的处理

未使用引脚是低功耗设计中最容易被忽视的漏电源。推荐做法是:

  • 配置为输入 + 上下拉,或配置为输出固定电平,二者选其一,不要悬空。
  • 若引脚在 PCB 上已接地,配置为输入上拉或输出高;若已接 VDD,配置为输入下拉或输出低。
  • 若引脚完全悬空,优先配置为输出低,或输入下拉,避免输入缓冲翻转。
  • 不要将未使用引脚配置为输出高又外部接地,否则会形成持续灌电流。
在量产固件中,建议在 main() 开头统一遍历所有未使用引脚,按 PCB 实际连接批量配置,避免遗漏。

五、外设时钟门控

nRF 外设默认在复位后处于关闭状态,但一旦被使能,即使不传输数据也会消耗电流。低功耗运行时应遵循“用完即关”的原则:

  • SPI/TWI/UART 传输完成后,调用 nrf_drv_xxx_disable() 或写 ENABLE=0 关闭外设。
  • PWM 停止输出后关闭 PWM 模块,并将输出引脚配置为确定电平。
  • ADC/TEMP 采集完成后关闭,避免持续采样。
  • 使用 nrf_pwr_mgmt_run() 进入 idle 前,确认没有外设仍处于使能状态。

下面这张图展示了从“外设使能”到“进入 System OFF”的引脚与外设状态变化关系,重点标出了哪些节点会产生额外电流,以及正确的关闭顺序。

GPIO 与外设低功耗状态转换 外设使能 SPI/TWI/UART 开启 PSEL 已分配引脚 传输完成 数据已收发 引脚仍被驱动 关闭外设 ENABLE=0 PSEL 断开 引脚重配置 输入+上下拉 或输出固定电平 ⚠ 引脚仍被驱动 可能形成灌/拉电流 ⚠ 外设时钟未关 持续消耗 mA 级电流 System OFF 与 GPIO Sense 唤醒路径 配置 Sense 引脚 上拉 + SENSE_LOW 进入 System OFF 按键按下 P0.14 被拉低 Sense 触发复位 芯片复位重启 从复位向量执行 重新初始化外设 注:System OFF 下 Sense 由 Always-On 域监测,典型静态电流约 0.3~1 µA(示例值,随芯片型号与温度变化)

六、低功耗 GPIO 唤醒实践

下面给出一个完整的按键唤醒示例,目标是在 System OFF 下由 P0.14 按键唤醒,唤醒后点亮 LED 并重新进入低功耗。示例参数基于 nRF52832,实际使用时需按芯片型号和 SDK 版本调整。

#include "nrf_gpio.h"
#include "nrf_pwr_mgmt.h"

#define KEY_PIN     14
#define LED_PIN     17

static void key_wakeup_init(void)
{
    /* 按键接 GND,按下为低电平,上拉 + 低电平 Sense */
    nrf_gpio_cfg_sense_input(KEY_PIN,
        NRF_GPIO_PIN_PULLUP,
        NRF_GPIO_PIN_SENSE_LOW);

    /* LED 输出低,避免上电瞬间点亮 */
    nrf_gpio_cfg_output(LED_PIN);
    nrf_gpio_pin_clear(LED_PIN);
}

int main(void)
{
    key_wakeup_init();

    /* 判断是否为按键唤醒:读取复位原因 */
    if (nrf_pwr_mgmt_shutdown_check() == NRF_PWR_MGMT_SHUTDOWN_GOTO_SYSOFF) {
        /* 示例:点亮 LED 200 ms 表示已唤醒 */
        nrf_gpio_pin_set(LED_PIN);
        nrf_delay_ms(200);
        nrf_gpio_pin_clear(LED_PIN);
    }

    /* 重新进入 System OFF,等待下一次按键 */
    nrf_pwr_mgmt_shutdown(NRF_PWR_MGMT_SHUTDOWN_GOTO_SYSOFF);

    while (1) { }
}

验证时可用 Nordic Power Profiler Kit 或万用表串联在 VDD 上观察电流:进入 System OFF 后电流应降到 1 µA 量级;按下按键瞬间出现一个短脉冲,随后芯片复位重启,电流回到运行态。若静态电流明显偏高,按以下顺序排查:

  • 是否有未使用引脚悬空,导致输入缓冲翻转;
  • 是否有外设(尤其是 UART、SPI)未关闭,时钟仍在运行;
  • 是否有引脚被外部电路强制拉到与内部上下拉相反的电平;
  • Sense 极性是否与按键实际电平一致,导致无法唤醒或误唤醒。
System OFF 下 RAM 内容不保持,唤醒等同于复位。若需要保留少量状态,应使用 RAM Retention 或写入 Flash,而不是依赖普通变量。

把上述配置落实到工程中,建议形成一张引脚配置表,逐脚记录方向、上下拉、Sense 极性和所属外设,在每次修改硬件或固件后复核。GPIO 低功耗没有复杂算法,关键在于“每个引脚都有确定状态、每个外设用完即关、每个唤醒源都经过实测验证”。