FPGA:UART串口接收
高干扰下UART串口接收思路
在上一篇文章中介绍过UART串口接收模块,其中一个需要优化的方向就是在高干扰情况下,如何实现可靠的UART串口接收任务。解决的思路是:在每一位进行多次采样,去掉干扰较强的初始传输段和传输结束段,取中间几段进行概率统计,如果逻辑 1 1 1的次数比较多,则认为接收的该位为逻辑 1 1 1,否则为逻辑 0 0 0。这里对每一位再进行 16 16 16次采样,去掉前面 6 6 6次和后面 4 4 4次,取中间 6 6 6次进行采样。
UART改善串口接收模块及其思路
波特率选择模块
为了实现适应不同波特率,这里引入一个波特率选择模块,一共提供 6 6 6种不同的波特率,分别是 9600 、 19200 、 38400 、 57600 、 115200 、 1562500 9600、19200、38400、57600、115200、1562500 9600、19200、38400、57600、115200、1562500,波特率设置信号定义为baud_set。bps_DR为波特率计数器最大值。默认情况下选择 9600 9600 9600波特率。逻辑如下:
parameter CLK_FRQ = 50000000;
localparam BAUD_9600_CNT = CLK_FRQ/9600/16 - 2;
localparam BAUD_19200_CNT = CLK_FRQ/19200/16 - 2;
localparam BAUD_38400_CNT = CLK_FRQ/38400/16 - 2;
localparam BAUD_57600_CNT = CLK_FRQ/57600/16 - 2;
localparam BAUD_115200_CNT = CLK_FRQ/115200/16 - 2;
localparam BAUD_1562500_CNT = CLK_FRQ/1562500/16 - 2;
always@(posedge clk or posedge reset)
if(reset)
bps_DR <= 16'd324;
else begin
case(baud_set)
0:bps_DR <= BAUD_9600_CNT;
1:bps_DR <= BAUD_19200_CNT;
2:bps_DR <= BAUD_38400_CNT;
3:bps_DR <= BAUD_57600_CNT;
4:bps_DR <= BAUD_115200_CNT;
5:bps_DR <= BAUD_1562500_CNT;
default:bps_DR <= BAUD_9600_CNT;
endcase
end
打拍延迟和边沿检测电路
因为输入信号是异步信号,需要对其进行打拍延迟同步,以及检测传输开始时需要进行下降沿检测。
其逻辑在上一节阐述过,这里不再赘述:
reg uart_rx_sync1; //同步寄存器
reg uart_rx_sync2; //同步寄存器
reg uart_rx_reg1; //数据寄存器
reg uart_rx_reg2; //数据寄存器
wire uart_rx_nedge;
//同步串行输入信号,消除亚稳态
always@(posedge clk or posedge reset)
if(reset)begin
uart_rx_sync1 <= 1'b0;
uart_rx_sync2 <= 1'b0;
end
else begin
uart_rx_sync1 <= uart_rx;
uart_rx_sync2 <= uart_rx_sync1;
end
//数据寄存器
always@(posedge clk or posedge reset)
if(reset)begin
uart_rx_reg1 <= 1'b0;
uart_rx_reg2 <= 1'b0;
end
else begin
uart_rx_reg1 <= uart_rx_sync2;
uart_rx_reg2 <= uart_rx_reg1;
end
//下降沿检测
assign uart_rx_nedge = !uart_rx_reg1 & uart_rx_reg2;
计数使能逻辑
这里不同点是在与我们对每一位进行 16 16 16次的上采样。当检测到下降沿的时候,意味着传输开始,这个时候需要将其置 1 1 1,另外分析两种异常情况:如果传输开始,我们检测到起始位为 1 1 1或者传输结束的时候检测到停止位为 0 0 0,那这个时候我们认为这一次传输数据有误,将使能信号置 0 0 0,对于起始位到底是 1 1 1还是 0 0 0,我们是使用一个小的统计计数,如下图所示,如果在起始位的中间 6 6 6个采样点得到的 1 1 1的数量超过 1 1 1次,即出现所谓的多次毛刺,这个时候则认为起始位为 1 1 1,同理,如果停止位采样时 6 6 6次数据出现的逻辑 1 1 1次数少于3次,则认为停止位为 0 0 0。逻辑如下:
always@(posedge clk or posedge reset)
if(reset)
uart_state <= 1'b0;
else if(uart_rx_nedge)
uart_state <= 1'b1;
else if(rx_done || (bps_cnt == 8'd12 && (START_BIT > 2)) || (bps_cnt == 8'd155 && (STOP_BIT < 3)))
uart_state <= 1'b0;
else
uart_state <= uart_state;
这里的START_BIT和STOP_BIT均是一个 3 3 3位计数器,统计逻辑 1 1 1出现的次数。
波特率分频计数器
与之前不同,这里是对一位数据进行 16 16 16次的上采样,所以分频计数器的逻辑为:
always@(posedge clk or posedge reset)
if(reset)
div_cnt <= 16'd0;
else if(uart_state)begin
if(div_cnt == bps_DR)
div_cnt <= 16'd0;
else
div_cnt <= div_cnt + 1'b1;
end
else
div_cnt <= 16'd0;
位计数器
对细分后的每一位进行计数,一共有 10 10 10位数据,每一位细分成 16 16 16次进行采样,所以细分后一次传输一共有 160 160 160个最小计数单位。这个计数器需要考虑下列异常情况:即起始位如果为 1 1 1,则传输认为是无效数据,不进行计数。或者当其计数到 159 159 159时进行清零,当分频计数器开始计时时让位计数自加。逻辑代码如下:
// bps_clk gen
always@(posedge clk or posedge reset)
if(reset)
bps_clk <= 1'b0;
else if(div_cnt == 16'd1)
bps_clk <= 1'b1;
else
bps_clk <= 1'b0;
//bps counter
always@(posedge clk or posedge reset)
if(reset)
bps_cnt <= 8'd0;
else if(bps_cnt == 8'd159 | (bps_cnt == 8'd12 && (START_BIT > 2)))
bps_cnt <= 8'd0;
else if(bps_clk)
bps_cnt <= bps_cnt + 1'b1;
else
bps_cnt <= bps_cnt;
完成标志信号
在一次数据传输完成后,即停止位发送完毕时,认为发送完成,此时将完成标志信号拉高。逻辑如下:
always@(posedge clk or posedge reset)
if(reset)
rx_done <= 1'b0;
else if(bps_cnt == 8'd159)
rx_done <= 1'b1;
else
rx_done <= 1'b0;
计数判断逻辑
在前面说过,对每一位数据的进行 16 16 16次上采样,去掉前面 6 6 6次和后面 4 4 4次,只统计中间 6 6 6次出现 1 1 1的次数,最多就是 6 6 6个点全是 1 1 1,所以需要一个深度为 3 3 3的计数器进行统计, 1 ∼ 6 1\sim 6 1∼6的三位二进制分别如下 001 、 010 、 011 、 100 、 101 、 110 001、010、011、100、101、110 001、010、011、100、101、110,当 1 1 1出现的次数不低于 4 4 4次,则认为传输的是逻辑 1 1 1,如下表
十进制 | 二进制 |
---|---|
1 | 001 |
2 | 010 |
3 | 011 |
4 | 100 |
5 | 101 |
6 | 110 |
我们可以取这个三位计数器的最高位作为该位的值。 和之前一样,先将统计数据存储在data_type_pre里面,其是一个二维数组,是一个 8 8 8 个 3 3 3 位寄存器组成的数组,每个寄存器可以独立存储一个 3 3 3 位的二进制值。
always@(posedge clk or posedge reset)
if(reset)begin
START_BIT <= 3'd0;
data_byte_pre[0] <= 3'd0;
data_byte_pre[1] <= 3'd0;
data_byte_pre[2] <= 3'd0;
data_byte_pre[3] <= 3'd0;
data_byte_pre[4] <= 3'd0;
data_byte_pre[5] <= 3'd0;
data_byte_pre[6] <= 3'd0;
data_byte_pre[7] <= 3'd0;
STOP_BIT <= 3'd0;
end
else if(bps_clk)begin
case(bps_cnt)
0:begin
START_BIT <= 3'd0;
data_byte_pre[0] <= 3'd0;
data_byte_pre[1] <= 3'd0;
data_byte_pre[2] <= 3'd0;
data_byte_pre[3] <= 3'd0;
data_byte_pre[4] <= 3'd0;
data_byte_pre[5] <= 3'd0;
data_byte_pre[6] <= 3'd0;
data_byte_pre[7] <= 3'd0;
STOP_BIT <= 3'd0;
end
6 ,7 ,8 ,9 ,10,11:START_BIT <= START_BIT + uart_rx_sync2;
22,23,24,25,26,27:data_byte_pre[0] <= data_byte_pre[0] + uart_rx_sync2;
38,39,40,41,42,43:data_byte_pre[1] <= data_byte_pre[1] + uart_rx_sync2;
54,55,56,57,58,59:data_byte_pre[2] <= data_byte_pre[2] + uart_rx_sync2;
70,71,72,73,74,75:data_byte_pre[3] <= data_byte_pre[3] + uart_rx_sync2;
86,87,88,89,90,91:data_byte_pre[4] <= data_byte_pre[4] + uart_rx_sync2;
102,103,104,105,106,107:data_byte_pre[5] <= data_byte_pre[5] + uart_rx_sync2;
118,119,120,121,122,123:data_byte_pre[6] <= data_byte_pre[6] + uart_rx_sync2;
134,135,136,137,138,139:data_byte_pre[7] <= data_byte_pre[7] + uart_rx_sync2;
150,151,152,153,154,155:STOP_BIT <= STOP_BIT + uart_rx_sync2;
default:
begin
START_BIT <= START_BIT;
data_byte_pre[0] <= data_byte_pre[0];
data_byte_pre[1] <= data_byte_pre[1];
data_byte_pre[2] <= data_byte_pre[2];
data_byte_pre[3] <= data_byte_pre[3];
data_byte_pre[4] <= data_byte_pre[4];
data_byte_pre[5] <= data_byte_pre[5];
data_byte_pre[6] <= data_byte_pre[6];
data_byte_pre[7] <= data_byte_pre[7];
STOP_BIT <= STOP_BIT;
end
endcase
end
always@(posedge clk or posedge reset)
if(reset)
data_byte <= 8'd0;
else if(bps_cnt == 8'd159)begin
data_byte[0] <= data_byte_pre[0][2];
data_byte[1] <= data_byte_pre[1][2];
data_byte[2] <= data_byte_pre[2][2];
data_byte[3] <= data_byte_pre[3][2];
data_byte[4] <= data_byte_pre[4][2];
data_byte[5] <= data_byte_pre[5][2];
data_byte[6] <= data_byte_pre[6][2];
data_byte[7] <= data_byte_pre[7][2];
end
整体代码
代码整合如下:
module uart_byte_rx(
clk,
reset,
baud_set,
uart_rx,
data_byte,
rx_done
);
input clk; //时钟输入
input reset; //复位信号输入
input [2:0] baud_set; //波特率设置
input uart_rx; //串口输入信号
output reg [7:0]data_byte; //串口接收的1byte数据
output reg rx_done; //1byte数据接收完成标志
parameter CLK_FRQ = 50000000;
localparam BAUD_9600_CNT = CLK_FRQ/9600/16 - 2;
localparam BAUD_19200_CNT = CLK_FRQ/19200/16 - 2;
localparam BAUD_38400_CNT = CLK_FRQ/38400/16 - 2;
localparam BAUD_57600_CNT = CLK_FRQ/57600/16 - 2;
localparam BAUD_115200_CNT = CLK_FRQ/115200/16 - 2;
localparam BAUD_1562500_CNT = CLK_FRQ/1562500/16 - 2;
reg uart_rx_sync1; //同步寄存器
reg uart_rx_sync2; //同步寄存器
reg uart_rx_reg1; //数据寄存器
reg uart_rx_reg2; //数据寄存器
reg [15:0]bps_DR; //分频计数最大值
reg [15:0]div_cnt; //分频计数器
reg bps_clk; //波特率时钟
reg [7:0] bps_cnt; //波特率时钟计数器
reg uart_state;//接收数据状态
wire uart_rx_nedge;
reg [2:0] START_BIT;
reg [2:0] STOP_BIT;
reg [2:0] data_byte_pre [7:0];
//同步串行输入信号,消除亚稳态
always@(posedge clk or posedge reset)
if(reset)begin
uart_rx_sync1 <= 1'b0;
uart_rx_sync2 <= 1'b0;
end
else begin
uart_rx_sync1 <= uart_rx;
uart_rx_sync2 <= uart_rx_sync1;
end
//数据寄存器
always@(posedge clk or posedge reset)
if(reset)begin
uart_rx_reg1 <= 1'b0;
uart_rx_reg2 <= 1'b0;
end
else begin
uart_rx_reg1 <= uart_rx_sync2;
uart_rx_reg2 <= uart_rx_reg1;
end
//下降沿检测
assign uart_rx_nedge = !uart_rx_reg1 & uart_rx_reg2;
always@(posedge clk or posedge reset)
if(reset)
bps_DR <= 16'd324;
else begin
case(baud_set)
0:bps_DR <= BAUD_9600_CNT;
1:bps_DR <= BAUD_19200_CNT;
2:bps_DR <= BAUD_38400_CNT;
3:bps_DR <= BAUD_57600_CNT;
4:bps_DR <= BAUD_115200_CNT;
5:bps_DR <= BAUD_1562500_CNT;
default:bps_DR <= BAUD_9600_CNT;
endcase
end
//counter
always@(posedge clk or posedge reset)
if(reset)
div_cnt <= 16'd0;
else if(uart_state)begin
if(div_cnt == bps_DR)
div_cnt <= 16'd0;
else
div_cnt <= div_cnt + 1'b1;
end
else
div_cnt <= 16'd0;
// bps_clk gen
always@(posedge clk or posedge reset)
if(reset)
bps_clk <= 1'b0;
else if(div_cnt == 16'd1)
bps_clk <= 1'b1;
else
bps_clk <= 1'b0;
//bps counter
always@(posedge clk or posedge reset)
if(reset)
bps_cnt <= 8'd0;
else if(bps_cnt == 8'd159 | (bps_cnt == 8'd12 && (START_BIT > 2)))
bps_cnt <= 8'd0;
else if(bps_clk)
bps_cnt <= bps_cnt + 1'b1;
else
bps_cnt <= bps_cnt;
always@(posedge clk or posedge reset)
if(reset)
rx_done <= 1'b0;
else if(bps_cnt == 8'd159)
rx_done <= 1'b1;
else
rx_done <= 1'b0;
always@(posedge clk or posedge reset)
if(reset)begin
START_BIT <= 3'd0;
data_byte_pre[0] <= 3'd0;
data_byte_pre[1] <= 3'd0;
data_byte_pre[2] <= 3'd0;
data_byte_pre[3] <= 3'd0;
data_byte_pre[4] <= 3'd0;
data_byte_pre[5] <= 3'd0;
data_byte_pre[6] <= 3'd0;
data_byte_pre[7] <= 3'd0;
STOP_BIT <= 3'd0;
end
else if(bps_clk)begin
case(bps_cnt)
0:begin
START_BIT <= 3'd0;
data_byte_pre[0] <= 3'd0;
data_byte_pre[1] <= 3'd0;
data_byte_pre[2] <= 3'd0;
data_byte_pre[3] <= 3'd0;
data_byte_pre[4] <= 3'd0;
data_byte_pre[5] <= 3'd0;
data_byte_pre[6] <= 3'd0;
data_byte_pre[7] <= 3'd0;
STOP_BIT <= 3'd0;
end
6 ,7 ,8 ,9 ,10,11:START_BIT <= START_BIT + uart_rx_sync2;
22,23,24,25,26,27:data_byte_pre[0] <= data_byte_pre[0] + uart_rx_sync2;
38,39,40,41,42,43:data_byte_pre[1] <= data_byte_pre[1] + uart_rx_sync2;
54,55,56,57,58,59:data_byte_pre[2] <= data_byte_pre[2] + uart_rx_sync2;
70,71,72,73,74,75:data_byte_pre[3] <= data_byte_pre[3] + uart_rx_sync2;
86,87,88,89,90,91:data_byte_pre[4] <= data_byte_pre[4] + uart_rx_sync2;
102,103,104,105,106,107:data_byte_pre[5] <= data_byte_pre[5] + uart_rx_sync2;
118,119,120,121,122,123:data_byte_pre[6] <= data_byte_pre[6] + uart_rx_sync2;
134,135,136,137,138,139:data_byte_pre[7] <= data_byte_pre[7] + uart_rx_sync2;
150,151,152,153,154,155:STOP_BIT <= STOP_BIT + uart_rx_sync2;
default:
begin
START_BIT <= START_BIT;
data_byte_pre[0] <= data_byte_pre[0];
data_byte_pre[1] <= data_byte_pre[1];
data_byte_pre[2] <= data_byte_pre[2];
data_byte_pre[3] <= data_byte_pre[3];
data_byte_pre[4] <= data_byte_pre[4];
data_byte_pre[5] <= data_byte_pre[5];
data_byte_pre[6] <= data_byte_pre[6];
data_byte_pre[7] <= data_byte_pre[7];
STOP_BIT <= STOP_BIT;
end
endcase
end
always@(posedge clk or posedge reset)
if(reset)
data_byte <= 8'd0;
else if(bps_cnt == 8'd159)begin
data_byte[0] <= data_byte_pre[0][2];
data_byte[1] <= data_byte_pre[1][2];
data_byte[2] <= data_byte_pre[2][2];
data_byte[3] <= data_byte_pre[3][2];
data_byte[4] <= data_byte_pre[4][2];
data_byte[5] <= data_byte_pre[5][2];
data_byte[6] <= data_byte_pre[6][2];
data_byte[7] <= data_byte_pre[7][2];
end
always@(posedge clk or posedge reset)
if(reset)
uart_state <= 1'b0;
else if(uart_rx_nedge)
uart_state <= 1'b1;
else if(rx_done || (bps_cnt == 8'd12 && (START_BIT > 2)) || (bps_cnt == 8'd155 && (STOP_BIT < 3)))
uart_state <= 1'b0;
else
uart_state <= uart_state;
endmodule
板级验证和测试
生成比特率,这里管脚分配为:
reg [2:0]Baud_set; 分配给三个拨码开关SW2,SW1,SW0
reg [7:0] data_type; 分配给8个LED灯
reset 分配给拨码开关SW7
clk 板载晶振
rx_done 蜂鸣器旁的LED灯
首先,选择默认波特率,9600,发送55,即8’b01010101
接下来,切换波特率,即将拨码开关切换至3’b100,即选择波特率为115200,传输AA,即8’b10101010,如下所示
该方法适用于工业干扰较强的环境下。
声明:该思路来源于小梅哥FPGA,点击可进入其视频页进行观看。