Menu Close

uart通信rx模块在FPGA实现的一种Verilog状态机写法(四)

上一篇文章里,我们介绍了rx模块在参数固定时后的写法,其实主要工作已经完成,剩下的就是对模块的扩展和完善。扩展和完善工作也不复杂,只要对uart几个可变参数进行变量化或参数化即可。在之前的文章中我们也曾这样做过,感兴趣的朋友可以也可以看一下之前tx端的写法(https://ed.2743.com/archives/3478)。下面我们按照同样的做法,完善一下rx模块。

图1

uart 字符帧结构

module uart_rx_all #(
    parameter BAUD      = 9600,
    parameter CLK_F     = 50_000_000,
    parameter PARITY    = 0,
    parameter DATA_BITS = 8,
    parameter STOPBIT   = 1
) 
(
    input wire clk,
    input wire rst,
    input wire rx,

    output reg [ DATA_BITS - 1: 0 ] data,
    output reg parity_err,  //接收校验错误
    output reg done         //接收完成提示
);


localparam NONE    = 0;
localparam ODD     = 1;
localparam EVEN    = 2;
localparam MARK    = 3;
localparam SPACE   = 4;

localparam STOPBIT_ONE         = 0;
localparam STOPBIT_ONEHALF     = 1;
localparam STOPBIT_TWO         = 2;


//【1】检测起始位
reg [ 1: 0 ] rx_r;

always@( posedge clk or posedge rst )
begin
    if ( rst )
        rx_r <= 0;
    else
        rx_r <= { rx_r[ 0 ], rx };
end

