4. 起始位检测机制:电平跳变检测、采样窗口、噪声抑制、边沿捕获
各位,咱们今天聊点实在的。UART 通信里,起始位检测是接收端的第一道关卡。你想想看,总线上一片空闲高电平,突然来了个低电平——这个下降沿,就是接收端开始工作的发令枪。如果这枪没听清楚,后面所有数据都是废的。
我刚开始做嵌入式驱动时,总觉得起始位检测不就是等个下降沿嘛,有什么难的?结果第一次调板子,通信时好时坏,查了三天才发现是噪声把边沿给淹了。从那以后,我对起始位检测的每一个细节都不敢马虎。
4.1 电平跳变检测:从空闲到起始的那一瞬间
UART 总线空闲时,TX 线保持高电平。发送端要发数据,先把线拉低一个位时间——这就是起始位。接收端要做的,就是捕捉这个高到低的跳变。
说白了,检测方法就两种:
- 硬件边沿检测:用 GPIO 的外部中断或 UART 模块自带的边沿检测电路。速度快,不占 CPU。
- 软件轮询检测:CPU 不断读 RX 引脚电平,发现从 1 变 0 就认为起始位来了。简单,但浪费算力。
我个人习惯,只要芯片有 UART 硬件模块,就绝不用软件轮询。为什么?因为软件轮询的采样时机不可控,你正在处理中断时,可能就错过了边沿。
关键点:电平跳变检测的精度直接决定了起始位定位的准确性。一个位时间的误差,可能导致整个帧的采样偏移。
4.2 采样窗口:在正确的时间点读正确的电平
检测到下降沿只是第一步。接下来要在起始位的中间位置采样,确认这确实是个有效的低电平,而不是毛刺。
标准做法是这样的:
- 检测到下降沿后,启动一个定时器。
- 等待半个位时间(即波特率周期的一半),在起始位的中点采样。
- 如果采样到低电平,确认起始位有效;如果采样到高电平,判定为噪声,丢弃。
为什么要等半个位时间?我解释一下。起始位的长度是一个完整的位时间,如果我在起始位的开始或结束位置采样,很容易受到信号上升/下降沿的抖动影响。中点是最稳定的位置。
来看一个典型的采样窗口代码片段:
// 假设波特率 115200,位时间约 8.68us
// 检测到下降沿后,延时半个位时间采样
void start_bit_detect(void) {
// 等待下降沿
while (RX_PIN_READ() == 1);
// 延时半个位时间
delay_us(BIT_TIME_US / 2);
// 采样确认
if (RX_PIN_READ() == 0) {
// 起始位有效,开始接收数据位
start_bit_valid = 1;
} else {
// 噪声,丢弃
start_bit_valid = 0;
}
}
经验之谈:实际项目中,我建议把采样窗口设计成可配置的。不同晶振精度、不同温度下,位时间会有微小漂移。我曾经在一个项目中把采样点设在 0.5 位时间,结果高温下通信失败,改成 0.45 位时间就好了。
4.3 噪声抑制:别让毛刺骗了你的接收端
工业现场、电机驱动、开关电源……这些环境里,信号线上全是噪声。一个几十纳秒的毛刺,就可能被误判为起始位。
常用的噪声抑制手段有这些:
| 方法 | 原理 | 优缺点 |
|---|---|---|
| 多次采样确认 | 检测到下降沿后,连续采样 3-5 次,全部为低才确认 | 简单可靠,但增加延迟 |
| 数字滤波 | 用移位寄存器对 RX 信号做中值或多数判决滤波 | 效果好,需要少量硬件资源 |
| 硬件施密特触发器 | 利用施密特输入的去抖特性,消除窄脉冲 | 零 CPU 开销,但依赖芯片外设 |
| 定时器窗口 | 只在预期起始位到达的时间窗口内使能边沿检测 | 抗干扰强,实现稍复杂 |
我遇到过最头疼的情况,是电机启动时产生的共模噪声直接耦合到 UART 线上。那个毛刺宽度刚好和起始位差不多,多次采样都躲不过。最后怎么解决的?我在硬件上加了一个 RC 低通滤波,截止频率设在波特率的 3 倍左右,把窄毛刺全滤掉了。
注意:噪声抑制不能过度。滤波太强会把正常的起始位边沿也磨圆了,导致采样点偏移。我曾经见过一个项目,RC 时间常数设得太大,9600 波特率下起始位被拉成了斜坡,接收端根本检测不到下降沿。
4.4 边沿捕获:硬件加速的精准检测
现代 MCU 的 UART 模块,基本都集成了边沿捕获功能。你不需要自己写轮询代码,硬件会自动检测下降沿并触发接收流程。
边沿捕获的核心机制是这样的:
- UART 模块内部有一个边沿检测器,持续监视 RX 引脚。
- 检测到下降沿时,硬件自动清零接收位计数器,并启动内部波特率时钟。
- 后续的数据位采样,全部由硬件时钟驱动,不需要软件干预。
我画了一张图,帮你理清整个起始位检测的流程:
这张图展示了从总线空闲到起始位确认的完整路径。注意那个菱形判断节点——噪声抑制不是可选项,是必须项。我见过太多工程师在实验室里跑得好好的,一到现场就出问题,十有八九是没做噪声处理。
4.5 实际配置中的注意事项
最后,我总结几个实际配置时容易踩的坑:
- 波特率误差:发送端和接收端的波特率误差必须在 ±2% 以内,否则起始位采样点会漂出有效窗口。我一般要求 ±1% 以内才放心。
- 中断优先级:起始位检测相关的中断,优先级要设高。如果被低优先级中断长时间阻塞,可能错过后续数据位的采样窗口。
- 引脚配置:RX 引脚要配置为输入模式,并开启内部上拉。浮空输入在噪声环境下会频繁触发下降沿中断。
- 调试技巧:用逻辑分析仪抓起始位波形,看下降沿是否干净、采样点是否在中间位置。我每次调 UART 驱动,第一件事就是抓波形确认起始位。
一个小技巧:如果你用 STM32 的 USART,可以开启 Oversampling by 16 模式。这样每个位时间采样 16 次,用多数判决确定电平,抗干扰能力比 Oversampling by 8 强很多。代价是最高波特率减半,但大多数应用场景下完全够用。
嗯,起始位检测就聊到这儿。说白了,就是「看准下降沿、稳住采样点、挡住噪声、交给硬件」。把这四点做到位,你的 UART 通信就成功了一半。