UART串口接收逻辑实验

本文围绕FPGA中UART串口接收展开,探讨从串行数据准确获取每一位数据的方法,介绍上升沿下降沿检测电路、波特率分频计数器使能逻辑等设计要点。还提及模块构建中亚稳态问题及解决办法,激励部分模拟uart产生过程,最后针对不同系统时钟差异进行设计优化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

任务前言

在这里插入图片描述

1.要点1

对于串口接收来说,我们应该怎么样从串行数据中准确的获取到每一位数据?
自己:按之前的思路,设置一个提醒信号。
在这里插入图片描述
我们注意到,开始的时候有一个上升的阶段。信号不可能立即的从0变到1,这总是要经历一个过程。
提问:这个存在的过程意味着什么?
我们信号在变化的这个过程,其值是0还是1是不可靠的,还在变化的过程中,还没有变到稳定的1上面去。
所以在中间点判断,是比较稳定的。
在这里插入图片描述
用一个计数器在下降沿计数,到一位数据发送完,清零,就和波特率分频计数器一样。
在这里插入图片描述
这样计算,在计数的中点接收数据。

2.要点2

如何检测串口接收数据的下降沿。
在这里插入图片描述
用一个非时钟信号作为D触发器的时钟,那么这个就是不合理的。
这样会使得D触发器的时钟质量很差,进而导致输出数据的错误和不可靠。也会使系统变的不稳定,所以仅推荐有时钟信号的作为D触发器的时钟。

提示:
在这里插入图片描述

在verilog中关于always 块的描述。
组合逻辑通常使用无时钟信号的always @ (*)块来描述,而顺序逻辑则使用带有时钟信号的always @(posedge clk)块。
在这里插入图片描述
就是前一个时刻为高电平,后一个时刻为低电平。就是经历了一个下降沿。
前一个时刻为低电平,后一个时刻为高电平。就是经历了一个上升沿。
下降沿,上升沿的检测:
这个在FPGA中有专门的电路可以实现。

上升沿下降沿检测电路

上升沿下降沿检测
在这里插入图片描述

3.要点3

如何设计波特率分频计数器使能逻辑。
在这里插入图片描述
使能逻辑,不因为前面的毛刺信号导致误判,发现不对后也能够停止计数,清零,等待下一个下降沿。

4.要点4

如何知道当前所读取的信号位于UART信号的哪一位?
答:还是可以使用位计数器的逻辑,计数到了哪一位,就是发送到了哪一位。

在这里插入图片描述

5.要点5

如何实现位接收的逻辑?
还是可以使用我们的case逻辑。
else begin
case(bit_cnt)
0: uart_tx <= 1’d0; //起始位是低电平。
1: uart_tx <= r_Data[0]; //1的时候发送数据的Bit0位。发送的是Data位。
2: uart_tx <= r_Data[1];
3: uart_tx <= r_Data[2];
4: uart_tx <= r_Data[3];
5: uart_tx <= r_Data[4];
6: uart_tx <= r_Data[5];
7: uart_tx <= r_Data[6];
8: uart_tx <= r_Data[7];
9: uart_tx <= 1’d1; //停止位为什么是1?根据信号传输的波形来的。
default:uart_tx <= uart_tx;
endcase
end

6.要点6

接受完成之后,产生一个接受完成的信号来表示接收完成。

一、串口接收是什么?

在这里插入图片描述
提问:假如说在在采样停止位的时候,采样到了低电平怎么办?
在这里插入图片描述
做法4,比较合理。但是实际中一般是做法3.
经过测试,CH340对于使用的是做法3.

二、具体实现

1.模块构建

代码如下(示例):

//位接收逻辑
   always@( posedge Clk or negedge Reset_n)
    if(!Reset_n)
        Rx_Data <= 8'd0;
    else if(baud_div_cnt ==MCNT_BAUD/2 )begin  //波特率计数器计数到最大值的一半的时候
        case(bit_cnt)   //根据bit_cnt的值,做一个存储。
            
            1:Rx_Data[0] <= uart_rx;   //1的时候发送数据的Bit0位。发送的是Data位。
            2:Rx_Data[1] <= uart_rx;
            3:Rx_Data[2] <= uart_rx;
            4:Rx_Data[3] <= uart_rx;
            5:Rx_Data[4] <= uart_rx;
            6:Rx_Data[5] <= uart_rx;
            7:Rx_Data[6] <= uart_rx;
            8:Rx_Data[1] <= uart_rx;
            
            default:Rx_Data <= Rx_Data; 
        endcase
     end

