0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

什么是串口(UART)?串口的組成和FPGA實現(xiàn)

硬件攻城獅 ? 來源:CSDN博主孤獨的單刀 ? 2024-01-03 11:43 ? 次閱讀

1、什么是串口(UART)?

串口作為常用的三大低速總線(UART、SPI、IIC)之一,在設(shè)計眾多通信接口和調(diào)試時占有重要地位。

串口(UART)全稱通用異步收發(fā)傳輸器(Universal Asynchronous Receiver/Transmitter),主要用于數(shù)據(jù)間的串行傳遞,是一種全雙工傳輸模式。

它在發(fā)送數(shù)據(jù)時將并行數(shù)據(jù)轉(zhuǎn)換成串行數(shù)據(jù)來傳輸,在接收數(shù)據(jù)時將接收到的串行數(shù)據(jù)轉(zhuǎn)換成并行數(shù)據(jù)。

“異步”兩個字即意味著在數(shù)據(jù)傳遞的兩個模塊之間使用的不是同步時鐘。

實際上在異步串口的傳輸中是不需要時鐘的,而是通過特定的時序來標(biāo)志傳輸?shù)拈_始(起始位--由高到低)和結(jié)束(結(jié)束位,拉高)。

2、串口的組成

2.1、串口的物理層

UART 通信只有兩根信號線,一根是發(fā)送數(shù)據(jù)端口線叫 tx(Transmitter),一根是接收數(shù)據(jù)端口線叫 rx(Receiver)

如圖所示,對于 PC 來說它的 tx 要和對于 FPGA來說的 rx 連接,同樣 PC 的 rx 要和 FPGA 的 tx 連接,如果是兩個 tx 或者兩個 rx 連接那數(shù)據(jù)就不能正常被發(fā)送出去和接收到。

eca06fb6-a9e9-11ee-8b88-92fbcf53809c.png

信號的傳輸由外部驅(qū)動電路實現(xiàn)。電信號的傳輸過程有著不同的電平標(biāo)準(zhǔn)和接口規(guī)范,針對異步串行通信的接口標(biāo)準(zhǔn)有RS232、RS422RS485等.

它們定義了接口不同的電氣特性,如RS-232是單端輸入輸 出,而RS-422/485為差分輸入輸出等。

傳輸距離較短時(不超過15m),RS232是串行通信最常用的接口標(biāo)準(zhǔn)。RS-232標(biāo)準(zhǔn)的串口最常見的接口類型為DB9,樣式如圖所示,工業(yè)控制領(lǐng)域中用到的工控機一般都配備多個串口,很多老式臺式機也都配有串口。

但是筆記本電腦以及較新一點 的臺式機都沒有串口,它們一般通過USB轉(zhuǎn)串口線來實現(xiàn)與外部設(shè)備的串口通信。

DB9接口定義以及各引腳功能說明如圖所示,我們一般只用到其中的2(RXD)、3 (TXD)、5(GND)引腳,其他引腳在普通串口模式下一般不使用:

ecbfde46-a9e9-11ee-8b88-92fbcf53809c.png

2.2、UART協(xié)議

UART 在發(fā)送或接收過程中的一幀數(shù)據(jù)由4部分組成,起始位、數(shù)據(jù)位、奇偶校驗位和停止位,如圖所示。

其中,起始位標(biāo)志著一幀數(shù)據(jù)的開始,停止位標(biāo)志著一幀數(shù)據(jù)的結(jié)束,數(shù)據(jù)位是一幀數(shù)據(jù)中的有效數(shù)據(jù)。

校驗位分為奇校驗和偶校驗,用于檢驗數(shù)據(jù)在傳輸過程中是否出錯。

奇校驗時,發(fā)送方應(yīng)使數(shù)據(jù)位中1的個數(shù)與校驗位中1的個數(shù)之和為奇數(shù);接收方在接收數(shù)據(jù)時, 對1的個數(shù)進行檢查.

若不為奇數(shù),則說明數(shù)據(jù)在傳輸過程中出了差錯。同樣,偶校驗則檢查1的個數(shù)是否為偶數(shù)。關(guān)于奇偶校驗可參考:Verilgo實現(xiàn)的FPGA奇偶校驗

ecc3d9c4-a9e9-11ee-8b88-92fbcf53809c.png

UART通信過程中的數(shù)據(jù)格式及傳輸速率是可設(shè)置的,為了正確的通信,收發(fā)雙方應(yīng)約定并遵循同樣的設(shè)置。

數(shù)據(jù)位可選擇為5、6、7、8位,其中8位數(shù)據(jù)位是最常用的,在實際應(yīng)用中一般都選擇8位數(shù)據(jù)位;校驗位可選擇奇校驗、偶校驗或者無校驗位;停止位可選擇1位(默認), 1.5或2位。

串口通信的速率用波特率表示,它表示每秒傳輸二進制數(shù)據(jù)的位數(shù),單位是bps(位 /秒),常用的波特率有9600、19200、38400、57600以及115200等。

如波特率9600則代表每秒傳輸9600bit數(shù)據(jù),以串口發(fā)送1個字節(jié)10bit算(起始位1bit+數(shù)據(jù)8bit+停止位1bit+NO校驗位),則傳輸1個字節(jié)需要的時間是1*10/9600秒。

3、串口發(fā)送模塊

3.1、接口定義與整體設(shè)計

發(fā)送模塊整體框圖、輸入輸出信號如下所示:

ecc76ef4-a9e9-11ee-8b88-92fbcf53809c.png

其中信號端口如下:

ecd9e46c-a9e9-11ee-8b88-92fbcf53809c.png

需要說明的是,uart_tx_data為需要發(fā)送的一個字節(jié)的數(shù)據(jù),uart_tx_en為發(fā)送使能位,當(dāng)其拉高,則代表此時通過串口發(fā)送數(shù)據(jù)線發(fā)送數(shù)據(jù)uart_tx_data。

3.2、設(shè)計思路

