赞
踩
串口处于空闲状态时,tx为高电平,故接收模块rx端口接收到的也是高电平。起始位为低电平,所以检测起始位只需要检测rx的下降沿即可,检测电路如下,采用二级D触发器解决亚稳态问题。
在实验室条件下,串口发送的数据通常是稳定的,在接收时取数据中点作为接收数据。
在工业环境下,干扰较多,信号中通常会产生冲激,用取数据中点的方法容易受到脉冲信号的影响,故用采样的方法保证接收数据的准确性。
上图为单bit数据接收的示意图,将每位数据等分成16份,两边标红的部分在数据跳变的边沿处,容易出现干扰,因此对中间绿色部分的稳定数据进行采样,采样第5-11份,共采样7次。
端口名称 | 方向 | 说明 |
---|---|---|
Clk | input | 系统时钟 |
Rst_n | input | 系统复位 |
rx | input | 串口接收端口 |
baud_set[2:0] | input | 波特率设置 |
data[7:0] | output | 8位并行接收数据 |
rx_done | output | 串口接收完成标志 |
uart_state | output | 串口接收状态标志 |
模块检测到起始位,即rx下降沿时,开始接收数据,uart_state置1。baud_set是数值为0-7的3位二进制数,分别对应波特率2400、4800、9600、19200、38400、57600、115200、256000;当串口接收完成时,rx_done置1,uart_state置0。
波特率是指每秒通信数据的比特个数,则每1/bps秒发送1bit数据,则波特率分频计数时间为
1
∗
1
0
9
/
b
p
s
1∗10^9/ bps
1∗109/bps ns,将数据等分为16份,对中间7份进行采样,系统时钟为50Mhz,得出分频计数值计算公式:
分频计数值
=
1
∗
1
0
9
/
b
p
s
/
16
/
20
−
1
分频计数值=1*10^9/bps/16/20-1
分频计数值=1∗109/bps/16/20−1
根据公式计算得出如下表格:
baud_set | 波特率 | 波特率分频计数值 |
---|---|---|
0 | 2400 | 1301 |
1 | 4800 | 650 |
2 | 9600 | 324 |
3 | 19200 | 161 |
4 | 38400 | 80 |
5 | 57600 | 53 |
6 | 115200 | 26 |
7 | 384000 | 11 |
和串口发送模块的设计类似,接收模块可包括以下7个部分:起始位检测、串口接收状态标志uart_state赋值、波特率分频计数最大值cnt_bpsmax赋值、波特率时钟分频计数器、波特率时钟计数器、数据采样接收、串口接收完成标志rx_done赋值。
(1)起始位检测
s0_Rx为二级同步寄存器,用于解决亚稳态问题,s1_Rx为数据寄存器,存储前一时钟周期rx值,用于判断起始位。
reg [1:0] s0_Rx; //同步寄存器,用于解决亚稳态问题 reg s1_Rx; //数据寄存器,用于判断起始位下降沿 wire nedge; //判断起始位下降沿 //二级同步寄存器,消除亚稳态 always@(posedge Clk or negedge Rst_n) if(!Rst_n) s0_Rx <= 2'b0; else s0_Rx <= {s0_Rx[0],rx}; //数据寄存器,储存上一周期的rx always@(posedge Clk or negedge Rst_n) if(!Rst_n) s1_Rx <= 1'b0; else s1_Rx <= s0_Rx[1]; assign nedge = s1_Rx && !s0_Rx[1];
起始位接收错误标志:
将每一位数据分为16份,取第5-11份进行采样,故波特率时钟计数器bps_cnt=12时,起始位已接收完成,此时判断起始位是否错误,若起始位错误,则START_BIT[2]=1。
wire START_ERR; //起始位接收错误标志
assign START_ERR = (bps_cnt == 8'd12) && START_BIT[2];
(2)串口接收状态标志uart_state赋值
当检测到起始位时,uart_state赋值为1;当停止位接收完毕时或起始位接收错误时,串口接收完成或停止,uart_state赋值为0。
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
uart_state <= 1'b0;
else if(nedge)
uart_state <= 1'b1;
else if(bps_cnt == 8'd159 || START_ERR)
uart_state <= 1'b0;
(3)波特率分频计数最大值cnt_bpsmax赋值
根据上面计算出的波特率设置表格对cnt_bpsmax进行赋值。复位时,cnt_bpsmax为324,表示默认波特率为9600。
always@(posedge Clk or negedge Rst_n)//波特率选择
if(!Rst_n)
cnt_bpsmax <= 16'd324;//默认波特率9600
else
case(baud_set)
0:cnt_bpsmax <= 16'd1301; //波特率2400
1:cnt_bpsmax <= 16'd650; //波特率4800
2:cnt_bpsmax <= 16'd324; //波特率9600
3:cnt_bpsmax <= 16'd161; //波特率19200
4:cnt_bpsmax <= 16'd80; //波特率38400
5:cnt_bpsmax <= 16'd53; //波特率57600
6:cnt_bpsmax <= 16'd26; //波特率115200
7:cnt_bpsmax <= 16'd11; //波特率256000
default:cnt_bpsmax <= 16'd324;
endcase
(4)波特率时钟分频计数器
得到波特率分频计数最大值后,编写波特率时钟分频计数器。
always@(posedge Clk or negedge Rst_n)//分频计数
if(!Rst_n)
cnt <= 16'd0;
else if(uart_state) begin
if(cnt == cnt_bpsmax)
cnt <= 16'd0;
else
cnt <= cnt + 1'b1;
end
else
cnt <= 16'b0;
(5)波特率时钟计数器
共接收10位:起始位、8位data、停止位,每位分为16份,bps_cnt计数160次。起始位接收错误时,波特率时钟停止计数。
always@(posedge Clk or negedge Rst_n)//波特率时钟计数
if(!Rst_n)
bps_cnt <= 8'd0;
else if(uart_state) begin
if(bps_cnt == 8'd159 || START_ERR)
bps_cnt <= 8'd0;
else if(cnt == 16'b1)
bps_cnt <= bps_cnt + 1'b1;
else
bps_cnt <= bps_cnt;
end
else
bps_cnt <= 8'd0;
(6)数据采样接收
每位数据采样7次,对7次采样结果累加,若结果为3、4、5、6,则认为该数据为1,若结果为0、1、2,则认为该数据为0。将每位数据的采样结果存储在位宽为3的寄存器中:
reg [2:0] rx_data [7:0];//8个3位寄存器
reg [2:0] START_BIT;
reg [2:0] STOP_BIT;
起始位在bps_cnt为5-11时采样,其它位数据在此基础上加16的倍数,每次采样将当前rx值s0_Rx[1]累加到起始位寄存器START_BIT中。
always@(posedge Clk or negedge Rst_n)//数据采样接收 if(!Rst_n) begin START_BIT <= 3'b0; for(i=0;i<8;i=i+1) rx_data[i] <= 3'b0; STOP_BIT <= 3'b0; end else if(cnt == 16'b1) begin case(bps_cnt) 0:begin START_BIT <= 3'b0; for(i=0;i<8;i=i+1) rx_data[i] <= 3'b0; STOP_BIT <= 3'b0; end 5, 6, 7, 8, 9, 10, 11: START_BIT <= START_BIT + s0_Rx[1]; 21, 22, 23, 24, 25, 26, 27: rx_data[0] <= rx_data[0] + s0_Rx[1]; 37, 38, 39, 40, 41, 42, 43: rx_data[1] <= rx_data[1] + s0_Rx[1]; 53, 54, 55, 56, 57, 58, 59: rx_data[2] <= rx_data[2] + s0_Rx[1]; 69, 70, 71, 72, 73, 74, 75: rx_data[3] <= rx_data[3] + s0_Rx[1]; 85, 86, 87, 88, 89, 90, 91: rx_data[4] <= rx_data[4] + s0_Rx[1]; 101,102,103,104,105,106,107: rx_data[5] <= rx_data[5] + s0_Rx[1]; 117,118,119,120,121,122,123: rx_data[6] <= rx_data[6] + s0_Rx[1]; 133,134,135,136,137,138,139: rx_data[7] <= rx_data[7] + s0_Rx[1]; 149,150,151,152,153,154,155: STOP_BIT <= STOP_BIT + s0_Rx[1]; default; endcase end else begin START_BIT <= START_BIT; for(i=0;i<8;i=i+1) rx_data[i] <= rx_data[i]; STOP_BIT <= STOP_BIT; end
数据全部采样完成时,对采样数据解码,计算每位数据的具体数值,当7次采样值大于等于3时,寄存器第2位为1,该位数据值也为1,因此rx_data[i][2]可作为每位数据的值,将其存储到data中。
always@(posedge Clk or negedge Rst_n)//采样数据解码
if(!Rst_n)
data <= 8'b0;
else if(bps_cnt == 8'd159)
for(i=0;i<8;i=i+1)
data[i] <= rx_data[i][2];
else
data <= data;
(7)串口接收完成标志rx_done赋值
停止位接收完成时,串口接收结束,rx_done置1。
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
rx_done <= 1'b0;
else if(bps_cnt == 8'd159)
rx_done <= 1'b1;
else
rx_done <= 1'b0;
串口接收源代码
//本模块用于实现串口数据接收,系统频率为50Mhz,时钟周期为20ns module uart_data_rx( input Clk, //系统时钟 input Rst_n, //系统复位 input rx, //串口接收端口 input [2:0] baud_set, //波特率设置 output reg [7:0] data, //8位并行接收数据 output reg rx_done, //串口接收完成标志 output reg uart_state //串口接收状态标志 ); reg [1:0] s0_Rx; //同步寄存器,用于解决亚稳态问题 reg s1_Rx; //数据寄存器,用于判断起始位下降沿 wire nedge; //判断起始位下降沿 wire START_ERR; //起始位接收错误标志 reg [15:0] cnt; //波特率分频计数器 reg [15:0] cnt_bpsmax; //波特率分频计数最大值 reg [7:0] bps_cnt; //波特率时钟计数器 reg [2:0] rx_data [7:0];//8个3位寄存器 reg [2:0] START_BIT; reg [2:0] STOP_BIT; integer i;//for循环使用 //二级同步寄存器,消除亚稳态 always@(posedge Clk or negedge Rst_n) if(!Rst_n) s0_Rx <= 2'b0; else s0_Rx <= {s0_Rx[0],rx}; //数据寄存器,储存上一周期的rx always@(posedge Clk or negedge Rst_n) if(!Rst_n) s1_Rx <= 1'b0; else s1_Rx <= s0_Rx[1]; assign nedge = s1_Rx && !s0_Rx[1]; assign START_ERR = (bps_cnt == 8'd12) && START_BIT[2]; always@(posedge Clk or negedge Rst_n)//波特率选择 if(!Rst_n) cnt_bpsmax <= 16'd324;//默认波特率9600 else case(baud_set) 0:cnt_bpsmax <= 16'd1301; //波特率2400 1:cnt_bpsmax <= 16'd650; //波特率4800 2:cnt_bpsmax <= 16'd324; //波特率9600 3:cnt_bpsmax <= 16'd161; //波特率19200 4:cnt_bpsmax <= 16'd80; //波特率38400 5:cnt_bpsmax <= 16'd53; //波特率57600 6:cnt_bpsmax <= 16'd26; //波特率115200 7:cnt_bpsmax <= 16'd11; //波特率256000 default:cnt_bpsmax <= 16'd324; endcase always@(posedge Clk or negedge Rst_n)//分频计数 if(!Rst_n) cnt <= 16'd0; else if(uart_state) begin if(cnt == cnt_bpsmax) cnt <= 16'd0; else cnt <= cnt + 1'b1; end else cnt <= 16'b0; always@(posedge Clk or negedge Rst_n)//波特率时钟计数 if(!Rst_n) bps_cnt <= 8'd0; else if(uart_state) begin if(bps_cnt == 8'd159 || START_ERR) bps_cnt <= 8'd0; else if(cnt == 16'b1) bps_cnt <= bps_cnt + 1'b1; else bps_cnt <= bps_cnt; end else bps_cnt <= 8'd0; always@(posedge Clk or negedge Rst_n)//数据采样接收 if(!Rst_n) begin START_BIT <= 3'b0; for(i=0;i<8;i=i+1) rx_data[i] <= 3'b0; STOP_BIT <= 3'b0; end else if(cnt == 16'b1) begin case(bps_cnt) 0:begin START_BIT <= 3'b0; for(i=0;i<8;i=i+1) rx_data[i] <= 3'b0; STOP_BIT <= 3'b0; end 5, 6, 7, 8, 9, 10, 11: START_BIT <= START_BIT + s0_Rx[1]; 21, 22, 23, 24, 25, 26, 27: rx_data[0] <= rx_data[0] + s0_Rx[1]; 37, 38, 39, 40, 41, 42, 43: rx_data[1] <= rx_data[1] + s0_Rx[1]; 53, 54, 55, 56, 57, 58, 59: rx_data[2] <= rx_data[2] + s0_Rx[1]; 69, 70, 71, 72, 73, 74, 75: rx_data[3] <= rx_data[3] + s0_Rx[1]; 85, 86, 87, 88, 89, 90, 91: rx_data[4] <= rx_data[4] + s0_Rx[1]; 101,102,103,104,105,106,107: rx_data[5] <= rx_data[5] + s0_Rx[1]; 117,118,119,120,121,122,123: rx_data[6] <= rx_data[6] + s0_Rx[1]; 133,134,135,136,137,138,139: rx_data[7] <= rx_data[7] + s0_Rx[1]; 149,150,151,152,153,154,155: STOP_BIT <= STOP_BIT + s0_Rx[1]; default; endcase end else begin START_BIT <= START_BIT; for(i=0;i<8;i=i+1) rx_data[i] <= rx_data[i]; STOP_BIT <= STOP_BIT; end always@(posedge Clk or negedge Rst_n)//采样数据解码 if(!Rst_n) data <= 8'b0; else if(bps_cnt == 8'd159) for(i=0;i<8;i=i+1) data[i] <= rx_data[i][2]; else data <= data; always@(posedge Clk or negedge Rst_n) if(!Rst_n) rx_done <= 1'b0; else if(bps_cnt == 8'd159) rx_done <= 1'b1; else rx_done <= 1'b0; always@(posedge Clk or negedge Rst_n) if(!Rst_n) uart_state <= 1'b0; else if(nedge) uart_state <= 1'b1; else if(bps_cnt == 8'd159 || START_ERR) uart_state <= 1'b0; endmodule
仿真时,需将之前写好的串口发送模块例化进来,模拟PC端串口发送数据8’hab和8’h23,由接收模块接收。
`timescale 1ns/1ns module uart_data_rx_tb(); //共用参数 reg Clk,Rst_n; reg [2:0] baud_set; wire rx_tx;//将tx连接rx //uart发送模块参数 reg send_en; reg [7:0] data_tx; wire uart_state_tx,tx_done; //uart接收模块参数 wire rx_done; wire uart_state_rx; wire [7:0] data_rx; uart_data_rx uart_data_rx( .Clk(Clk), .Rst_n(Rst_n), .rx(rx_tx), .baud_set(baud_set), .data(data_rx), .rx_done(rx_done), .uart_state(uart_state_rx) ); uart_data_tx PC_tx( .Clk(Clk), .Rst_n(Rst_n), .send_en(send_en), .data(data_tx), .baud_set(baud_set), .tx(rx_tx), .tx_done(tx_done), .uart_state(uart_state_tx) ); initial Clk = 1; always #10 Clk = ~Clk; initial begin Rst_n = 1'b0; send_en = 1'b0; data_tx = 8'h0; baud_set = 3'd2; #201;//20+1为了和其他时钟错开,便于观察结果 Rst_n = 1'b1; #200; data_tx = 8'hab; send_en = 1'b1; #20; send_en = 1'b0; @(posedge tx_done);//等待发送完成 #5000000; data_tx = 8'h23; send_en = 1'b1; #20; send_en = 1'b0; @(posedge tx_done); #5000000; $stop; end endmodule
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。