串口通信是系统设计中比较基部分,其原理其实也很通俗易懂。单次建立通信会传输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 -正点原子 达芬奇开发板教程