第一级流水线:波特率时钟生成与过采样技术
各位同学,今天我们正式开始串口接收模块的流水线改造。第一级流水线要解决什么问题呢?说白了,就是怎么在正确的时刻,准确地采集到串口线上的数据。
串口通信不像SPI或I2C那样有独立的时钟线。它只有一根数据线,发送端按约定的速率往线上丢数据,接收端得自己想办法跟上这个节奏。这个节奏,就是波特率。
波特率时钟的本质
波特率(Baud Rate)定义了每秒传输的符号数。对于UART来说,一个符号就是一个bit。所以115200波特率,就是每秒传115200个bit。每个bit持续的时间是:
1 / 115200 ≈ 8.68 微秒
接收端需要在这个时间窗口内,找到每个bit的中间位置进行采样。为什么是中间?因为边沿容易受噪声干扰,中间最稳定。我在项目中遇到过,有些同学直接拿波特率时钟去采样,结果在边沿附近采到毛刺,数据全乱了。
过采样技术:为什么要采16次?
你可能会问:既然每个bit持续8.68微秒,我直接生成一个8.68微秒的时钟去采样不就行了?
嗯,理论上可以。但实际中,发送端和接收端的时钟不可能完全同步。晶振有误差,温度会漂移。如果只采一次,一旦采样点偏离了bit中心,就可能采到错误的数据。
过采样技术就是为了解决这个问题。我个人的习惯是用16倍过采样,也就是在每个bit时间内采样16次。这样做的优势很明显:
- 抗抖动:16次采样中,多数值可以投票决定真实电平
- 定位准确:可以精确找到起始位的下降沿,误差在1/16 bit以内
- 时钟容错:允许收发双方的时钟有±4%左右的偏差
核心公式:
采样时钟频率 = 波特率 × 过采样倍数
例如:115200 × 16 = 1.8432 MHz
计数器设计:从系统时钟到采样时钟
假设我们的系统时钟是50MHz,要生成1.8432MHz的采样时钟,怎么做?用计数器分频。
先算分频系数:
50,000,000 / 1,843,200 ≈ 27.13
取整为27。但27.13不是整数,意味着每个采样周期会有累积误差。怎么办?
我早期做这个的时候,直接用了27分频,结果跑了几万个bit之后,采样点漂移了将近半个bit。后来我改用了一种叫「累加器分频」的方法,误差被均匀分散,效果好了很多。
累加器分频法(推荐)
这种方法不用取整除法,而是用一个累加器不断累加一个步进值。当累加器溢出时,就产生一个采样时钟脉冲。
// 参数定义
parameter SYS_CLK = 50_000_000; // 系统时钟 50MHz
parameter BAUD_RATE = 115200; // 目标波特率
parameter OVERSAMPLE = 16; // 过采样倍数
// 计算步进值(定点数,高16位为整数部分)
localparam STEP = (BAUD_RATE * OVERSAMPLE * 2**16) / SYS_CLK;
// STEP = (115200 * 16 * 65536) / 50_000_000 ≈ 2416
reg [15:0] accum;
wire sample_clk;
always @(posedge sys_clk) begin
accum <= accum + STEP;
end
assign sample_clk = accum[15]; // 取最高位作为采样时钟
这段代码的精妙之处在于:累加器每16个系统时钟周期左右溢出一次,但溢出时刻不是固定的,而是根据STEP值均匀分布。这样产生的采样时钟,长期来看频率非常准确。
我的经验:累加器位宽建议用16位或24位。位宽越大,频率精度越高,但消耗的寄存器也越多。对于115200波特率,16位足够了。
采样点的选择:为什么是第8个采样点?
有了16倍过采样的时钟,每个bit时间内我们会得到16个采样值。那么,取哪一个作为最终的数据呢?
答案是第8个采样点,也就是正中间的那个。为什么?
- 第0~7个采样点:靠近bit的前半段,可能受前一个bit的边沿影响
- 第8个采样点:正好在bit的中间,最稳定
- 第9~15个采样点:靠近bit的后半段,可能受下一个bit的边沿影响
我曾经在一个项目中,因为采样点选得太靠前,导致在高速率(921600波特率)下频繁出错。后来改成第8个采样点,问题就解决了。
起始位检测:同步的关键
串口接收的起点是检测到起始位(高电平变低电平)。在空闲状态下,RX线为高电平。当检测到下降沿时,启动采样计数器。
但这里有个坑:毛刺也可能产生下降沿。我建议的做法是:
- 连续采样到3个低电平,才确认是真正的起始位
- 确认起始位后,从第8个采样点开始采集数据位
- 每16个采样时钟采集一个数据位
避坑指南:我曾经在一个工业控制项目中,因为没做毛刺滤波,结果电机启动时的电磁干扰让串口误触发了几百次起始位检测。从那以后,我所有的串口模块都加了3次连续采样确认的逻辑。
本章知识体系
下面这张图展示了第一级流水线的核心逻辑:
参数配置参考表
| 系统时钟 | 目标波特率 | 过采样倍数 | 采样时钟频率 | 累加器步进值(16位) |
|---|---|---|---|---|
| 50 MHz | 9600 | 16 | 153.6 kHz | 201 |
| 50 MHz | 115200 | 16 | 1.8432 MHz | 2416 |
| 50 MHz | 921600 | 8 | 7.3728 MHz | 9665 |
| 100 MHz | 115200 | 16 | 1.8432 MHz | 1208 |
小技巧:如果系统时钟不是50MHz的整数倍,别慌。累加器分频法天然支持非整数分频,你只需要重新算一下STEP值就行。我一般用Python或者Excel算好,直接写到参数里。
好了,第一级流水线的核心内容就这些。总结一下:我们用累加器分频法生成了精确的采样时钟,用16倍过采样保证了采样点的稳定性,用第8个采样点获取了最可靠的数据。下一级流水线,我们会用这些采样点来恢复出完整的串口数据帧。