該模塊支持任意波特率(理論上)的發(fā)送,但需要在使用該模塊時使用參數(shù)將其例化,數(shù)據(jù)位8位,起始位和停止位各1位,無奇偶校驗

當(dāng)使能信號有效后拉高發(fā)送標(biāo)志信號,標(biāo)志模塊進入發(fā)送過程;當(dāng)發(fā)送完10個bit后,拉低發(fā)送標(biāo)志信號,標(biāo)志發(fā)送過程結(jié)束。使能信號有效時將要發(fā)送的數(shù)據(jù)寄存。

假設(shè)波特率為9600,則發(fā)送一個bit的時間為1s/9600,一個數(shù)據(jù)的傳輸共10bit(數(shù)據(jù)位8位,起始位和停止位各1位),則共需要1s/9600;

假設(shè)系統(tǒng)時鐘為50MHz(參數(shù)化以便適應(yīng)不同的系統(tǒng)頻率),則其周期為20ns,那么發(fā)送一個bit所需要的系統(tǒng)周期數(shù)為(1s/9600)/ 20ns ≈ 5208(個)。

在發(fā)送過程中使用一個計數(shù)器計數(shù),計數(shù)區(qū)間為(0~5208-1),這樣的區(qū)間一共10個(一個字節(jié)需要發(fā)送10個bit);

此外還需一個計數(shù)器對發(fā)送的bit數(shù)計數(shù)(每當(dāng)上一個計數(shù)器計數(shù)到5207則表示發(fā)送完了一個bit),計數(shù)區(qū)間(0~9)

在發(fā)送過程,根據(jù)計數(shù)器的值(發(fā)送bit計數(shù)器),對發(fā)送數(shù)據(jù)線進行操作。

若發(fā)送bit計數(shù)器 = 0,則代表此時需要發(fā)送起始位;

若發(fā)送bit計數(shù)器 = 1,則代表此時需要發(fā)送發(fā)送數(shù)據(jù)的最低位LSB(數(shù)據(jù)的發(fā)送總是低位在前,高位在后);

······

若發(fā)送bit計數(shù)器 = 8,則代表此時需要發(fā)送發(fā)送數(shù)據(jù)的最高位MSB;

若發(fā)送bit計數(shù)器 = 9,則代表此時需要發(fā)送停止位;

發(fā)送數(shù)據(jù)線在不處于發(fā)送狀態(tài)時需拉高,以滿足UART時序的空閑狀態(tài)

3.3、Verilg代碼

根據(jù)上述設(shè)計思路,部分發(fā)送模塊代碼如下:

 
// *******************************************************************************************************
// ** 作者 : 孤獨的單刀                             
// ** 郵箱 : zachary_wu93@163.com
// ** 博客 : https://blog.csdn.net/wuzhikaidetb 
// ** 日期 : 2022/07/31 
// ** 功能 : 1、基于FPGA的串口發(fā)送驅(qū)動模塊;
//    2、可設(shè)置波特率BPS、主時鐘CLK_FRE;
//    3、起始位1bit,數(shù)據(jù)位8bit,停止位1bit,無奇偶校驗;                                                                       
//    4、每發(fā)送1個字節(jié)后拉高uart_tx_done一個周期,可用于后續(xù)發(fā)送多字節(jié)模塊。                                                                       
// ******************************************************************************************************* 
 
module uart_tx
#(
 parameter integer BPS  = 9_600  , //發(fā)送波特率
 parameter  integer CLK_FRE = 50_000_000 //主時鐘頻率
)
(
//系統(tǒng)接口
 input    sys_clk   ,   //系統(tǒng)時鐘
 input    sys_rst_n  ,   //系統(tǒng)復(fù)位,低電平有效
//用戶接口 
 input [7:0]  uart_tx_data ,   //需要通過UART發(fā)送的數(shù)據(jù),在uart_tx_en為高電平時有效
 input   uart_tx_en  ,   //發(fā)送有效,當(dāng)其為高電平時,代表此時需要發(fā)送的數(shù)據(jù)有效
//UART發(fā)送 
 output reg  uart_tx_done ,   //成功發(fā)送1BYTE數(shù)據(jù)后拉高一個周期
 output  reg  uart_txd     //UART發(fā)送數(shù)據(jù)線tx
);
 
 
//當(dāng)發(fā)送使能信號到達時,寄存待發(fā)送的數(shù)據(jù)以免后續(xù)變化、丟失
always @(posedge sys_clk or negedge sys_rst_n)begin
 if(!sys_rst_n)
  uart_tx_data_reg <=8'd0;
 ?else if(uart_tx_en) ? ? ? ? ? ? ?//要發(fā)送有效的數(shù)據(jù)
 ? ?uart_tx_data_reg <= uart_tx_data; ? ?//寄存需要發(fā)送的數(shù)據(jù) ? ? ?
 ?else 
 ? ?uart_tx_data_reg <= uart_tx_data_reg;
end ? ?
//當(dāng)發(fā)送使能信號到達時,進入發(fā)送過程
always @(posedge sys_clk or negedge sys_rst_n)begin
 ?if(!sys_rst_n)
 ? ?tx_state <=1'b0; ?
 ?else if(uart_tx_en) ? ? ? ? ? ? ? ? ? ? ? ?
 ? ?tx_state <= 1'b1; ? ? ? ? ? ?//發(fā)送信號有效則進入發(fā)送過程
 ?//發(fā)送完了最后一個數(shù)據(jù)則退出發(fā)送過程 ? ?
 ?else if((bit_cnt == BITS_NUM - 1'b1) && (clk_cnt == BPS_CNT - 1'b1)) ? ?
 ? ?tx_state <= 1'b0; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
 ?else 
 ? ?tx_state <= tx_state; ?
end
 
