对于学习STM32时,GPIO是一个最常用的一个结构,帮助实现了输入输出控制、上拉/下拉电阻、推挽/开漏等功能。但尽管十分常用,但如果没有好好的深入了解这一功能结构。在之后的STM32学习中时常会碰到配置GPIO引脚但纠结于要用开漏,推挽,还是复用等一系列问题,好像掌握了,但却又不知道如何去判断使用。因此,我直接从最底层的电路结构开始,详细讲述GPIO。
1.钳位保护二极管:结构、电性与原理
+3.3V (VDD)
|
┌──┴──┐
│ D1↑ │ ← 保护上拉二极管(正向:阳极→阴极)
I/O Pin ──┤ ├──→ 内部逻辑
│ D2↓ │ ← 保护下拉二极管(反向:阴极→阳极)
└──┬──┘
|
GND
工作原理:
当 输入电压 > VDD + Vf ≈ 3.3 + 0.6 = 3.9V:
上方二极管 D1 正向导通,电流流向芯片内部 VDD,防止高压损坏内部逻辑电路
当 输入电压 < VSS – Vf ≈ 0V – 0.6V = -0.6V:
下方二极管 D2 正向导通,把异常负电压释放到地(VSS)
可能大家会问,vf是什么
Vf = 正向导通压降(Forward Voltage)
对于硅二极管,Vf ≈ 0.6~0.7V
表示当二极管阳极比阴极高约0.6V,它就会导通,允许电流流过
它的作用:
抵御静电放电(ESD)
抵御高电压误触(例如误接 5V)
保证引脚输入不会烧毁内部 MOSFET 栅极
注意:
这些二极管只能承受mA 级别电流(短时间)
如果长时间高压接入,会导致 VDD 被拖高,整个芯片不稳定或损坏
若你需要外接 5V 信号,应在串口前加 限流电阻(如 330Ω) 或光耦/电平转换芯片
2.上拉 / 下拉电阻与开关
VDD GND
| |
┃30kΩ ┃30kΩ
| |
┌─┴─────┐ ┌──────┴─┐
│上拉开关│ │下拉开关│
└──┬────┘ └────┬──┘
| |
└───GPIO 引脚───┘
它的作用:
在输入状态下,引脚可能接了按钮、传感器,有时候没有接任何东西
如果悬空,电平状态是不确定的 → 导致输入一直抖动或变化
上拉使其默认电平为高,下拉使其默认为低
稳定电平是数字逻辑正常运行的前提条件
开关控制原理
每条电阻前面有个 MOSFET 开关
由 PUPDR 寄存器控制导通:
00:全部关闭(浮空输入)
01:上拉开关导通
10:下拉开关导通
注意事项:
这些上/下拉是内部电阻,阻值较大(30~50kΩ)
不适合做电流负载,只是用于逻辑维持
开漏模式下,必须配合外部上拉或开启内部上拉
输入模式 | 按键-GND | 上拉电阻 | 松手时不悬空 |
输入模式 | 按键-VCC | 下拉电阻 | 松手时不悬空 |
输入模式 | 集电极/光耦输出 | 上拉电阻 | 拉成高电平 |
输出模式(开漏) | 接信号线/I²C | 上拉电阻 | 提供高电平能力 |
输出模式(推挽) | – | 不需要电阻 | 主动输出,电平稳定 |
总结
只有“悬空”才需要“拉”,只有“不能主动高”才需要“上拉”
推挽不用拉,输入/开漏必须拉!
3.TTL施密特触发器
施密特触发器的本质:
是一种带滞回特性的电压比较器。
它的作用是:避免输入信号在电平变化临界点抖动时产生多次误触发。
滞回特性图
(自己画了个小图便于理解)
滞回解释:
上升时要超过 高门槛(V_H)才认为是 1
下降时要低于 低门槛(V_L)才认为是 0
中间的电压变化不改变输出,避免毛刺、抖动
为什么 STM32 GPIO 输入必须使用施密特触发器
假设你用 GPIO 检测按钮输入:
按钮抬起/按下瞬间,可能存在抖动或接触不良
电压可能在 1.5V 附近多次震荡
没有施密特触发器时,CPU可能每次读都不一样
使用施密特触发器后,输入稳定可靠
适应模拟输入或缓慢变化信号
不是所有数字输入都是方波!有些是缓慢变化的模拟信号:(可以参考我的小图)
电容充放电
长电缆信号延迟
没有滞回,系统会频繁抖动
施密特触发器提供了数字逻辑输入的“安全门槛”
按钮输入 | 必须有 | 有机械抖动,易误判 |
电容触发信号 | 必须有 | 信号变化慢 |
模拟信号采样 | 不要有 | 会影响 ADC 结果,应设置为模拟模式断开缓冲器 |
数字外设输入(USART、SPI) | 必须有 | 抗干扰要求高 |
高速通信(USB、CAN) | 会关闭缓冲器 | 专用引脚自带高速输入电路 |
4.输出驱动结构:P-MOS & N-MOS
VDD ──┬────────────┐
│ P-MOS │
└────▲───────┘
│
GPIO ─────→ 引脚
│
┌────▼───────┐
│ N-MOS │
└────────────┘
│
GND
P-MOS(上拉):
源极接 VDD,漏极接 GPIO
当栅极电压远低于源极时导通
即 G = 0V → 导通 → 引脚输出 VDD(高电平)
N-MOS(下拉):
源极接 GND,漏极接 GPIO
当栅极电压远高于源极时导通
即 G = 3.3V → 导通 → 引脚接地(低电平)
推挽模式(两者交替导通):
输出高 → P-MOS ON, N-MOS OFF → 引脚 = VDD
输出低 → P-MOS OFF, N-MOS ON → 引脚 = GND
开漏模式(只用 N-MOS):
输出高 → N-MOS OFF → 悬空
输出低 → N-MOS ON → 拉低
它的作用:
是引脚能否提供强电平驱动的核心器件
确保引脚能输出干净、强劲、低阻抗的电平
所以说什么时候用推挽什么时候用开漏
我总结了一个常用规律
“我一个人控制一根线” → 用推挽
“很多人共用一根线” → 用开漏
控制负载(LED、电机、蜂鸣器) | 推挽 | 我要“拉高也拉低”,就选推挽 |
跟别人共用一根数据线(I2C、1-wire) | 开漏 | 多人一起用线,避免冲突,用开漏 |
连接 NPN 三极管/光耦/继电器 | 开漏 | 我要“只拉低”,对方自己上拉 |
通信(UART、SPI) | 推挽 | 数字波形必须干净,高低都要出 |
多个从机/芯片连接一根中断线 | 开漏 | 多人发信号 → 只能拉低 → 开漏 |
STM32 控制 5V 设备(电平不兼容) | 开漏 | STM32 只能悬空,靠 5V 上拉 |
因此,总结一下。
我用 GPIO 输出一个信号 →
┌──我只用自己控制这根线?
│ ├──是 → 推挽!
│ └──否 →
│ ┌──有多个设备共用这根线?
│ │ └──是 → 开漏!
│ └──控制目标是集电极输入(如光耦)?
│ └──是 → 开漏!
└──我要输出 PWM、数字信号、UART? → 推挽!
5.输入输出寄存器
ODR:输出数据寄存器
控制输出状态(置1输出高,置0输出低)
通过逻辑控制 P-MOS/N-MOS 开关
IDR:输入数据寄存器
读取 TTL触发器输出的值
程序判断引脚是否为高电平或低电平
BSRR:原子置位/清零寄存器
写 1 到低16位 → 设置对应引脚为高
写 1 到高16位 → 清除对应引脚为低
一次性修改多个引脚而不会中断打断 → 原子操作
6.复用输入/输出控制模块
控制逻辑:
MODER 控制当前引脚模式:
00 = 输入
01 = 输出
10 = 复用功能
11 = 模拟
AFRL / AFRH 选择复用功能编号(0~15)
AF1 = TIM
AF7 = USART
AF4 = I2C(具体看STM32引脚手册)
它的作用:
实现 GPIO 引脚的“多功能多用途” → 极大提升资源利用率
每个引脚最多可复用 16 种不同外设通道
何时用呢
不由你手动控制电平,而是由外设控制这个引脚电平!
USART(串口) | TX 引脚 | 串口要自动发送数据,不再由你手动控制引脚高低 |
TIM(定时器) | PWM 引脚 | 定时器自动输出脉冲波形 |
SPI(串行外设) | MOSI、SCK | 波形自动由 SPI 控制 |
CAN 总线 | TX 引脚 | 高速协议必须由外设模块控制 |
I²C 总线 | SDA、SCL | 需要硬件产生 START、ACK、STOP 等条件 |
只要你的某个引脚需要被一个“硬件外设”控制电平变化,就必须配置成复用功能输出。
MODER | 控制引脚的模式(输入 / 输出 / 复用 / 模拟) |
OTYPER | 控制输出类型(推挽 / 开漏) |
OSPEEDR | 控制输出速度(低 / 中 / 高 / 极高) |
PUPDR | 控制是否上拉/下拉 |
IDR | 输入数据读取 |
ODR | 输出数据设置(当模式为输出时) |
AFR[0]/AFR[1] | 控制复用功能选择(AF0 ~ AF15) |
钳位二极管 | 过压保护 | 无需控制 | 防止ESD烧毁内部 |
上下拉电阻 | 电平默认值 | PUPDR | 保证输入稳定 |
TTL触发器 | 电平采样 | 自动生效 | 将模拟变为数字电平 |
输入驱动器 | 连接外设 | MODER+AFR | 把IO信号输入功能模块 |
输出MOS结构 | 电平驱动 | ODR/BSRR + OTYPER | 驱动输出强电平 |
控制寄存器 | 软件接口 | CPU读写 | 软件对硬件的唯一桥梁 |
复用控制 | 多用共享 | MODER + AFRL/H | 节省引脚实现多功能 |
评论前必须登录!
注册