判断条件:波特率计数器计数到最大值的一半的时候
问:这里为什么没有起始位和停止位?
答:else if(baud_div_cnt == MCNT_BAUD/2) && (bit_cnt == 0)&& (uart_rx == 1)) 前面的部分判断过。

else if(baud_div_cnt ==MCNT_BAUD/2 )前置条件一定不能少。
通过这个条件,才能限制后面的每一位的稳定。每一个赋值逻辑都是在中点进行。不加这个逻辑,每个时钟周期都会执行一次。
最后存进RX_Date的,是最后一个时钟周期的时候。存进去的是我们

在这里插入图片描述
存进去的是某个值,最后一个时钟周期的时候,uart_rx的状态。只有下一个时刻变了,这个才不会再更新。

有时候会出现搞不清楚上升沿和下降沿的时候,r_uart_rx上次的信号,uart_rx这次的信号。
uart_rx的同步操作

亚稳态。

其他信号都是由Clk驱动的,只有uart_rx信号是由外部输入的,发生变化
其有可能与Clk上升沿很近的位置发生变化,就会产生D端口无法正确的存储数据,无法满足D触发器的必要条件。导致D触发器无法有效的来判断存储这个时刻的uart存储状态。有可能引起D触发器的一个输出震荡。从而让逻辑电路的工作不稳定,这样的情况就称之为亚稳态。
在这里插入图片描述
根据经验,在D触发器的信号前面在加两级D触发器,可以有效的避免这样的情况。对输入信号进行同步,
就能将该信号同步到原本工作的时钟域来。
再去使用D触发器的输出就可以避免亚稳态对我们的影响。

对dff1进行打拍存储,用dff1代替了原本没有同步过的uart_rx。

书写:
在写一些信号的时候,可以假设他已经存在了,先用,然后后面再补全这些信号。

完整代码

module uart_byte_rx(
    Clk,
    Reset_n,
    Rx_Data,
    uart_rx,
    Rx_Done    
    );
    input Clk;
    input Reset_n;
    input uart_rx;        //串口接收信号
    output reg Rx_Done;
    output reg [7:0]Rx_Data;
    
    parameter CLOCK_FREQ = 50_000_000;
    parameter BAUD = 9600;
    parameter MCNT_BAUD = CLOCK_FREQ / BAUD - 1;
    
    reg [7:0]r_Rx_Data;   //8位的D触发器用来存储Rx_Data。
    
    reg en_baud_cnt;
    reg [29:0]baud_div_cnt; //波特率分频计数器    
    reg [3:00]bit_cnt;
    
    wire w_Rx_Done;
    
    reg r_uart_rx;          //下降沿标志信号。
    reg dff0_uart_rx,dff1_uart_rx;
    
    
//波特率分频计数器逻辑
    always@( posedge Clk or negedge Reset_n)
    if(!Reset_n)
	   baud_div_cnt <= 0;
    else if(en_baud_cnt)begin
	   if(baud_div_cnt == MCNT_BAUD)  
		  baud_div_cnt <= 0;
	   else
		  baud_div_cnt <= baud_div_cnt + 1'd1;
    end
    else
	   baud_div_cnt <= 0;  //不使能的时候保持计数器为0




//UART 信号边沿检测逻辑
    always@( posedge Clk)  //这个部分就是通过赋值语句多了两级D触发器。
        dff0_uart_rx <= uart_rx;
    
    always@( posedge Clk)
        dff1_uart_rx <= dff0_uart_rx;

    always@( posedge Clk)
        r_uart_rx <= dff1_uart_rx;   //r_uart_rx上次的信号,uart_rx这次的信号。
    
    assign nedge_uart_rx = ((dff1_uart_rx) && (r_uart_rx == 1));  //当下降沿标志信号为1和串口接收信号为0的时候。对dff1进行打拍存储,用dff1代替了原本没有同步过的uart_rx。
    

//波特率计数器使能逻辑
    always@( posedge Clk or negedge Reset_n)
    if(!Reset_n)
        en_baud_cnt <= 0;
    else if(nedge_uart_rx)  //下降沿标志信号。
        en_baud_cnt <= 1;  //
    else
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值