0%

STM32--浅析GPIO八大模式

前言

回顾前面三篇博客,我们已经接触过几种 GPIO 模式:

博客 用到的 GPIO 模式
STM32再出发 推挽输出(驱动 LED)
三色LED流水灯 推挽输出(三个 LED)
按键输入 上拉输入(KEY2 内部上拉)+ 浮空输入+外部上拉(KEY1 板载上拉)

除了上述几种模式,GPIO还有其他的输入输出模式。这篇文章就是对于GPIO八大模式的总结和梳理,顺便把前几篇博客里实际用到的 GPIO 模式串联起来分析一下。


GPIO 内部结构速览

在拆解 8 种模式之前,先认识一下 GPIO 引脚内部的核心组件。

image-20260727221629861

关键元件

元件 作用
保护二极管 钳位电压:引脚电压 > VDD 时电流引入 VDD,< VSS 时引入 VSS,防止高压损坏芯片
施密特触发器(TTL肖密特触发器) 将模拟电压转换为干净的数字电平,具备滞回特性,有效抗噪声
上拉/下拉电阻 输入模式下决定悬空引脚的电平状态,约 40kΩ,可软件配置
P-MOS 输出驱动器的上管,导通时将引脚接到 VDD(输出高电平)
N-MOS 输出驱动器的下管,导通时将引脚接到 VSS(输出低电平)
输出寄存器 锁存 CPU 要输出的电平值
输入寄存器 锁存引脚上当前的电平值

GPIO 内部的工作模式,本质上就是控制这些开关元件的通断组合。不同的通断组合 = 不同的工作模式。


输入模式(4种)

输入模式的核心:数据从引脚 → 内部总线(CPU 读取)。

image-20260727213728237

1. 浮空输入

内部状态: 上下拉电阻均断开,施密特触发器导通。

引脚状态: 完全由外部电路决定。如果引脚什么都不接,电平是不确定的——可能是高、可能是低、甚至可能是中间电平。

在 CubeMX 中: 选 GPIO_Input → Pull 选 No pull

实际例子: 按键输入博客里的 KEY1(PB12) 用的就是”浮空输入 + 外部上拉电阻”。板子上已经焊了一个上拉电阻把 PB12 拉到 VDD,所以引脚悬空时是确定的——高电平。按下按键接地,读到低电平。

image-20260727214136258

2. 上拉输入

内部状态: 内部上拉电阻(约 40kΩ)接通到 VDD,施密特触发器导通。

引脚状态: 悬空时默认高电平。外部驱动为低电平时,可以读到低电平。

在 CubeMX 中: 选 GPIO_Input → Pull 选 Pull-up

实际例子: 按键输入博客里的 KEY2(PB13) 用的就是内部上拉。这个引脚板子上没有配外部上拉电阻,所以在 CubeMX 中配置 Pull-up,让芯片内部的电阻来负责拉高。功能和 KEY1 一样,只是上拉电阻的位置不同——一个在芯片外面,一个在芯片里面。

image-20260727214222002

3. 下拉输入

内部状态: 内部下拉电阻(约 40kΩ)接通到 VSS,施密特触发器导通。

引脚状态: 悬空时默认低电平。外部驱动为高电平时,可以读到高电平。

在 CubeMX 中: 选 GPIO_Input → Pull 选 Pull-down

和上拉输入的关系: 上拉和下拉是对称的——上拉是电阻接到 VDD(默认高),下拉是电阻接到 VSS(默认低)。

4. 模拟输入

内部状态: 施密特触发器关闭(数字输入路径断开),引脚直接连接到 ADC 模块的模拟开关。

引脚状态: 不读高低电平,读电压模拟量。

在 CubeMX 中: 引脚功能选 ADC_INx(比如 ADC1_IN0),或直接选 GPIO_Analog

用途: ADC 采样。比如读取电位器电压、温度传感器输出等。后续学到 ADC 时会用到。


输出模式(4种)

输出模式的核心:数据从内部总线 → 引脚(CPU 写值出去),同时也可以回读引脚当前电平。

image-20260727214346566

5. 推挽输出

内部状态: 输出寄存器控制 P-MOS 和 N-MOS 交替导通:

输出 P-MOS N-MOS 引脚电平
1 导通 断开 VDD(高电平)
0 断开 导通 VSS(低电平)

