GPIO、定时器与中断
本文性质声明:本机无开发板,全部内容未实测,为手册级框架。站内对照:conc-mutex(临界区与数据竞争实测)、os-scheduling(优先级与调度延迟)、c-restricted-environment-and-volatile(volatile 与寄存器)。
一、GPIO 的输出模式:推挽 vs 开漏
| 模式 | 行为 | 用途 |
|---|---|---|
| 推挽(push-pull) | 输出强 0 或强 1,能灌/拉电流 | 驱动 LED、MOS 管、电平信号 |
| 开漏(open-drain) | 只能拉低或浮空(高阻) | 线与(多设备共享一根线)、电平转换(上拉电阻到目标电平)、I2C |
开漏 + 上拉的三个场合:多主共享总线(I2C/1-Wire)、电平不匹配(3.3V MCU 驱动 5V 传感器:开漏 + 上拉到 5V,MCU 只拉低——本站 win-audio 量过的跨电压场景的 GPIO 版)、以及”断电时不让它拖累总线”。
输入侧:上拉/下拉/浮空三选一。浮空输入在干扰下乱跳(CMOS 输入阻抗极高,天线效应)——没用到的引脚也要配:要么上拉、要么模拟模式,这是低功耗和 EMC 的共同要求。施密特触发输入能消抖,但别依赖它做按键消抖(第二节)。
二、按键消抖:硬件与状态机两条路
机械触点的弹跳持续 1–20ms——不处理就是一次按下被读成多次。
硬件三法:RC 低通 + 施密特触发器(经典,省软件)、专用消抖芯片、MCU 内置的数字滤波器(有的定时器有输入滤波配置)。软件两法:
- 延时法(简单粗暴):检测到按下
delay(20ms)再读——阻塞式,浪费 CPU,只适合初始化或裸机小循环。 - 状态机法(推荐):定时器(如 5ms tick)里采样,用计数窗口确认:
// 每 5ms 调一次:连续 4 次同状态才认为稳定
if (raw == last_raw) { if (cnt < 4) cnt++; }
else { cnt = 0; last_raw = raw; }
if (cnt == 4) report_stable(last_raw); // 消抖后的稳定状态
状态机法还能直接带上长按/连发/组合键:稳定为按下后开始计时,>1s 报长按、之后每 200ms 报连发——旋钮和键盘的手感全靠这层。中断 + 定时器窗口的组合(中断唤醒、定时器确认)兼顾低功耗和可靠。
三、定时器:计数、PWM、捕获
通用定时器的三种用法,都建立在”预分频器 + 自动重装载计数器”上:
- 基本计数/时基:
PSC分频时钟,ARR定周期——整个系统的心跳(RTOS 的 tick、消抖窗口、超时计时)。SysTick 是 Cortex-M 内置的专用时基(24 位递减),RTOS 通常优先用它。 - PWM:
CNT < CCR输出一种电平、反之另一种——占空比 = CCR/ARR。三板斧:中心对齐 vs 边沿对齐(电机控制关心)、死区(半桥上下管不能同时导通——高级定时器的互补输出+死区插入就是为它)、分辨率陷阱(ARR 定小了占空比调不细,电机高频啸叫的常见原因)。 - 输入捕获:记录外部事件发生时的
CNT值——测频率/占空比/脉宽。测高频用间接法:两个捕获边沿的差值 ÷ 时钟频率。超声波测距、红外解码、编码器读数都走这条路。正交编码器接口(QEI,有的定时器带)把 A/B 相的解码也硬件化了。
定时器的 DMA 联动是性能分水岭:PWM 的 CCR 数组用 DMA 自动搬运(波形合成不占 CPU)、ADC 结果用 DMA 收(emb-adc 会展开)。
四、中断优先级:分组、嵌套与临界区
NVIC 的优先级机制(emb-mcu 提过数值小=优先级高):
- 抢占优先级 vs 子优先级:
NVIC_SetPriorityGrouping把 8 位优先级切成两段。抢占优先级决定嵌套(高的打断低的),子优先级只决定同抢占级时的排队(不嵌套)。分组设错 = “明明配了优先级却看到中断嵌套了”。 - 优先级位数由芯片定:M3 常见可配 4 位(16 级),M0 只有 2 位——移植代码时别假设级数。
- 同优先级不嵌套:低抢占级的中断处理中,同级的后来者排队——临界区的黄金窗口:把”读改写共享变量”的中断源临时降到比使用者低一档,或用
__disable_irq()包住几个指令。
临界区的三条军规:能短必短(本站实测过:一次条件变量唤醒 11.35µs = 186 次原子自旋交接——中断里做的事要按这个尺度掂量);中断里不能等锁(等 = 死等,持锁的中断处理进不来);ISR 和任务共享的数据,要么 volatile + 单字访问,要么关中断保护复合操作(本站 c-restricted-environment 与 conc-mutex 分别从语言和并发侧讲过)。
五、排障索引
| 症状 | 入口 |
|---|---|
| 按键一次触发多次 | 没消抖(第二节);或消抖阈值低于弹跳时长 |
| PWM 高频啸叫 | ARR 太小分辨率不足;死区没配(半桥) |
| 测频在高频时偏低 | 捕获中断处理太慢漏边沿;改用 DMA/定时器从模式 |
| 中断优先级不生效 | 分组理解错(子优先级不嵌套);位数配超了被截断 |
| 偶发死机 | 中断里调了会阻塞的东西;临界区里嵌套了中断改共享数据 |
| 低功耗下电流大 | IO 浮空、定时器时钟没关(emb-lowpower) |
六、未实测清单
- 本文所有 GPIO/定时器行为——无开发板,未测。
- 具体型号的寄存器与中断级数——以你的 Reference Manual 为准。
- 消抖/PWM 的实测效果——未测。
- 用户 Linux 开发机上的 RV1103 项目外设——该机不可达,未涉及。