【Verilog硬件语言学习笔记4】FPGA串口通信

发布于:2025-07-04 ⋅ 阅读:(19) ⋅ 点赞:(0)

串口通信是系统设计中比较基部分,其原理其实也很通俗易懂。单次建立通信会传输8个bit,其时序也很简单,这里就不再赘述了。其对应的实例代码如下所示;

首先是接受部分(因为我的变量命名也很规范,通俗易懂,所以我不再详细介绍):

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2025/03/13 12:00:48
// Design Name: 
// Module Name: uart_rx
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//

module uart_rx(
    input                               clk                        ,
    input                               rst_n                      ,
    input                               uart_rxd                   ,

    output reg                          uart_rx_done               ,
    output reg         [   7: 0]        uart_rx_data                
    );

// parameter to define
    parameter                           CLK_FREQ                   = 50000000;
    parameter                           UART_BPS                   = 115200;
    localparam                          BAUD_CNT_MAX               = CLK_FREQ / UART_BPS;

// reg define
    reg                                 uart_rxd_d0                 ;
    reg                                 uart_rxd_d1                 ;
    reg                                 uart_rxd_d2                 ;
    reg                                 rx_flag                     ;
    reg                [   3: 0]        rx_cnt                      ;
    reg                [  15: 0]        baud_cnt                    ;
    reg                [   7: 0]        rx_data_t                   ;

// wire define
    wire                                start_en                    ;
// Main Code
// Because the sign fall from high to low, use this to catch the signal;
    assign                              start_en                    = uart_rxd_d2 & (~uart_rxd_d1) & (~rx_flag);

// aiming to asynchronous signal processing
always @(posedge clk or negedge rst_n)
begin
    if (!rst_n) begin
        uart_rxd_d0 <= 0;
        uart_rxd_d1 <= 0;
        uart_rxd_d2 <= 0;
    end
    else begin
        uart_rxd_d0 <= uart_rxd;
        uart_rxd_d1 <= uart_rxd_d0;
        uart_rxd_d2 <= uart_rxd_d1;
    end
end

// define the rx_flag
always @(posedge clk or negedge rst_n)
begin
    if (!rst_n)
        rx_flag <= 0;
    else if (start_en)
        rx_flag <= 1'b1;
    else if ((rx_cnt == 4'd9) && (baud_cnt == BAUD_CNT_MAX / 2 - 1'b1))
        rx_flag <= 0;
    else
        rx_flag <= rx_flag;
end

// Baud rate counter amplitude
always @(posedge clk or negedge rst_n)
begin
    if (!rst_n)
        baud_cnt <= 0;
    else if (rx_flag) begin
        if (baud_cnt <= BAUD_CNT_MAX - 1)
            baud_cnt <= baud_cnt + 1;
        else
            baud_cnt <= 0;
    end
    else
        baud_cnt <= 0;
end

// Accept data(rx_cnt) assignment
always @(posedge clk or negedge rst_n)
begin
    if (!rst_n)
        rx_cnt <= 0;
    else if (rx_flag) begin
        if (baud_cnt == BAUD_CNT_MAX-1) begin
            rx_cnt <= rx_cnt + 1;
        end
        else
            rx_cnt <= rx_cnt;
    end
    else
        rx_cnt <= 0;
end

// based on the rx_cnt to restore the rxd data
always @(posedge clk or negedge rst_n)
begin
    if (!rst_n)
        rx_data_t <= 0;
    else if (rx_flag) begin
        if (baud_cnt == BAUD_CNT_MAX / 2 - 1) begin
            case (rx_cnt)
                4'd1: rx_data_t[0] <= uart_rxd_d2;
                4'd2: rx_data_t[1] <= uart_rxd_d2;
                4'd3: rx_data_t[2] <= uart_rxd_d2;
                4'd4: rx_data_t[3] <= uart_rxd_d2;
                4'd5: rx_data_t[4] <= uart_rxd_d2;
                4'd6: rx_data_t[5] <= uart_rxd_d2;
                4'd7: rx_data_t[6] <= uart_rxd_d2;
                4'd8: rx_data_t[7] <= uart_rxd_d2;
                default: ;
            endcase
        end
        else
            rx_data_t <= rx_data_t;
    end
    else
        rx_data_t <= 0;
