串口收发UART(Verilog HDL)
UART(Universal Asynchronous Receiver Transmitter,通用异步收发器)是一种异步串行通讯协议,首要用于计算机和嵌入式体系之间的数据交换。
完结UART通讯的接口规范和总线规范包含RS-232、RS449、RS423和RS485等,接口规范规则了通讯规范的电气特性、传输速率、衔接特性和机械特性。
文章摘要:本篇文章方针规划一个格局为开端位+8位数据(无校验)+中止位的串口收发,接纳PC上位机RS232总线信号后,从头打包转发至PC端显现(构成回环),数据完好无错码状况。
要害词: 异步时钟;亚稳态;异步串行通讯;Verilog HDL
方针规划框图:
【串行帧格局】
UART通讯运用两条信号线进行数据传输:发送数据线(TX)和接纳数据线(RX)。数据以数据帧的方法传输,每个数据帧由开端位、数据位(一般为5到9位)、校验位(可选)和中止位(一般为1到2位)组成。
发送方在发送数据之前,会先发送一个开端位(一般为低电平),然后依照设定的数据位和校验位发送数据,最终发送一个或多个中止位(一般为高电平)以标识数据传输的结束。接纳方在检测到开端位后,会开端接纳数据,直到检测到中止位中止(起止式协议)。一起,接纳方会根据约好的校验方法对数据进行校验。
1、开端位(Start Bit):一般是一个独自的位,逻辑值为0(低电平),表明字符开端,接纳端同步到发送端的数据流。
2、数据位(Data Bits):帧的主体部分,包含传输信息,数据位数量能够根据需求进行装备,常见的有5位、6位、7位、8位等。
3、校验位(Parity Bit):可选位,校验位能够是偶校验(Even Parity)、奇校验(Odd Parity)或无校验(No Parity),偶校验意味着数据位中1的个数(包含校验位)应该是偶数;奇校验则是奇数。
4、中止位(Stop Bits):标志字符的结束,并答应接纳器在接纳下一个字符之前有时刻复位。中止位的数量能够是1位、1.5位或2位。
5、闲暇位(Idle Line):在两个字符之间,线路一般坚持在高电平状况(逻辑值为1)称为闲暇线状况,它答应接纳端在字符之间有时刻来检测和呼应线路状况的改变。
数据传输中,波特率(Baud rate)是一个重要的参数,表明单位时刻内传输的码元符号的个数(一般指二进制位)。波特率并不直接等同于比特率(bit rate),因为每个码元符号或许包含多个比特。但在许多状况下,特别是在串行通讯中,一个码元符号便是一个比特,此刻波特率就等于比特率。
传送速率为960字符/秒,而每个字符为10位(或许是1个开端位、8个数据位和1个中止位)。总的二进制位数传送速率是960字符/秒 × 10位/字符 = 9600位/秒,所以波特率便是9600。
【异步传输存在亚稳态】
在数字电路规划中同步树立至关重要,异步时钟域之间的信号通讯不可防止存在亚稳态问题。UART接纳进程,开端位的检测尤为要害,过错的开端位检测或许导致整个数据包的接纳失利。为了防止因亚稳态导致的采样过错和电路毛病,规划者必须在接口处采纳牢靠的同步化办法。
亚稳态:简略来说,亚稳态是指触发器(Flip-Flop)或其他数字电路元件无法在某个规则的时刻段内到达一个可承认的安稳状况。
发生原因:首要因为违反了触发器的树立和坚持时刻(Setup and Hold Time)要求。在时钟上升沿前后的特定时刻窗口内,假如数据输入端口上的数据发生改变,就会发生时序违规,然后导致亚稳态的呈现,如下图。
体现特征:在安稳期间,触发器或许输出一些中间级电平,或许处于振动状况。而且,这种无用的输出电平能够沿信号通道上的各个触发器级联式传达下去,导致整个体系的不安稳。
削减亚稳态影响:
亚稳态震动时刻(Tmet)关系到后级寄存器的收集安稳问题,Tmet影响要素包含器材的生产工艺、温度、环境等。
当Tmet1时刻长到大于一个采样周期后,那第二级寄存器就会收集到亚稳态,可是从第二级寄存器输出的信号便是相对安稳的了。因为寄存器自身就有减小Tmet时刻让数据快速安稳的作用,第二级寄存器的Tmet2的持续时刻持续延长到大于一个采样周期这种状况虽然会存在,可是其概率是极小的。在第三级寄存器时,数据传输简直到达稳态。
单比特信号从慢速时钟域同步到快速时钟域需求运用打两拍的方法消除亚稳态。榜首级寄存器发生亚稳态并经过自死后能够安稳输出的概率为70%~80%,第二级寄存器能够安稳输出的概率为99%左右,再多加寄存器的级数改进作用就不显着了,Verilog HDL程序如下。
always @(posedge sys_clk or negedge sys_rst)begin
if(!sys_rst) rx_reg1 <= 1'b1;
else rx_reg1 <= rx;
end
always @(posedge sys_clk or negedge sys_rst)begin
if(!sys_rst) rx_reg2 <= 1'b1;
else rx_reg2 <= rx_reg1;
end
always @(posedge sys_clk or negedge sys_rst)begin
if(!sys_rst) rx_reg3 <= 1'b1;
else rx_reg3 <= rx_reg2;
end
【接纳端处理逻辑】
在本篇中,通讯格局:开端位+8位数据+无校检+1位中止位。sys_clk
为体系时钟(在这里仅作为时钟参阅)设置为50Mhz(20ns),异步通讯的波特率设定为9600Baud,则波特率计数器Baud_cnt
最大值 = 1/9600x50_000_000 ≈ 5208。
bit_flag
和 bit_cnt
:位标志和位计数器,用于盯梢当时接纳到的位的数量和状况。rx_reg1
, rx_reg2
, rx_reg3
:接纳寄存器,用于暂存接纳到的数据,rx_flag
和 po_flag
:接纳和发送的标志,用于指示接纳或发送进程的状况。rx
和 rx_data_out
:接纳到的数据和输出数据。详细UART接纳端时序图如下。
数据流:当start
信号被触发时,UART开端接纳数据,并使能work_en
信号,数据位(XData[0]
到 XData[7]
)被接连接纳并存储在rx_reg1
、rx_reg2
、rx_reg3
等寄存器中。在数据接纳进程中,Baud_cnt[12:0]
、bit_flag
和 bit_cnt
用于确保数据以正确的波特率接纳,并盯梢当时位的状况。一旦一切数据位和中止位(Xstop
)都被接纳,UART将rx_data_out
设置为接纳到的数据,并或许设置rx_flag
以指示数据已预备好。
根据时序图,接续完结其他类型信号时序程序逻辑:
always @(posedge sys_clk or negedge sys_rst)begin//处理start_flag逻辑
if(!sys_rst) start_flag <= 1'b0;
else if((rx_reg3 == 1'b1)&&(rx_reg2 == 1'b0)&&(work_en == 1'b0))start_flag <= 1'b1;
else start_flag <= 1'b0;
end
always @(posedge sys_clk or negedge sys_rst)begin//处理work_en逻辑
if(!sys_rst) work_en <= 1'b0;
else if(start_flag) work_en <= 1'b1;
else if((bit_cnt == 4'd8)&&(bit_flag == 1'b1)) work_en <= 1'b0;
else work_en <= work_en;
end
always @(posedge sys_clk or negedge sys_rst)begin//处理baud_cnt逻辑
if(!sys_rst) baud_cnt <= 13'd0;
else if((baud_cnt == BAUD_CNT_MAX - 1'b1)||(work_en == 1'b0))baud_cnt <= 13'd0;
else baud_cnt <= baud_cnt + 1'b1;
end
always @(posedge sys_clk or negedge sys_rst)begin//处理bit_flag逻辑
if(!sys_rst) bit_flag <= 1'b0;
else if(baud_cnt == BAUD_CNT_MAX/2 - 1'b1) bit_flag <= 1'b1;
else bit_flag <= 1'b0;
end
always @(posedge sys_clk or negedge sys_rst)begin//处理bit_cnt逻辑
if(!sys_rst) bit_cnt <= 4'd0;
else if((bit_cnt == 4'd8)&&(bit_flag == 1'b1)) bit_cnt <= 4'd0;
else if(bit_flag) bit_cnt <= bit_cnt + 1'b1;
end
always @(posedge sys_clk or negedge sys_rst)begin//处理rx_data逻辑
if(!sys_rst) rx_data <= 8'b0;
else if((bit_cnt >= 4'd1)&&(bit_cnt<= 4'd8)&&(bit_flag))
rx_data <= {rx_reg3,rx_data[7:1]};
end
always @(posedge sys_clk or negedge sys_rst)begin//处理rx_flag逻辑
if(!sys_rst) rx_flag <= 1'd0;
else if((bit_cnt == 4'd8)&&(bit_flag)) rx_flag <= 1'b1;
else rx_flag <= 1'd0;
end
always @(posedge sys_clk or negedge sys_rst)begin//处理po_data逻辑
if(!sys_rst) po_data <= 8'd0;
else if(rx_flag) po_data <= rx_data;
end
always @(posedge sys_clk or negedge sys_rst)begin//处理po_flag逻辑
if(!sys_rst) po_flag <= 1'b0;
else po_flag <= rx_flag;
end
齐备后,编写了一个简略的仿真程序,验证接纳的时序状况。从下图可知,三级寄存器顺次往后延迟了一个时钟周期,即20ns,这完结了方针希望,用于减小亚稳态影响,确保时序精确。
再看接纳rx信号线数据的处理状况,待一轮字符数据处理完结后,po_data
得到了正确的串行输入数据。波特率计数器在计数到13’d5027后归零,等候使能信号拉高。
【发送端处理逻辑】
较于接纳端,发送端逻辑比较简略。pi_data
:输入的8位数据信号,pi_flag
:输入信号标志。Baud_cnt
为波特率计数器。输入发送标志为高电平,发送端将预备的数据,按位计数输出到串行信号总线tx
,顺次拉低信号线,发送开端标志+8位数据信号。比特计数器计数到4'd9拉低使能线,表明单个字符数据输出结束,拉高信号线标志中止至闲暇状况。
always @(posedge sys_clk or negedge sys_rst)begin//处理work_en逻辑
if(!sys_rst) work_en <= 1'b0;
else if((bit_cnt == 4'd9)&&bit_flag) work_en <= 1'b0;
else if(pi_flag) work_en <= 1'b1;
end
always @(posedge sys_clk or negedge sys_rst)begin//处理baud_cnt逻辑
if(!sys_rst) baud_cnt <= 13'b0;
else if((baud_cnt == BAUD_CNT_MAX - 1'b1)||(!work_en)) baud_cnt <= 13'b0;
else if(work_en) baud_cnt <= baud_cnt + 1'b1;
end
always @(posedge sys_clk or negedge sys_rst)begin//处理bit_cnt逻辑
if(!sys_rst) bit_cnt <= 4'd0;
else if((bit_cnt == 4'd9)&&(bit_flag == 1'b1)) bit_cnt <= 4'd0;
else if((work_en == 1'b1)&&(bit_flag == 1'b1)) bit_cnt <= bit_cnt + 1'b1;
end
always @(posedge sys_clk or negedge sys_rst)begin//处理bit_flag逻辑
if(!sys_rst) bit_flag <= 1'b0;
else if(baud_cnt == 13'd1) bit_flag <= 1'd1;
else bit_flag <= 1'b0;
end
always @(posedge sys_clk or negedge sys_rst)begin//处理tx发送逻辑
if(!sys_rst) tx <= 1'b0;
else if(bit_flag == 1'b1)begin
case(bit_cnt)
0 :tx <= 1'b0;
1 :tx <= pi_data[0];
2 :tx <= pi_data[1];
3 :tx <= pi_data[2];
4 :tx <= pi_data[3];
5 :tx <= pi_data[4];
6 :tx <= pi_data[5];
7 :tx <= pi_data[6];
8 :tx <= pi_data[7];
9 :tx <= 1'b1;
default: tx<= 1'b1;
endcase
end
end
齐备后,编写了仿真程序,验证发送的时序状况。检测到输入标志为高电平,work_en
使能有用,波特率计数器开端计数,比特位计数器待其计数到13'd1时计数加1,这样防止比特错判。
从下面仿真图能够看到,tx发送输出的串行信号时序是与pi_data
坚持一致的。
【全体验证UART】
首要,树立五组恣意字符数据,经UART发送端向UART接纳端转发。经过仿真程序进行剖析,因为接纳端与PC源数据比较,对数据的处理要迟后一个字符周期,即5208x20x10ns,从下图可得到,数据处理串行数据tran_data
后坚持正确,而且输出数据out_data
与源数据坚持一致,即可验证模块规划暂时是没有大问题。
最终,将UART接纳端和发送端模块实例化为文章最初的规划结构。在PC端发送恣意三组数据后,如下图COM11端口接纳数据而且正确,规划到此验证成功,满意希望。
文献参阅:
[1] 野火]FPGA Verilog开发实战攻略——根据Altera EP4CE10 征程Pro开发板 文档 (embedfire.com);
[2] FPGA中亚稳态——让你无处可逃 - 屋檐下的龙卷风 - 博客园 (cnblogs.com);
[3] ww1.microchip.com/downloads/en/DeviceDoc/70000582e.pdf;
[4]万敏. 异步时序电路中的亚稳态规划与剖析[D]. 上海:上海交通大学,2008.
本篇文章中运用的Verilog程序模块,若有需见网页左栏Gitee库房链接:憨大头的妙妙屋: 真美妙! (gitee.com)