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


     一、信号说明

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

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

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

    二、代码

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

    1. module uart_rx
    2. #(parameter bote=115200,
    3. parameter period=50_000_000
    4. )
    5. (
    6. input clk ,
    7. input rst ,
    8. input rx ,
    9. output reg [7:0] data ,
    10. output reg data_valid);
    11. reg reg1;
    12. reg reg2;
    13. reg reg3;
    14. reg start_flag;
    15. reg work_en;
    16. reg [15:0] bote_cnt;
    17. reg bit_flag;
    18. reg [3:0] bit_cnt;
    19. reg [7:0] rx_data;
    20. reg rx_flag;
    21. parameter max=period/bote;
    22. always@(posedge clk or negedge rst) begin
    23. if(!rst)
    24. reg1<=1'b1;
    25. else
    26. reg1<=rx;
    27. end
    28. always@(posedge clk or negedge rst) begin
    29. if(!rst)
    30. reg2<=1'b1;
    31. else
    32. reg2<=reg1;
    33. end
    34. always@(posedge clk or negedge rst) begin
    35. if(!rst)
    36. reg3<=1'b1;
    37. else
    38. reg3<=reg2;
    39. end
    40. always@(posedge clk or negedge rst) begin
    41. if(!rst)
    42. start_flag<=1'b0;
    43. else if((reg2==1'b0)&&(reg3==1'b1)&&(work_en==1'b0))
    44. start_flag<=1'b1;
    45. else
    46. start_flag<=1'b0;
    47. end
    48. always@(posedge clk or negedge rst) begin
    49. if(!rst)
    50. work_en<=1'b0;
    51. else if(start_flag==1'b1)
    52. work_en<=1'b1;
    53. else if((bit_cnt==4'd8)&&(bit_flag==1'b1))
    54. work_en<=1'b0;
    55. else
    56. work_en<=work_en;
    57. end
    58. always@(posedge clk or negedge rst) begin
    59. if(!rst)
    60. bote_cnt<=16'b0;
    61. else if((bit_cnt==4'd8)&&(bit_flag==1'b1))
    62. bote_cnt<=16'b0;
    63. else if((work_en==1'b1)&&(bote_cnt==max-1))
    64. bote_cnt<=16'b0;
    65. else if(work_en==1'b1)
    66. bote_cnt<=bote_cnt+1'b1;
    67. else
    68. bote_cnt<=16'b0;
    69. end
    70. always@(posedge clk or negedge rst) begin
    71. if(!rst)
    72. bit_flag<=1'b0;
    73. else if(bote_cnt==max/2)
    74. bit_flag<=1'b1;
    75. else
    76. bit_flag<=1'b0;
    77. end
    78. always@(posedge clk or negedge rst) begin
    79. if(!rst)
    80. bit_cnt<=4'b0;
    81. else if((bit_flag==1'b1)&&(bit_cnt==4'd8))
    82. bit_cnt<=4'b0;
    83. else if(bit_flag==1'b1)
    84. bit_cnt<=bit_cnt+1'b1;
    85. else
    86. bit_cnt<=bit_cnt;
    87. end
    88. always@(posedge clk or negedge rst) begin
    89. if(!rst)
    90. rx_data<=8'b0;
    91. else if((bit_flag==1'b1)&&(bit_cnt>=4'd1)&&(bit_cnt<=4'd8))
    92. rx_data<={reg3,rx_data[7:1]};
    93. else
    94. rx_data<=rx_data;
    95. end
    96. always@(posedge clk or negedge rst) begin
    97. if(!rst)
    98. rx_flag<=1'b0;
    99. else if((bit_flag==1'b1)&&(bit_cnt==4'd8))
    100. rx_flag<=1'b1;
    101. else
    102. rx_flag<=1'b0;
    103. end
    104. always@(posedge clk or negedge rst) begin
    105. if(!rst)
    106. data<=8'b0;
    107. else if(rx_flag==1'b1)
    108. data<=rx_data;
    109. else
    110. data<=data;
    111. end
    112. always@(posedge clk or negedge rst) begin
    113. if(!rst)
    114. data_valid<=1'b0;
    115. else
    116. data_valid<=rx_flag;
    117. end
    118. endmodule

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

    三、测试代码

    测试代码如下:

    1. `timescale 1ns/1ns
    2. module uart_rx_tb();
    3. parameter dada=5208*20;
    4. reg clk;
    5. reg rst;
    6. reg rx ;
    7. wire [7:0] data;
    8. wire data_valid;
    9. uart_rx
    10. #(.bote(9600),
    11. .period(50_000_000)
    12. )
    13. uart_rx_inst
    14. (
    15. .clk (clk ),
    16. .rst (rst ),
    17. .rx (rx ),
    18. .data (data ),
    19. .data_valid(data_valid));
    20. always#10 clk=~clk;
    21. initial begin
    22. clk=1'b0;
    23. rst=1'b0;
    24. #20
    25. rst=1'b1;
    26. end
    27. initial begin
    28. rx=1'b1;
    29. #200
    30. rx=1'b0;
    31. #dada
    32. rx=1'b1;
    33. #dada
    34. rx=1'b1;
    35. #dada
    36. rx=1'b1;
    37. #dada
    38. rx=1'b1;
    39. #dada
    40. rx=1'b1;
    41. #dada
    42. rx=1'b1;
    43. #dada
    44. rx=1'b1;
    45. #dada
    46. rx=1'b1;
    47. #dada
    48. rx=1'b1;
    49. #dada
    50. rx=1'b1;
    51. end
    52. /* task rxx (input [7:0]test_data); begin:loop
    53. integer i;
    54. for(i=0;i<10;i=i+1) begin
    55. case(i)
    56. 4'd0:rx<=1'b0;
    57. 4'd1:rx<=data[0];
    58. 4'd2:rx<=data[1];
    59. 4'd3:rx<=data[2];
    60. 4'd4:rx<=data[3];
    61. 4'd5:rx<=data[4];
    62. 4'd6:rx<=data[5];
    63. 4'd7:rx<=data[6];
    64. 4'd8:rx<=data[7];
    65. 4'd9:rx<=1'b1;
    66. endcase
    67. end
    68. #(5208*20);
    69. end
    70. endtask */
    71. /* initial begin
    72. #200
    73. rxx(8'd0);
    74. rxx(8'd1);
    75. rxx(8'd2);
    76. rxx(8'd3);
    77. rxx(8'd4);
    78. rxx(8'd5);
    79. rxx(8'd6);
    80. rxx(8'd7);
    81. end */
    82. endmodule

     四、总结

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

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

  • 相关阅读:
    Scrapy第七篇:数据存储(ORM框架采用peewee)
    【电子通识】USB发展历史
    -bash: jps: command not found
    Camtasia Studio 2024软件最新版下载【安装详细图文教程】
    软件项目管理【UML-组件图】
    [音视频学习笔记]六、自制音视频播放器Part1 -新版本ffmpeg,Qt +VS2022,都什么年代了还在写传统播放器?
    【ARMv8 SIMD和浮点指令编程】NEON 乘法指令——asimdrdm
    Elasticsearch
    vivo z1换电池笔记
    Pyppeteer中文文档
  • 原文地址:https://blog.csdn.net/weixin_62966975/article/details/136486550