end

// assignment the data received
always @(posedge clk or negedge rst_n)
begin
    if (!rst_n) begin
        uart_rx_done <= 0;
        uart_rx_data <= 0;
    end
    else if (rx_cnt == 4'd9 && baud_cnt == BAUD_CNT_MAX / 2 - 1) begin
        uart_rx_done <= 1;
        uart_rx_data <= rx_data_t;
    end
    else begin
        uart_rx_done <= 0;
        uart_rx_data <= uart_rx_data;
    end
end

endmodule

然后是发送部分:

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2025/03/13 17:14:19
// Design Name: 
// Module Name: uart_tx
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module uart_tx(
    input                               clk                        ,
    input                               rst_n                      ,
    input                               uart_tx_en                 ,
    input              [   7: 0]        uart_tx_data               ,
    output reg                          uart_txd                   ,
    output reg                          uart_tx_busy                
    );

// parameter define
    parameter                           CLK_FREQ                   = 50000000;
    parameter                           UART_BPS                   = 115200;
    localparam                          BAUD_CNT_MAX               = CLK_FREQ/UART_BPS;

// reg define
    reg                [   7: 0]        tx_data_t                   ;
    reg                [   3: 0]        tx_cnt                      ;
    reg                [  15: 0]        baud_cnt                    ;

// Main Code
always @(posedge clk or negedge rst_n)
    begin
        if(!rst_n)     begin
            tx_data_t<=0;
            uart_tx_busy<=0;
        end
        else if(uart_tx_en)        begin
            tx_data_t<=uart_tx_data;
            uart_tx_busy<=1;
        end
        else if(tx_cnt==4'd9&&baud_cnt==BAUD_CNT_MAX-1) begin
            tx_data_t<=0;
            uart_tx_busy<=0;
        end
        else begin
            tx_data_t<=tx_data_t;
            uart_tx_busy<=uart_tx_busy;
        end
    end

// Baud counter
always @(posedge clk or negedge rst_n)
    begin
        if(!rst_n)
            baud_cnt<=0;
        else if(uart_tx_en)
            baud_cnt<=0;
        else if(uart_tx_busy) begin
            if(baud_cnt<BAUD_CNT_MAX-1)
                baud_cnt<=baud_cnt+1;
            else
                baud_cnt<=0;
        end
        else
            baud_cnt<=0;
    end

// assign the tx_cnt
always @(posedge clk or negedge rst_n)
    begin
        if(!rst_n)
            tx_cnt<=0;
        else if(uart_tx_busy) begin
            if(baud_cnt==BAUD_CNT_MAX-1)
                tx_cnt<=tx_cnt+1;
            else
                tx_cnt<=tx_cnt;
        end
        else
            tx_cnt<=0;
    end

always @(posedge clk or negedge rst_n)
    begin
        if(!rst_n)
            uart_txd<=1;
        else if(uart_tx_busy) begin
            case(tx_cnt)
                4'd0: uart_txd<=1'b0;
                4'd1: uart_txd<=tx_data_t[0];
                4'd2: uart_txd<=tx_data_t[1];
                4'd3: uart_txd<=tx_data_t[2];
                4'd4: uart_txd<=tx_data_t[3];
                4'd5: uart_txd<=tx_data_t[4];
                4'd6: uart_txd<=tx_data_t[5];
                4'd7: uart_txd<=tx_data_t[6];
                4'd8: uart_txd<=tx_data_t[7];
                4'd9: uart_txd<=1;
                default: uart_txd<=1;
            endcase
        end
        else
            uart_txd<=1'b1;
    end

endmodule

参考文献:

达芬奇之 FPGA 开发指南  V2.2  -正点原子 达芬奇开发板教程