//發(fā)送數(shù)據(jù)完畢后拉高發(fā)送完畢信號一個周期,指示一個字節(jié)發(fā)送完畢
always @(posedge sys_clk or negedge sys_rst_n)begin
 ?if(!sys_rst_n)
 ? ?uart_tx_done <=1'b0;
 ?//發(fā)送數(shù)據(jù)完畢后拉高發(fā)送完畢信號一個周期 ? ? 
 ?else if((bit_cnt == BITS_NUM - 1'b1) && (clk_cnt == BPS_CNT - 1'b1)) ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 
 ? ?uart_tx_done <=1'b1; ? ? ? ? ? ? ? ? ? ?
 ?else 
 ? ?uart_tx_done <=1'b0;
end
//進入發(fā)送過程后,啟動時鐘計數(shù)器與發(fā)送個數(shù)bit計數(shù)器
always @(posedge sys_clk or negedge sys_rst_n)begin
 ?if(!sys_rst_n)begin
 ? ?clk_cnt <= 32'd0;
 ? ?bit_cnt <= 4'd0;
 ?end
 ?else if(tx_state) begin ? ? ? ? ? ? ? ? ? ?//在發(fā)送狀態(tài)
 ? ?if(clk_cnt < BPS_CNT - 1'd1)begin ? ? ? ? ? ?//一個bit數(shù)據(jù)沒有發(fā)送完
 ? ? ?clk_cnt <= clk_cnt + 1'b1; ? ? ? ? ? ? ?//時鐘計數(shù)器+1
 ? ? ?bit_cnt <= bit_cnt; ? ? ? ? ? ? ? ? ?//bit計數(shù)器不變
 ? ?end ? ? ? ? ?
 ? ?else begin ? ? ? ? ? ? ? ? ? ? ? ?//一個bit數(shù)據(jù)發(fā)送完了 ?
 ? ? ?clk_cnt <= 32'd0; ? ? ? ? ? ? ? ? ?//清空時鐘計數(shù)器,重新開始計時
 ? ? ?bit_cnt <= bit_cnt+1'b1; ? ? ? ? ? ? ?//bit計數(shù)器+1,表示發(fā)送完了一個bit的數(shù)據(jù)
 ? ?end ? ? ? ? ?
 ?end ? ? ? ? ?
 ?else begin ? ? ? ? ? ? ? ? ? ? ? ? ?//不在發(fā)送狀態(tài)
 ? ?clk_cnt <= 32'd0; ? ? ? ? ? ? ? ? ? ? ? ? ? ? //清零
 ? ?bit_cnt <= 4'd0; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//清零
 ?end
end
endmodule

3.4、Testbench

Testbench的設(shè)計如下:

設(shè)定波特率230400(這樣的目的是為了更方便的觀察發(fā)送使能信號uart_tx_en,節(jié)約時間)

3000ns后,拉高發(fā)送使能信號uart_tx_en一個周期,同時生成1個8bit的隨機數(shù)據(jù)給uart_tx_data作為要發(fā)送的數(shù)據(jù)

觀察UART上TX線的時序是否滿足要求


// *******************************************************************************************************
// ** 作者 : 孤獨的單刀                             
// ** 郵箱 : zachary_wu93@163.com
// ** 博客 : https://blog.csdn.net/wuzhikaidetb 
// ** 日期 : 2022/07/29 
// ** 功能 : 1、對基于FPGA的串口發(fā)送驅(qū)動模塊的測試testbench
//    2、發(fā)送一個8bit的隨機數(shù)據(jù),觀測其波形是否符合UART時序                                                                     
// *******************************************************************************************************  
 
`timescale 1ns/1ns //定義時間刻度
 
module tb_uart_tx();
 
reg    sys_clk   ;   
reg    sys_rst_n  ;   
reg [7:0]  uart_tx_data ;
reg    uart_tx_en  ;
   
wire    uart_txd  ;
 
parameter integer BPS  = 'd230400  ;   //波特率
parameter integer CLK_FRE = 'd50_000_000 ;   //系統(tǒng)頻率50M
 
 
localparam integer BIT_TIME = 'd1000_000_000 / BPS ; //計算出傳輸每個bit所需要的時間
 
initial begin 
 sys_clk <=1'b0; ?
 ?sys_rst_n <=1'b0; ? ?
 ?uart_tx_en <=1'b0;
 ?uart_tx_data <=8'd0; ? ? ? ?
 ?#80 ? ? ? ? ? ? ? ? ? ? //系統(tǒng)開始工作
 ? ?sys_rst_n <=1'b1;
 ? ?
 ?#200
 ? ?@(posedge sys_clk);
 ? ?uart_tx_en <=1'b1; ?
 ? ?uart_tx_data <= ({$random} % 256); ? ?//發(fā)送8位隨機數(shù)據(jù)
 ?#20 ?
 ? ?uart_tx_en <=1'b0;
 ?
 ?#(BIT_TIME * 10) ? ? ? ? ? ? ?//發(fā)送1個BYTE需要10個bit
 ?#200 $finish; ? ? ? ? ? ? ? ?//結(jié)束仿真
end
 
always #10 sys_clk=~sys_clk; ? ? ? ? ?//定義主時鐘,周期20ns,頻率50M
 
//例化發(fā)送驅(qū)動模塊
uart_tx #(
 ?.BPS ? ? ?(BPS ? ? ?), ? ?
 ?.CLK_FRE ? ?(CLK_FRE ? ?) ? ?
) ?
uart_tx_inst( ?
 ?.sys_clk ? ?(sys_clk ? ?), ? ? ?
 ?.sys_rst_n ? ?(sys_rst_n ? ?),
 ?
 ?.uart_tx_data ?(uart_tx_data ?), ? ? ?
 ?.uart_tx_en ? ?(uart_tx_en ? ?), ? ?
 ?.uart_tx_done ?(uart_tx_done ?), ? ?
 ?.uart_txd ? ?(uart_txd ? ?) ?
);
 
endmodule

3.5、仿真結(jié)果分析

仿真結(jié)果如下圖(注釋很詳細):

下圖中可以看到發(fā)送模塊發(fā)送了1個數(shù)據(jù)8'h24,一段時間后發(fā)送結(jié)束,并無法直接觀察發(fā)送線TX上的時序。

eceeeac4-a9e9-11ee-8b88-92fbcf53809c.png

整體仿真時序

下圖中可以看到發(fā)送模塊發(fā)送了1個數(shù)據(jù)8'h24,一段時間后發(fā)送結(jié)束,且可以看到發(fā)送線TX在以符合UART時序的方式發(fā)送數(shù)據(jù)00100100(低位在前、高位在后),即8'h24。

ecfa7664-a9e9-11ee-8b88-92fbcf53809c.png

單次發(fā)送時序

可以看到仿真結(jié)果是符合預(yù)期設(shè)計要求的。

3.6、上板實測

至此已經(jīng)順利完成了發(fā)送模塊的仿真驗證,接下來使用一塊Altera Cyclone IV E的開發(fā)板上板實測。

編寫一個發(fā)送模塊測試模塊,該模塊調(diào)用串口發(fā)送模塊,并按一定間隔(默認1s)拉高發(fā)送使能信號和生成發(fā)送數(shù)據(jù),發(fā)送數(shù)據(jù)從0x01開始累加1,直到0xFF(溢出到0x00)。

同時在電腦上使用串口調(diào)試軟件接收發(fā)送過來的數(shù)據(jù)。根據(jù)串口調(diào)試軟件接收到的數(shù)據(jù)判斷串口發(fā)送模塊是否能成功工作。

發(fā)送模塊驗證模塊代碼如下:

 
// *******************************************************************************************************
// ** 作者 : 孤獨的單刀                             
// ** 郵箱 : zachary_wu93@163.com
// ** 博客 : https://blog.csdn.net/wuzhikaidetb 
// ** 日期 : 2022/07/29 
// ** 功能 : 1、基于FPGA的串口發(fā)送驅(qū)動模塊的測試模塊;
//    2、每個1s發(fā)送1個遞增1的數(shù)據(jù)到上位機。
// ******************************************************************************************************* 
 
module uart_tx_test
(
//系統(tǒng)接口
 input    sys_clk   ,
 input    sys_rst_n  , 
//UART發(fā)送線 
 output   uart_txd      //UART發(fā)送線
); 
 
parameter integer BPS  = 'd230400  ;  //波特率
parameter integer CLK_FRE = 'd50_000_000 ;  //系統(tǒng)頻率50M 
 
reg  [31:0] cnt_time; 
reg    uart_tx_en;       //發(fā)送使能,當(dāng)其為高電平時,代表此時需要發(fā)送數(shù)據(jù)  
reg  [7:0]  uart_tx_data;      //需要通過UART發(fā)送的數(shù)據(jù),在uart_tx_en為高電平時有效
 
//1s計數(shù)模塊,每隔1s發(fā)送一個數(shù)據(jù)和拉高發(fā)送使能信號一次;數(shù)據(jù)從0開始遞增1
always @(posedge sys_clk or negedge sys_rst_n)begin
 if(!sys_rst_n)begin
  cnt_time <= 'd0;
 ? ?uart_tx_en <= 1'd0;
 ? ?uart_tx_data <= 8'd0;
 ?end
 ?else if(cnt_time == (50_000_000 - 1'b1))begin
 ? ?cnt_time <= 'd0;
 ? ?uart_tx_en <= 1'd1; ? ? ? ? ? ? ?//拉高發(fā)送使能
 ? ?uart_tx_data <= uart_tx_data + 1'd1; ? ?//發(fā)送數(shù)據(jù)累加1
 ?end
 ?else begin
 ? ?cnt_time <= cnt_time + 1'd1;
 ? ?uart_tx_en <= 1'd0;
 ? ?uart_tx_data <= uart_tx_data; 
 ?end
end 
 
//例化發(fā)送模塊
uart_tx
#(
 ?.BPS ? ? ?(BPS ? ? ?),
 ?.CLK_FRE ? ?(CLK_FRE ? ?)
) ?
uart_tx_inst
( ?
 ?.sys_clk ? ?(sys_clk ? ?),
 ?.sys_rst_n ? ?(sys_rst_n ? ?),
 ?.uart_tx_en ? ?(uart_tx_en ? ?),
 ?.uart_tx_data ?(uart_tx_data ?),
 ?.uart_tx_done ?( ? ? ? ?), ?
 ?.uart_txd ? ?(uart_txd ? ?)
); ?
 
endmodule

串口調(diào)試軟件結(jié)果如下:

依次接收到了數(shù)據(jù)01、02、03······。說明我們的發(fā)送模塊工作正常。

ed22b674-a9e9-11ee-8b88-92fbcf53809c.png

4、串口接收模塊

4.1、接口定義與整體設(shè)計

接收模塊整體框圖、輸入輸出信號如下所示:

ed3446c8-a9e9-11ee-8b88-92fbcf53809c.png

其中信號描述如下:

ed406066-a9e9-11ee-8b88-92fbcf53809c.png

需要說明的是,uart_rx_data為接收的一個字節(jié)的數(shù)據(jù),uart_rx_done為接收完成標(biāo)志位,當(dāng)其拉高,則代表此時接收到的串口數(shù)據(jù)uart_rx_data有效。

4.2、設(shè)計思路

該模塊支持任意波特率(理論上)的接收,但需要在使用該模塊時使用參數(shù)將其例化,數(shù)據(jù)位8位,起始位和停止位各1位,無奇偶校驗

串口的傳輸是以起始位開始的,而起始位是將數(shù)據(jù)線拉低 ,所以我們需要捕捉數(shù)據(jù)線的下降沿,將接收數(shù)據(jù)線打拍3次,捕捉其下降沿。

當(dāng)捕捉到接收數(shù)據(jù)線的下降沿,拉高接收標(biāo)志信號,標(biāo)志模塊進入接收過程;當(dāng)接收完10個bit后,拉低接收標(biāo)志信號,標(biāo)志接收過程結(jié)束

假設(shè)波特率為9600,則傳輸一個bit的時間為1s/9600,一個數(shù)據(jù)的傳輸共10bit(數(shù)據(jù)位8位,起始位和停止位各1位),則共需要1s/960;

假設(shè)系統(tǒng)時鐘為50MHz(參數(shù)化以便適應(yīng)不同的系統(tǒng)頻率),則其周期為20ns,那么傳輸一個bit所需要的系統(tǒng)周期數(shù)為(1s/960)/ 20ns ≈ 5208(個)。

在接收過程中使用一個計數(shù)器計數(shù),計數(shù)區(qū)間為(0~5208-1),這樣的區(qū)間一共10個(一個字節(jié)需要傳輸10個bit);

此外還需一個計數(shù)器對接收的bit數(shù)計數(shù)(每當(dāng)上一個計數(shù)器計數(shù)到5207則表示接收完了一個bit),計數(shù)區(qū)間(0~9)。

在接收過程,根據(jù)計數(shù)器的值(接收bit計數(shù)器),在每個bit計數(shù)器的中間接收數(shù)據(jù),將其移位寄存(在電平中間數(shù)據(jù)最穩(wěn)定)

若接收bit計數(shù)器 = 0,則代表是起始位,不需要接收

若接收bit計數(shù)器 = 1,則代表此時接收到數(shù)據(jù)的最低位LSB(數(shù)據(jù)的傳輸總是低位在前,高位在后),將其賦值給寄存數(shù)據(jù)的最低位;

······

若接收bit計數(shù)器 = 8,則代表此時接收到數(shù)據(jù)的最高位MSB,將其賦值給寄存數(shù)據(jù)的最高位;

若接收bit計數(shù)器 = 9,則代表是停止位,不需要接收

4.3、Verilg代碼

根據(jù)上述設(shè)計思路,部分代碼如下:

 
// *******************************************************************************************************
// ** 作者 : 孤獨的單刀                             
// ** 郵箱 : zachary_wu93@163.com
// ** 博客 : https://blog.csdn.net/wuzhikaidetb 
// ** 日期 : 2022/08/05 
// ** 功能 : 1、基于FPGA的串口接收驅(qū)動模塊;
//    2、可重新設(shè)置波特率BPS、主時鐘CLK_FRE;
//    3、起始位1bit,數(shù)據(jù)位8bit,停止位1bit,無奇偶校驗。                                                                       
// *******************************************************************************************************   
 
module uart_rx
#(
 parameter integer BPS  = 9_600  ,  //發(fā)送波特率
 parameter  integer CLK_FRE = 50_000_000  //輸入時鐘頻率
) 
( 
//系統(tǒng)接口
 input     sys_clk   ,   //50M系統(tǒng)時鐘
 input     sys_rst_n  ,   //系統(tǒng)復(fù)位
//UART接收線 
 input     uart_rxd  ,   //接收數(shù)據(jù)線
//用戶接口 
 output reg    uart_rx_done ,   //數(shù)據(jù)接收完成標(biāo)志,當(dāng)其為高電平時,代表接收數(shù)據(jù)有效
 output reg [7:0] uart_rx_data    //接收到的數(shù)據(jù),在uart_rx_done為高電平時有效
);
 
assign neg_uart_rxd = uart_rx_d3 & (~uart_rx_d2); //捕獲數(shù)據(jù)線的下降沿,用來標(biāo)志數(shù)據(jù)傳輸開始
 
//將數(shù)據(jù)線打3拍,作用1:同步不同時鐘域信號,防止亞穩(wěn)態(tài);作用2:捕獲下降沿
always@(posedge sys_clk or negedge sys_rst_n)begin
 if(!sys_rst_n)begin
  uart_rx_d1 <= 1'b0;
 ? ?uart_rx_d2 <= 1'b0;
 ? ?uart_rx_d3 <= 1'b0;
 ?end
 ?else begin
 ? ?uart_rx_d1 <= uart_rxd;
 ? ?uart_rx_d2 <= uart_rx_d1;
 ? ?uart_rx_d3 <= uart_rx_d2;
 ?end ? ?
end
//捕獲到數(shù)據(jù)下降沿(起始位0)后,拉高傳輸開始標(biāo)志位,并在第9個數(shù)據(jù)(終止位)的傳輸過程正中(數(shù)據(jù)比較穩(wěn)定)再將傳輸開始標(biāo)志位拉低,標(biāo)志傳輸結(jié)束
always@(posedge sys_clk or negedge sys_rst_n)begin
 ?if(!sys_rst_n)
 ? ?rx_en <= 1'b0;
 ?else begin 
 ? ?if(neg_uart_rxd ) ? ? ? ? ? ? ? ?
 ? ? ?rx_en <= 1'b1;
 ? ?//接收完第9個數(shù)據(jù)(終止位)將傳輸開始標(biāo)志位拉低,標(biāo)志傳輸結(jié)束,判斷高電平
 ? ?else if((bit_cnt == 4'd9) && (clk_cnt == BPS_CNT >> 1'b1) && (uart_rx_d3 == 1'b1) )
   rx_en <= 1'b0;
 ? ?else 
 ? ? ?rx_en <= rx_en; ? ? ?
 ?end
end
//當(dāng)數(shù)據(jù)傳輸?shù)浇K止位時,拉高傳輸完成標(biāo)志位,并將數(shù)據(jù)輸出
always@(posedge sys_clk or negedge sys_rst_n)begin
 ?if(!sys_rst_n)begin
 ? ?uart_rx_done <= 1'b0;
 ? ?uart_rx_data <= 8'd0;
 ?end ?
 ?//結(jié)束接收后,將接收到的數(shù)據(jù)輸出
 ?else if((bit_cnt == 4'd9) && (clk_cnt == BPS_CNT >> 1'd1) && (uart_rx_d3 == 1'b1))begin  
  uart_rx_done <= 1'b1; ? ? ? ? ? ? ? ? ?//僅僅拉高一個時鐘周期
 ? ?uart_rx_data <= uart_rx_data_reg; ?
 ?end ? ? ? ? ? ? ?
 ?else begin ? ? ? ? ?
 ? ?uart_rx_done <= 1'b0; ? ? ? ? ? ? ? ? ?//僅僅拉高一個時鐘周期
 ? ?uart_rx_data <= uart_rx_data;
 ?end
end
 
//時鐘每計數(shù)一個BPS_CNT(傳輸一位數(shù)據(jù)所需要的時鐘個數(shù)),即將數(shù)據(jù)計數(shù)器加1,并清零時鐘計數(shù)器
always@(posedge sys_clk or negedge sys_rst_n)begin
 ?if(!sys_rst_n)begin
 ? ?bit_cnt <= 4'd0;
 ? ?clk_cnt <= 32'd0;
 ?end
 ?else if(rx_en)begin ? ? ? ? ? ? ? ? ? ? ? ? ? ?//在接收狀態(tài)
 ? ?if(clk_cnt < BPS_CNT - 1'b1)begin ? ? ? ? ? ? ? ? //一個bit數(shù)據(jù)沒有接收完
 ? ? ?clk_cnt <= clk_cnt + 1'b1; ? ? ? ? ? ? ? ? ? ?//時鐘計數(shù)器+1
 ? ? ?bit_cnt <= bit_cnt; ? ? ? ? ? ? ? ? ? ? ? ? ? //bit計數(shù)器不變
 ? ?end ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 
 ? ?else begin ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//一個bit數(shù)據(jù)接收完了 ?
 ? ? ?clk_cnt <= 32'd0; ? ? ? ? ? ? ? ? ? ? ? ? ? ? //清空時鐘計數(shù)器,重新開始計時
 ? ? ?bit_cnt <= bit_cnt + 1'b1; ? ? ? ? ? ? ? ? ? ?//bit計數(shù)器+1,表示接收完了一個bit的數(shù)據(jù)
 ? ?end ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 
 ?end ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 
 ? ?else begin ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//不在接收狀態(tài)
 ? ? ?bit_cnt <= 4'd0; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//清零
 ? ? ?clk_cnt <= 32'd0; ? ? ? ? ? ? ? ? ? ? ? ? ? ? //清零
 ? ?end ? ?
end
 
endmodule

4.4、Testbench

仿真模塊的Testbench設(shè)計如下:

設(shè)定波特率230400(這樣的目的是為了更方便的觀察發(fā)送使能信號uart_tx_en)

定義一個任務(wù)task,該任務(wù)將輸入使用波特率230400一個bit一個bit的輸出,模擬上位機發(fā)送數(shù)據(jù)給FPGA

3000ns后,發(fā)送第1個隨機數(shù)據(jù)

發(fā)送完了第1個隨機數(shù)據(jù)后發(fā)送第2個隨機數(shù)據(jù),一共發(fā)送4個隨機數(shù)據(jù)

 
// *******************************************************************************************************
// ** 作者 : 孤獨的單刀                             
// ** 郵箱 : zachary_wu93@163.com
// ** 博客 : https://blog.csdn.net/wuzhikaidetb 
// ** 日期 : 2022/07/29 
// ** 功能 : 1、對基于FPGA的串口接收驅(qū)動模塊的測試testbench
//    2、通過構(gòu)建一個task來模擬上位機時序發(fā)送數(shù)據(jù)給串口接收驅(qū)動,觀察該模塊能否成功接收數(shù)據(jù)。
//    3、依次發(fā)送4個隨機的8bit數(shù)據(jù)                                                                       
// *******************************************************************************************************   
 
`timescale 1ns/1ns //定義時間刻度
 
//模塊、接口定義
module tb_uart_rx();
 
reg    sys_clk   ;   
reg    sys_rst_n  ;   
reg    uart_rxd  ;
 
wire    uart_rx_done ;  
wire [7:0] uart_rx_data ;
 
localparam integer BPS  = 'd230400    ; //波特率
localparam integer CLK_FRE = 'd50_000_000   ; //系統(tǒng)頻率50M
localparam integer CNT   = 1000_000_000 / BPS ; //計算出傳輸每個bit所需要的時間,單位:ns
 
 
//初始時刻定義
initial begin 
 $timeformat(-9, 0, " ns", 10); //定義時間顯示格式 
 sys_clk =1'b0; 
 sys_rst_n <=1'b0; ? ?
 ?uart_rxd <=1'b1;
 ?
 ?#20 //系統(tǒng)開始工作
 ?sys_rst_n <=1'b1;
 ?
 ?#3000
 ?rx_byte({$random} % 256); ? ?//生成8位隨機數(shù)1
 ?rx_byte({$random} % 256); ? ?//生成8位隨機數(shù)2
 ?rx_byte({$random} % 256); ? ? ? //生成8位隨機數(shù)3
 ?rx_byte({$random} % 256); ? ? ? //生成8位隨機數(shù)4 ?
 ?#60 ?$finish();
end
 
//每當(dāng)成功接收一個BYTE的數(shù)據(jù),就在測試端窗口打印出來
always @(posedge sys_clk)begin
 ?if(uart_rx_done)begin
 ? ?$display("@time%t", $time); ?
 ? ?$display("rx : 0x%h",uart_rx_data);
 ?end
end
 
//定義任務(wù),每次發(fā)送的數(shù)據(jù)10 位(起始位1+數(shù)據(jù)位8+停止位1)
task rx_byte(
 ?input [7:0] data
);
 ?integer i; //定義一個常量
 ?//用 for 循環(huán)產(chǎn)生一幀數(shù)據(jù),for 括號中最后執(zhí)行的內(nèi)容只能寫 i=i+1
 ?for(i=0; i<10; i=i+1) begin
 ? ?case(i)
 ? ?0: uart_rxd <= 1'b0; ? ?//起始位
 ? ?1: uart_rxd <= data[0]; ? ?//LSB
 ? ?2: uart_rxd <= data[1];
 ? ?3: uart_rxd <= data[2];
 ? ?4: uart_rxd <= data[3];
 ? ?5: uart_rxd <= data[4];
 ? ?6: uart_rxd <= data[5];
 ? ?7: uart_rxd <= data[6];
 ? ?8: uart_rxd <= data[7]; ? ?//MSB
 ? ?9: uart_rxd <= 1'b1; ? ?//停止位
 ? ?endcase
 ? ?#CNT; ? ? ? ? ? ? //每發(fā)送 1 位數(shù)據(jù)延時
 ?end ? ?
endtask ? ? ? ? ? ? ? //任務(wù)結(jié)束
 
//設(shè)置主時鐘
always #10 sys_clk <= ~sys_clk; ? ?//時鐘20ns,50M
 
//例化被測試的串口接收驅(qū)動
uart_rx
#(
 ?.BPS ? ? ?(BPS ? ? ?), ? ?
 ?.CLK_FRE ? ?(CLK_FRE ? ?) ? ? ?
)
uart_rx_inst(
 ?.sys_clk ? ?(sys_clk ? ?), ? ? ?
 ?.sys_rst_n ? ?(sys_rst_n ? ?), ? ? ?
 ?.uart_rxd ? ?(uart_rxd ? ?), ? ? ?
 ?.uart_rx_done ?(uart_rx_done ?), ? ?
 ?.uart_rx_data ?(uart_rx_data ?) ?
);
 
endmodule

4.5、仿真結(jié)果分析

仿真結(jié)果如下圖(注釋很詳細):

下圖中分別發(fā)送了4個數(shù)據(jù)8'h24--8'h81--8'h09--8'h63;接收模塊分別接收到了4個數(shù)據(jù)8'h24--8'h81--8'h09--8'h63。發(fā)送、接收數(shù)據(jù)一致。

ed5d6238-a9e9-11ee-8b88-92fbcf53809c.png

接收總體時序

下圖是第1次接收數(shù)據(jù)(8'h24,即00100100)是的時序圖。

ed79b01e-a9e9-11ee-8b88-92fbcf53809c.png

單個字節(jié)接收時序

4.6、上板測試

至此已經(jīng)順利完成了接收模塊的仿真驗證,接下來使用一塊Altera Cyclone IV E的開發(fā)板上板測試。

首先生成一個IP核--ISSP(In-System Sources and Probes),這個IP核可以提供一個輸出用來在線輸出,相當(dāng)于一個簡單的信號發(fā)生器--Source,此外還可以提供探針Probes來在線監(jiān)控信號的輸出。

在本次設(shè)計中,我們使用Probes來觀察串口接收數(shù)據(jù)。ISSP調(diào)用如下:

ed895686-a9e9-11ee-8b88-92fbcf53809c.png

編寫一個接收模塊驗證模塊,該模塊調(diào)用接收模塊,ISSP IP核。同時在電腦上使用串口調(diào)試軟件發(fā)送數(shù)據(jù),根據(jù)接收到的數(shù)據(jù)判斷串口接收模塊是否能成功工作。

接收模塊驗證模塊uart_rx_test代碼如下:

 
// *******************************************************************************************************
// ** 作者 : 孤獨的單刀                             
// ** 郵箱 : zachary_wu93@163.com
// ** 博客 : https://blog.csdn.net/wuzhikaidetb 
// ** 日期 : 2022/07/29 
// ** 功能 : 1、基于FPGA的串口接收驅(qū)動模塊的測試模塊;
//    2、例化串口接收驅(qū)動與ISSP IP核;
//    3、使用上位機發(fā)送隨機數(shù)據(jù)到FPGA,通過觀察ISSP監(jiān)測到的接收驅(qū)動接收的數(shù)據(jù)來進行測試。                                                                       
// *******************************************************************************************************   
 
module uart_rx_test
(
//系統(tǒng)接口
 input     sys_clk   ,
 input     sys_rst_n  , 
//UART接收線  
 input    uart_rxd     //接收數(shù)據(jù)線
); 
 
parameter integer BPS  = 'd230400  ;  //波特率230400
parameter integer CLK_FRE = 'd50_000_000 ;  //系統(tǒng)頻率50MHZ 
 
wire [7:0] uart_rx_data;
 
//例化接收模塊
uart_rx
#(
 .BPS   (BPS   ),
 .CLK_FRE  (CLK_FRE  )
) 
uart_rx_isnt
( 
 .sys_clk  (sys_clk  ),   
 .sys_rst_n  (sys_rst_n  ),   
 .uart_rxd  (uart_rxd  ),   
 .uart_rx_done (    ),   
 .uart_rx_data (uart_rx_data )   
);
 
