串口UART接收

串口接收:串---->并

空闲状态,rx为高电平,发送起始位的时候会置为低电平。

假如采样起始位的时候,采到了低电平怎么办

做法1:该数据直接舍弃,也就是不通知外界完成了一个数据的接收,不产生标志信号
做法2:当成新的起始位来解
做法3:当成正确的数据通知外部
做法4:通知外界接收完成,但同时也输出一个错误标志信号,告知外界,此次接收的数据是错误的

1.设计部分

module uart_rx_1(input clk ,
                 input reset_n ,
                 input uart_rx ,
                 output reg [7:0]rx_data,
                 output reg rx_done    );
    //默认使用波特率BAUD 9600  时钟频率 CLK_FREQ  50MHz
    parameter BAUD = 9600;
    parameter CLK_FREQ = 50_000_000;
    parameter bps_c = CLK_FREQ / BAUD ;    
    reg rx_en ;   
    reg[3:0] rx_flag;
        // bps 
        reg [30:0] counter_bps ;        
      always@(posedge clk or negedge reset_n)
        if(! reset_n) 
            counter_bps <= 0 ;
        else if (rx_en)
            if(counter_bps == bps_c - 1)
                counter_bps <= 0 ;
            else
                counter_bps <= counter_bps + 1'b1 ;
        else
            counter_bps <= 0 ;
        reg dff_rx_0 , dff_rx_1 ;
        reg r_uart_rx; 
        wire neg_rx_go ;
        always@(posedge clk )    
            dff_rx_0 <= uart_rx ;
        always@(posedge clk )    
            dff_rx_1 <= dff_rx_0 ;
        always@(posedge clk )    
            r_uart_rx <= dff_rx_1 ;
            
        assign neg_rx_go = (dff_rx_1 == 0)&&(r_uart_rx == 1);
        
      // rx_en 
        always@(posedge clk or negedge reset_n)
        if(! reset_n) 
            rx_en <= 0 ;
        else if(neg_rx_go) 
            rx_en <= 1 ;
        else if((rx_flag==9)&&(counter_bps == bps_c - 1))
            rx_en <= 0 ;
        else if((rx_flag==0)&&(counter_bps == bps_c/2 )&&(dff_rx_1==1)) 
            rx_en <= 0 ;
               
     // rx_flag
     //   always@(posedge clk or negedge reset_n)
     //   if(!reset_n) rx_flag <= 4'b0000 ;
     //   else if(counter_bps == bps_c - 1)
     //   begin
     //       if(rx_flag == 9)
     //           rx_flag <= 0 ;
     //       else
     //           rx_flag <= rx_flag + 1'b1 ;
     //   end 

     // rx_flag
        always@(posedge clk or negedge reset_n)
        if(!reset_n) 
               rx_flag <= 4'b0000 ;
        else if((rx_flag == 9)&&(counter_bps == bps_c /2)) 
                rx_flag <= 4'b0000 ;
        else if(counter_bps == bps_c - 1)  
                rx_flag <= rx_flag + 1'b1 ;

     // [7:0]r_rx_data   
     reg [7:0] r_rx_data;
     always@(posedge clk )
       if(!rx_en) r_rx_data <= r_rx_data;
       else if(counter_bps == bps_c / 2)
        begin 
            case(rx_flag)
            1 : r_rx_data[0] <= dff_rx_1;
            2 : r_rx_data[1] <= dff_rx_1;
            3 : r_rx_data[2] <= dff_rx_1;
            4 : r_rx_data[3] <= dff_rx_1;
            5 : r_rx_data[4] <= dff_rx_1;
            6 : r_rx_data[5] <= dff_rx_1;
            7 : r_rx_data[6] <= dff_rx_1;
            8 : r_rx_data[7] <= dff_rx_1;
            default : r_rx_data <= r_rx_data;
            endcase
        end      


    // rx_done
     always@(posedge clk)
            rx_done <= (rx_flag==9)&&(counter_bps == bps_c - 1);
    // rx_data ;
       always@(posedge clk)
           if(rx_done) rx_data <= r_rx_data;  
endmodule

2.模块分析

(1)两级触发器,用于消除亚稳态

