当前位置:网站首页>基於FPGA的UART接口設計
基於FPGA的UART接口設計
2022-07-23 18:59:00 【qq_44985628】
一、頂層設計思路:
UART即通用异步收發傳輸接口(Universal Asynchronous Receiver/Transmitter),簡稱串口,是一種常用的通信接口,其協議原理就不贅述了,不了解的可以自己查閱資料。(不贅述不代錶不重要,相反,對於每一個FPGA設計,充分理解原理是基礎和前提,而FPGA和Verilog只是工具。)用FPGA來實現UART,關鍵就是要將UART收發數據時的時序用Verilog描述出來。
根據UART協議的原理,可以將整個UART分為兩個模塊:串口接收模塊“UART_RX”和串口發送模塊“UART_TX”,前者將接收到的1比特串行數據“uart_rxd”轉化為8比特並行數據“data[7:0]”,後者又將8比特並行數據“data[7:0]”轉化回1比特串行數據“uart_txd”輸出,最終實現串行數據的收發。UART頂層功能框圖如圖1所示:

二、串口接收模塊設計思路:
該模塊實現將接收到的1比特串行數據轉化為8比特並行數據,而只有當有數據輸入時,該模塊才去工作,所以需要一個使能信號“rxd_en”來控制該模塊,當“rxd_en”有效時,該模塊才工作。根據UART協議原理,接收到的1比特串行數據最開始的一比特為起始比特(“0”),而“uart_rxd”在空閑時為“1”,故剛開始接收到串行數據時,“uart_rxd”必定會產生一個下降沿,所以可以檢測這個下降沿,每當下降沿到來的同時將“rxd_en”置為有效,從而使該模塊開始工作;每當所有串行數據都被接收完畢時,再把“rxd_en”置為無效,同時發出接收完成標志“rx_done”,從而關閉該模塊。(關於在Verilog裏怎麼實現實現邊沿檢測,可以看我寫的關於DDS信號發生器的那篇博文。)
在數據接收的過程中,速率以串口波特率為准,常用的串口波特率有:1200、2400、4800、9600、14400、57600、115200等等。所以需要一個波特率計數器“baud_cnt”在特定的波特率下對主時鐘進行計數,每當這個波特率計數器計滿時,接收一比特數據。例如,主時鐘為50MHz,當波特率為9600時,波特率計數器的最大值應該為:50000000/9600-1=5207,此時,每當波特率計數器計到5207時就清零,同時接收一比特串行數據。
在本次設計中,每個數據的數據比特共有10比特(1比特起始比特、8比特數據比特、1比特停止比特),故在接收過程中,還需要一個比特計數器“bit_cnt”來對每個串行數據的數據比特進行計數,具體操作為:每當波特率計數器計滿時,比特計數器就自增1,直到比特計數器的值為9時清零。
在接收過程中,為了接收到穩定的串行數據,本設計在每一比特串行數據的中間對其進行采樣和接收,具體操作為:每當波特率計數器計到最大值的一半時,就對當前的串行數據進行采樣,然後根據比特計數器的值,將采樣後的值賦給相應的並行數據比特。此外,為了消除亞穩態,待接收的串行數據應該先通過一個兩比特的寄存器進行緩沖後再進行邊沿檢測。
根據以上分析,做出串口接收模塊的時序圖如圖2所示(“uart_rxd_r”是“uart_rxd”消除亞穩態後再通過邊沿檢測寄存器後的信號):

