新手上路
- 积分
- 48
- 金钱
- 48
- 注册时间
- 2023-12-1
- 在线时间
- 4 小时
|
楼主 |
发表于 2023-12-23 17:41:00
|
显示全部楼层
看了源码找到了大概原因:
1、教材中的源码使用的是对比2个reg判断是否为下降沿,而不是真正的下降沿触发。
2、RXD的输入电压可能是缓变的,系统时钟为采样时钟,采样时钟的周期可能远短于信号的上升时间或下降时间,导致电平判决时刻处于CMOS的中间电平。
3、CMOS中间电平判决时,判决结果既可能是1也可能是0。导致逻辑电平反复在0、1翻转。
加上看不懂课程,我重写了代码。使用真正的下降沿触发(节约n个d触发器)
思路是:
1、准备好采样时钟,计数器。
2、在检测到rxd的下降沿后,启动采样时钟(对齐判决时钟相位)
3、每个采样时钟上升沿进行抽样判决,采样计数器累加。
4、最后1个bit判决完成后,在最后一个采样周期结束时,拉起done信号。
我另外画了电路图,个人觉得做这个设计需要先画数电的电路图,不然看课程的波形一头雾水。
完整源码如下:
module uart_rx(
input clk , //系统时钟
input rst_n , //系统复位,低有效
input uart_rxd , //UART接收端口
output reg uart_rx_done, //UART接收完成信号
output [7:0] uart_rx_data //UART接收到的数据
);
parameter CLK_FREQ = 50000000; //系统时钟频率
parameter UART_BPS = 115200 ; //串口波特率
localparam BAUD_CNT_MAX = CLK_FREQ/UART_BPS; //为得到指定波特率,对系统时钟计数BPS_CNT次
//reg define
reg [3:0 ] rx_cnt ; //接收数据计数器
reg [15:0] baud_cnt ; //波特率计数器
//reg [9:0 ] rx_data_t ; //接收数据寄存器
reg sample_clk;
reg rx_flag;
//起始位下降沿触发
initial rx_flag <= 1'b0;
always @(negedge uart_rxd or posedge uart_rx_done)begin
if(!uart_rxd)
rx_flag <= 1'b1;
else if(uart_rx_done)
rx_flag <= 1'b0;
end
//波特率的计数器
initial sample_clk = 0;
always @(posedge clk or negedge rst_n,rx_flag ) begin
if(!rst_n || !rx_flag)
baud_cnt <= 16'd0;
else if(rx_flag) //处于接收过程时,波特率计数器(baud_cnt)进行循环计数
if(baud_cnt < BAUD_CNT_MAX/2 - 1'b1)
baud_cnt <= baud_cnt + 16'b1;
else begin
baud_cnt <= 16'd0; //计数达到一个波特率周期后清零
sample_clk <= ~sample_clk;
end
else
baud_cnt <= 16'd0; //接收过程结束时计数器清零
end
//复位条件:1、起始位为1。2、停止位为0.
//uart_rx_done,rx_cnt == 9
//采样计数器
always @(posedge sample_clk or negedge rst_n,rx_flag)begin
if(!rst_n || !rx_flag)
rx_cnt <= 4'd0;
else if(sample_clk && rx_cnt < 4'd10)
rx_cnt <= rx_cnt + 1;
else
rx_cnt <= 4'd0;
end
//相位对齐,采样
reg [9:0] rx_temp; //数据缓存,等同于原子哥代码中的rx_data_t
initial rx_temp <= 10'd0;
always @(posedge sample_clk,rx_flag or negedge rst_n)begin
if(!rst_n)
rx_temp <= 10'd0;
else if(sample_clk)begin
rx_temp = rx_temp << 1;
rx_temp[0] = uart_rxd;
end
else if(rx_flag) //新的数据到达
rx_temp <= 10'd0;
else
rx_temp <= rx_temp;
end
//结束标记,采样时钟下降沿确认
wire check;
initial uart_rx_done <= 1'b0;
assign check = rx_temp[9] & !rx_temp[0]; //协议检查,确认起始位和停止位
always @(posedge rx_flag or negedge rst_n,sample_clk)begin
if(!rst_n)
uart_rx_done <= 1'b0;
else if(rx_cnt == 4'ha && sample_clk == 1'b0)
uart_rx_done <= 1'b1;
else if(rx_flag)
uart_rx_done <= 1'b0;
end
assign uart_rx_data[7:0] = rx_temp[8:1]; //直连
endmodule
|
|