在 CubeMX 中: GPIO_Output → Output Mode 选 Push-Pull

特点: 驱动能力强,高电平和低电平都能主动输出。这是最常用的输出模式。

实际例子: 前两篇博客所有的 LED 点灯代码全都是推挽输出。翻开 CubeMX 配置能看到:

1
2
3
PA7 Output Mode:  Push-Pull
PA6 Output Mode: Push-Pull
PB0 Output Mode: Push-Pull

LED 需要足够的电流才能亮,推挽输出的驱动能力正好满足需求。

下面是推挽输出控制I/O口,输出高电平和低电平的电流流通图(假设I/O口连接一个小灯,小灯另一端连接GND)

image-20260727214716103

image-20260727214908086

6. 开漏输出

内部状态: P-MOS 始终断开(不工作),只有 N-MOS 受控:

输出 P-MOS N-MOS 引脚电平
1 断开 断开 高阻态(引脚浮空)
0 断开 导通 VSS(低电平)

在 CubeMX 中: GPIO_Output → Output Mode 选 Open-Drain

关键区别: 推挽能主动输出高电平,开漏不行。开漏输出高电平时引脚处于高阻态——必须靠外部上拉电阻才能拉高到想要的电压。

下面是开漏输出控制I/O口,输出高电平和低电平的电流流通图(假设I/O口连接一个小灯,小灯另一端连接来自外部的5V而非GND)

注意:此时需要使用5V容忍的I/O口,不能用普通的,否则会使上方的保护二极管长期导通,将5V引入电源中,造成损坏。

image-20260727215442581

image-20260727215312807

推挽输出 vs 开漏输出 对比

推挽输出 开漏输出
高电平 P-MOS 激活 N-MOS 断开,3.3V P-MOS 断开 N-MOS 断开,由外部电路决定
低电平 P-MOS 断开 N-MOS 激活,0V P-MOS 断开 N-MOS 激活,0V
优点 可以直接输出 3.3V 配合外部电路更加灵活
缺点 只能输出 3.3V 高电平实际是高阻态,无法输出电流

为什么需要开漏输出?两个经典场景:

场景一:电平转换
如果芯片是 3.3V 的,但想驱动一个 5V 的器件——开漏输出 + 外部上拉到 5V 就能实现。推挽输出去接的话,3.3V 的高电平可能不被识别为高电平。

场景二:I2C 通信的”线与”
多个器件共享一根数据线(SDA),任何一个器件都可以把总线拉低。如果其中一个输出高电平,另一个同时输出低电平,用推挽输出的话两个 MOS 管会”打架”(短路风险)。开漏输出+上拉电阻天然支持这种”谁都可以拉低”的结构。

7. 复用推挽输出

内部状态: 和推挽输出电路完全一样,只不过控制 P-MOS/N-MOS 的信号不再是输出寄存器,而是片上外设(USART、SPI、定时器等)。

在 CubeMX 中: 配置外设时自动设置(比如把 PA9 配成 USART1_TX,CubeMX 自动设为复用推挽)

实际例子: 以后配串口时,CubeMX 的提示里多半就是这个。

8. 复用开漏输出

内部状态: 和开漏输出电路完全一样,只不过控制 N-MOS 的信号来自片上外设。

在 CubeMX 中: 配置外设时自动设置(比如把 PB6 配成 I2C1_SCL,CubeMX 自动设为复用开漏)

实际例子: I2C 通信。如果使用硬件 I2C 外设(而不是软件模拟),SCL 和 SDA 引脚会被配置为复用开漏输出。

下面两张图为普通的输出和复用的输出的电流流经图

alt_output

alt_input


为什么没有”复用输入”模式?

留意到一个问题:输出模式有普通和复用两种,但输入模式只有 4 种,没有”复用输入”这个分类。

原因是输入模式下,普通输入和复用功能共用同一路信号——都通过施密特触发器,送到输入寄存器,同时也送给片上外设。

所以无论引脚工作在什么模式下(只要不是模拟输入),外设都能读到引脚的值。不需要额外的”复用输入”模式。

有例外:ADC 的模拟输入走的是独立路径(不走施密特触发器),所以模拟输入被划为独立的输入模式。


八大模式速查表