根據時序圖,就可以進行串口接收模塊的RTL描述,編寫的Verilog代碼如下:
`timescale 1ns / 1ps
module UART_RX(
input clk, //主時鐘,50MHz
input rst, //複比特,高電平有效
input uart_tx_data, //發送給串口的串行數據
output reg [7:0] uart_rx_data, //串口接收後的並行數據
output reg rx_done //接收完成標志
);
parameter CLK_F = 50000000; //主時鐘頻率
parameter UART_B = 9600; //串口波特率
parameter B_CNT = CLK_F / UART_B; //波特率計數器的最大值
reg [1:0] uart_tx_data_r1; //用於消除輸入串行數據的亞穩態
reg [1:0] uart_tx_data_r2; //輸入串行數據邊沿檢測寄存器
reg rxd_en; //接收使能信號
reg [15:0] baud_cnt = 16'd0; //115200波特率計數器
reg [3:0] bit_cnt = 4'd0; //比特計數器
reg [7:0] rx_data; //接收數據寄存器
/***************消除輸入串行數據亞穩態*************/
always @ (posedge clk)
begin
uart_tx_data_r1 <= {
uart_tx_data_r1[0] , uart_tx_data};
end
/***************輸入串行數據邊沿檢測*************/
always @ (posedge clk)
begin
uart_tx_data_r2 <= {
uart_tx_data_r2[0] , uart_tx_data_r1[1]};
end
/***************接收使能信號控制*************/
always @ (posedge clk or negedge rst)
begin
if (rst)
rxd_en <= 1'd0;
else
begin
if (uart_tx_data_r2 == 2'b10)
rxd_en <= 1'd1;
else if ((bit_cnt == 4'd9) && (baud_cnt == B_CNT / 2))
rxd_en <= 1'd0;
else
rxd_en <= rxd_en;
end
end
/***************波特率計數器控制*************/
always @ (posedge clk or negedge rst)
begin
if (rst)
baud_cnt <= 16'd0;
else if (rxd_en)
begin
if (baud_cnt == B_CNT - 1)
baud_cnt <= 16'd0;
else
baud_cnt <= baud_cnt + 1'b1;
end
else
baud_cnt <= 16'd0;
end
/***************比特計數器控制*************/
always @ (posedge clk or negedge rst)
begin
if (rst)
bit_cnt <= 4'd0;
else if (rxd_en)
begin
if (baud_cnt == B_CNT - 1)
bit_cnt <= bit_cnt + 1'b1;
else
bit_cnt <= bit_cnt;
end
else
bit_cnt <= 4'd0;
end
/***************接收緩存*************/
always @ (posedge clk or negedge rst)
begin
if (rst)
rx_data <= 8'd0;
else if (rxd_en)
begin
if (baud_cnt == B_CNT / 2)
begin
case (bit_cnt)
4'd1:rx_data[0] <= uart_tx_data_r2[1];
4'd2:rx_data[1] <= uart_tx_data_r2[1];
4'd3:rx_data[2] <= uart_tx_data_r2[1];
4'd4:rx_data[3] <= uart_tx_data_r2[1];
4'd5:rx_data[4] <= uart_tx_data_r2[1];
4'd6:rx_data[5] <= uart_tx_data_r2[1];
4'd7:rx_data[6] <= uart_tx_data_r2[1];
4'd8:rx_data[7] <= uart_tx_data_r2[1];
default:;
endcase
end
else
rx_data <= rx_data;
end
else
rx_data <= 8'd0;
end
/***************接收*************/
always @ (posedge clk or negedge rst)
begin
if (rst)
uart_rx_data <= 8'd0;
else if (bit_cnt == 4'd9)
uart_rx_data <= rx_data;
else
uart_rx_data <= 8'd0;
end
/***************接收完成標志控制*************/
always @ (posedge clk or negedge rst)
begin
if (rst)
rx_done <= 1'd0;
else if (bit_cnt == 4'd9)
rx_done <= 1'd1;
else
rx_done <= 1'd0;
end
endmodule
編寫的testbeach如下:
`timescale 1ns / 1ps
module tb_uart_rx();
reg clk; //主時鐘,50MHz
reg rst; //複比特,低電平有效
reg uart_tx_data; //發送給串口的串行數據
wire [7:0] uart_rx_data; //串口接收後的並行數據
wire rx_done; //接收完成標志
/***************模塊例化*************/
UART_RX tb_uart_rx(
.clk(clk),
.rst(rst),
.uart_tx_data(uart_tx_data),
.uart_rx_data(uart_rx_data),
.rx_done(rx_done)
);
/***************產生主時鐘*************/
always #10 clk = ~clk;
/***************初始化*************/
initial
begin
clk = 1'd0;
rst = 1'd0;
uart_tx_data = 1'b1;
#1000000
uart_tx_data = 1'b0;
#200000
uart_tx_data = 1'b1;
#200000
uart_tx_data = 1'b0;
#200000
uart_tx_data = 1'b1;
end
endmodule
仿真結果如圖3所示,可以看到接收功能已實現:
三、串口發送模塊設計思路:
該模塊實現將8比特並行數據轉化回1比特串行數據輸出,與接收模塊一樣,串口發送模塊也需要一個使能信號“txd_en”來控制,當“txd_en”有效時,模塊才工作。根據UART原理,只有接收模塊接收完成後,發送模塊才能開始工作,故接收模塊裏的接收完成標志即是發送模塊的發送開始標志“txd_start”,所以可以通過檢測發送開始標志的上昇沿來使能“txd_en”,從而使能發送模塊。
根據以上分析,做出串口發送模塊的時序圖如圖4所示:

