串口通信是系統設計中比較基部分,其原理其實也很通俗易懂。單次建立通信會傳輸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 defineparameter CLK_FREQ = 50000000;parameter UART_BPS = 115200;localparam BAUD_CNT_MAX = CLK_FREQ / UART_BPS;// reg definereg 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 definewire 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)
beginif (!rst_n) beginuart_rxd_d0 <= 0;uart_rxd_d1 <= 0;uart_rxd_d2 <= 0;endelse beginuart_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)
beginif (!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;elserx_flag <= rx_flag;
end// Baud rate counter amplitude
always @(posedge clk or negedge rst_n)
beginif (!rst_n)baud_cnt <= 0;else if (rx_flag) beginif (baud_cnt <= BAUD_CNT_MAX - 1)baud_cnt <= baud_cnt + 1;elsebaud_cnt <= 0;endelsebaud_cnt <= 0;
end// Accept data(rx_cnt) assignment
always @(posedge clk or negedge rst_n)
beginif (!rst_n)rx_cnt <= 0;else if (rx_flag) beginif (baud_cnt == BAUD_CNT_MAX-1) beginrx_cnt <= rx_cnt + 1;endelserx_cnt <= rx_cnt;endelserx_cnt <= 0;
end// based on the rx_cnt to restore the rxd data
always @(posedge clk or negedge rst_n)
beginif (!rst_n)rx_data_t <= 0;else if (rx_flag) beginif (baud_cnt == BAUD_CNT_MAX / 2 - 1) begincase (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: ;endcaseendelserx_data_t <= rx_data_t;endelserx_data_t <= 0;
end// assignment the data received
always @(posedge clk or negedge rst_n)
beginif (!rst_n) beginuart_rx_done <= 0;uart_rx_data <= 0;endelse if (rx_cnt == 4'd9 && baud_cnt == BAUD_CNT_MAX / 2 - 1) beginuart_rx_done <= 1;uart_rx_data <= rx_data_t;endelse beginuart_rx_done <= 0;uart_rx_data <= uart_rx_data;end
endendmodule
然后是發送部分:
`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 defineparameter CLK_FREQ = 50000000;parameter UART_BPS = 115200;localparam BAUD_CNT_MAX = CLK_FREQ/UART_BPS;// reg definereg [ 7: 0] tx_data_t ;reg [ 3: 0] tx_cnt ;reg [ 15: 0] baud_cnt ;// Main Code
always @(posedge clk or negedge rst_n)beginif(!rst_n) begintx_data_t<=0;uart_tx_busy<=0;endelse if(uart_tx_en) begintx_data_t<=uart_tx_data;uart_tx_busy<=1;endelse if(tx_cnt==4'd9&&baud_cnt==BAUD_CNT_MAX-1) begintx_data_t<=0;uart_tx_busy<=0;endelse begintx_data_t<=tx_data_t;uart_tx_busy<=uart_tx_busy;endend// Baud counter
always @(posedge clk or negedge rst_n)beginif(!rst_n)baud_cnt<=0;else if(uart_tx_en)baud_cnt<=0;else if(uart_tx_busy) beginif(baud_cnt<BAUD_CNT_MAX-1)baud_cnt<=baud_cnt+1;elsebaud_cnt<=0;endelsebaud_cnt<=0;end// assign the tx_cnt
always @(posedge clk or negedge rst_n)beginif(!rst_n)tx_cnt<=0;else if(uart_tx_busy) beginif(baud_cnt==BAUD_CNT_MAX-1)tx_cnt<=tx_cnt+1;elsetx_cnt<=tx_cnt;endelsetx_cnt<=0;endalways @(posedge clk or negedge rst_n)beginif(!rst_n)uart_txd<=1;else if(uart_tx_busy) begincase(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;endcaseendelseuart_txd<=1'b1;endendmodule
參考文獻:
達芬奇之 FPGA 開發指南 ?V2.2 ?-正點原子 達芬奇開發板教程