编号 模式 方向 核心特点 典型用途
1 浮空输入 输入 上下拉全关,电平完全由外部决定 外部已有上下拉电阻的按键
2 上拉输入 输入 内部接 VDD,默认高电平 无外部上拉的按键
3 下拉输入 输入 内部接 VSS,默认低电平 默认需为低电平的输入
4 模拟输入 输入 绕开施密特触发器,直通 ADC ADC 采样
5 推挽输出 输出 P/N-MOS 交替,可主动输出高低 驱动 LED、普通数字信号
6 开漏输出 输出 只有 N-MOS,高阻态需外部上拉 I2C、电平转换
7 复用推挽 输出 信号源改由外设控制 串口 TX、PWM、SPI
8 复用开漏 输出 外设控制 + 开漏特性 硬件 I2C

从前面三篇博客回看 GPIO 配置

第一篇:STM32再出发 — PA7 推挽输出

1
2
3
// CubeMX 配置:PA7 → GPIO_Output, Push-Pull, No pull, Low speed
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_7); // 翻转电平
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_7, GPIO_PIN_SET); // 拉高

LED 是纯数字负载,只需要开关两个状态就够。推挽输出的高电平驱动电流足以点亮 LED。

第二篇:三色LED流水灯 — 三个推挽输出

1
2
3
PB0 → Push-Pull  (LED_RED)
PA7 → Push-Pull (LED_GREEN)
PA6 → Push-Pull (LED_BLUE)

三个 LED 都一样,推挽输出 + 状态机控制亮灭组合。没有特殊情况需要特殊模式。

第三篇:按键输入 — KEY1 浮空输入,KEY2 上拉输入

1
2
3
4
// KEY1(PB12):浮空输入,板载外部上拉 → 按下读低电平
// KEY2(PB13):内部上拉输入,CubeMX 配 Pull-up → 按下读低电平
if (HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin) == GPIO_PIN_RESET)
// 按键被按下

这里就体现了两种输入模式的选择差异——外部已有上拉电阻的用浮空输入,没有的就用内部上拉。 同一个功能(按键检测),因为硬件设计不同,选择的 GPIO 模式也不同。


进阶:一些值得思考的问题

1. 推挽输出能同时输出高和低吗?

不能。 P-MOS 和 N-MOS 是交替工作的,不可能同时导通。如果两者不小心同时导通了(称为”穿通”),VDD 和 VSS 直接短路,电流瞬间增大,芯片会发热甚至烧毁。幸好 CubeMX 和 HAL 库会处理好这些,不需要我们操心。

2. 开漏输出为什么不内置上拉电阻?

因为上拉电阻的值需要根据应用场景决定。I2C 总线通常用 4.7kΩ,高速时会用 1.5kΩ,低功耗场合会用到 10kΩ。ST 不知道你的应用场景,所以把选择权留给了用户。

3. GPIO 速度(Speed)选 Low 还是 High?

与 GPIO 模式无关但值得注意。在 CubeMX 配置里有个 Maximum output speed 选项:

速度等级 适用场景
Low (2MHz) LED、按键、普通 GPIO 信号
Medium (10MHz) 串口、中等频率信号
High (50MHz) SPI、高速通信

速度越高,功耗越大、电磁干扰越强。所以对于 LED 这种低速应用,选 Low 就够了,没必要开 High。前两篇博客里选的 Low 是正确的选择。


总结

学完这 8 种模式再回头看 CubeMX 的引脚配置界面,选项就不再是零散的下拉菜单了——每个选项对应的是内部某条电路的开关状态:

  • 选 Input 还是 Output → 决定输入驱动还是输出驱动接入引脚
  • 选 Pull-up/No pull/Pull-down → 决定内部上拉/下拉电阻是否接通
  • 选 Push-Pull/Open-Drain → 决定 P-MOS 是否参与工作
  • 选 GPIO/Alternate → 决定控制信号来自寄存器还是外设

理解了内部电路的结构,CubeMX 配置就不再是”乱填的”,而是——我知道我配置了什么,也知道为什么这么配。

学完这 8 种模式之后,下一步就可以学习中断(EXTI)了——当按键不需要靠 while 循环轮询,而是按下时 CPU 主动响应,就是中断的用武之地。这也是按键输入博客末尾提到的”进阶思路”。


参考资源