FPGA-UART-TX-一段式状态机

闲来无事,写一个uart发送端的一段式状态机。uart写法很多,一段,两段,三段状态机,甚至不用状态机,直接用计数器都可以。

远程仓库,需要请自取:https://gitee.com/mikoxxx/FPGA_UART_TX

闲话少叙直接上代码:

module uart_tx_yiduan (
    input                   clk         ,           // 系统时钟
    input                   rst_n       ,           // 复位信号

    input       [7:0]       uart_tx_data,           // 需要发送的数据
    input                   tx_en       ,           // 发送使能

    output    reg           uart_txd                // 发送的数据
);
    parameter               BAUT_RATE            = 115200;       // 波特率--1秒钟发送多少个bit
    parameter               CLOCK_PERIOD         = 50_000_000;  // 系统时钟  20ns 

    parameter               BAUT_CNT_MAX         = CLOCK_PERIOD / BAUT_RATE;    // 发送一个bit的数据需要多少个clk 

    reg         [3:0]               i;          // 状态机变量
    reg         [8:0]               baut_cnt;   // 波特率计数器
    reg                             tx_busy;    // 发送中的标识信号


    always @(posedge clk or negedge rst_n) begin
        if (~rst_n)
            tx_busy <= 1'b0;
        else if (i == 11)       // 发送完之后,拉低忙信号,代表现在可以发送下一数据
            tx_busy <= 1'b0;
        else if (tx_en)
            tx_busy <= 1'b1;    // 使能信号拉高,然后进入忙状态
    end

    // 波特计数器
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n)
            baut_cnt <= 9'd0;
        else if (~tx_busy || baut_cnt == BAUT_CNT_MAX - 1) // 计数到波特计数最大值,归0.
            baut_cnt <= 9'd0;
        else if (tx_busy)   // 需要在发送状态时,才开始计数
            baut_cnt <= baut_cnt + 1;
    end


    // 一段式状态机
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            i               <= 0;
            uart_txd        <= 1'b1;
        end
        else begin
            case (i)
                0: begin
                    if (tx_en)  // 使能信号到了,进入下面的发送状态
                        i <= 1;
                    else
                        i <= 0;
                end
                1,2,3,4,5,6,7,8,9,10: begin
                    if (i == 1)
                        uart_txd <= 1'b0;    // 发送起始位
                    else if (i > 1 && i <= 9)
                        uart_txd <= uart_tx_data[i - 2];  // 发送数据段
                    else if (i == 10) begin
                        uart_txd    <= 1'b1;        // 发送停止位
                    end

                    if (baut_cnt == BAUT_CNT_MAX - 1) begin 
                        i <= i + 1;
                    end
                end
                11: begin   // 这里因为最后的停止位少了一个时钟,所以多用了一个状态,相当于打了一拍。
                    i <= 0;     
                end
                default: i <= 0;
            endcase
        end
    end
    
    
endmodule

这里还设计了一个top模块,一秒钟发送一次8‘h55。

代码如下:

module top_uart_tx (
    input               clk,
    input               rst_n,

    output              uart_txd    // 发送的数据
);

    parameter               CNT_MAX_1S      = 50_000_000;   // 1秒钟计数的最大值
    parameter               BAUT_RATE       = 115200;       // 波特率--1秒钟发送多少个bit

    reg     [25:0]          cnt_1s      ;
    reg                     tx_en       ;
    reg      [7:0]          uart_tx_data;

    
    // 1秒钟计数器
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n)
            cnt_1s <= 0;
        else if (cnt_1s == CNT_MAX_1S - 1)
            cnt_1s <= 0;
        else
            cnt_1s <= cnt_1s + 1;
    end

    // 每隔1秒拉高一次tx_en
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n)
            tx_en <= 0;
        else if (cnt_1s == CNT_MAX_1S - 1) // && ~tx_busy
            tx_en <= 1'b1;
        else
            // 使能信号一般是一个脉冲,维持一个时钟,所以这里记得拉低
            tx_en <= 1'b0;
    end

    uart_tx_yiduan #(
        .BAUT_RATE                (BAUT_RATE)
    ) u_uart_tx(
        .clk                (clk),
        .rst_n              (rst_n),
        .uart_tx_data       (8'h55),    // 固定发送8'h55
        .tx_en              (tx_en),
        .uart_txd           (uart_txd)
    );
    
endmodule

仿真的代码也一起贴了吧:

`timescale 1ns/1ns

module tb_uart_tx ();
    parameter  T = 20; 

    reg                     sys_clk;
    reg                     sys_rst_n;
    wire                    uart_txd; 

    // 这里为了仿真,增加了波特率,减少了发送间隔时间
    parameter               BAUT_RATE               = 11520000;       // 波特率--1秒钟发送多少个bit
    parameter               CNT_MAX_1S              = 50;           


    initial begin
        sys_clk             = 1'b0;
        sys_rst_n           = 1'b0;     //复位
        #(T+1)
        sys_rst_n           = 1'b1;
    end

    always #(10) sys_clk = ~sys_clk;

    top_uart_tx #(
        .BAUT_RATE                  (BAUT_RATE),
        .CNT_MAX_1S                 (CNT_MAX_1S)
    ) u_top_uart_tx(
        .clk                (sys_clk),
        .rst_n              (sys_rst_n),
        .uart_txd           (uart_txd)
    );
    
endmodule

需要注意一点,在写XDC文件的时候,需要找到uart_txd对应的管脚进行约束,否则无效。

然后打开串口助手:

一秒钟可以到一个55显示。

然后自己也可以根据需要修改间隔时间和发送的数据。

如有错误,欢迎评论或私聊指出,谢谢~~~~~

更多嵌入式,FPGA资料可以参考:天津大学四川院FPGA实训 -- www.sxfpga.cn

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值