串口通信:uart接收模块

1.功能描述

  设计一个串口数据接收模块。能够以设定的波特率(与发射端口速率匹配)接收数据,并输出保存到一个寄存器中。

2.过程描述

  ①边沿检测器,识别出起始位时让接收使能端有效。这里需要排除边沿脉冲的干扰,识别出的起始位不能是个瞬时脉冲。

  ②采样脉冲:区别于发射端,接收端需要对接收的数据进行采样。为保证接受到的数据的准确性,需要设定采样频率(奈奎斯特采样频率)。如下:

   遵循uart协议的串口通信,每个数据为10位,包含8个数据位,1起始位,1结束位。在接收端接收时也要按照这个规律接收。具体思路是先判断起始位,然后逐个接收数据位,在结束位出现后把接收的数据赋给寄存器。

    

如上图所示 ,在采样时,我们把一位划分成16份,舍弃前5后4取中间七份(中心采样)(要求是奇数份,用来判断接收的数据是零还是一),在每一个1/16位中心产生一个采样脉冲,当此脉冲出现时,把它加到寄存器上,最终根据寄存器的值确定接收的数据大小(大于3为1,否则为0)。

编程的思路:此电路仍是时序电路,所以要确定用到的数据和时钟信号的关系。

      ①根据设定的波特率确定每一位的持续时间,进而确定每一位的计数值bit_tim。

      ②根据每一位的计数值确定每1/16位的计数值bit_tim_16,使用div_cnt寄存器计数到bit_tim_16-1.

      ③利用bit_tim_16产生采样脉冲bit16_pulse。

      ④利用bit_tim_16设计16*10个 状态,用bit16_cnt存储计数.

      ⑤设计一个寄存器edge_detect检测下降沿来判断起始位,产生接收脉冲,进而设置接收使能端receive_en。

      ⑥利用状态计数器bit16_cnt和采样脉冲bit16_pulse采样,保存采样值r_data。

      ⑦采样完成后设置结束信号rx_done。

      ⑧把储存的采样值r_data经过判决后存进输出寄存器data。

调试的经验:

      ①对design source 和 testbench 文件修改之后,不需要重新综合,可以直接relaunch stimulation 。这样子就不用重新添加变量,可以提高调试速度。

      ②Verilog在作运算时,结果取整数,舍弃小数位,因此在设置状态持续时间时,用运算得到的值可能会偏大/小,导致仿真时,误差使得结束位提前出现,tb中@检测不到,从而出错。在实                   际中不会出错,但仍然有误差。具体看误差是否可以忽略。

      ③在仿真验证时,延时可以人为给定,波形出错的原因可能是跟design的时间对不上,不代表design source 设计的逻辑错误,可以对两者进行对比检查。

      ④注意多语句有无遗漏begin-end语句。

      ⑤开始检测起始位时,由于需要寄存器存值,出现一个时钟周期的误差,检测到边沿后再给receive_en赋值,又有一个时钟周期的误差。但由于采样取的是位中心,误差对本次设计的影响               可以忽略。

注意:

      ①采样脉冲的意义:让保存采样值的寄存器r_data在一个状态里只加一次。不然由于一个状态不止一个时钟周期,在代码设计上容易加很多次。

      ②保存到输出寄存器的时间可以在所有位都采样完之后,对所有位的采样寄存器进行判决即可。

      ③为保证接收到了起始位,要在边沿检测的基础上,对起始位进行判决,是0才对。

      ④用一个位宽为二的 寄存器作为边沿检测器,在整个过程中都会一直进行检测。但是不影响结果。

      ⑤每接受完一个数据后,要对保存采样的寄存器清零,不然会影响下一次接收。

新语法:

      ①设置二维寄存器格式:类型  [ 位宽 ] 名字 [ 寄存器个数]

       位宽[2:0]指的是三个位宽,范围0~7。寄存器个数[2:0]是指三个寄存器,不是指7个寄存器。

       引用时,格式为:名字【第几个寄存器】【哪个/哪几个数据】。注意,引用时不能直接对多个寄存器赋值,而定义时可以。

       ②case里面对于没用到的状态,无需动作,可以直接写default ; 。

       ③task系统任务。待补充。见语法书。可以在task中不止可以修改task中定义的变量值,而且可以修改task外,本文件里面定义的变量的值。注意task和function的区别,对比记忆。

       ④<=在always中可以是赋值语句,非阻塞赋值,也可以作为判断语句中,意为小于等于。           

