关于UART协议的基础理论部分已经在上一篇文章中讲述,不再重复介绍。
UART通信协议
本文主要介绍如何使用Verlilog编程,通过FPGA实现UART串口通信回环工程。在本工程中所使用的系统时钟为50MHz,如果选择115200的波特率进行数据传输,那么传输1bit所用的时钟周期数就是50_000_000 / 115200。

uart模块: uart串口通信顶层设计模块,包含uart_tx、uart_rx、control模块。
uart_rx模块: UART串口数据接收模块,将上位机发送的串行数据接收后转换成并行数据发送给control模块。
control模块: UART控制模块,将接收到的并行数据存储到FIFO中,当读FIFO条件满足时输出。
uart_tx模块: UART串口数据发送模块,将从control读出的并行数据转换成串行数据发送给上位机。
// **************************************************************
// Author: Zhang JunYi
// Create Date: 2022.11.04
// Design Name: uart
// Module Name: uart
// Target Device: Cyclone IV E (EP4CE6F17C8)
// Tool versions: Quartus Prime 18.1
// Description: UART串口通信顶层设计模块
// **************************************************************
module uart (input clk ,input rst_n ,input uart_rxd ,output uart_txd
);// 信号定义wire [7:0] rx_dout ;wire rx_dout_vld ;wire [7:0] ctrl_dout ; wire ctrl_dout_vld ;wire ready ;// 模块例化uart_rx u_uart_rx (/*input */.clk (clk ),/*input */.rst_n (rst_n ),/*input */.uart_rx (uart_rxd ), // 接收到的串口数据/*output [7:0] */.rx_dout (rx_dout ), // 串行数据转换成并行数据后输出给control模块/*output */.rx_dout_vld (rx_dout_vld ));control u_control (/*input */.clk (clk ),/*input */.rst_n (rst_n ),/*// uart_rx*//*input [7:0] */.rx_din (rx_dout ),/*input */.rx_din_vld (rx_dout_vld ),/*// uart_tx*//*input */.ready (ready ),/*output [7:0] */.ctrl_dout (ctrl_dout ),/*output */.ctrl_dout_vld (ctrl_dout_vld ));uart_tx u_uart_tx (/*input */.clk (clk ),/*input */.rst_n (rst_n ),/*// control*//*input [7:0] */.tx_din (ctrl_dout ),/*input */.tx_din_vld (ctrl_dout_vld ),/*output */.ready (ready ), // 给control模块的握手信号,表示可以接收数据进行发送 /*// 上位机*//*output */.uart_tx (uart_txd ));endmodule
// **************************************************************
// Author: Zhang JunYi
// Create Date: 2022.11.04
// Design Name: uart
// Module Name: uart_rx
// Target Device: Cyclone IV E (EP4CE6F17C8)
// Tool versions: Quartus Prime 18.1
// Description: UART串口通信数据接收模块
// **************************************************************
`include "param.v"
module uart_rx (input clk ,input rst_n ,input uart_rx , // 接收到的串口数据output [7:0] rx_dout , // 串行数据转换成并行数据后输出给control模块output rx_dout_vld
);// 参数定义// 信号定义reg [19:0] cnt_baud ; // 波特率计数器wire add_cnt_baud ;wire end_cnt_baud ; reg [3:0] cnt_bit ; // bit计数器wire add_cnt_bit ;wire end_cnt_bit ;reg rx_flag ; // 开始接收数据标志reg [1:0] uart_rx_r ; // uart_rx同步打拍wire rx_nedge ; // uart_rx下降沿reg [9:0] dout_data ;// cnt_baudalways @(posedge clk or negedge rst_n)beginif(!rst_n)begincnt_baud <= 0 ;endelse if(add_cnt_baud)beginif(end_cnt_baud)begincnt_baud <= 0 ;end else begin cnt_baud <= cnt_baud + 1 ;end endendassign add_cnt_baud = rx_flag ;assign end_cnt_baud = add_cnt_baud && (cnt_baud == `BAUD - 1) ;// cnt_bitalways @(posedge clk or negedge rst_n)beginif(!rst_n)begincnt_bit <= 0 ;endelse if(add_cnt_bit)beginif(end_cnt_bit)begincnt_bit <= 0 ;end else begin cnt_bit <= cnt_bit + 1 ;end endendassign add_cnt_bit = end_cnt_baud ;assign end_cnt_bit = add_cnt_bit && (cnt_bit == 9 || dout_data[0] == 1'b1) ; // uart_rx_ralways @(posedge clk or negedge rst_n)beginif(!rst_n)beginuart_rx_r <= 0 ;endelse beginuart_rx_r <= {uart_rx_r[0],uart_rx} ;endend// uart_rx下降沿检测assign rx_nedge = ~uart_rx_r[0] & uart_rx_r[1] ;// rx_flagalways @(posedge clk or negedge rst_n)beginif(!rst_n)beginrx_flag <= 1'b0 ;endelse if(rx_nedge)beginrx_flag <= 1'b1 ;endelse if(end_cnt_bit)beginrx_flag <= 1'b0 ;endend// dout_dataalways @(posedge clk or negedge rst_n)beginif(!rst_n)begindout_data <= 0 ;endelse if(rx_flag & cnt_baud == 0)begindout_data[cnt_bit] <= uart_rx_r[0] ;endend// 输出assign rx_dout = dout_data[8:1] ;assign rx_dout_vld = end_cnt_bit && dout_data[0] == 1'b0 ;endmodule
// **************************************************************
// Author: Zhang JunYi
// Create Date: 2022.11.04
// Design Name: uart
// Module Name: control
// Target Device: Cyclone IV E (EP4CE6F17C8)
// Tool versions: Quartus Prime 18.1
// Description: UART串口通信控制模块
// **************************************************************
`include "param.v"
module control (input clk ,input rst_n ,// uart_rxinput [7:0] rx_din ,input rx_din_vld ,// uart_txinput ready ,output [7:0] ctrl_dout ,output ctrl_dout_vld
);// 参数定义// 信号定义wire rdreq ;wire wrreq ;wire [7:0] fifo_q ;wire rdempty ;wire [2:0] rdusedw ;wire wrfull ;wire [2:0] wrusedw ;reg rd_flag ; // fifo可读标志// rd_flagalways @(posedge clk or negedge rst_n)beginif(!rst_n)beginrd_flag <= 1'b0 ;endelse if(rdusedw > 4)beginrd_flag <= 1'b1 ;endelse if(rdempty)beginrd_flag <= 1'b0 ;endend// fifo例化fifo fifo_inst (.aclr ( ~rst_n ),.data ( rx_din ),.rdclk ( clk ),.rdreq ( rdreq ),.wrclk ( clk ),.wrreq ( wrreq ),.q ( fifo_q ),.rdempty ( rdempty ),.rdusedw ( rdusedw ),.wrfull ( wrfull ),.wrusedw ( wrusedw ));assign wrreq = ~wrfull && rx_din_vld ;assign rdreq = rd_flag && ready ;// 输出assign ctrl_dout = fifo_q ;assign ctrl_dout_vld = rdreq ;endmodule
// **************************************************************
// Author: Zhang JunYi
// Create Date: 2022.11.04
// Design Name: uart
// Module Name: uart_tx
// Target Device: Cyclone IV E (EP4CE6F17C8)
// Tool versions: Quartus Prime 18.1
// Description: UART串口通信数据发送模块模块
// **************************************************************
`include "param.v"
module uart_tx (input clk ,input rst_n ,// controlinput [7:0] tx_din ,input tx_din_vld ,output ready , // 给control模块的握手信号,表示可以接收数据进行发送 // 上位机output uart_tx
);// 参数定义// 信号定义reg [19:0] cnt_baud ; // 波特率计数器wire add_cnt_baud ;wire end_cnt_baud ; reg [3:0] cnt_bit ; // bit计数器wire add_cnt_bit ;wire end_cnt_bit ;reg tx_flag ; // 数据传输标志reg [9:0] tx_data ; // 寄存将要发送的数据reg dout ; // 并行数据转串行数据发送reg vld ;// cnt_baudalways @(posedge clk or negedge rst_n)beginif(!rst_n)begincnt_baud <= 0 ;endelse if(add_cnt_baud)beginif(end_cnt_baud)begincnt_baud <= 0 ;end else begin cnt_baud <= cnt_baud + 1 ;end endendassign add_cnt_baud = tx_flag ;assign end_cnt_baud = add_cnt_baud && (cnt_baud == `BAUD - 1) ;// cnt_bitalways @(posedge clk or negedge rst_n)beginif(!rst_n)begincnt_bit <= 0 ;endelse if(add_cnt_bit)beginif(end_cnt_bit)begincnt_bit <= 0 ;end else begin cnt_bit <= cnt_bit + 1 ;end endendassign add_cnt_bit = end_cnt_baud ;assign end_cnt_bit = add_cnt_bit && (cnt_bit == 9) ; // tx_flagalways @(posedge clk or negedge rst_n)beginif(!rst_n)begintx_flag <= 1'b0 ;endelse if(tx_din_vld)begintx_flag <= 1'b1 ;endelse if(end_cnt_bit)begintx_flag <= 1'b0 ;endend// vldalways @(posedge clk or negedge rst_n)beginif(!rst_n)beginvld <= 1'b0 ;endelse beginvld <= tx_din_vld ;endend// tx_dataalways @(posedge clk or negedge rst_n)beginif(!rst_n)begintx_data <= 0 ;endelse if(vld)begintx_data <= {1'b1,tx_din,1'b0} ; // 停止位 + 数据 + 起始位 低位在前发送endend// doutalways @(posedge clk or negedge rst_n)beginif(!rst_n)begindout <= 1'b1 ;endelse if(tx_flag)begindout <= tx_data[cnt_bit] ;endend// 输出assign uart_tx = dout ;assign ready = ~tx_flag ;endmodule
仿真代码如下:
`timescale 1ns/1psmodule uart_tb ();reg tb_clk ;reg tb_rst_n ;reg [7:0] data ;reg data_vld ;wire ready ;wire tx_data ;wire uart_txd ;wire [7:0] rx_dout ;wire rx_dout_vld ;// 模块例化uart_tx uart_tx_pc ( // 模拟上位机发送数据/*input */.clk (tb_clk ),/*input */.rst_n (tb_rst_n ),/*// control*//*input [7:0] */.tx_din (data ),/*input */.tx_din_vld (data_vld ),/*output */.ready (ready ), // 给control模块的握手信号,表示可以接收数据进行发送 /*// 上位机*//*output */.uart_tx (tx_data ));uart u_uart (/*input */.clk (tb_clk ),/*input */.rst_n (tb_rst_n ),/*input */.uart_rxd (tx_data ),/*output */.uart_txd (uart_txd ));// 参数定义parameter CYCLE = 20 ;always #(CYCLE / 2) tb_clk = ~tb_clk ; // 50M时钟initial begintb_clk = 1'b1;tb_rst_n = 1'b1;data = 0;data_vld = 1'b0;# (CYCLE * 2);tb_rst_n = 1'b0;# (CYCLE * 2);# 2;tb_rst_n = 1'b1;# (CYCLE * 10);Send(8'hf9);Send(8'h99);Send(8'hAE);Send(8'hBC);Send(8'h55);Send(8'hE1);# (CYCLE * 100);$stop; endtask Send;input [7:0] send_data ;begindata = send_data ;data_vld = 1'b1 ;# CYCLE ;data_vld = 1'b0 ;// @(posedge ready)# (CYCLE*440*10) ;endendtaskendmodule


这里通过串口调试助手来与FPGA进行串口通信测试,可以看见通过串口收发数据正确。

在调试串口的时候发现设备管理器中串口无法识别,显示PL2303HXA自2012已停产,请联系供应商,如下图:

这时需要下载一个旧版本的驱动,下载链接如下:
链接:https://pan.baidu.com/s/1FTtfgc2k2fw-9Ck1_tRbRQ
提取码:7kf2
下载完成后解压安装,点击更新驱动程序→浏览电脑选择旧版本双击安装即可:
