FPGA常用通信协议 —UART(二)—UART接收

 一、信号说明

  因为是接收端,所以输入的是RX,发送端一次发8位串行数据,在本模块中,要接收这8位数据并转换为并行数据,因为最终要实现数据的回环,这8位并行数据会在下一个模块中被转换为串行数据再发出去,需要一个数据有效信号,当它拉高时表示八位数据接收完成,可以进行并串转换并发送了。

  时钟采用50Mhz,下面是信号列表

reg1,reg2,reg3 rx打拍后的信号
work_en 拉高表示正在接收信号
bote_cnt 波特计数器,记到最大表示一个波特的结束
bit_flag 信号稳定标志
rx_data 并行数据

二、代码

上一篇我们简要介绍了UART,讲了UART的基本时序,下面给出UART接收端的代码。

module uart_rx
 #(parameter bote=115200,
      parameter period=50_000_000
      )

(
    input               clk       ,
    input               rst       ,
    input               rx        ,
    output  reg [7:0]   data      ,
    output  reg         data_valid);
   
    
   


    reg         reg1;
    reg         reg2;
    reg         reg3;
    reg         start_flag;
    reg         work_en;
    reg [15:0]  bote_cnt;
    reg         bit_flag;
    reg [3:0]   bit_cnt;
    reg [7:0]   rx_data;
    reg         rx_flag;
    
    parameter max=period/bote;
    
    
    
    
    always@(posedge clk or negedge rst) begin
    if(!rst)
    reg1<=1'b1;
    else
    reg1<=rx;
    end
    
    always@(posedge clk or negedge rst) begin
    if(!rst)
    reg2<=1'b1;
    else
    reg2<=reg1;
    end
    
    always@(posedge clk or negedge rst) begin
    if(!rst)
    reg3<=1'b1;
    else
    reg3<=reg2;
    end
    
    always@(posedge clk or negedge rst) begin
    if(!rst)
    start_flag<=1'b0;
    else if((reg2==1'b0)&&(reg3==1'b1)&&(work_en==1'b0))
    start_flag<=1'b1;
    else
    start_flag<=1'b0;
    end
    
    always@(posedge clk or negedge rst) begin
    if(!rst)
    work_en<=1'b0;
    else if(start_flag==1'b1)
    work_en<=1'b1;
    else if((bit_cnt==4'd8)&&(bit_flag==1'b1))
    work_en<=1'b0;
    else
    work_en<=work_en;
    end
    
    
    always@(posedge clk or negedge rst) begin
    if(!rst)
    bote_cnt<=16'b0;
    else if((bit_cnt==4'd8)&&(bit_flag==1'b1))
    bote_cnt<=16'b0;
    else if((work_en==1'b1)&&(bote_cnt==max-1))
    bote_cnt<=16'b0;
    else if(work_en==1'b1)
    bote_cnt<=bote_cnt+1'b1;
    else
    bote_cnt<=16'b0;
    end
    
    always@(posedge clk or negedge rst) begin
    if(!rst)
    bit_flag<=1'b0;
    else if(bote_cnt==max/2)
    bit_flag<=1'b1;
    else
    bit_flag<=1'b0;
    end
    
    always@(posedge clk or negedge rst) begin
    if(!rst)
    bit_cnt<=4'b0;
    else if((bit_flag==1'b1)&&(bit_cnt==4'd8))
    bit_cnt<=4'b0;
    else if(bit_flag==1'b1)
    bit_cnt<=bit_cnt+1'b1;
    else
    bit_cnt<=bit_cnt;
    end
    
    always@(posedge clk or negedge rst) begin
    if(!rst)
    rx_data<=8'b0;
    else if((bit_flag==1'b1)&&(bit_cnt>=4'd1)&&(bit_cnt<=4'd8))
    rx_data<={reg3,rx_data[7:1]};
    else
    rx_data<=rx_data;
    end
    
    always@(posedge clk or negedge rst) begin
    if(!rst)
    rx_flag<=1'b0;
    else if((bit_flag==1'b1)&&(bit_cnt==4'd8))
    rx_flag<=1'b1;
    else
    rx_flag<=1'b0;
    end
    
    
    always@(posedge clk or negedge rst) begin
    if(!rst)
    data<=8'b0;
    else if(rx_flag==1'b1)
    data<=rx_data;
    else
    data<=data;
    end
    
    always@(posedge clk or negedge rst) begin
    if(!rst)
    data_valid<=1'b0;
    else
    data_valid<=rx_flag;
    end
    
    endmodule
    

  在接收数据时有一个要注意的地方,就是什么时候读取,由于一个波特维持的时间很长,所以在波特的1/2处左右接收数据最好,这时候数据最稳定,对应代码中的bit_flag,就是实现这一功能的。

三、测试代码

测试代码如下:

`timescale 1ns/1ns
module uart_rx_tb();

parameter dada=5208*20;
    reg  clk;       
    reg  rst;       
    reg  rx ;       
    wire  [7:0] data;
    wire  data_valid;

uart_rx 
    #(.bote(9600),
      .period(50_000_000)
)
uart_rx_inst
(
    .clk       (clk       ),
    .rst       (rst       ),
    .rx        (rx        ),
    .data      (data      ),
    .data_valid(data_valid));
   

    
    always#10 clk=~clk;
    
    initial begin
    clk=1'b0;
    rst=1'b0;
    #20
    rst=1'b1;
    end
    
    
    
    
    
    initial begin
    rx=1'b1;
    #200
    rx=1'b0;
    #dada
    rx=1'b1;
    #dada
    rx=1'b1;
    #dada
    rx=1'b1;
    #dada
    rx=1'b1;
    #dada
    rx=1'b1;
    #dada
    rx=1'b1;
    #dada
    rx=1'b1;
    #dada
    rx=1'b1;
    #dada
    rx=1'b1;
    #dada
    rx=1'b1;
    end
    
    
    
    
    
    
    
    
    
    
    
    
/*     task rxx (input [7:0]test_data); begin:loop
    integer i;
    for(i=0;i<10;i=i+1) begin
    case(i)
     4'd0:rx<=1'b0;
     4'd1:rx<=data[0];
     4'd2:rx<=data[1];
     4'd3:rx<=data[2];
     4'd4:rx<=data[3];
     4'd5:rx<=data[4];
     4'd6:rx<=data[5];
     4'd7:rx<=data[6];
     4'd8:rx<=data[7];
     4'd9:rx<=1'b1;
    endcase
    end
    #(5208*20);
    end
    endtask */
    
 /*    initial begin
    #200
    rxx(8'd0);
    rxx(8'd1);
    rxx(8'd2);
    rxx(8'd3);
    rxx(8'd4);
    rxx(8'd5);
    rxx(8'd6);
    rxx(8'd7);
    end */
    
    endmodule


     

 四、总结

  代码实际比较简单,只有一点是要解释一下的,UART是异步通信,从其他设备传来的RX是一个不同步的数据,为了减小信号在不同时钟域的亚稳态问题(涉及到数字集成电路设计的专业知识,感兴趣的兄弟们可以自己查一查),这里将输入的RX打了三拍,为了同步数据,也是为了减小亚稳态带来的问题。

  关于测试代码,本来是想写一个task来发数据的,结果写完了不太对,就采用了这种朴素的方式验证,兄弟们有需求的自己改一下吧。

版权声明:本文为博主作者:毛豆仙人原创文章,版权归属原作者,如果侵权,请联系我们删除!

原文链接:https://blog.csdn.net/weixin_62966975/article/details/136486550

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
社会演员多的头像社会演员多普通用户
上一篇 2024年4月1日
下一篇 2024年4月1日

相关推荐

此站出售,如需请站内私信或者邮箱!