//例化ISSP作為觀測手段
issp_uart_rx issp_uart_rx_inst
(
 .probe   (uart_rx_data ),   //觀測接收數(shù)據(jù)
 .source    (    )   
);
 
endmodule

下載程序后,在Quartus II中打開In-System Sources and Probes Editor,然后使用串口調(diào)試軟件發(fā)送數(shù)據(jù)0x55--0xaa--0x88(隨機選的3個),觀察 In-System Sources and Probes Editor中寄存器的值,分別如下:

ed97117c-a9e9-11ee-8b88-92fbcf53809c.png

eda8d4a2-a9e9-11ee-8b88-92fbcf53809c.png

edbec4a6-a9e9-11ee-8b88-92fbcf53809c.png

5、總結(jié)

串口作為一種常用的通信協(xié)議與調(diào)試手段,請一定要熟練掌握!

基于FPGA的串口實現(xiàn)不難,只要注意根據(jù)波特率合理設(shè)計計數(shù)器即可,在此計數(shù)器的調(diào)動下可以實現(xiàn)數(shù)據(jù)的發(fā)送與接收。

來源:CSDN博主「孤獨的單刀」的原創(chuàng)文章

審核編輯:湯梓紅

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • FPGA
    +關(guān)注

    關(guān)注

    1620

    文章

    21510

    瀏覽量

    598879
  • 接口
    +關(guān)注

    關(guān)注

    33

    文章

    8254

    瀏覽量

    149942
  • 計數(shù)器
    +關(guān)注

    關(guān)注

    32

    文章

    2241

    瀏覽量

    93969
  • 串口
    +關(guān)注

    關(guān)注

    14

    文章

    1533

    瀏覽量

    75452
  • uart
    +關(guān)注

    關(guān)注

    22

    文章

    1199

    瀏覽量

    100823

