UART协议,空闲时,TX和RX数据线都是通过上拉电阻拉高的状态,这样才能在起始位到来时检测到一个下降的边沿。
uart_rx.v模块输入输出示意图
RX_start。首先,找到起始位的开始时刻RX_start,这是一个接收字节的最一开始。对RX打拍两次得到RX_1、RX_2。并找出传输每个字节的RX的起始时刻RX_start。
(注意!:只有在cnt=0时RX出现的下降沿才是起始时刻,否则RX传输过程中也会有由高电平变低电平的情况,这种情况显然不能判定为起始时刻)
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)begin
if(!RSTn)begin
RX_1<=1'b0;
RX_2<=1'b0;
end
else begin
RX_1<=RX;
RX_2<=RX_1;
end
end
assign RX_start = ((~RX_1&RX_2)&&(cnt==32'd0))?1'b1:1'b0;
//******************************************************
// cnt
//******************************************************
always@(posedge clk or negedge RSTn)begin
if(!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<=32'd0;
else
cnt<=cnt;
end
//******************************************************
// RX_data_r
//******************************************************
always@(posedge clk or negedge RSTn)begin
if(!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]};
else
RX_data_r<=RX_data_r;
end
//******************************************************
// RX_data RX_OK
//******************************************************
always@(posedge clk or negedge RSTn)begin
if(!RSTn)
RX_data<=8'd0;
else if(cnt==bitstop_OK)
RX_data<=RX_data_r;
else
RX_data<=RX_data;
end
always@(posedge clk or negedge RSTn)begin
if(!RSTn)
RX_OK<=1'b0;
else if(cnt==bitstop_OK)
RX_OK<=1'b1;
else
RX_OK<=1'b0;
end
endmodule
发射模块的程序非常简单,就是在对应的时刻拉高或者拉低电平,达到模拟TX信号线的目的。
uart_rx.v模块输入输出示意图
TX_start是个单周期脉冲,是外界给的,当有TX_start单脉冲时,发送TX_data寄存器中的数据。
module uart_tx#(
parameter OSC = 50_000_000,
parameter BPS = 9600,
parameter bit_1_cnt = 5208, //bit_1_cnt = OSC/BPS
parameter 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)begin
if(!RSTn)
TX_data_r<=8'd0;
else if(TX_start==1'b1)
TX_data_r<=TX_data;
else
TX_data_r<=TX_data_r;
end
//******************************************************
// cnt
//******************************************************
always@(posedge clk or negedge RSTn)begin
if(!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<=32'd0;
else
cnt<=cnt;
end
//******************************************************
// TX
//******************************************************
always@(posedge clk or negedge RSTn)begin
if(!RSTn)
TX<=1'b1;
else if(cnt>=32'd1&&cnt=bit1_send&&cnt=bit2_send&&cnt=bit3_send&&cnt=bit4_send&&cnt=bit5_send&&cnt=bit6_send&&cnt=bit7_send&&cnt=bit8_send&&cnt=bitstop_send&&cnt