RS-232 Receiver

举报
李锐博恩 发表于 2021/07/15 03:13:21 2021/07/15
【摘要】 上篇博文讲到了RS-232 Transmitter,这篇博文讲RS-232 Receiver. 如下Receiver示意图: 我们的实现是这样的: 该模块在RxD线路上组装数据。 当接收到一个字节时,它出现在“数据”总线上。 一旦收到完整的字节,“data_ready”就会被置位一个时钟。 请注意,“data”仅在“data_ready”被声明时有效。 剩下的时间,...

上篇博文讲到了RS-232 Transmitter,这篇博文讲RS-232 Receiver.

如下Receiver示意图:

我们的实现是这样的:

该模块在RxD线路上组装数据。
当接收到一个字节时,它出现在“数据”总线上。 一旦收到完整的字节,“data_ready”就会被置位一个时钟。
请注意,“data”仅在“data_ready”被声明时有效。 剩下的时间,不要使用它,因为新的数据可能会混乱。

过采样


异步接收器必须以某种方式与输入信号同步(它通常无法访问发送器使用的时钟)。

为了确定新数据字节何时到来,我们通过以波特率频率的倍数对信号进行过采样来寻找“开始”位。
一旦检测到“开始”位,我们就以已知的波特率对线路进行采样,以获取数据位。
接收器通常以波特率的16倍对输入信号进行过采样。 我们在这里使用了8次......对于115200个波特,它的采样率为921600Hz。

假设我们有一个“Baud8Tick”信号,每秒有效921600次。

既然是要产生这样的波特率,我们给出波特率产生代码(RS232 波特率时钟产生方法?):


  
  1. module BaudGen #(
  2. parameter Clkfrequency = 25000_000,
  3. parameter Baud = 115200,
  4. parameter Oversampling = 8,
  5. parameter Ratio = Clkfrequency/(Baud*Oversampling),
  6. parameter AddWidth = 16,
  7. parameter Inc = (1<<AddWidth)/Ratio
  8. )
  9. (
  10. input clk,
  11. input enable,
  12. output BaudTick
  13. );
  14. reg [AddWidth:0] acc = 0;
  15. always@(posedge clk) begin
  16. if(enable) begin
  17. acc <= acc[AddWidth - 1 : 0] + Inc;
  18. end
  19. else begin
  20. acc <= Inc;
  21. end
  22. end
  23. assign BaudTick = acc[AddWidth];
  24. endmodule