wire en;        //起始位信号
assign en = ( rx_r == 2'b10 ) ? 1'b1 : 1'b0;


//【2】使用状态机来接收rx线上的数据
localparam [31:0] CNT_MAX = CLK_F / BAUD; //接收bit位计数(时钟周期个数)上限值


//[1] 定义状态空间
localparam FSM_IDLE     = 0;  //空闲状态
localparam FSM_START    = 1;  //接收起始位状态
localparam FSM_DATA     = 2;  //接收数据位
localparam FSM_PARITY   = 3;  //接收校验位
localparam FSM_STOP     = 4;  //接收停止位


//校验位
reg ifparity;       //是否有检验位
reg parity_value;   //校验位的值

//[2] 定义状态变量
reg [ 2: 0 ] cstate;

//[3] 实现状态机
//每个状态做状态判断和跳转,以及根据状态输出

reg [ DATA_BITS - 1: 0 ] data_r;    //暂存接收到的数据
reg [ 31: 0 ] cnt;                  //时钟周期计数,上限就是CNT_MAX
reg startbit;                       //接收到的起始位值
reg [ 3: 0 ] num;                   //接收到的数据位个数

wire baud_tick = (cnt ==  CNT_MAX[31:0] - 1) ? 1'b1 : 1'b0;
always@( posedge clk or posedge rst )
begin
    if ( rst )
    begin
        cstate <= 0;
        data_r <= 0;
        num <= 0;
        done <= 0;
        cnt <= 0;
        startbit <= 0;
        parity_err <= 0;
    end
    else
    case ( cstate )
        FSM_IDLE :
        begin
            data_r <= 0;
            done <= 0;
            if ( en )
            begin
                cstate <= FSM_START;
                cnt <= 0;
            end
            else
                cstate <= FSM_IDLE;
        end
        FSM_START :
        begin
            cnt <= cnt + 1;
            startbit <= rx_r[ 1 ];
            if ( cnt == CNT_MAX[31:1] && startbit ) //在bit位中点时进行采样
                cstate <= FSM_IDLE ;                //收到开始位为1 ,说明前面是个干扰信号
//            else if ( startbit == 0 && cnt == CNT_MAX - 1 )
            else if ( cnt == CNT_MAX - 1 )
            begin
                cstate <= FSM_DATA;     //收到开始位为0 ,是起始位
                cnt <= 0;
            end
        end
        FSM_DATA :
        begin
            cnt <= cnt + 1 ;
            if ( cnt == CNT_MAX[31:1] )              //在bit位中点时进行采样
                data_r[ num ] <= rx_r[ 1 ];

            if ( cnt == CNT_MAX - 1 && num <= DATA_BITS - 1 )  //接收bit位少于DATA_BITS时继续接收
            begin
                num <= num + 1 ;
                cnt <= 0;
            end
            else if ( num == DATA_BITS )    //接收bit位大于DATA_BITS时跳转
                                            //写的明明是==,为何说大于?因为bit位数为8,计数应该是0-7
            begin
                cstate <= ifparity ? FSM_PARITY : FSM_STOP; //有无校验位的判断
                cnt <= 0;
                num <= 0;
            end
        end
        FSM_PARITY :
        begin
            cnt <= cnt + 1;
            if ( cnt == CNT_MAX[31:1] )
            begin
                if ( parity_value == rx_r[ 1 ] )    //计算出的校验位的值 和 发送过来的校验位是否相等?
                    parity_err <= 1'b0;
                else
                    parity_err <= 1'b1;             //保证奇偶校验错误也只发单周期的高电平
            end
            else if ( cnt == CNT_MAX - 1 )
            begin
                cstate <= FSM_STOP;
                cnt <= 0;
            end
        end
        FSM_STOP :
        begin
            cnt <= cnt + 1;

            case ( STOPBIT )    //不同长度停止位的处理
            STOPBIT_ONE :       //1停止位
            begin
                if ( cnt == CNT_MAX[31:1] )
                begin
                    cstate <= FSM_IDLE;
                    data <= data_r;
                    done <= 1;
                    cnt <= 0;
                end
            end
            STOPBIT_ONEHALF:    //1.5停止位
            begin
                if( cnt == ( CNT_MAX[31:0] - 1) )  
                begin
                    cstate <= FSM_IDLE;
                    data <= data_r;
                    done <= 1;
                    cnt <= 0;
                end
            end
            STOPBIT_TWO :       //2停止位 
            begin
                if( cnt == ( CNT_MAX[31:0] + CNT_MAX[31:1] - 1) )
                begin
                    cstate <= FSM_IDLE;
                    data <= data_r;
                    done <= 1;
                    cnt <= 0;
                end
            end
            default : 
            begin
                if ( cnt == CNT_MAX - 1 )
                begin
                    cstate <= FSM_IDLE;
                    data <= data_r;
                    done <= 1;
                    cnt <= 0;
                end
            end
            endcase
        end
        default :
        begin
            cstate <= 0;
            data_r <= 0;
            num <= 0;
            done <= 0;
            cnt <= 0;
            startbit <= 0;
            parity_err <= 0;
        end
    endcase

end


always@( * )
begin
    if ( rst )
    begin
        ifparity = 0 ;
        parity_value = 0 ;
    end
    else
    case ( PARITY )
        NONE :
        begin
            ifparity = 0 ;
            parity_value = 0 ;
        end
        ODD :
        begin
            ifparity = 1 ;
            parity_value = ~( ^ data_r ) ;
        end
        EVEN :
        begin
            ifparity = 1 ;
            parity_value = ^ data_r ;
        end
        MARK :
        begin
            ifparity = 1 ;
            parity_value = 1 ;
        end
        SPACE :
        begin
            ifparity = 1 ;
            parity_value = 0 ;
        end
        default :
        begin
            ifparity = 0 ;
            parity_value = 0 ;
        end
    endcase
end

endmodule

 

仿真工程:

`timescale 1ns/1ps

module tb();

reg clk = 0;
always #10 clk = ~clk;



reg rst = 0;
initial
begin
    rst = 1;
    #20;
    rst = 0;
    #10;
    
    #800000;
    $stop;
    
end

    
wire txrx;


uart_core #
(
    .SIM_DEBUG  ("TRUE"),
    .BAUD       (115200),          
    .CLK_F      (50_000_000)      
)
uart_core_inst
(
    .clk    (clk),
    .rx     (txrx),
    .tx     (txrx),
    .rst    (rst)
);


endmodule

 

仿真波形:

图2

 

工程代码(quartus)

[content_control]

uart_core_test.zip

[/content_control]

 

 

Posted in FPGA 教材教案, Verilog, Verilog

1 Comment

发表回复

相关链接