一、接收模块uart_rx.v
UART协议,空闲时,TX和RX数据线都是通过上拉电阻拉高的状态,这样才能在起始位到来时检测到一个下降的边沿。
UART数据格式
uart_rx.v模块输入输出示意图
-
RX_start。首先,找到起始位的开始时刻RX_start,这是一个接收字节的最一开始。对RX打拍两次得到RX_1、RX_2。并找出传输每个字节的RX的起始时刻RX_start。
(注意!:只有在cnt=0时RX出现的下降沿才是起始时刻,否则RX传输过程中也会有由高电平变低电平的情况,这种情况显然不能判定为起始时刻)
- cnt。最小是0,最大是传输(1+8+0.5)byte所用时间。cnt平时为0,RX_start出现时cnt立刻开始累加,一直累加到最大值才停止然后恢复到0,等待下一个RX_start到来从新触发cnt累加。cnt不为零的时候,就是说明RX线上正在传输一个字节。
- 在何时刻读取RX线上的数据位?定义一个8位的临时寄存器——RX_data_r。在cnt累加到bit1_OK时,到达LSB最低位的中间时刻,中间时刻是最稳定的所以选择在每一位的中间时刻保存该位。以此类推,当cnt累加到bit8_OK时即保存到第8位MSB,这样就保存完一个字节。
- 输出结果RX_data、RX_OK。每次都是在cnt累加到bitstop_OK处,把RX_data_r赋值给RX_data,同时产生一个与数据同步的单脉冲RX_OK告知外界本字节接收完且数据可用(注意!:cnt累加到bitstop_OK处时,其实不算完全接收完,因为刚刚接收完停止位的一半)
- uart_rx.v程序如下:
module uart_rx#(parameter OSC = 50_000_000,parameter BPS = 9600,parameter bit_1_cnt = 5208, //bit_1_cnt = OSC/BPS。代表接收1位UART数据时,需要的clk周期个数parameter bit_0_5_cnt = 2604, //bit_0_5_cnt = bit_1_cnt/2。代表接收0.5位UART数据时,需要的clk周期个数parameter cnt_max = 52083 //cnt_max = bit_1_cnt*10。代表接收10位UART数据时,需要的clk周期个数
)(input clk,input RSTn,//input RX,output reg [7:0] RX_data, //接收到的字节数据,在停止位的中间时刻被赋值生效output reg RX_OK //当一个字节传输完后,也就接收完毕,在停止位结束的最后一刻产生一个接收完成单脉冲信号。RX_data和RX_OK不同步
);//******************************************************
// parameter
//******************************************************
localparam bit1_OK = bit_0_5_cnt+bit_1_cnt;
localparam bit2_OK = bit_0_5_cnt+bit_1_cnt+bit_1_cnt;
localparam bit3_OK = bit_0_5_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt;
localparam bit4_OK = bit_0_5_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt;
localparam bit5_OK = bit_0_5_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt;
localparam bit6_OK = bit_0_5_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt;
localparam bit7_OK = bit_0_5_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt;
localparam bit8_OK = bit_0_5_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt;
localparam bitstop_OK = bit_0_5_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt;//-------------------------RX_1,RX_2-----------------------------------
reg RX_1,RX_2; //对输入进来的RX信号打两拍,方便检测出下降沿
wire RX_start; //起始位到来那一时刻,产生一个单脉冲信号//-------------------------cnt-----------------------------------
reg [31:0] cnt;//-------------------------RX_data_r-----------------------------------
reg [7:0] RX_data_r;//******************************************************
// RX_1,RX_2
//******************************************************
always@(posedge clk or negedge RSTn)beginif(!RSTn)beginRX_1<=1'b0;RX_2<=1'b0;endelse beginRX_1<=RX;RX_2<=RX_1;end
endassign RX_start = ((~RX_1&RX_2)&&(cnt==32'd0))?1'b1:1'b0;
//******************************************************
// cnt
//******************************************************
always@(posedge clk or negedge RSTn)beginif(!RSTn)cnt<=32'd0;else if(cnt==32'd0&&RX_start==1'b1) //平常cnt等于0,当检测到RX_start脉冲,就开始计数,一直计10个UART比特位发送时长(根据波特率不同,cnt计数最大值也不同)cnt<=32'd1;else if(cnt!=32'd0&&cnt<cnt_max-bit_0_5_cnt)cnt<=cnt+1'b1;else if(cnt>=cnt_max-bit_0_5_cnt)cnt<=32'd0;else cnt<=cnt;
end
//******************************************************
// RX_data_r
//******************************************************
always@(posedge clk or negedge RSTn)beginif(!RSTn)RX_data_r<=8'd0;else if(cnt==bit1_OK)RX_data_r<={RX,RX_data_r[7:1]};else if(cnt==bit2_OK)RX_data_r<={RX,RX_data_r[7:1]};else if(cnt==bit3_OK)RX_data_r<={RX,RX_data_r[7:1]};else if(cnt==bit4_OK)RX_data_r<={RX,RX_data_r[7:1]};else if(cnt==bit5_OK)RX_data_r<={RX,RX_data_r[7:1]};else if(cnt==bit6_OK)RX_data_r<={RX,RX_data_r[7:1]};else if(cnt==bit7_OK)RX_data_r<={RX,RX_data_r[7:1]};else if(cnt==bit8_OK)RX_data_r<={RX,RX_data_r[7:1]};elseRX_data_r<=RX_data_r;
end
//******************************************************
// RX_data RX_OK
//******************************************************
always@(posedge clk or negedge RSTn)beginif(!RSTn)RX_data<=8'd0;else if(cnt==bitstop_OK)RX_data<=RX_data_r;elseRX_data<=RX_data;
endalways@(posedge clk or negedge RSTn)beginif(!RSTn)RX_OK<=1'b0;else if(cnt==bitstop_OK)RX_OK<=1'b1;elseRX_OK<=1'b0;
endendmodule
二、发送模块uart_tx.v
发射模块的程序非常简单,就是在对应的时刻拉高或者拉低电平,达到模拟TX信号线的目的。
uart_rx.v模块输入输出示意图
TX_start是个单周期脉冲,是外界给的,当有TX_start单脉冲时,发送TX_data寄存器中的数据。
- 进入模块,只有当TX_start=1时,才将TX_data寄存到TX_data_r。其余时刻保持刚刚寄存下来的值。
- cnt是时间轴,什么时刻发送第几位,都是根据cnt判断的。最小是0,最大是cnt_max。平时cnt是0,只有当TX_start到来时cnt才开始累加,一直累加到cnt_max(10个完整的数据位占用的等长的clk周期个数)
- TX。0~cnt_max平均分成10份,对应着10个数据位,以次发送(改变电平)。
- TX_end。cnt==cnt_max时,产生TX_end单脉冲,表示完成一个字节的传输。
- uart_tx.v程序如下:
module uart_tx#(parameter OSC = 50_000_000,parameter BPS = 9600,parameter bit_1_cnt = 5208, //bit_1_cnt = OSC/BPSparameter bit_0_5_cnt = 2604,parameter cnt_max = 52083 //cnt_max = bit_1_cnt*10
)(input clk,input RSTn,//input [7:0] TX_data, //要发送的数据input TX_start, //发送开始单脉冲使能。TX_data和TX_start同步到来output reg TX,output TX_end
);//******************************************************
// parameter
//******************************************************
localparam bit1_send = bit_1_cnt;
localparam bit2_send = bit_1_cnt+bit_1_cnt;
localparam bit3_send = bit_1_cnt+bit_1_cnt+bit_1_cnt;
localparam bit4_send = bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt;
localparam bit5_send = bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt;
localparam bit6_send = bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt;
localparam bit7_send = bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt;
localparam bit8_send = bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt;
localparam bitstop_send = bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt+bit_1_cnt;//------------------TX_data_r-----------------------------
reg [7:0] TX_data_r; //每次在TX_start到来时,寄存一下要发送的数据TX_data给TX_data_r寄存器//------------------cnt-----------------------------
reg [31:0] cnt;//******************************************************
// TX_data_r
//******************************************************
always@(posedge clk or negedge RSTn)beginif(!RSTn)TX_data_r<=8'd0;else if(TX_start==1'b1)TX_data_r<=TX_data;elseTX_data_r<=TX_data_r;
end
//******************************************************
// cnt
//******************************************************
always@(posedge clk or negedge RSTn)beginif(!RSTn)cnt<=32'd0;else if(cnt==32'd0&&TX_start==1'b1) //平时cnt=0,当TX_start到来时cnt开始累加1,一直加到对应波特率下10个比特位全部发送完cnt<=32'd1;else if(cnt!=32'd0&&cnt<cnt_max)cnt<=cnt+1'b1;else if(cnt>=cnt_max)cnt<=32'd0;elsecnt<=cnt;
end
//******************************************************
// TX
//******************************************************
always@(posedge clk or negedge RSTn)beginif(!RSTn)TX<=1'b1;else if(cnt>=32'd1&&cnt<bit1_send)TX<=1'b0; //起始位else if(cnt>=bit1_send&&cnt<bit2_send)TX<=TX_data_r[0];else if(cnt>=bit2_send&&cnt<bit3_send)TX<=TX_data_r[1];else if(cnt>=bit3_send&&cnt<bit4_send)TX<=TX_data_r[2];else if(cnt>=bit4_send&&cnt<bit5_send)TX<=TX_data_r[3];else if(cnt>=bit5_send&&cnt<bit6_send)TX<=TX_data_r[4];else if(cnt>=bit6_send&&cnt<bit7_send)TX<=TX_data_r[5];else if(cnt>=bit7_send&&cnt<bit8_send)TX<=TX_data_r[6];else if(cnt>=bit8_send&&cnt<bitstop_send)TX<=TX_data_r[7];else if(cnt>=bitstop_send&&cnt<cnt_max)TX<=1'b1; //停止位elseTX<=1'b1;
end
//******************************************************
// TX_end
//******************************************************
assign TX_end = (cnt==cnt_max)?1'b1:1'b0;endmodule