学习记录2、串口UART发送模块FPGA状态机思路实现

状态机实现uart发送

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2024/05/22 10:42:06
// Design Name: 
// Module Name: uart_tx_fsm
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module uart_tx_fsm(
    input clk,
    input rst_n,
    input [7:0] data,
    input data_en,

    output reg uart_tx
    );


parameter SYS_FREQ = 50_000_000; //系统时钟
parameter UART_B = 115200;       //波特率
parameter CNT_PERIOD = 50_000_000/115200;  //计数器最大值
parameter CNT_PERIOD_HALF = CNT_PERIOD >> 1;  //计数器中间值
parameter BIT_CNT = 8;        //一个字节


parameter  IDLE   = 3'd0; //空闲状态
parameter  START  = 3'd1; //接收到下降沿,完成同步,开始接收数据
parameter  DATA   = 3'd2; //接收数据
parameter  VERIFY = 3'd3; //接收完成校验
parameter  STOP   = 3'd4; //接收完成

//rx 打两拍,检测上升沿,完成同步,在收到使能信号后开始,字节传送,避免亚稳态
reg status_pre; 
reg status_post;
//时钟计时,在一个波特中计数
reg [8:0] cnt_data;
//波特计数,在一个接收周期中记录波特数
reg [3:0] cnt_bit_num;
//有限状态机,现态和次态
reg [2:0] now_status;
reg [2:0] next_status;

wire up_edge;

//data_en 上升沿检测
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)begin
        status_pre <= 0;
        status_post <= 0;
    end
    else
    begin
        status_pre <= status_post;
        status_post <= data_en;
    end
end
assign up_edge = (~status_pre) & status_post;


//时钟计数,时钟计数应该对齐上升沿
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)begin
        cnt_data <= 0;
        cnt_bit_num <= 0;
    end
    else if(now_status != IDLE)
    begin
        if(cnt_data <CNT_PERIOD)
            cnt_data <= cnt_data + 1;
        else begin
            cnt_data <= 0;
            if(cnt_bit_num <= BIT_CNT)
                cnt_bit_num <= cnt_bit_num + 1;
            else
                cnt_bit_num <= 0;
        end

    end
    else
        cnt_data <= 0;
end

//状态转换

always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        now_status <= IDLE;
    else
        now_status <= next_status;
end

//状态转移条件
always @(*) begin
    case(now_status) 
        IDLE:
        begin
            if(up_edge)
                next_status = START;
            else
                next_status = IDLE;
        end
        START:
        begin
            if(cnt_bit_num == 1 && cnt_data ==0 )
                next_status = DATA;
            else
                next_status = START;
        end                
        DATA:
            if(cnt_bit_num == 9 && cnt_data ==0 )
                next_status = IDLE;
            else
                next_status = DATA;
        default:now_status = IDLE;
    endcase
end

//每个状态下的动作
always @(posedge clk or negedge rst_n) begin
    
    case(now_status) 
        IDLE:
        begin
            uart_tx <= 1;
            cnt_bit_num <= 0;
        end
        START:
        begin
            if(cnt_data <= CNT_PERIOD && cnt_data >= 0)
                uart_tx <= 0;
            else
                uart_tx <= 1;
        end
        DATA:
        begin
            case(cnt_bit_num)
                1: uart_tx <= data[0];
                2: uart_tx <= data[1];
                3: uart_tx <= data[2];
                4: uart_tx <= data[3];
                5: uart_tx <= data[4];
                6: uart_tx <= data[5];
                7: uart_tx <= data[6];
                8: uart_tx <= data[7];
                default: uart_tx <= 1;
            endcase    
        end

        default: uart_tx <= 1;
   
    endcase
end
endmodule

testbench

`timescale  1ns / 1ns

module tb_uart_tx_fsm;

// uart_tx_fsm Parameters
parameter PERIOD           = 20               ;
parameter SYS_FREQ         = 50_000_000       ;
parameter UART_B           = 115200           ;
parameter CNT_PERIOD       = 50_000_000/115200;
parameter CNT_PERIOD_HALF  = CNT_PERIOD >> 1  ;
parameter BIT_CNT          = 8                ;
parameter IDLE             = 3'd0             ;
parameter START            = 3'd1             ;
parameter DATA             = 3'd2             ;
parameter VERIFY           = 3'd3             ;
parameter STOP             = 3'd4             ;

// uart_tx_fsm Inputs
reg   clk                                  = 0 ;
reg   rst_n                                = 0 ;
reg   [7:0]  data                          = 8'b01101100;
reg   data_en                              = 0 ;

// uart_tx_fsm Outputs
wire  uart_tx                              ;


initial
begin
    forever #(PERIOD/2)  clk=~clk;
end

initial
begin
    #(PERIOD*2) rst_n  =  1;
end

initial
begin
    #100 data_en =  1;
    #8650 data_en = 0; 
end


uart_tx_fsm #(
    .SYS_FREQ        ( SYS_FREQ        ),
    .UART_B          ( UART_B          ),
    .CNT_PERIOD      ( CNT_PERIOD      ),
    .CNT_PERIOD_HALF ( CNT_PERIOD_HALF ),
    .BIT_CNT         ( BIT_CNT         ),
    .IDLE            ( IDLE            ),
    .START           ( START           ),
    .DATA            ( DATA            ),
    .VERIFY          ( VERIFY          ),
    .STOP            ( STOP            ))
 u_uart_tx_fsm (
    .clk                     ( clk            ),
    .rst_n                   ( rst_n          ),
    .data                    ( data     [7:0] ),
    .data_en                 ( data_en        ),

    .uart_tx                 ( uart_tx        )
);



endmodule

传统仿真

状态机仿真

 

 

 UART接收状态机思路

 传统UART代码来自正点原子例程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值