测试代码:


  
  1. `timescale 1ns / 1ps
  2. module BaudGen_tb;
  3. reg clk;
  4. reg enable;
  5. wire BaudTick;
  6. initial begin
  7. clk = 0;
  8. forever
  9. #20 clk = ~clk;
  10. end
  11. initial enable = 1;
  12. BaudGen Baud8Gen(
  13. .clk(clk),
  14. .enable(1),
  15. .BaudTick(BaudTick)
  16. );
  17. endmodule

之前,产生115200波特率的仿真图中,8点几usBaudTick有效一个时钟,如今,要产生8倍波特率,所以按理说,BaudTick应该1点几us有效一次,发现确实如此。

使用此波特率时钟,可以达到过采样的目的。

其他模块设计设计


首先,输入的“RxD”信号与我们的时钟没有关系。
我们使用两个D触发器对其进行过采样,并将其与我们的时钟域同步。

 

reg [1:0] RxD_sync;
always @(posedge clk) if(Baud8Tick) RxD_sync <= {RxD_sync[0], RxD};

紧接着我们过滤数据,因此RxD线上的短尖峰不会让起始位出错。

reg [1:0] RxD_cnt;
reg RxD_bit;

always @(posedge clk)
if(Baud8Tick)
begin
  if(RxD_sync[1] && RxD_cnt!=2'b11) RxD_cnt <= RxD_cnt + 1;
  else 
  if(~RxD_sync[1] && RxD_cnt!=2'b00) RxD_cnt <= RxD_cnt - 1;

  if(RxD_cnt==2'b00) RxD_bit <= 0;
  else
  if(RxD_cnt==2'b11) RxD_bit <= 1;
end

一旦检测到“开始”,状态机允许我们检查接收到的每个位。

reg [3:0] state;

always @(posedge clk)
if(Baud8Tick)
case(state)
  4'b0000: if(~RxD_bit) state <= 4'b1000; // start bit found?
  4'b1000: if(next_bit) state <= 4'b1001; // bit 0
  4'b1001: if(next_bit) state <= 4'b1010; // bit 1
  4'b1010: if(next_bit) state <= 4'b1011; // bit 2
  4'b1011: if(next_bit) state <= 4'b1100; // bit 3
  4'b1100: if(next_bit) state <= 4'b1101; // bit 4
  4'b1101: if(next_bit) state <= 4'b1110; // bit 5
  4'b1110: if(next_bit) state <= 4'b1111; // bit 6
  4'b1111: if(next_bit) state <= 4'b0001; // bit 7
  4'b0001: if(next_bit) state <= 4'b0000; // stop bit
  default: state <= 4'b0000;
endcase

请注意,我们使用了“next_bit”信号,从一个位到另一个位。(如何过采样的时钟是波特率的16倍可以这么写)

reg [2:0] bit_spacing;

always @(posedge clk)
if(state==0)
  bit_spacing <= 0;
else
if(Baud8Tick)
  bit_spacing <= bit_spacing + 1;

wire next_bit = (bit_spacing==7);

Finally a shift register collects the data bits as they come.

reg [7:0] RxD_data;
always @(posedge clk) if(Baud8Tick && next_bit && state[3]) RxD_data <= {RxD_bit, RxD_data[7:1]};

上面的解析是参考链接的原作者的意思(https://www.fpga4fun.com/SerialInterface4.html),最后人家还提供了总体代码,但是由于我的水平还没达到那么水准,所以阅读起来比较困难,所以改成了自己的版本并仿真测试。

接收器的Verilog描述为:


  
  1. `timescale 1ns / 1ps
  2. //
  3. // Engineer: Reborn Lee
  4. // Create Date: 2019/05/26 21:02:29
  5. // Module Name: asy_receiver
  6. //
  7. module asy_receiver(
  8. input clk,
  9. input RxD,//The module assembles data from the RxD line as it comes.
  10. output reg [7:0] RxD_data,
  11. output reg RxD_data_ready //As a byte is being received, it appears on the "data" bus. Once a complete byte has been received, "data_ready" is asserted for one clock.
  12. );
  13. parameter Clkfrequency = 25000000; // 25MHz
  14. parameter Baud = 115200;
  15. parameter Oversampling = 8; // needs to be a power of 2
  16. // we oversample the RxD line at a fixed rate to capture each RxD data bit at the "right" time
  17. // 8 times oversampling by default, use 16 for higher quality reception
  18. reg [3:0] RxD_state = 0;
  19. wire OversamplingTick; //过采样时钟
  20. BaudGen #(
  21. .Clkfrequency(Clkfrequency),
  22. .Baud(Baud),
  23. .Oversampling(Oversampling)
  24. )U_Baud8Gen(
  25. .clk(clk),
  26. .enable(1'b1),
  27. .BaudTick(OversamplingTick)
  28. );
  29. /* wire RxD_bit = RxD;
  30. wire sampleNow = 1'b1; // receive one bit per clock cycle */
  31. // synchronize RxD to our clk domain
  32. reg [1:0] RxD_sync = 2'b11;
  33. always @(posedge clk)
  34. if(OversamplingTick)
  35. RxD_sync <= {RxD_sync[0], RxD};
  36. // and filter it
  37. reg [1:0] Filter_cnt = 2'b11;
  38. reg RxD_bit = 1'b1;
  39. always @(posedge clk)
  40. if(OversamplingTick) begin
  41. if(RxD_sync[1]==1'b1 && Filter_cnt!=2'b11)
  42. Filter_cnt <= Filter_cnt + 1'd1;
  43. else
  44. if(RxD_sync[1]==1'b0 && Filter_cnt!=2'b00)
  45. Filter_cnt <= Filter_cnt - 1'd1;
  46. if(Filter_cnt==2'b11)
  47. RxD_bit <= 1'b1;
  48. else
  49. if(Filter_cnt==2'b00) RxD_bit <= 1'b0;
  50. end
  51. // and decide when is the good time to sample the RxD line
  52. function integer log2(input integer v); //定义一个函数用于求Oversampling的位数
  53. begin
  54. log2=0;
  55. while(v>>log2) log2=log2+1;
  56. end
  57. endfunction
  58. parameter l2o = log2(Oversampling);
  59. reg [l2o-2:0] OversamplingCnt = 0;
  60. always @(posedge clk)
  61. if(OversamplingTick) OversamplingCnt <= (RxD_state==0) ? 1'd0 : OversamplingCnt + 1'd1;
  62. wire sampleNow = OversamplingTick && (OversamplingCnt==Oversampling/2-1);
  63. // now we can accumulate the RxD bits in a shift-register
  64. always @(posedge clk)
  65. case(RxD_state)
  66. 4'b0000: if(~RxD_bit) begin
  67. RxD_state <= 4'b0001; // start bit found?
  68. //RxD_state <= 4'b1000;
  69. end
  70. 4'b0001: if(sampleNow) RxD_state <= 4'b1000; // sync start bit to sampleNow
  71. 4'b1000: if(sampleNow) RxD_state <= 4'b1001; // bit 0
  72. 4'b1001: if(sampleNow) RxD_state <= 4'b1010; // bit 1
  73. 4'b1010: if(sampleNow) RxD_state <= 4'b1011; // bit 2
  74. 4'b1011: if(sampleNow) RxD_state <= 4'b1100; // bit 3
  75. 4'b1100: if(sampleNow) RxD_state <= 4'b1101; // bit 4
  76. 4'b1101: if(sampleNow) RxD_state <= 4'b1110; // bit 5
  77. 4'b1110: if(sampleNow) RxD_state <= 4'b1111; // bit 6
  78. 4'b1111: if(sampleNow) RxD_state <= 4'b0010; // bit 7
  79. 4'b0010: if(sampleNow) RxD_state <= 4'b0000; // stop bit
  80. default: RxD_state <= 4'b0000;
  81. endcase
  82. always @(posedge clk)
  83. if(sampleNow && RxD_state[3]) RxD_data <= {RxD_bit, RxD_data[7:1]};
  84. always @(posedge clk) begin
  85. RxD_data_ready <= (sampleNow && RxD_state==4'b0010 && RxD_bit); // make sure a stop bit is received
  86. end
  87. endmodule

测试文件为:


  
  1. `timescale 1ns / 1ps
  2. module asy_receiver_tb(
  3. );
  4. reg clk;
  5. reg RxD;
  6. wire RxD_data_ready;
  7. wire [7 : 0] RxD_data; // data received, valid only (for one clock cycle) when RxD_data_ready is asserted
  8. parameter DATA0 = 10'b1101001010; //从低位开始发送
  9. // parameter DATA1 = 10'b1010011010;
  10. reg [9:0] data_in;
  11. integer i;
  12. initial begin
  13. clk = 0;
  14. forever
  15. #20 clk = ~clk;
  16. end
  17. initial begin
  18. data_in = DATA0;
  19. for(i = 0; i < 9; i = i + 1) begin
  20. #8700
  21. RxD = data_in[i];
  22. end
  23. #26100
  24. for(i = 0; i < 9; i = i + 1) begin
  25. #8700
  26. RxD = data_in[i];
  27. end
  28. //#10000 $stop;
  29. end
  30. asy_receiver u0(
  31. .clk(clk),
  32. .RxD(RxD),
  33. .RxD_data_ready(RxD_data_ready),
  34. .RxD_data(RxD_data)
  35. );
  36. endmodule

仿真波形:

没有认真分析代码,也许会看的眼花缭乱,但是我们仍可以从中看出一些信息:

例如RxD_data_ready有效的时刻,代表着此时我们接受的数据可以使用了。

稍微放大:

从这个仿真波形我们可以看出,我们的RxD_bit,我们采样也是采样的这个信号,它是RxD同步且滤波后的输出。

就到这里吧。

下一篇博文,还要讲讲如何联合用这些模块。

参考链接:博文中已给出

文章来源: reborn.blog.csdn.net,作者:李锐博恩,版权归原作者所有,如需转载,请联系作者。

原文链接:reborn.blog.csdn.net/article/details/90580269

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。