原文標(biāo)題:串口(UART)的FPGA實現(xiàn)(含源碼工程)

文章出處:【微信號:mcu168,微信公眾號:硬件攻城獅】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    IO模擬串口UART

    IO模擬串口UART 本文介紹GPIO模擬UART的算法和實現(xiàn)
    發(fā)表于 04-03 14:11 ?86次下載

    niosii的UART串口通信

    niosii的UART串口通信niosii的UART串口通信。
    發(fā)表于 04-06 17:03 ?1次下載

    UART串口在SIM卡讀寫中的應(yīng)用

    UART串口在SIM卡讀寫中的應(yīng)用,下來看看
    發(fā)表于 08-19 16:51 ?0次下載

    實驗五 UART串口通訊實驗

    UART串口通訊
    發(fā)表于 01-22 20:51 ?8次下載

    Uart串口與RS232串口的區(qū)別

    Uart這里指的是TTL電平的串口;RS232指的是RS232電平的串口。TTL電平串口是一般芯片的串口的輸入和輸出端,可以接不通的芯片完成
    發(fā)表于 11-20 17:48 ?6w次閱讀

    基于51單片機的UART串口通信

    基于51單片機的UART串口通信詳解。
    發(fā)表于 11-21 10:14 ?6.3w次閱讀
    基于51單片機的<b class='flag-5'>UART</b><b class='flag-5'>串口</b>通信

    uart是什么意思?認識uart串口

    設(shè)備沒有顯示屏,無法獲得嵌入式設(shè)備實時數(shù)據(jù)信息,通過UART串口和超級終端相連,打印嵌入式設(shè)備輸出信息。并且在對嵌入式系統(tǒng)進行跟蹤和調(diào)試時,UART串口了是必要的通信手段。比如:網(wǎng)絡(luò)路
    發(fā)表于 12-06 14:51 ?10.9w次閱讀
    <b class='flag-5'>uart</b>是什么意思?認識<b class='flag-5'>uart</b><b class='flag-5'>串口</b>

    UART串口WiFi模塊的工作原理及應(yīng)用

    隨著物聯(lián)網(wǎng)智能家居應(yīng)用的日漸豐富,越來越多的WiFi工程師開始更多的關(guān)注UART串口WiFi模塊,為讓新手工程師更快的將UART串口WiFi模塊應(yīng)用于各類智能家居應(yīng)用中,本篇SKYLA
    的頭像 發(fā)表于 01-14 09:27 ?1w次閱讀
    <b class='flag-5'>UART</b><b class='flag-5'>串口</b>WiFi模塊的工作原理及應(yīng)用

    UART串口WiFi模塊的工作原理及應(yīng)用

    隨著物聯(lián)網(wǎng)智能家居應(yīng)用的日漸豐富,越來越多的 WiFi 工程師開始更多的關(guān)注 UART 串口 WiFi 模塊,為讓新手工程師更快的將 UART 串口 WiFi 模塊應(yīng)用于各類智能家居應(yīng)
    發(fā)表于 01-08 08:00 ?23次下載
    <b class='flag-5'>UART</b><b class='flag-5'>串口</b>WiFi模塊的工作原理及應(yīng)用

    如何使用Jlink實現(xiàn)虛擬串口功能

    串口調(diào)試是單片機開發(fā)過程必不可少的一個功能,一般是使用一個UART-TTL的串口模塊來實現(xiàn)串口的功能,其實下載調(diào)試使用的Jlink仿真器也可
    發(fā)表于 06-04 17:52 ?11次下載
    如何使用Jlink<b class='flag-5'>實現(xiàn)</b>虛擬<b class='flag-5'>串口</b>功能

    C51的UART 串口通信

    C51的UART 串口通信
    發(fā)表于 11-29 12:21 ?11次下載
    C51的<b class='flag-5'>UART</b> <b class='flag-5'>串口</b>通信

    UART串口通訊

    UART串口通訊總結(jié)前言串口的基本概念串口配置的基本屬性串口(Serial port)和RS-232串口
    發(fā)表于 12-20 19:31 ?24次下載
    <b class='flag-5'>UART</b><b class='flag-5'>串口</b>通訊

    正點原子Mini Linux—UART串口簡單介紹

    UART串口一、I.MUX6ULL串口UART1、串口原理2、I.MUX6U的UART時鐘源設(shè)置
    發(fā)表于 01-12 20:14 ?0次下載
    正點原子Mini Linux—<b class='flag-5'>UART</b><b class='flag-5'>串口</b>簡單介紹

    單片機IO口模擬UART串口通信

    為了讓大家充分理解 UART 串口通信的原理,我們先把 P3.0 和 P3.1 當(dāng)做 IO 口來進行模擬實際串口通信的過程,原理搞懂后,我們再使用寄存器配置實現(xiàn)
    發(fā)表于 02-09 10:25 ?23次下載
    單片機IO口模擬<b class='flag-5'>UART</b><b class='flag-5'>串口</b>通信

    UART串口通信協(xié)議是什么?

    UART (Universal Asynchronous Receiver/Transmitter) 是一種通信接口協(xié)議,用于實現(xiàn)串口通信。它是一種簡單的、可靠的、廣泛應(yīng)用的串口通信協(xié)
    的頭像 發(fā)表于 03-19 17:26 ?955次閱讀