根據時序圖,編寫的Verilog代碼如下:
`timescale 1ns / 1ps
module UART_TX(
input clk, //主時鐘,50MHz
input rst, //複比特,高電平有效
input txd_start, //發送開始標志
input [7:0] uart_rx_data, //串口接收到的並行數據
output reg uart_tx_data //串口發送的串行數據
);
parameter CLK_F = 50000000; //主時鐘頻率
parameter UART_B = 9600; //串口波特率
parameter B_CNT = CLK_F / UART_B; //波特率計數器的最大值
reg [1:0] txd_start_r; //發送開始標志邊沿檢測寄存器
reg txd_en; //發送使能信號
reg [15:0] baud_cnt = 16'd0; //115200波特率計數器
reg [3:0] bit_cnt = 4'd0; //比特計數器
reg [7:0] tx_data; //發送數據寄存器
/***************發送開始標志邊沿檢測*************/
always @ (posedge clk)
begin
txd_start_r <= {
txd_start_r[0] , txd_start};
end
/***************發送使能信號控制*************/
always @ (posedge clk or negedge rst)
begin
if (rst)
txd_en <= 1'd0;
else
begin
if (txd_start_r == 2'b01)
txd_en <= 1'd1;
else if ((bit_cnt == 4'd9) && (baud_cnt == B_CNT / 2))
txd_en <= 1'd0;
else
txd_en <= txd_en;
end
end
/***************發送緩存*************/
always @ (posedge clk or negedge rst)
begin
if (rst)
tx_data <= 8'd0;
else
begin
if (txd_start_r == 2'b01)
tx_data <= uart_rx_data;
else if ((bit_cnt == 4'd9) && (baud_cnt == B_CNT / 2))
tx_data <= 8'd0;
else
tx_data <= tx_data;
end
end
/***************波特率計數器控制*************/
always @ (posedge clk or negedge rst)
begin
if (rst)
baud_cnt <= 16'd0;
else if (txd_en)
begin
if (baud_cnt == B_CNT - 1)
baud_cnt <= 16'd0;
else
baud_cnt <= baud_cnt + 1'b1;
end
else
baud_cnt <= 16'd0;
end
/***************比特計數器控制*************/
always @ (posedge clk or negedge rst)
begin
if (rst)
bit_cnt <= 4'd0;
else if (txd_en)
begin
if (baud_cnt == B_CNT - 1)
bit_cnt <= bit_cnt + 1'b1;
else
bit_cnt <= bit_cnt;
end
else
bit_cnt <= 4'd0;
end
/***************發送*************/
always @ (posedge clk or negedge rst)
begin
if (rst)
uart_tx_data <= 1'd1;
else if (txd_en)
begin
case (bit_cnt)
4'd0:uart_tx_data <= 1'd0;
4'd1:uart_tx_data <= tx_data[0];
4'd2:uart_tx_data <= tx_data[1];
4'd3:uart_tx_data <= tx_data[2];
4'd4:uart_tx_data <= tx_data[3];
4'd5:uart_tx_data <= tx_data[4];
4'd6:uart_tx_data <= tx_data[5];
4'd7:uart_tx_data <= tx_data[6];
4'd8:uart_tx_data <= tx_data[7];
4'd9:uart_tx_data <= 1'd1;
default:;
endcase
end
else
uart_tx_data <= 1'd1;
end
endmodule
編寫的testbeach如下:
`timescale 1ns / 1ps
module tb_uart_tx();
reg clk; //主時鐘,50MHz
reg rst; //複比特,低電平有效
reg txd_start; //發送開始標志
reg [7:0] uart_rx_data; //串口接收到的並行數據
wire uart_tx_data; //串口發送的串行數據
/***************模塊例化*************/
UART_TX tb_uart_tx(
.clk(clk),
.rst(rst),
.txd_start(txd_start),
.uart_rx_data(uart_rx_data),
.uart_tx_data(uart_tx_data)
);
/***************產生主時鐘*************/
always #10 clk = ~clk;
/***************初始化*************/
initial
begin
clk = 1'd0;
rst = 1'd0;
txd_start = 1'd0;
uart_rx_data = 8'h5a;
#20
txd_start = 1'd1;
#20
txd_start = 1'd0;
#100000
uart_rx_data = 8'h3f;
#100000
uart_rx_data = 8'he6;
end
endmodule
仿真結果如圖5所示,可以看到發送功能已實現:
圖5
四、頂層代碼及其上板調試:
兩個子模塊設計完成後,按照頂層功能框圖可以編寫頂層的RTL描述,編寫的Verilog代碼如下(由於我的開發板上的時鐘是差分時鐘,故需要調用一個差分信號轉單端信號的設計原語“IBUFDS”,該原語的使用很簡單,在這裏就不專門介紹了,不了解的可以自己查閱資料):
`timescale 1ns / 1ps
module UART_TOP(
input clk_p, //差分主時鐘正端,50MHz
input clk_n, //差分主時鐘負端,50MHz
input rst, //複比特,高電平有效
input uart_rxd, //接收端
output uart_txd //發送端
);
parameter CLK_FREQ = 50000000; //主時鐘頻率
parameter UART_BPS = 460800; //串口波特率
wire clk; //主時鐘,50MHz
wire [7:0] uart_rx_data_w; //串口裏的並行數據線
wire rx_done_w; //接收完成信號線
/***************差分時鐘轉單端時鐘*************/
IBUFDS #(
.DIFF_TERM("FALSE"),
.IBUF_LOW_PWR("TRUE"),
.IOSTANDARD("DEFAULT")
) IBUFDS_inst (
.O(clk),
.I(clk_p),
.IB(clk_n)
);
/***************調用串口接收模塊*************/
UART_RX #(
.CLK_F(CLK_FREQ),
.UART_B(UART_BPS)
)
uut_rxd(
.clk(clk),
.rst(rst),
.uart_tx_data(uart_rxd),
.uart_rx_data(uart_rx_data_w),
.rx_done(rx_done_w)
);
/***************調用串口發送模塊*************/
UART_TX #(
.CLK_F(CLK_FREQ),
.UART_B(UART_BPS)
)
uut_txd(
.clk(clk),
.rst(rst),
.txd_start(rx_done_w),
.uart_rx_data(uart_rx_data_w),
.uart_tx_data(uart_txd)
);
endmodule
綜合、實現後,進行上板調試,為了簡單起見,本設計采用回環的方式來調試驗證,即PC發送數據到FPGA上,FPGA通過串口接收數據後再通過串口發送回PC。調試結果如圖6所示,可以看到,所設計的串口工作正常:

边栏推荐
- Three things programmers want to do most | comics
- How to understand: common code block, construction block, static block? What does it matter?
- quota的使用方法
- 1259. 不相交的握手 动态规划
- 并非原创的原文路径【如有侵权 请原博主联系删除】
- 一文了解 NebulaGraph 上的 Spark 项目
- 11.神经网络基本概念
- An SQL question about grouping query
- 【2020】【论文笔记】基于二维光子晶体的光控分光比可调Y——
- [2020] [paper notes] new terahertz detection - Introduction to terahertz characteristics, various terahertz detectors
猜你喜欢

Problems and methods of creating multiple projects under one solution in VS2010
![[whole process of game modeling model production] 3ds Max and ZBrush produce radio receivers](/img/c9/302a52d2c9f6fc3b5971e9a0ea55e6.png)
[whole process of game modeling model production] 3ds Max and ZBrush produce radio receivers

Modeling just learning is very confused. How to learn the next generation role modeling process?

入行3D建模有前景吗?就业高薪有保障还是副业接单赚钱多

使用kail破解wifi密码

LM393低功耗双电压比较器参数、引脚、应用详解

Redis【2022最新面试题】

JUC并发编程【详解及演示】

【攻防世界WEB】难度三星9分入门题(终):fakebook、favorite_number
![[2020] [paper notes] phase change materials and Hypersurfaces——](/img/cc/a69afb3acd4b73a17dbbe95896404d.png)
[2020] [paper notes] phase change materials and Hypersurfaces——
随机推荐
代码整洁,高效的系统方法
?前台传参的问题待确认
【论文阅读】GETNext: Trajectory Flow Map Enhanced Transformer for Next POI Recommendation
Completion report of communication software development and Application
使用kail破解wifi密码
[paper reading] gettext: trajectory flow map enhanced transformer for next POI recommendation
【2020】【论文笔记】基于Rydberg原子的——
integer 和==比较
【攻防世界WEB】难度四星12分进阶题:Cat
1、 Reptile concept and basic process
Three things programmers want to do most | comics
sklearn 分类器常见问题
Paddlenlp之UIE分类模型【以情感倾向分析新闻分类为例】含智能标注方案)
Spark 安装与启动
学次世代建模是场景好还是角色好?选对职业薪资多一半
Learn about spark project on nebulagraph
Common problems of sklearn classifier
FPGA实现IIC协议(一)IIC总线协议
What is the use of tampermonkey?
SQL 语句练习