02-GPIO输出.md 7.0 KB

02 GPIO 输出 —— 点灯(HAL)

目标:控制 PC13(板载 LED)以 500ms 间隔闪烁

IO 端口内部结构

                      VDD
                       │
                 ┌─────┤
                 │P-MOS│    P-MOS 导通 = 引脚输出高电平
                 │     │
    输出数据 ←──┐┴──  │
                 │     │
                 │N-MOS│    N-MOS 导通 = 引脚输出低电平
                 │     │
                 └─┬───┤
                   │    │
                    └────┘
                       │
                     GND

详细工作原理

GPIO 端口位结构

GPIO结构框图

STM32 GPIO 的每个引脚内部由以下模块构成:

  1. 保护二极管:VDD 与引脚之间接一个上钳位二极管,引脚与 GND 之间接一个下钳位二极管。当外部电压超过 VDD + 0.5V 或低于 GND - 0.5V 时,二极管导通将电压钳位在安全范围,防止内部 CMOS 电路损坏。
  2. 输出驱动级:由一对互补的 P-MOS(上管)和 N-MOS(下管)组成,通过控制两管的栅极实现推挽或开漏输出。
  3. 输出数据寄存器(ODR):32 位寄存器,每位对应一个 GPIO 引脚。写 ODR 控制输出电平,但读 ODR 返回的是上次写入的值而非引脚实际电平。
  4. 位设置/复位寄存器(BSRR):32 位寄存器,低 16 位写 1 置位(对应 ODR 位写 1),高 16 位写 1 复位(对应 ODR 位写 0)。BSRR 写入是原子操作,无需关中断,而读-改-写 ODR 可能被中断打断产生毛刺。
  5. 输出速度控制:通过输出驱动器的电流限幅和 slew rate 控制逻辑,限制输出波形的上升/下降时间。

推挽输出(Push-Pull)工作原理

推挽与开漏

  • 写 1:P-MOS 导通,N-MOS 关断,引脚直接连接到 VDD(3.3V),输出高电平。
  • 写 0:P-MOS 关断,N-MOS 导通,引脚直接连接到 GND(0V),输出低电平。
  • 双 MOS 交替工作,输出阻抗低,驱动能力强。适合驱动 LED、普通数字逻辑输入。

开漏输出(Open-Drain)工作原理

  • 写 1:P-MOS 始终关断,N-MOS 关断,引脚呈高阻态(Hi-Z),电平由外部上拉电阻决定。
  • 写 0:N-MOS 导通,引脚接到 GND,输出低电平。
  • 开漏输出用于线或(Wired-AND) 总线结构,如 I2C 的 SDA/SCL 线,多个器件可共享一根线;也用于驱动比 VDD 更高电压的负载(输出高电平时由外部上拉拉到目标电压)。

输出电流能力

STM32F103 GPIO 的直流输出特性:

  • I_OH(max)(源电流,高电平输出):约 25mA(单引脚),全芯片总和不超过 150mA。
  • I_OL(max)(灌电流,低电平输出):约 25mA(单引脚)。
  • 实际使用时建议单引脚不超过 20mA,直接驱动 LED 需串限流电阻:R = (VDD - V_F) / I_LED

输出速度与压摆率

GPIO 的三档输出速度对应不同的驱动级压摆率(slew rate):

档位 上升/下降时间 对应源端阻抗 适用频率上限
2MHz(Low) ~125ns ~40Ω ≤ 2MHz
10MHz(Medium) ~25ns ~20Ω ≤ 10MHz
50MHz(High) ~5ns ~10Ω ≤ 50MHz
  • 上升/下降时间由 IO 驱动器的输出晶体管的尺寸和偏置电流决定,档位越高驱动级提供的瞬态电流越大,边沿越陡。
  • 边沿越陡,信号中的高频谐波分量越多,EMI 辐射越强。对于 LED 等低速信号默认选 Low(2MHz)即可。

