当前位置:   article > 正文

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

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来发数据的,结果写完了不太对,就采用了这种朴素的方式验证,兄弟们有需求的自己改一下吧。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/258963
推荐阅读
相关标签
  

闽ICP备14008679号