通用异步收发器——UART的RX模块两种实现方式本文链接https://blog.csdn.net/your_username/article/details/your_article_id摘要本文全面介绍了 UART通用异步收发器RX 接收模块的两种实现方式。首先从通信基础概念入手详细讲解了同步/异步通信、串行/并行通信、单工/半双工/全双工等前置知识并深入解析了 UART 通信协议的帧结构空闲位、起始位、数据位、校验位、停止位以及 RS-232、RS-422、RS-485 等物理层标准。第二部分重点展示了两种 Verilog 实现方案基于时序图的计数器方法和基于状态机的设计方法两种方案均包含完整的模块代码和仿真测试代码并提供了仿真波形图对比。文章适合 FPGA/数字电路初学者学习 UART 接收模块的设计原理与实现细节。目录一、uart简介1.1 通信基础概念前置知识1.1.1 同步通信和异步通信1.1.2 串行通信和并行通信1.1.3 单工、半双工、全双工1.1.4 名词解析1.2.1 UART 通信协议协议层・帧结构1.1.2 UART 物理层标准RS-232 / RS-422 / RS-485二、verilog代码编写2.1 RX 时序图及状态转移图2.2 RX 模块代码及仿真2.2.1 代码2.2.2 仿真波形一、UART简介1.1 通信基础概念前置知识1.1.1 同步通信和异步通信区分核心收发是否共享独立时钟信号线同步通信原理设备间单独引出时钟 CLK 信号线双方严格按照时钟边沿同步采样数据帧结构无起始/停止位连续批量传输数据流特点传输效率高、适合高速大数据必须多一根时钟线硬件成本更高代表SPI、I2C。异步通信UART 采用原理无独立时钟线收发各自本地时钟提前约定统一波特率依靠帧内起始 / 停止位做单次字符同步帧结构每一组独立字符带起始位、停止位字符间可存在空闲间隔特点仅需数据线硬件简单单帧带同步冗余位传输效率低于同步代表UART/TTL 串口、RS232、RS485。1.1.2 串行通信和并行通信核心区别为同时传输数据位数并行通信定义多条传输线一个字节 8bit 各位同时并行发送硬件至少 8 根数据线 控制 / 状态线布线多优缺点瞬时速度快、短距离稳定长线易出现串扰、时序 偏移远距离不可用适用场景老式打印机、板内芯片短距离互联。串行通信定义仅 1 根数据线数据 bit逐位顺序传输硬件仅 TX/RX/GND 三根基础线布线极简优缺点线路成本低、抗干扰更好、支持千米级远距离同等时钟下传输耗时更长适用场景UART 串口、RS485、USB、以太网等是嵌入式、工控主流通信方式。1.1.3 单工、半双工、全双工单工通信数据只能单向固定传输A→B 永久单向无反向通道例广播、遥控器发射端。半双工通信双向可收发但同一时刻只能单方向传输分时切换收发仅一组差分信号线例标准 RS485。全双工通信收发拥有独立线路任意时刻可同时双向收发收发通道物理隔离例TTL-UART、RS232、RS422。1.1.4 名词解析波特率 (bps)每秒传输二进制 bit 总数单位 bits per second例 9600bps 代表 1 秒传输 9600 个比特单 bit 时长≈104.2us常用标准值1200/2400/4800/9600/19200/38400/57600/115200TTL 电平单片机原生 UART 电平逻辑 1≈3.3V/5V逻辑 0≈0V仅适合 1 米内短距离板间通信差分信号RS422/485 采用双信号线A/B接收端通过两根线电压差值判断 0/1大幅抑制工业共模干扰单端信号RS232/TTL-UART以 GND 地线为电压参考长线易受干扰LSB 先发UART 数据位固定先传输最低位最后最高位和常规字节书写顺序相反。1.2.1 UART 通信协议协议层・帧结构UART 全称通用异步收发传输器是异步串行全双工通信核心作用CPU 并行字节 ↔ 串行比特流互转一帧完整结构顺序空闲位 → 起始位 → 数据位 → 校验位可选 → 停止位空闲位电平总线无数据时持续高电平逻辑 1补充说明规范中空闲保持通常大于 300 个波特时钟工程中两帧之间依靠停止位高电平充当空闲间隔无数据长时间保持作用接收端持续检测下降沿等待起始信号。起始位固定 1bit电平强制拉低逻辑 0作用产生下降沿触发接收端时钟校准告知接收方“有效数据即将到来”。数据位可配置 5~8bit工程默认 8bit传输顺序LSB 最低位先发MSB 最高位最后用途承载实际业务数据7bit 常用 ASCII 字符8bit 传输完整二进制字节。校验位5 种可选配置无校验最常用校验位为数据位后 1bit用于简单检错双方配置必须完全一致无校验None不传输校验位节省带宽奇校验 (Odd)数据位 校验位中1 的总个数为奇数偶校验 (Even)数据位 校验位中1 的总个数为偶数标记校验 (Mark)校验位永久固定为 1空格校验 (Space)校验位永久固定为 0。停止位可选 1/1.5/2bit高电平逻辑 1作用单帧数据结束标识同时给两端时钟微小偏差提供校准窗口取舍规则停止位越多通信容错越强、抗波特率误差越好但单帧传输耗时增加、速率下降低速远距离场景推荐 2 位停止位1.1.2 UART 物理层标准RS-232 / RS-422 / RS-485三者底层均复用 UART 异步帧协议仅电气电平、传输方式、总线拓扑不同可通过电平转换芯片互相兼容程序逻辑无需修改。RS232传输方式单端非平衡传输以 GND 为电压参考双工模式全双工接线最简 3 根线 TXD、RXD、GND电平规则负逻辑逻辑 1 -3V~-15V逻辑 0 3V~15V与 TTL 电平不兼容必须搭配 MAX232 转换芯片最高速率20Kbps最大距离标准 50 英尺实际稳定仅 15 米总线拓扑严格点对点仅允许 2 台设备互联不可挂载多节点优缺点PC 自带 DB9 串口、接线简单抗干扰差、距离短、易损坏芯片适用电脑调试控制台、短距离设备调试。RS485传输方式差分平衡传输双绞线 A/B 两根信号线抑制共模干扰双工模式标准半双工单组差分线分时收发接线仅 2 根差分线 A、B公共 GND 可选电平规则A-B 电压2V 为逻辑 0A-B-2V 为逻辑 1电平幅值低不易烧毁芯片兼容 TTL最高速率10Mbps短线 10 米内最大距离标准 4000 英尺9600bps 下可达 3000 米总线拓扑总线型多点网络标准支持 32 个收发器增强芯片可扩展至 128 节点优缺点超长距离、工业强抗干扰、多设备组网半双工收发分时需要控制收发使能引脚适用工业 PLC 总线、Modbus RTU、传感器多节点组网。RS422传输方式差分平衡传输双工模式全双工接线4 根差分线TX TX-、RX RX-独立收发差分通道最高速率10Mbps最大距离约 1219 米总线拓扑一点对多点仅 1 个主发送器最多 10 个接收从站硬件要求300 米以上长线必须在总线远端焊接终端匹配电阻优缺点全双工可同时收发、差分抗干扰4 根线布线复杂节点数量远少于 RS-485适用需要同时收发、中等距离多点采集场景。二、Verilog代码编写2.1 UART RX 时序图及状态转移图)2.2 RX 模块代码及仿真2.2.1 代码由时序图写的代码//接收模块:串并转换 //由时序图设计 module rx ( input wire clk , input wire rst_n , input wire uart_rxd_r ,//接收的串行数据 output reg [7:0] data ,//串并转换后的并行数据 output wire rx_doen //握手信号提示我这个字节已经接收完成 ); parameter BAUD 5207 ;//单波特周期数 reg [12:0] baud_cnt ;//单波特计数器 reg [1:0] rx_reg ;//接收串行数据的二级寄存 reg [3:0] bit ;//比特数0-9 reg [7:0] data_reg ;//并行数据寄存信号 reg rx_en ;//数据接收使能 wire flag ;//空闲位下降沿检测信号、也是使能拉高的瞬间 //接收信号的二级寄存 always (posedge clk or negedge rst_n) if(rst_n 0) rx_reg 2b11; else rx_reg {rx_reg[0],uart_rxd_r}; //flag信号 assign flag (rx_reg 2b10 rx_en 0) ? 1 : 0;//二级寄存中遇到下降沿的时候出现10信号,且是在使能信号无效的时候被触发 //先写两个计数器 //单波特计数器 always (posedge clk or negedge rst_n) if(rst_n 0) baud_cnt 0; else if(rx_en) begin if(baud_cnt BAUD) baud_cnt 0; else baud_cnt baud_cnt 1; end else baud_cnt 0; //比特数计数器 always (posedge clk or negedge rst_n) if(rst_n 0) bit 0; else if(rx_en) begin if(baud_cnt BAUD) bit bit 1; else bit bit; end else bit 0; //使能信号 always (posedge clk or negedge rst_n) if(rst_n 0) rx_en 0; else if(flag) rx_en 1; else if(baud_cnt BAUD/2 1 bit 9) rx_en 0; //进行数据的串并转换 always (posedge clk or negedge rst_n) if(rst_n 0) data_reg 0; else if(rx_en)begin if(bit 0 bit 9 baud_cnt BAUD/2)//在1-8的时候找到中间采样点 // data {rx_reg[1],data[7:1]} //通过移位寄存的方法得到 data_reg[bit - 1] rx_reg[1]; //同步变化从最低位开始向高位赋值 else data_reg data_reg; end else data_reg 0; //握手信号与并行数据 assign data (baud_cnt BAUD/2 1 bit 9) ? data_reg : 0; assign rx_doen (baud_cnt BAUD/2 1 bit 9) ? 1 : 0; endmodule时序图的仿真代码timescale 1ns/1ps module tb (); reg clk ; reg rst_n ; reg rxd_r ; wire [7:0] data ; wire rx_doen; initial begin clk 0 ; rst_n 0 ; rxd_r 1 ; #20 rst_n 1 ; #2000 rxd_r 0 ; //空闲位结束起始位的开始拉低了 #1060 rxd_r 1 ; //开始以波特为单位进行数据接收,第一个数据 #1060 rxd_r 1 ; #1060 rxd_r 0 ; #1060 rxd_r 1 ; #1060 rxd_r 1 ; #1060 rxd_r 0 ; #1060 rxd_r 0 ; #1060 rxd_r 1 ; //8个数据接收完成 #1060 rxd_r 1 ; //停止位一半、空闲位一半 #1060 rxd_r 0 ; //空闲位结束起始位的开始拉低了 #1060 rxd_r 0 ; //开始以波特为单位进行数据接收,第一个数据 #1060 rxd_r 0 ; #1060 rxd_r 1 ; #1060 rxd_r 1 ; #1060 rxd_r 0 ; #1060 rxd_r 1 ; #1060 rxd_r 1 ; #1060 rxd_r 1 ; //8个数据接收完成 #1060 rxd_r 1 ; //停止位一半、空闲位一半 #1060 $stop; end always #10 clk ~clk;//50MHZ时钟 rx #( .BAUD (52) //1060ns一个波特 )rx_inst( .clk (clk ), .rst_n (rst_n ), .uart_rxd_r (rxd_r ), .data (data ), .rx_doen (rx_doen) ); endmodule状态机的代码///基于状态机的思想、且加入校验位(这里选取的是偶校验) module rx ( input wire clk , input wire rst_n , input wire rx ,//接收的串行数据:包含空闲位、起始位、数据位、校验位、停止位 output reg [7:0] data_rx ,//数据位输出 output reg done_rx //数据有效信号校验没问题之后 ); parameter KONGXIAN 4b0001 , //空闲状态 JIESHOU 4b0010 , //接收状态 JIAOYAN 4b0100 , //校验状态 STOP 4b1000 , //停止状态 SYSCLK 50_000_000 , //系统时钟50MHZ BAUD 9600 , //波特率 DELAY SYSCLK /BAUD ; //一比特周期数 reg [12:0] baud_cnt ;//单波特计数器 reg [1:0] rx_reg ;//接收串行数据的二级寄存 reg [3:0] bit ;//比特数0-10 reg [8:0] data_reg ;//并行数据寄存信号,以及校验位 wire parity_ok ~(^data_reg);//偶校验通过判断(9位异或为0→通过) reg [3:0] state_c , state_n ;//现态和次态 //接受信号的二级寄存 always (posedge clk or negedge rst_n) if(rst_n 0) rx_reg 2b11; else rx_reg {rx_reg[0],rx}; //状态机 //第一段 always (posedge clk or negedge rst_n) if(rst_n 0) state_c KONGXIAN; else state_c state_n; //第二段、状态转移 always (*) case (state_c) KONGXIAN:state_n (rx_reg 2b10) ? JIESHOU : KONGXIAN ;//下降沿一来就跳到接收态 JIESHOU :state_n (bit 10) ? JIAOYAN : JIESHOU ;//bit10 即跳 JIAOYAN :state_n (parity_ok) ? STOP : KONGXIAN ;//用组合校验结果不能用上一拍 done_rx STOP :state_n (baud_cnt DELAY/2 - 1) ? KONGXIAN: STOP ;//计数半个比特 default :state_n KONGXIAN; endcase //第三段、操作 always (posedge clk or negedge rst_n) if(rst_n 0)begin baud_cnt 0 ; bit 0 ; data_reg 0 ; data_rx 0 ; done_rx 0 ; end else case (state_c) KONGXIAN:begin data_reg 0 ; baud_cnt 0 ; bit 0 ; done_rx 0 ; end JIESHOU :begin if(baud_cnt DELAY - 1)begin baud_cnt 0; bit bit 1; end else begin baud_cnt baud_cnt 1; bit bit; end if(bit 0 bit 10 baud_cnt DELAY/2 - 1)//在1-8的时候找到中间采样 data_reg[bit - 1] rx_reg[1]; //同步变化从最低位开始向高位赋值,最高位是校验位 else data_reg data_reg; end JIAOYAN :begin if(parity_ok) //校验通过才锁存数据(与 done_rx 同拍对齐) data_rx data_reg[7:0]; done_rx parity_ok; //偶校验通过拉高下一拍 STOP 里拉低→短脉冲 end STOP :begin done_rx 1b0; //拉低 done_rx形成 1 拍短脉冲 if(baud_cnt DELAY - 1) baud_cnt 0; else begin baud_cnt baud_cnt 1 ; end end default :begin data_reg 0;baud_cnt 0;bit 0;done_rx 0;end endcase endmodule状态机对应的仿真代码timescale 1ns/1ps module tb (); reg clk ; reg rst_n ; reg rxd_r ; wire [7:0] data ; wire rx_doen; initial begin clk 0 ; rst_n 0 ; rxd_r 1 ; #20 rst_n 1 ; #2000 rxd_r 0 ; //空闲位结束起始位的开始拉低了 #1060 rxd_r 1 ; //校验位偶 #1060 rxd_r 1 ; //开始以波特为单位进行数据接收,第一个数据 #1060 rxd_r 1 ; #1060 rxd_r 0 ; #1060 rxd_r 1 ; #1060 rxd_r 1 ; #1060 rxd_r 0 ; #1060 rxd_r 0 ; #1060 rxd_r 1 ; //8个数据接收完成 #1060 rxd_r 1 ; //停止位一半、空闲位一半 #1060 rxd_r 0 ; //空闲位结束起始位的开始拉低了 #1060 rxd_r 1 ; //校验位偶错的 #1060 rxd_r 0 ; //开始以波特为单位进行数据接收,第一个数据 #1060 rxd_r 0 ; #1060 rxd_r 1 ; #1060 rxd_r 1 ; #1060 rxd_r 0 ; #1060 rxd_r 0 ; #1060 rxd_r 1 ; #1060 rxd_r 1 ; //8个数据接收完成 #1060 rxd_r 1 ; //停止位一半、空闲位一半 #1060 $stop; end always #10 clk ~clk;//50MHZ时钟 rx #( .SYSCLK (52), .BAUD (1), .DELAY (52) //1060ns一个波特 )rx_inst( .clk (clk ), .rst_n (rst_n ), .rx (rxd_r ), .data_rx (data ), .done_rx (rx_doen) ); endmodule2.2.2 仿真波形由时序图仿真出来的波形由状态机仿真出来的波形2.2.3 两种实现方法对比为了帮助读者更清晰地理解两种设计方法的差异与适用场景下表从多个维度对基于时序图的计数器方法和基于状态机的设计方法进行对比分析对比维度基于时序图的计数器方法基于状态机的设计方法设计思路直接根据时序图使用计数器波特计数器、比特计数器和使能信号控制数据采样与转换。逻辑围绕“计数到特定时刻采样”展开。将接收过程抽象为有限状态机FSB明确划分空闲、接收、校验、停止等状态状态转移清晰。代码复杂度相对较低。核心是几个计数器和条件判断代码结构线性易于理解。相对较高。需要定义状态、编写三段式状态机现态、次态、输出逻辑代码结构更模块化。资源占用较少。主要消耗寄存器用于计数器和数据暂存组合逻辑简单。稍多。除了计数器和数据寄存器还需要额外的状态寄存器。但现代FPGA资源丰富差异通常可忽略。抗干扰能力一般。依赖于精确的计数器对齐若波特率偏差或噪声导致起始位检测偏差可能采样错位。较强。状态机在“校验状态”可进行检错如奇偶校验校验失败可丢弃错误帧或请求重发容错性更好。可扩展性较弱。添加新功能如校验位、可变数据位长度需修改多个计数条件和采样逻辑耦合度高。很强。状态机结构清晰新增状态如地址匹配、帧头检测或修改行为如支持不同校验方式只需增加或修改对应状态及转移条件模块化程度高。调试与维护较简单。逻辑直接仿真波形与计数器值对应关系明确。更清晰。通过观察当前状态即可了解模块所处阶段调试时更容易定位问题所在阶段。适用场景适用于对资源极度敏感、功能固定如固定8N1无校验、波特率稳定且环境干扰小的简单应用。适用于功能要求复杂需支持校验、可变帧格式、可靠性要求高、可能需扩展如多字节帧、协议解析的工业或通信系统。总结时序图计数器方法以其直观、资源少的特点适合入门和简单场景状态机方法则以更好的结构性、可扩展性和可靠性成为工程实践中的主流选择。读者可根据项目具体需求进行选择。