一、UART协议基础
关于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读出的并行数据转换成串行数据发送给上位机。
三、代码实现
1、uart顶层设计模块
// **************************************************************
// 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
2、uart_rx串口数据接收模块
// **************************************************************
// 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_baud
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_baud <= 0 ;
end
else if(add_cnt_baud)begin
if(end_cnt_baud)begin
cnt_baud <= 0 ;
end
else begin
cnt_baud <= cnt_baud + 1 ;
end
end
end
assign add_cnt_baud = rx_flag ;
assign end_cnt_baud = add_cnt_baud && (cnt_baud == `BAUD - 1) ;
// cnt_bit
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_bit <= 0 ;
end
else if(add_cnt_bit)begin
if(end_cnt_bit)begin
cnt_bit <= 0 ;
end
else begin
cnt_bit <= cnt_bit + 1 ;
end
end
end
assign add_cnt_bit = end_cnt_baud ;
assign end_cnt_bit = add_cnt_bit && (cnt_bit == 9 || dout_data[0] == 1'b1) ;
// uart_rx_r
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
uart_rx_r <= 0 ;
end
else begin
uart_rx_r <= {uart_rx_r[0],uart_rx} ;
end
end
// uart_rx下降沿检测
assign rx_nedge = ~uart_rx_r[0] & uart_rx_r[1] ;
// rx_flag
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
rx_flag <= 1'b0 ;
end
else if(rx_nedge)begin
rx_flag <= 1'b1 ;
end
else if(end_cnt_bit)begin
rx_flag <= 1'b0 ;
end
end
// dout_data
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
dout_data <= 0 ;
end
else if(rx_flag & cnt_baud == 0)begin
dout_data[cnt_bit] <= uart_rx_r[0] ;
end
end
// 输出
assign rx_dout = dout_data[8:1] ;
assign rx_dout_vld = end_cnt_bit && dout_data[0] == 1'b0 ;
endmodule
3、control控制模块
// **************************************************************
// 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_rx
input [7:0] rx_din ,
input rx_din_vld ,
// uart_tx
input 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_flag
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
rd_flag <= 1'b0 ;
end
else if(rdusedw > 4)begin
rd_flag <= 1'b1 ;
end
else if(rdempty)begin
rd_flag <= 1'b0 ;
end
end
// 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
4、uart_tx串口数据发送模块
// **************************************************************
// 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 ,
// control
input [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_baud
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_baud <= 0 ;
end
else if(add_cnt_baud)begin
if(end_cnt_baud)begin
cnt_baud <= 0 ;
end
else begin
cnt_baud <= cnt_baud + 1 ;
end
end
end
assign add_cnt_baud = tx_flag ;
assign end_cnt_baud = add_cnt_baud && (cnt_baud == `BAUD - 1) ;
// cnt_bit
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_bit <= 0 ;
end
else if(add_cnt_bit)begin
if(end_cnt_bit)begin
cnt_bit <= 0 ;
end
else begin
cnt_bit <= cnt_bit + 1 ;
end
end
end
assign add_cnt_bit = end_cnt_baud ;
assign end_cnt_bit = add_cnt_bit && (cnt_bit == 9) ;
// tx_flag
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
tx_flag <= 1'b0 ;
end
else if(tx_din_vld)begin
tx_flag <= 1'b1 ;
end
else if(end_cnt_bit)begin
tx_flag <= 1'b0 ;
end
end
// vld
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
vld <= 1'b0 ;
end
else begin
vld <= tx_din_vld ;
end
end
// tx_data
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
tx_data <= 0 ;
end
else if(vld)begin
tx_data <= {1'b1,tx_din,1'b0} ; // 停止位 + 数据 + 起始位 低位在前发送
end
end
// dout
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
dout <= 1'b1 ;
end
else if(tx_flag)begin
dout <= tx_data[cnt_bit] ;
end
end
// 输出
assign uart_tx = dout ;
assign ready = ~tx_flag ;
endmodule
四、仿真
仿真代码如下:
`timescale 1ns/1ps
module 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 begin
tb_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;
end
task Send;
input [7:0] send_data ;
begin
data = send_data ;
data_vld = 1'b1 ;
# CYCLE ;
data_vld = 1'b0 ;
// @(posedge ready)
# (CYCLE*440*10) ;
end
endtask
endmodule
五、上板验证
这里通过串口调试助手来与FPGA进行串口通信测试,可以看见通过串口收发数据正确。
六、踩坑事项
在调试串口的时候发现设备管理器中串口无法识别,显示PL2303HXA自2012已停产,请联系供应商,如下图:
这时需要下载一个旧版本的驱动,下载链接如下:
链接:https://pan.baidu.com/s/1FTtfgc2k2fw-9Ck1_tRbRQ
提取码:7kf2文章来源:https://www.toymoban.com/news/detail-454299.html
下载完成后解压安装,点击更新驱动程序→浏览电脑选择旧版本双击安装即可:文章来源地址https://www.toymoban.com/news/detail-454299.html
到了这里,关于【FPGA】FPGA实现UART串口通信回环的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!