reg dff_rx_0 , dff_rx_1 ;
        reg r_uart_rx; 
        wire neg_rx_go ;
        always@(posedge clk )    
            dff_rx_0 <= uart_rx ;
        always@(posedge clk )    
            dff_rx_1 <= dff_rx_0 ;
        always@(posedge clk )    
            r_uart_rx <= dff_rx_1 ;
            
        assign neg_rx_go = (dff_rx_1 == 0)&&(r_uart_rx == 1);

neg_rx_go为1可能表示接收器处于一种“准备就绪”的状态,即它已经接收到了数据并且当前没有新的数据正在被接收。neg_rx_go 信号通过比较dff_rx_1(当前值)和r_uart_rx(前一个值)来检测是否有数据从高电平跳变到低电平,这通常表示接收到新的有效数据。

为了处理毛刺信号,即在比特周期的一半时,如果信号是高电平,可能是噪声等引起的毛刺,而不是有效的起始位。

(2)锁存器

// rx_done
     always@(posedge clk)
            rx_done <= (rx_flag==9)&&(counter_bps == bps_c - 1);
    // rx_data ;
       always@(posedge clk)
           if(rx_done) rx_data <= r_rx_data;  

rx_data会在传送过程中发生变化,和上篇串口发送模块一样,需要设置锁存器r_rx_data

3.测试

接收是从低位往高位接收

`timescale 1ns / 1ns
module uart_rx_tb();
    reg clk , reset_n ;
    reg uart_rx;
    wire [7:0]rx_data;
    wire rx_done;
    uart_rx_1 uart_rx_tb_( .clk(clk) ,
               .reset_n(reset_n) ,
               .uart_rx(uart_rx) ,
               .rx_data(rx_data),
               .rx_done(rx_done)    );
    
    initial clk = 1 ;
    always #10 clk = ~clk ;
    initial
    begin
        
        reset_n = 0 ;
        uart_rx = 1 ;
        #201;
        reset_n = 1 ;  #2000;     
        //   0F   0000_1111
        uart_rx = 0 ; #(5208*20);
        uart_rx = 1 ; #(5208*20);
        uart_rx = 1 ; #(5208*20);
        uart_rx = 1 ; #(5208*20);
        uart_rx = 1 ; #(5208*20);
        uart_rx = 0 ; #(5208*20);
        uart_rx = 0 ; #(5208*20);
        uart_rx = 0 ; #(5208*20);
        uart_rx = 0 ; #(5208*20);
        uart_rx = 1 ; #(5208*20);
        #200000;
     //   55   0101_0101
        uart_rx = 0 ; #(5208*20);
        uart_rx = 1 ; #(5208*20);
        uart_rx = 0 ; #(5208*20);
        uart_rx = 1 ; #(5208*20);
        uart_rx = 0 ; #(5208*20);
        uart_rx = 1 ; #(5208*20);
        uart_rx = 0 ; #(5208*20);
        uart_rx = 1 ; #(5208*20);
        uart_rx = 0 ; #(5208*20);
        uart_rx = 1 ; #(5208*20);
        #200000;
        
        $stop;
    end
    
    
endmodule

结果:


4.优化

正常传输 :波特率115200    传输一位bit的时间1/115200=8680.5ns    

                                              传输十位就需要86805ns

如果是误差传输:例如发送时间由于设备原因少了10ns,即传输位时间为8670.5,则传输十位就需要80705,发送器就少了100ns,而接收器比发送器多了100ns,会导致发送方发完第一个数据后,会接着立即发送第二个数据,而接收方还处于接收上一个数据的状态,忽略掉新一个数据的起始位,导致第二个数据接受失败!这个bug很难发现问题,最终通过示波器抓取波形分析才能找到问题。

问题解决:通过在停止位的位时间的中点,就停止数据接收,节省0.5s的时间,避免下一个信号到来错过起始位。

即将stop信号提前到蓝线处:

代码修改:

//(rx_flag==9)&&(counter_bps == bps_c - 1)
(rx_flag==9)&&(counter_bps == bps_c / 2)

//rx_done<=(rx_flag==9)&&(counter_bps==bps_c/2)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值