RS-232 Receiver

    xiaoxiao2025-06-14  23

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

    如下Receiver示意图:

    我们的实现是这样的:

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

    过采样


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

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

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

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

    module BaudGen #( parameter Clkfrequency = 25000_000, parameter Baud = 115200, parameter Oversampling = 8, parameter Ratio = Clkfrequency/(Baud*Oversampling), parameter AddWidth = 16, parameter Inc = (1<<AddWidth)/Ratio ) ( input clk, input enable, output BaudTick ); reg [AddWidth:0] acc = 0; always@(posedge clk) begin if(enable) begin acc <= acc[AddWidth - 1 : 0] + Inc; end else begin acc <= Inc; end end assign BaudTick = acc[AddWidth]; endmodule

    测试代码:

    `timescale 1ns / 1ps module BaudGen_tb; reg clk; reg enable; wire BaudTick; initial begin clk = 0; forever #20 clk = ~clk; end initial enable = 1; BaudGen Baud8Gen( .clk(clk), .enable(1), .BaudTick(BaudTick) ); 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;elseif(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描述为:

    `timescale 1ns / 1ps // // Engineer: Reborn Lee // Create Date: 2019/05/26 21:02:29 // Module Name: asy_receiver // module asy_receiver( input clk, input RxD,//The module assembles data from the RxD line as it comes. output reg [7:0] RxD_data, 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. ); parameter Clkfrequency = 25000000; // 25MHz parameter Baud = 115200; parameter Oversampling = 8; // needs to be a power of 2 // we oversample the RxD line at a fixed rate to capture each RxD data bit at the "right" time // 8 times oversampling by default, use 16 for higher quality reception reg [3:0] RxD_state = 0; wire OversamplingTick; //过采样时钟 BaudGen #( .Clkfrequency(Clkfrequency), .Baud(Baud), .Oversampling(Oversampling) )U_Baud8Gen( .clk(clk), .enable(1'b1), .BaudTick(OversamplingTick) ); /* wire RxD_bit = RxD; wire sampleNow = 1'b1; // receive one bit per clock cycle */ // synchronize RxD to our clk domain reg [1:0] RxD_sync = 2'b11; always @(posedge clk) if(OversamplingTick) RxD_sync <= {RxD_sync[0], RxD}; // and filter it reg [1:0] Filter_cnt = 2'b11; reg RxD_bit = 1'b1; always @(posedge clk) if(OversamplingTick) begin if(RxD_sync[1]==1'b1 && Filter_cnt!=2'b11) Filter_cnt <= Filter_cnt + 1'd1; else if(RxD_sync[1]==1'b0 && Filter_cnt!=2'b00) Filter_cnt <= Filter_cnt - 1'd1; if(Filter_cnt==2'b11) RxD_bit <= 1'b1; else if(Filter_cnt==2'b00) RxD_bit <= 1'b0; end // and decide when is the good time to sample the RxD line function integer log2(input integer v); //定义一个函数用于求Oversampling的位数 begin log2=0; while(v>>log2) log2=log2+1; end endfunction parameter l2o = log2(Oversampling); reg [l2o-2:0] OversamplingCnt = 0; always @(posedge clk) if(OversamplingTick) OversamplingCnt <= (RxD_state==0) ? 1'd0 : OversamplingCnt + 1'd1; wire sampleNow = OversamplingTick && (OversamplingCnt==Oversampling/2-1); // now we can accumulate the RxD bits in a shift-register always @(posedge clk) case(RxD_state) 4'b0000: if(~RxD_bit) begin RxD_state <= 4'b0001; // start bit found? //RxD_state <= 4'b1000; end 4'b0001: if(sampleNow) RxD_state <= 4'b1000; // sync start bit to sampleNow 4'b1000: if(sampleNow) RxD_state <= 4'b1001; // bit 0 4'b1001: if(sampleNow) RxD_state <= 4'b1010; // bit 1 4'b1010: if(sampleNow) RxD_state <= 4'b1011; // bit 2 4'b1011: if(sampleNow) RxD_state <= 4'b1100; // bit 3 4'b1100: if(sampleNow) RxD_state <= 4'b1101; // bit 4 4'b1101: if(sampleNow) RxD_state <= 4'b1110; // bit 5 4'b1110: if(sampleNow) RxD_state <= 4'b1111; // bit 6 4'b1111: if(sampleNow) RxD_state <= 4'b0010; // bit 7 4'b0010: if(sampleNow) RxD_state <= 4'b0000; // stop bit default: RxD_state <= 4'b0000; endcase always @(posedge clk) if(sampleNow && RxD_state[3]) RxD_data <= {RxD_bit, RxD_data[7:1]}; always @(posedge clk) begin RxD_data_ready <= (sampleNow && RxD_state==4'b0010 && RxD_bit); // make sure a stop bit is received end endmodule

    测试文件为:

    `timescale 1ns / 1ps module asy_receiver_tb( ); reg clk; reg RxD; wire RxD_data_ready; wire [7 : 0] RxD_data; // data received, valid only (for one clock cycle) when RxD_data_ready is asserted parameter DATA0 = 10'b1101001010; //从低位开始发送 // parameter DATA1 = 10'b1010011010; reg [9:0] data_in; integer i; initial begin clk = 0; forever #20 clk = ~clk; end initial begin data_in = DATA0; for(i = 0; i < 9; i = i + 1) begin #8700 RxD = data_in[i]; end #26100 for(i = 0; i < 9; i = i + 1) begin #8700 RxD = data_in[i]; end //#10000 $stop; end asy_receiver u0( .clk(clk), .RxD(RxD), .RxD_data_ready(RxD_data_ready), .RxD_data(RxD_data) ); endmodule

    仿真波形:

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

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

    稍微放大:

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

    就到这里吧。

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

    参考链接:博文中已给出

    最新回复(0)