在推挽输出 (Push-Pull) 模式下,P-MOS 和 N-MOS 交替导通:

  • 写 1 → P-MOS 导通 → 引脚接 VDD (3.3V)
  • 写 0 → N-MOS 导通 → 引脚接 GND (0V)

在开漏输出 (Open-Drain) 模式下,P-MOS 被断开:

  • 写 1 → 高阻态(引脚既不高也不低,需要外部上拉电阻拉到 VDD)
  • 写 0 → N-MOS 导通 → 引脚接 GND

板载 LED 通常接 PC13,低电平点亮:GPIO_PIN_RESET(0) = 亮, GPIO_PIN_SET(1) = 灭

GPIO 8 种工作模式速查

STM32 的 GPIO 有 8 种模式,分为 4 种输入 + 2 种输出 + 2 种复用:

模式 宏定义 用途
模拟输入 GPIO_MODE_ANALOG ADC 输入
浮空输入 GPIO_MODE_IN_FLOATING 外部有上下拉时用
上拉输入 GPIO_MODE_IPU 按键(松开高电平)
下拉输入 GPIO_MODE_IPD 按键(松开低电平)
推挽输出 GPIO_MODE_OUTPUT_PP 普通 LED、普通 IO
开漏输出 GPIO_MODE_OUTPUT_OD I2C SDA/SCL
复用推挽 GPIO_MODE_AF_PP USART TX、SPI SCK/MOSI
复用开漏 GPIO_MODE_AF_OD I2C 的复用模式

CubeMX 配置

  • Pinout → 选中 PC13 → 设为 GPIO_Output
  • 参数设置:
    • GPIO output level:Low(初始状态熄灭,改为 High 就是初始点亮)
    • GPIO mode:Output Push-Pull
    • Pull-up/Pull-down:No pull(或 Pull-up)
    • Maximum output speed:Low(LED 用低速即可)

关键 HAL API

函数 说明
HAL_GPIO_WritePin(GPIOx, GPIO_PIN_x, PinState) 设置引脚为高/低电平
HAL_GPIO_TogglePin(GPIOx, GPIO_PIN_x) 翻转引脚电平
HAL_Delay(uint32_t Delay) 毫秒级阻塞延时(依赖 SysTick 中断)

PinState

  • GPIO_PIN_SET = 高电平 (1)
  • GPIO_PIN_RESET = 低电平 (0)

完整代码

int main(void)
{
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();

    while (1)
    {
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET);  // 点亮
        HAL_Delay(500);
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET);    // 熄灭
        HAL_Delay(500);
    }
}

或用 Toggle 简化:

while (1)
{
    HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);
    HAL_Delay(500);
}

GPIO 输出速度的选择

GPIO 有 3 档输出速度,需要正确匹配应用场景:

速度档位 上升/下降时间 适用场景 理由
Low (2MHz) ~125ns LED、按键、普通 GPIO 最慢 = EMI 最小,省电
Medium (10MHz) ~25ns 几 MHz 以下的 SPI/I2C 中等频率够用
High (50MHz) ~5ns 高速 SPI、SDIO、FSMC 速度优先,EMI 较大

输出速度指 IO 驱动电路的响应速度,不是信号频率上限。选过高速度会增加功耗和 EMI 辐射。能用 Low 就选 Low。

常见坑点

  • ⚠️ 先查原理图确认 LED 极性:低电平点亮(RESET=亮)还是高电平点亮(SET=亮)?
  • ⚠️ HAL_Init() 必须在 HAL_Delay() 之前调用,否则 SysTick 未初始化,延时无效
  • ⚠️ GPIO 时钟在 MX_GPIO_Init() 中自动开启 (__HAL_RCC_GPIOC_CLK_ENABLE()),不需要手动加
  • ⚠️ 复用功能(如 USART 的 TX 引脚)必须选 AF_PPAF_OD,不能是普通 Output