问题
基于上一节uart串口接收逻辑的内容,还存在一点缺陷,需要设计优化后才能应用于实际项目中。:
由于在不同的数据收发系统之间,uart控制器工作的时钟频率有差异,时钟自身的频率误差也不一样,可能数据传输时间略小于接收时间,导致数据的收发时刻有轻微的错位(详见下方示意图)。若两轮数据发送的时间间隔很短,刚发完第一轮数据就又立刻发送第二轮数据,就可能导致第二轮数据的接受失败。
解决方法
在stop停止位提前结束,只使用0.5位的时间,为发送器提供0.5位的容错时间,提前回到空闲态,等待新一轮数据的接收
设计代码修改
将停止位的采样时间也均改为中点处,即bit_cnt=9,且波特分频计数器计数到一半 = MCNT_BAUD/2
原代码:
修改后:
完整设计代码
`timescale 1ns / 1ps
module uart_byte_rx(
Clk,
Reset_n,
uart_rx,
Data,
Rx_Done
);
input Clk;
input Reset_n;
input uart_rx;
output reg [7:0]Data;
output reg Rx_Done;
reg [29:0]baud_div_count;
reg [3:0]bit_cnt;
reg En;
wire w_Rx_Done;
reg r_uart_rx;//D触发器
wire nedge_uart_rx;
reg dff0_uart_rx , dff1_uart_rx;
reg [7:0]r_Data; //八位寄存器,用于临时寄存8位uart_data,待8位数据全部接收完毕后,再从该寄存器中一起输出
parameter BAUD = 9600;
parameter CLOCK_FREQ = 50_000_000;
parameter MCNT_BAUD = CLOCK_FREQ / BAUD - 1;
//波特分频计数器
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
baud_div_count <= 0;
else if(En)begin //使能端有效
if(baud_div_count == MCNT_BAUD)
baud_div_count <= 0;
else
baud_div_count <= baud_div_count + 1'd1;
end
else //使能端无效
baud_div_count <= 0;
//下降沿检测逻辑
always@(posedge Clk)
dff0_uart_rx <= uart_rx;
always@(posedge Clk)
dff1_uart_rx <= dff0_uart_rx;
always@(posedge Clk)
r_uart_rx <= dff1_uart_rx;
assign nedge_uart_rx = (dff1_uart_rx == 0) && (r_uart_rx == 1);
//使能信号逻辑 注意:毛刺干扰也不能计数
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
En <= 0;
else if(nedge_uart_rx)
En <= 1;
else if((bit_cnt == 9)&&(baud_div_count == MCNT_BAUD/2)) //10位数据接收完成
En <= 0;
else if((bit_cnt == 0)&&(baud_div_count == MCNT_BAUD/2)&&(dff1_uart_rx == 1)) //起始时有毛刺
En <= 0;
//位计数器
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
bit_cnt <= 0;
else if((baud_div_count == MCNT_BAUD/2) && (bit_cnt == 9))
bit_cnt <= 0;
else if(baud_div_count == MCNT_BAUD) //bit_cnt不等于9时能计满,需自加
bit_cnt <= bit_cnt + 1'd1;
//接收完毕停止计数
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
En <= 0;
else if(((bit_cnt == 9)&&(baud_div_count == MCNT_BAUD)))
En <= 1;
//数据位接收逻辑(串转并)
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
Data <= 8'd0;
else if(baud_div_count == MCNT_BAUD/2)begin //中点处采样
case(bit_cnt)
1:r_Data[0] <= dff1_uart_rx;
2:r_Data[1] <= dff1_uart_rx;
3:r_Data[2] <= dff1_uart_rx;
4:r_Data[3] <= dff1_uart_rx;
5:r_Data[4] <= dff1_uart_rx;
6:r_Data[5] <= dff1_uart_rx;
7:r_Data[6] <= dff1_uart_rx;
8:r_Data[7] <= dff1_uart_rx;
default:r_Data <= r_Data;
endcase
end
// 接收完成标志信号
assign w_Rx_Done = (bit_cnt == 9)&&(baud_div_count == MCNT_BAUD/2);
always@(posedge Clk)
Rx_Done <= w_Rx_Done;
always@(posedge Clk)
if(w_Rx_Done)
Data <= r_Data;
endmodule