1.波特率的计算
波特率:简而言之,串口传输的波特率即为每秒钟传输二进制的位数。
例子:统时钟50M(为串口提供时钟的时钟频率),波特率115200/9600
系统时钟 50M
钟周期为 1/50_000_000 = 20ns
计一个数需要 1/50_000_000 = 20ns
一个周期计数 1s/20ns = 50_000_000个数
波特率 9600 1s传输9600个bit
传输1bit需要 1/9600 s
传输1bit需要 计数(1/9600)/(1/50_000_000) = 50_000_000/9600 = 5208
波特率 115200 1s传输115200个bit
传输1bit需要 1/11520 s
传输1bit需要 计数(1/115200)/(1/50_000_000) = 50_000_000/115200 = 434
2.串口的传输格式
起始位永远低电平,停止位永远高电平
由基础知识知50M系统时钟—波特率为115200条件下传输1bit需要计数个数为434。那么1Byte(串口传输格式为:1bit起始位+8bit数据位+1bit停止位)是不是循环计数10次434就可以传输完毕。
直接上图:图中描述了1Byte数据传输的示意图,重点都在图里!!!。
3.什么时候去采样串口线上的数据呢?
(如何保证串口传输接收来的数据是正确的?)
观察上图,Buad_Flag信号(通道2)表示了传输1Bit传输的间隔,每遇到1个Buad_Flag=1的信号,数据线上切换1次数据,所以两个Buad_Flag=1之间的数据是稳定的数据,根据抽样定理是不是应该在两个Buad_Flag=1信号的中间去采样数据呢,其实就是在1bit数据持续期间的中间点采样,才能得到最稳定的数据。见下图,重点都在图里!!!
图中序号①-⑩分别为10bit数据的采样点,采样点处提取数据为0101_0101(0x55),低位在前。
收发波特率一致:
收发波特率不一致,导致RX端不能正常接收:
4.波特率计数器
module baudgen
(
input clk,
input rst_n,
input work_en,
output reg [3:0] bit_cnt,
output bit_flag
);
parameter BAUD_CNT_MAX = 50_000_000/9600;
parameter CNT_SAMP_MAX = BAUD_CNT_MAX/2;
reg [12:0] cnt;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt <= 0;
else
cnt <= work_en ? ((cnt == BAUD_CNT_MAX-1) ? 0 : cnt + 1) : 0;
end
assign bit_flag = (cnt == CNT_SAMP_MAX - 1) ? 1 : 0;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
bit_cnt <= 0;
else if(bit_flag ==1 && bit_cnt == 4'd9)
bit_cnt <= 0;
else if(bit_flag ==1 && work_en == 1)
bit_cnt <= bit_cnt + 1;
end
endmodule
5.RX接受模块
- rx_reg2数据之前用寄存器打两拍使输入数据进行同步消除亚稳态,rx_reg3比rx_reg2延后一拍。
- 检测第一个下降沿((~rx_reg2) && (rx_reg3) && (~work_en))后work_en拉高(波特计数器在此期间才进行计数),当9个数据(含起始位)传输完成后work_en拉低(使得波特计数器停止计数并清零,以保证下次数据到来时从零开始计数)。
- 波特率计数器(在work_en)为高电平时开始进行计数),9600bps使用50Mhz(20ns)时钟计数需要计5208≈[(1/9600)/20ns],波特率计数器计数到中间值时bit_flag产生一个时钟的高电平。
- bit_flag产生高电平时bit_cnt加1,以计数有几位(一个比特8位)数据到来,数据到来时先来低位,后来高位,要将低位进行右移。8位数据(有用数据,不含起始位)均传输完成后拉高一个时钟的高电平。
- 输出完整的8位有效数据。
- 输出数据有效标志(比rx_flag延后一个时钟周期,为了和po_data同步)
module uart_rx
(
input wire clk,
input wire rst_n,
input wire bit_flag,
input wire [3:0] bit_cnt,
input wire rx,
output reg work_en,
output reg [7:0] data_out,
output reg data_finish
);
reg reg1,reg2,reg3; //sync reg
reg start_nedge; //start
reg [7:0] rx_data; //rx data
reg rx_finish; //rx finish
//sync
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
begin
reg1 <= 0;
reg2 <= 0;
reg3 <= 0;
end
else
begin
reg1 <= rx;
reg2 <= reg1;
reg3 <= reg2;
end
end
//start negedge in (~work_en) state
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
start_nedge <= 0;
else if((~work_en) && (reg3 && (~reg2)))
start_nedge <= 1;
else
start_nedge <= 0;
end
// create work_en signal in start_nedge and finish in (bit_cnt==9 && bit_flag==1)
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
work_en <= 0;
else if(start_nedge)
work_en <= 1;
else if(bit_cnt == 4'd9 && bit_flag == 1)
work_en <= 0;
end
//receive rx data in (1<=bit_cnt<=8 && bit_flag==1)
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
rx_data <= 0;
else if(bit_cnt >=4'd1 && bit_cnt <= 4'd8 && bit_flag == 1)
rx_data <= {reg3,rx_data[7:1]};
end
//receive rx finish in (bit_cnt==9 && bit_flag==1)
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
rx_finish <= 0;
else if(bit_cnt == 4'd9 && bit_flag == 1)
rx_finish <= 1;
else
rx_finish <= 0;
end
//data out from rx_data when rx_finish
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
data_out <= 0;
else if(rx_finish)
data_out <= rx_data;
end
//data_finish from rx_finish
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
data_finish <= 0;
else
data_finish <= rx_finish;
end
endmodule
6.TX发送模块
module uart_tx
(
input wire clk,
input wire rst_n,
input wire bit_flag,
input wire [3:0] bit_cnt,
input wire [7:0] data_in,
input wire data_start,
output reg work_en,
output reg tx,
output reg data_finish
);
//send data work_en
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
work_en <= 0;
else if(data_start)
work_en <= 1;
else if(bit_cnt == 4'd9 && bit_flag == 1)
work_en <=0;
end
//data_in to tx by bit_cnt when bit_flag
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
tx <= 1;
else if(bit_flag)
case(bit_cnt)
0:tx <= 0;
1:tx <= data_in[0];
2:tx <= data_in[1];
3:tx <= data_in[2];
4:tx <= data_in[3];
5:tx <= data_in[4];
6:tx <= data_in[5];
7:tx <= data_in[6];
8:tx <= data_in[7];
9:tx <= 1;
default:tx <= 1;
endcase
end
//data finish out
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
data_finish <= 0;
else if(bit_cnt==4'd9 && bit_flag == 1)
data_finish <= 1;
else
data_finish <= 0;
end
endmodule