3.设计输入

  接口: 1.输入端:

      数据输入端口uart_tx,接受波特率设置端口baud_rate,时钟信号端口,复位信号端口。

      2.输出端:

        输出接收的数据端口data,接收完成信号端口rx_done。

4.代码 

module uart_receive_1(
    clk ,
    reset ,
    baud_rate ,
    uart_tx, 
    data ,
    rx_done   
    );
    input  clk ;
    input reset ;
    input [2:0]baud_rate ;
    input uart_tx ;
    output reg [7:0]data ;
    output reg rx_done ;
    
    reg [2:0]r_data[7:0] ;//接收每一位数据
    reg [2:0]sta_bit ;
    reg [2:0]sto_bit ;
    
    reg [17:0]bit_tim ;//每一位持续的时间(计数)
    always@(baud_rate)  //在这里一个 码元由一位组成,所以波特率=比特率
        begin
            case(baud_rate)         //常见的串口传输波特率
            3'd0 : bit_tim = 1000000000/300/20 ; //波特率为300
            3'd1 : bit_tim = 1000000000/1200/20 ; //波特率为1200
            3'd2 : bit_tim = 1000000000/2400/20 ; //波特率为2400
            3'd3 : bit_tim = 1000000000/9600/20 ; //波特率为9600
            3'd4 : bit_tim = 1000000000/19200/20 ; //波特率为19200
            3'd5 : bit_tim = 1000000000/115200/20 ; //波特率为115200
            default bit_tim = 1000000000/9600/20 ;   //多余的寄存器位置放什么:默认速率
            endcase
     end
    
    wire [17:0]bit_tim_16 ;//每1/16位的持续时间(计数)
    assign bit_tim_16 = bit_tim / 16;
    
    wire [8:0]bit16_mid ; //在中心点产生采样脉冲 
    assign bit16_mid = bit_tim_16 / 2 ;
    
    //边沿检测
    reg [1:0]edge_detect ;
    always @( posedge clk or negedge reset )
    begin
        if (!reset )
            edge_detect <= 2'd0 ;
        else 
            begin
            edge_detect[0] <= uart_tx ;
            edge_detect[1] <= edge_detect[0] ;
            end
    end    

    wire byte_sta_neg ;
    assign byte_sta_neg = ( edge_detect == 2'b10 ) ? 1 : 0 ;//输入的数据开始出现下降沿,说明出现了起始位(一直运行?)
         
    reg receive_en ;//接收使能端
    reg [17:0]div_cnt ;//每1/16bit内的计数
     reg [7:0]bit16_cnt ;//计数到了第几个状态(10位,每位分成16份,总共160个状态)
    always @( posedge clk or negedge reset )
    begin
         if (!reset )
            receive_en <= 1'd0 ;
        else if ( byte_sta_neg )    //检测到下降沿,使能段有效(只要有下降沿就使能?)
            receive_en <= 1'd1 ;
        else if ( (rx_done) || (sta_bit >= 3'd4 ))    
            receive_en <= 1'd0 ;    //检测到结束信号,使能端无效
        else if ( ( bit16_cnt == 8'd159 ) && (div_cnt == bit_tim_16 - 1'd1 ) )//跑完159后re_en置零
            receive_en <= 1'd0 ;
    end
             
    
    always@( posedge clk or negedge reset )
    begin
        if ( ! reset )
            div_cnt <= 18'd0 ;
        else if (receive_en)
        begin
            if ( div_cnt == bit_tim_16 - 1'd1 )//计数,每1/16bit清零
                div_cnt <= 18'd0 ;               
            else
                div_cnt <= div_cnt + 1'b1 ; 
        end
        else 
            div_cnt <= 18'd0 ;
    end
    
    reg bit16_pulse ;//产生采样脉冲
    always@( posedge clk or negedge reset )
    begin
        if ( ! reset )
            bit16_pulse <= 18'd0 ;
        else if (receive_en)
            if ( div_cnt == bit16_mid )
                bit16_pulse <= 1'd1 ;
            else
                bit16_pulse <= 1'd0 ;
        else
                bit16_pulse <= 1'd0 ;                
    end       
   
    always@( posedge clk or negedge reset )
    begin
        if ( ! reset )
            bit16_cnt <= 8'd0 ;
        else if (receive_en)
        begin    
            if (( bit16_cnt == 8'd159 ) && (div_cnt == bit_tim_16 - 1'd1 ))
                bit16_cnt <= 8'd0 ;
            else if ( div_cnt == bit_tim_16 - 1'd1 )
                bit16_cnt <= bit16_cnt + 1'b1 ;
        end
    end
      
    always@(posedge clk or negedge reset)
    begin
    if(!reset)
    begin
        sta_bit   <= 3'd0 ;
        r_data[0] <= 3'd0 ;
        r_data[1] <= 3'd0 ;
        r_data[2] <= 3'd0 ;
        r_data[3] <= 3'd0 ;
        r_data[4] <= 3'd0 ;
        r_data[5] <= 3'd0 ;
        r_data[6] <= 3'd0 ;
        r_data[7] <= 3'd0 ;
        sto_bit   <= 3'd0 ;
    end
    else if (bit16_pulse)//舍弃前5后4取中7
        case(bit16_cnt)
            0: 
            begin 
            sta_bit   <= 3'd0 ;
            r_data[0] <= 3'd0 ;
            r_data[1] <= 3'd0 ;
            r_data[2] <= 3'd0 ;
            r_data[3] <= 3'd0 ;
            r_data[4] <= 3'd0 ;
            r_data[5] <= 3'd0 ;
            r_data[6] <= 3'd0 ;
            r_data[7] <= 3'd0 ;
            sto_bit   <= 3'd0 ;
            end
            5,6,7,8,9,10,11 : sta_bit <= sta_bit + uart_tx ;
            21,22,23,24,25,26,27 : r_data[0] <= r_data[0] + uart_tx ;
            37,38,39,41,42,43,44 : r_data[1] <= r_data[1] + uart_tx ; 
            53,54,55,56,57,58,59 : r_data[2] <= r_data[2] + uart_tx ;
            69,70,71,72,73,74,75 : r_data[3] <= r_data[3] + uart_tx ;
            85,86,87,88,89,90,91 : r_data[4] <= r_data[4] + uart_tx ;
            101,102,103,104,105,106,107 : r_data[5] <= r_data[5] + uart_tx ;
            117,118,119,120,121,122,123 : r_data[6] <= r_data[6] + uart_tx ;
            133,134,135,136,137,138,139 : r_data[7] <= r_data[7] + uart_tx ;
            149,150,151,152,153,154,155 : sto_bit <= sto_bit + uart_tx ;
            default ;
        endcase
    end

    always@( posedge clk or negedge reset )
    begin
        if ( ! reset )
            rx_done <= 8'd0 ;
        else if ( ( bit16_cnt == 8'd159 ) && (div_cnt == bit_tim_16 - 1'd1 ) )//跑完159后产生一个rx_done信号
            rx_done <= 8'd1 ;
        else if (rx_done <= 8'd1 )
            rx_done <= 8'd0 ;
    end         
    
    always@( posedge clk or negedge reset )//接收完数据发出rx_done后,把数据从r_data传递给data
    begin
        if ( ! reset )
            data <= 8'd0 ;
        else if ( rx_done )
        begin
            data[0] = ( r_data[0] >3 ) ? 1 : 0 ;
            data[1] = ( r_data[1] >3 ) ? 1 : 0 ;
            data[2] = ( r_data[2] >3 ) ? 1 : 0 ;
            data[3] = ( r_data[3] >3 ) ? 1 : 0 ;
            data[4] = ( r_data[4] >3 ) ? 1 : 0 ;
            data[5] = ( r_data[5] >3 ) ? 1 : 0 ;
            data[6] = ( r_data[6] >3 ) ? 1 : 0 ;
            data[7] = ( r_data[7] >3 ) ? 1 : 0 ;
        end
        else if ( receive_en )
            data <= 8'd0 ;
    end
         
    
endmodule
`timescale 1ns/1ns
module uart_receive_tb();

    reg clk ;
    reg reset ;
    reg [2:0]baud_rate ;
    reg uart_tx ;
    wire [7:0]data ;
    wire rx_done ;
    
    uart_receive_1 uart_receive_1(
        clk ,
        reset ,
        baud_rate ,
        uart_tx, 
        data ,
        rx_done   
        );
    
    initial clk = 1 ;
    always #10 clk = ! clk ;
    initial 
    begin
    reset = 0 ;
    baud_rate = 0 ;
    uart_tx = 1 ;
    #201 ;
    reset = 1 ;
    baud_rate = 3'd5 ;
    uart_input(8'h4f) ;
    @(posedge rx_done) ;
    #200 ;
    uart_input(8'h3a) ;
    @(posedge rx_done) ;
    #200 ;
    uart_input(8'hbc) ;
    @(posedge rx_done) ;
    #200;
    $stop;
    end
    
    task uart_input ;//设定一个任务uart_inpt,有一个输入端uart_tx_data_stm 。在这个task里可以对task外的变量进行赋值
        input [7:0]uart_tx_data_stm ;//不返回值,所以不能用x=uart_input。而是直接uart_input。
        begin   //结构简单的begin-end
        uart_tx = 1 ;
        #20 ;
        uart_tx  = 0 ;
        #8640 ;
        uart_tx = uart_tx_data_stm[0] ;
        #8640 ;
        uart_tx = uart_tx_data_stm[1] ;
        #8640 ;
        uart_tx = uart_tx_data_stm[2] ;
        #8640 ;
        uart_tx = uart_tx_data_stm[3] ;
        #8640 ;
        uart_tx = uart_tx_data_stm[4] ;
        #8640 ;
        uart_tx = uart_tx_data_stm[5] ;
        #8640 ;
        uart_tx = uart_tx_data_stm[6] ;
        #8640 ;
        uart_tx = uart_tx_data_stm[7] ;
        #8640 ;
        uart_tx  = 1 ;
        #8640 ;
        end     
    endtask
endmodule

  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
| | 4 | 0 | 0 | NULL | | 5 | 0 | 0 UART接收模块是用于从外部设备接收数据到UART接口的模块。它通常含一个串行接收器、一个时钟发生器和一个数据缓存器。 在设计UART接收模块时,需要考虑以下几个方面: 1. 波特率:UART通信的波特率是指单位时间内传输的比特数。需要根据具体的应用场景确定波特率,并将其设置为UART接收模块的时钟频率。 2. 数据长度:UART通信中,数据长度通常为8位或9位。需要确定具体的数据长度,并针对性地设计UART接收模块。 3. 奇偶校验:UART通信中,可以选择奇偶校验来检查数据传输的正确性。需要根据应用场景确定奇偶校验方式,并在UART接收模块中进行实现。 4. 停止位:UART通信中,停止位通常为1位或2位。需要确定具体的停止位长度,并在UART接收模块中实现。 在设计完成后,可以使用仿真软件进行模拟验证。常见的仿真软件包括ModelSim、Quartus II等。通过仿真,可以检验UART接收模块的正确性,发现并解决潜在问题。 具体的UART接收模块实现方法可以参考以下步骤: 1. 在模块中定义需要的输入输出端口,包括时钟信号、串行数据信号、复位信号、数据输出信号等。 2. 在模块中实现串行数据的接收逻辑。这包括将串行数据转换为并行数据、进行奇偶校验、判断停止位等。 3. 在模块中实现数据输出逻辑。这包括将接收到的数据存储到数据缓存器中,并在需要的时候输出数据。 4. 在模块中实现复位逻辑。这包括在接收到复位信号时将数据缓存器清零,并恢复模块到初始状态。 5. 使用仿真软件进行模拟验证,并根据仿真结果进行调试,直至模块功能正确。 需要注意的是,在实际应用中,UART接收模块通常需要与其他模块进行配合,才能完成数据的传输和处理。因此,在设计时需要考虑与其他模块的接口问题,并确保接口的兼容性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值