Verilog实现UART之一:接收模块

1.UART的帧格式  

  异步串行数据的一般格式是:起始位+数据位+停止位,其中起始位1位,8位数据位,奇校验、偶校验或无校验位;停止位可以是1、2位,LSB first:

2.接收原理:

  由于UART是异步传输,没有传输同步时钟。为了能保证数据传输的正确性,采样模块利用16倍数据波特率的时钟进行采样,假设波特率为115200,则采样时钟为clk16x=115200×16。每个数据占据16个采样时钟周期,1bit起始位+8bit数据为+1bit停止位=10bit,因此一帧共占据16×10=160个采样时钟,考虑到每个数据为可能有1-2个采样时钟周期的便宜,因此将各个数据位的中间时刻作为采样点,以保证采样不会滑码或误码。一般UART一帧的数据位数为8,这样即使每个数据有一个时钟的误差,接收端也能正确地采样到数据。因此,采样时刻为24(跳过起始位的16个时钟)、40、56、72、88、104、120、136、152(停止位),如下图所示:

Verilog实现UART之一:接收模块

  其中,RX为接收引脚,CNT为对采样时钟进行计数的计数器;

3.代码实现:

1 /******************************************************************************

  2 *

  3 *            Module    :     rx_module

  4 *          File Name   :     rx_module.v

  5 *            Author    :     JC_Wang

  6 *            Version   :     1.0

  7 *            Date      :     2012/12/5

  8 *         Description  :     UART接收模块

  9 *           

 10 *

 11 ********************************************************************************/

 12 

 13 module rx_module(

 14     input               GClk,         /*       system topest clock                      */

 15     input               clk16x,       /*       sample clock,16×115200                   */

 16     input               rst_n,        /*        glabol reset signal                     */

 17     input               rx,           /*           serial data in                       */

 18     output reg          DataReady,    /*       a complete byte has been received        */

 19     output reg[7:0]     DataReceived  /*       Bytes received                           */

 20 );

 21 

 22  

 23 /*  捕获rx的下降沿,即起始信号  */    

 24 reg trigger_r0;

 25 wire neg_tri;

 26 always@(posedge GClk or negedge rst_n)  /*下降沿使用全局时钟来捕获的,其实用clk16x来捕获也可以*/

 27 begin

 28     if(!rst_n)

 29         begin

 30             trigger_r0<=1'b0;

 31         end

 32     else

 33         begin    

 34             trigger_r0<=rx;

 35         end

 36 end

 37 

 38 assign neg_tri = trigger_r0 & ~rx;

 39 

 40 //----------------------------------------------    

 41 /*     counter control      */

 42 reg cnt_en;

 43 always@(posedge GClk or negedge rst_n)

 44 begin

 45     if(!rst_n)

 46         cnt_en<=1'b0;

 47     else if(neg_tri==1'b1)      /*如果捕获到下降沿,则开始计数*/

 48         cnt_en<=1'b1;

 49     else if(cnt==8'd152)

 50         cnt_en<=1'b0;

 51    

 52 end

 53 //---------------------------------------------

 54 /*      counter module ,对采样时钟进行计数       */

 55 reg [7:0] cnt;

 56 always@(posedge clk16x or negedge rst_n)

 57 begin

 58     if(!rst_n)

 59         cnt<=8'd0;

 60     else if(cnt_en)

 61         cnt<=cnt+1'b1;

 62     else

 63         cnt<=8'd0;

 64 

 65 end

 66 //---------------------------------------------

 67 /*      receive module        */

 68 reg StopBit_r;

 69 always@(posedge clk16x or negedge rst_n)

 70 begin

 71     if(!rst_n)

 72         begin

 73             DataReceived<=8'b0;

 74         end

 75     else if(cnt_en)

 76         case(cnt)

 77             8'd24:   DataReceived[0] <= rx;    /*在各个采样时刻,读取接收到的数据*/

 78             8'd40:   DataReceived[1] <= rx;

 79             8'd56:   DataReceived[2] <= rx;

 80             8'd72:   DataReceived[3] <= rx;

 81             8'd88:   DataReceived[4] <= rx;

 82             8'd104:  DataReceived[5] <= rx;

 83             8'd120:  DataReceived[6] <= rx;

 84             8'd136:  DataReceived[7] <= rx;

 85             

 86         endcase

 87 

 88 end

 89 

 90 always@(posedge clk16x or negedge rst_n)

 91 begin

 92     if(!rst_n)

 93         DataReady<=1'b0;

 94     else if (cnt==8'd152)

 95         DataReady<=1'b1;       //接收到停止位后,给出数据准备好标志位

 96     else 

 97         DataReady<=1'b0;

 98 end

 99 

100 endmodule

101 

注:

1)采样时钟clk16x必须是波特率的16倍,波特率任意设置如57600、9600等皆可,只要满足16倍关系;

2)此模块经过测试上万字节的接收,从未出错!

3)引入了最高时钟对起始信号下降沿进行捕获,会造成什么不良影响,比如所谓的“时钟满天飞”问题,博主还不清楚,若您有好的讲解,请您发链接给我,在此感谢!

 

