别再死记硬背UART协议了!用这个带FIFO的环回实例彻底搞懂串口通信
2026/6/11 9:24:45 网站建设 项目流程

从波形到字节:用FIFO增强型UART环回实验彻底掌握串口通信

第一次接触UART通信时,看着示波器上跳动的波形和代码里复杂的状态机,我完全无法将理论文档中的"起始位"、"停止位"这些概念与实际信号对应起来。直到亲手实现了一个带FIFO缓冲的环回测试系统,那些抽象的协议条文才真正活了起来。本文将带你用工程师的视角,通过可运行的Verilog实例,理解UART如何将并行数据转化为串行波形,以及FIFO如何优雅地解决数据流速不匹配的问题。

1. UART通信的本质:时间维度上的数据舞蹈

1.1 异步串行通信的时空观

在50MHz的时钟域里,115200bps的波特率意味着每个bit持续约434个时钟周期。这种巨大的速度差异正是理解UART的第一个关键点——时间尺度转换。发送端需要将并行的8位数据"拉伸"成串行的波形,而接收端则要在正确的时间点"采样"这些信号。

示波器上典型的UART帧结构:

空闲状态(高电平) → 起始位(低) → D0 → D1 → ... → D7 → 停止位(高)

注:LSB(最低有效位)先发送是UART的标准做法

1.2 波特率同步的魔术

没有时钟线的异步通信,接收方如何确定采样时刻?答案在于:

  • 起始边沿触发计时
  • 在bit时间中点采样(避免边沿抖动)
  • 累计误差不超过半个bit时间

Verilog实现的关键计数器:

parameter CLK_FREQ = 50_000_000; parameter BAUD_RATE = 115200; localparam BIT_CYCLES = CLK_FREQ / BAUD_RATE; // 434 always @(posedge clk) begin if (state == DATA && cnt_baud == (BIT_CYCLES>>1)) rx_data[bit_cnt] <= rx; // 中点采样 end

2. 状态机:UART协议的执行者

2.1 发送端的三段式状态机

经典的状态机实现清晰地分离了时序逻辑和组合逻辑:

// 状态定义 localparam IDLE = 3'b001; localparam START = 3'b010; localparam DATA = 3'b100; // 状态转移 always @(*) begin case(cstate) IDLE : nstate = (tx_vld) ? START : IDLE; START : nstate = (bit_done) ? DATA : START; DATA : nstate = (bit_done) ? IDLE : DATA; default: nstate = IDLE; endcase end // 输出控制 always @(*) begin case(cstate) IDLE : tx = 1'b1; START : tx = 1'b0; DATA : tx = tx_data[bit_cnt]; default: tx = 1'b1; endcase end

2.2 接收端的亚稳态处理

异步信号rx直接来自外部引脚,必须考虑:

  • 两级寄存器同步消除亚稳态
  • 边沿检测电路
  • 数字滤波(连续多次采样判定)
// 同步链 reg [1:0] rx_sync; always @(posedge clk) rx_sync <= {rx_sync[0], rx}; // 下降沿检测 wire start_detect = rx_sync[1] & ~rx_sync[0];

3. FIFO:数据流速的缓冲器

3.1 为什么需要FIFO?

当发送和接收端处理速度不一致时:

  • 发送突发数据可能丢失
  • 接收端未就绪导致数据覆盖
  • 系统吞吐量下降

FIFO的参数选择考量:

参数典型值设计考虑
数据宽度8bit匹配UART字节长度
深度16-32平衡延迟和资源占用
时钟域单时钟简化设计

3.2 FIFO控制逻辑的精妙之处

module ctrl( input clk, input [7:0] rx_data, input rx_vld, input tx_ready, output tx_vld, output [7:0] tx_data ); fifo u_fifo ( .wrclk(clk), .wrreq(rx_vld && !full), .data(rx_data), .rdclk(clk), .rdreq(tx_ready && !empty), .q(tx_data), .wrfull(full), .rdempty(empty) ); assign tx_vld = tx_ready && !empty; endmodule

关键点:wrreq和rdreq的握手信号避免了溢出和下溢

4. 环回测试:从理论到实践的闭环

4.1 系统级验证方法

  1. 自发自收测试:FPGA发送特定模式(如0x55、0xAA)
  2. 误码率测试:长时间运行统计错误率
  3. 压力测试:极限波特率下的稳定性

4.2 调试技巧宝典

  • 使用SignalTap抓取内部信号
  • 分段验证(先测试单独发送/接收)
  • 波特率容错测试(±3%偏差)
  • 示波器触发设置:下降沿+脉宽触发

常见问题排查表:

现象可能原因解决方案
接收数据错位波特率不匹配检查时钟分频计算
偶发数据丢失FIFO溢出增加深度或流控
停止位错误采样点偏移调整中点采样位置
长时间无响应状态机卡死添加看门狗超时

5. 性能优化进阶之路

5.1 时钟域交叉处理

当发送和接收使用不同时钟时:

  • 异步FIFO的正确配置
  • Gray码转换避免亚稳态
  • 握手机制确保数据完整

5.2 硬件流控实战

RTS/CTS信号的使用场景:

  • 当FIFO接近满时拉高RTS
  • 对方检测到CTS为高时暂停发送
  • 防止数据丢失的硬件保障

Verilog实现片段:

assign rts = (fifo_usedw > FIFO_DEPTH-2); // 预留安全余量 // 发送端逻辑 always @(posedge clk) begin if (cts && tx_vld) send_data <= next_byte; end

在完成这个环回实验后,建议尝试修改波特率参数,观察不同速率下的信号质量变化。实际项目中,我通常会先用较低的波特率(如9600bps)验证基本功能,再逐步提高速率进行压力测试。FIFO深度的选择也需要根据具体应用场景——对于突发数据传输,适当增加深度可以显著降低丢包率。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询