FPGA——异步串行通信(UART)接收
一、设计思路
- 一般的串口接收是采集每一个比特位的中点,如果在及其恶劣的环境下,比特位中点数据有可能会与真实数据相悖,如何解决这样的问题?
- 将每一个比特位分成16份,采集中间7个点,再计算7个点出现0和1的概率,概率大的即为真实数据
- 计数器设计
- 分成16段的波特率计数器,完整波特率计数(16),比特位计数(9)
- 为什么只接收9个数据?
- 第十位为空闲位,没有接收的意义,并且,如果在第十位接收完成产生的done信号,会使以done信号接收串口数据的模块延迟接收数据,而带来一系列的问题
二、串口发送代码
module my_uart_rx(
rst_n ,
clk ,
uart_rx ,
baud_set ,
data ,
rx_done
);
parameter DATA_W = 8; //接收数据位宽
parameter BAUD_W = 10; //波特率计数器位宽
parameter SYNC_W = 3; //边沿检测信号位宽
parameter SAMP_N = 16; //采样点个数,每个比特16个采样点
parameter SAMP_W = 4; //采样点位宽
parameter BYTE_W = 4; //比特计数器位宽
parameter BYTE_N = 9; //比特计数器个数,不需要对空闲位进行处理
parameter BAUDS_W = 3; //波特率设置器的位宽
input rst_n;
input clk;
input uart_rx;
input [BAUDS_W-1:0] baud_set;
output [DATA_W-1:0] data;
output rx_done;
reg [DATA_W-1:0] data;
reg rx_done;
reg [BAUD_W-1:0] cnt_baud;
wire add_cnt_baud;
wire end_cnt_baud;
reg [SAMP_W-1:0] cnt_sample;
wire add_cnt_sample;
wire end_cnt_sample;
reg [BYTE_W-1:0] cnt_byte;
wire add_cnt_byte;
wire end_cnt_byte;
reg [BAUD_W-1:0] baud;
reg [SYNC_W-1:0] uart_sync;
//寄存器数组,表示9个位宽为3的寄存器,用来存放采样点
reg [2:0] data_tmp[8:0];
wire nedge_flag;
reg add_flag;
//波特率计数器
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt_baud <= 0;
else if(add_cnt_baud)begin
if(end_cnt_baud)
cnt_baud <= 0;
else
cnt_baud <= cnt_baud + 1\'b1;
end
end
assign add_cnt_baud = add_flag;
assign end_cnt_baud = add_cnt_baud && cnt_baud == baud - 1;
//采样点计数器
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt_sample <= 0;
else if(add_cnt_sample)begin
if(end_cnt_sample)
cnt_sample <= 0;
else
cnt_sample <= cnt_sample + 1\'b1;
end
end
assign add_cnt_sample = end_cnt_baud;
assign end_cnt_sample = add_cnt_sample && cnt_sample == SAMP_N - 1;
//比特计数器
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt_byte <= 0;
else if(add_cnt_byte)begin
if(end_cnt_byte)
cnt_byte <= 0;
else
cnt_byte <= cnt_byte + 1\'b1;
end
end
assign add_cnt_byte = end_cnt_sample;
assign end_cnt_byte = add_cnt_byte && cnt_byte == BYTE_N - 1;
//取1bit数据中间的7个点进行采样计算
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
data_tmp[0] <= 0;
data_tmp[1] <= 0;
data_tmp[2] <= 0;
data_tmp[3] <= 0;
data_tmp[4] <= 0;
data_tmp[5] <= 0;
data_tmp[6] <= 0;
data_tmp[7] <= 0;
data_tmp[8] <= 0;
end
else if(cnt_baud==((baud>>2) - 1) && add_cnt_baud)begin
case(cnt_sample)
5,6,7,8,9,10,11:data_tmp[cnt_byte] <= data_tmp[cnt_byte] + uart_rx;
default:data_tmp[cnt_byte] <= data_tmp[cnt_byte];
endcase
end
else if(end_cnt_byte)begin
data_tmp[0] <= 0;
data_tmp[1] <= 0;
data_tmp[2] <= 0;
data_tmp[3] <= 0;
data_tmp[4] <= 0;
data_tmp[5] <= 0;
data_tmp[6] <= 0;
data_tmp[7] <= 0;
data_tmp[8] <= 0;
end
end
//判断采样点0,1的个数如果一个比特位采到1的个数比0多,那么输出1,否则输出0
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
data <= 0;
else if(end_cnt_sample && cnt_byte >= 1 && cnt_byte < 9)
data[cnt_byte-1] <= (data_tmp[cnt_byte] >= 4);
end
// 波特率选择器,16点采样
// (1/baud_rate)*50MHz/16
always @(*)begin
case(baud_set)
3\'d0:baud = 5208;//600bps
3\'d1:baud = 2604;//1200bps
3\'d2:baud = 1302;//2400bps
3\'d3:baud = 651 ;//4800bps
3\'d4:baud = 325 ;//9600bps
3\'d5:baud = 163 ;//19200bps
3\'d6:baud = 81 ;//38400bps
3\'d7:baud = 54 ;//57600bps
default:baud = 325;
endcase
end
//边沿检测
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
uart_sync <= 3\'b111;
else
uart_sync <= {uart_sync[1:0],uart_rx};
end
assign nedge_flag = uart_sync[2:1]==2\'b10;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
rx_done <= 0;
else if(end_cnt_byte)
rx_done <= 1;
else
rx_done <= 0;
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
add_flag <= 0;
else if(nedge_flag)
add_flag <= 1;
else if(end_cnt_byte)
add_flag <= 0;
end
endmodule
三、串口仿真代码
`timescale 1ns / 1ns
module my_uart_tx_tb();
parameter CYCLE = 20;
parameter RST_TIME = 3;
reg rst_n;
reg clk;
reg uart_rx;
wire [2:0] baud_set;
wire [7:0] data;
wire [7:0] data_in;
wire rx_done;
assign baud_set = 4;
my_uart_rx my_uart_rx(
rst_n ,
clk ,
uart_rx ,
baud_set ,
data ,
rx_done
);
initial begin
clk = 1;
forever
#(CYCLE/2)
clk = ~clk;
end
initial begin
rst_n = 1;
#3;
rst_n = 0;
#(RST_TIME*CYCLE)
rst_n = 1;
end
initial begin
uart_rx = 1\'b1;
@(posedge rst_n);
#(8*CYCLE);
uart_tx(8\'hAA);
@(posedge rx_done);
#100000;
uart_tx(8\'h78);
@(posedge rx_done);
#100000;
uart_tx(8\'h38);
@(posedge rx_done);
#100000;
uart_tx(8\'h47);
@(posedge rx_done);
#100000;
$stop;
end
task uart_tx;
input [7:0] data_in;
begin
uart_rx = 1\'b0;
#(5208*CYCLE);
uart_rx = data_in[0];
#(5208*CYCLE);
uart_rx = data_in[1];
#(5208*CYCLE);
uart_rx = data_in[2];
#(5208*CYCLE);
uart_rx = data_in[3];
#(5208*CYCLE);
uart_rx = data_in[4];
#(5208*CYCLE);
uart_rx = data_in[5];
#(5208*CYCLE);
uart_rx = data_in[6];
#(5208*CYCLE);
uart_rx = data_in[7];
#(5208*CYCLE);
uart_rx = 1\'b1;
end
endtask
endmodule
版权声明:本文为cnlntr原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。