4.如果需要校验位的朋友,可以参考xilinx 公司的参考设计:

`timescale 1 ns / 1 ns



module rcvr (dout,data_ready,framing_error,parity_error,rxd,clk16x,rst,rdn) ;



input rxd ;                /*数据接收端*/

input clk16x ;            /*采样时钟*/

input rst ;                /*复位信号,高电平有效*/

input rdn ;                /*数据接收使能,低电平有效*/

output [7:0] dout ;         /*数据输出*/

output data_ready ;        /*数据接收完毕标志位*/

output framing_error ;       /*帧错误标志位*/

output parity_error ;        /*校验位错误标志位*/



reg rxd1 ;   

reg rxd2 ;

reg clk1x_enable ;

reg [3:0] clkdiv ;

reg [7:0] rsr ;

reg [7:0] rbr ;

reg [3:0] no_bits_rcvd ;



reg data_ready ;



reg parity ;

reg parity_error ;

reg framing_error ;



wire clk1x ;



assign dout = !rdn ? rbr : 8'bz ;       /*在允许接收的情况下,输出接收到的数据,否则保持高阻态*/



/*下降沿捕获模块*/

always @(posedge clk16x or posedge rst)

begin

    if (rst)

    begin

        rxd1 <= 1'b1 ;

        rxd2 <= 1'b1 ;

    end

    else 

    begin

        rxd1 <= rxd ;

        rxd2 <= rxd1 ;

    end

end



always @(posedge clk16x or posedge rst)

begin

    if (rst)

        clk1x_enable <= 1'b0;

    else if (!rxd1 && rxd2)       /*如果捕获到下降沿,则开始计数*/

        clk1x_enable <= 1'b1 ;

    else if (no_bits_rcvd == 4'b1100)

        clk1x_enable <= 1'b0 ;

end



/*数据准备好标志位的控制模块*/

always @(posedge clk16x or posedge rst or negedge rdn)

begin

    if (rst)

        data_ready = 1'b0 ;

    else if (!rdn)

        data_ready = 1'b0 ;

    else

        if (no_bits_rcvd == 4'b1011)

        data_ready = 1'b1 ;

end



/*计数模块,产生一个对clk16x进行16分频的时钟信号,用该信号的上升沿进行采样 */

always @(posedge clk16x or posedge rst)

begin

    if (rst)

        clkdiv = 4'b0000 ;

    else if (clk1x_enable)

        clkdiv = clkdiv +1 ;

end



assign clk1x = clkdiv[3] ;    



/*对clk1x进行计数*/

always @(posedge clk1x or posedge rst or negedge clk1x_enable)

if (rst)

  no_bits_rcvd = 4'b0000;

else

  if (!clk1x_enable)

    no_bits_rcvd = 4'b0000 ;

  else

    no_bits_rcvd = no_bits_rcvd + 1 ;



/*采样进程*/ 

always @(posedge clk1x or posedge rst) 

if (rst)

   begin 

    rsr <= 8'b0 ;

    rbr <= 8'b0 ; 

    parity <= 1'b1 ; 

    parity_error = 1'b0 ; 

end 

else

   begin 

      if (no_bits_rcvd >= 4'b0001 && no_bits_rcvd <= 4'b1001) 

    begin 

      rsr[0] <= rxd2 ; 

      rsr[7:1] <= rsr[6:0] ; //数据左移 

      parity <= parity ^ rsr[7] ; //校验位 

    end 

    else if (no_bits_rcvd == 4'b1010)  

    begin 

      rbr <= rsr ; //输出接收到的数据 

    end 

    else if (!parity) 

      parity_error = 1'b1 ; //判断校验位是否正确

     else if ((no_bits_rcvd == 4'b1011) && (rxd2 != 1'b1))//判断是否收到停止位,否则给出帧错误信号 

      framing_error = 1'b1 ; 

    else framing_error = 1'b0 ; 

    end

 endmodule

 

 

 

 

 

你可能感兴趣的:(Verilog)