1、什么是串口(UART)?
串口作為常用的三大低速總線(UART、SPI、IIC)之一,在設(shè)計(jì)眾多通信接口和調(diào)試時(shí)占有重要地位。
串口(UART)全稱通用異步收發(fā)傳輸器(Universal Asynchronous Receiver/Transmitter),主要用于數(shù)據(jù)間的串行傳遞,是一種全雙工傳輸模式。
它在發(fā)送數(shù)據(jù)時(shí)將并行數(shù)據(jù)轉(zhuǎn)換成串行數(shù)據(jù)來(lái)傳輸,在接收數(shù)據(jù)時(shí)將接收到的串行數(shù)據(jù)轉(zhuǎn)換成并行數(shù)據(jù)。
“異步”兩個(gè)字即意味著在數(shù)據(jù)傳遞的兩個(gè)模塊之間使用的不是同步時(shí)鐘。
實(shí)際上在異步串口的傳輸中是不需要時(shí)鐘的,而是通過(guò)特定的時(shí)序來(lái)標(biāo)志傳輸?shù)拈_(kāi)始(起始位--由高到低)和結(jié)束(結(jié)束位,拉高)。
2、串口的組成
2.1、串口的物理層
UART 通信只有兩根信號(hào)線,一根是發(fā)送數(shù)據(jù)端口線叫 tx(Transmitter),一根是接收數(shù)據(jù)端口線叫 rx(Receiver)
如圖所示,對(duì)于 PC 來(lái)說(shuō)它的 tx 要和對(duì)于 FPGA來(lái)說(shuō)的 rx 連接,同樣 PC 的 rx 要和 FPGA 的 tx 連接,如果是兩個(gè) tx 或者兩個(gè) rx 連接那數(shù)據(jù)就不能正常被發(fā)送出去和接收到。
信號(hào)的傳輸由外部驅(qū)動(dòng)電路實(shí)現(xiàn)。電信號(hào)的傳輸過(guò)程有著不同的電平標(biāo)準(zhǔn)和接口規(guī)范,針對(duì)異步串行通信的接口標(biāo)準(zhǔn)有RS232、RS422、RS485等.
它們定義了接口不同的電氣特性,如RS-232是單端輸入輸 出,而RS-422/485為差分輸入輸出等。
傳輸距離較短時(shí)(不超過(guò)15m),RS232是串行通信最常用的接口標(biāo)準(zhǔn)。RS-232標(biāo)準(zhǔn)的串口最常見(jiàn)的接口類型為DB9,樣式如圖所示,工業(yè)控制領(lǐng)域中用到的工控機(jī)一般都配備多個(gè)串口,很多老式臺(tái)式機(jī)也都配有串口。
但是筆記本電腦以及較新一點(diǎn) 的臺(tái)式機(jī)都沒(méi)有串口,它們一般通過(guò)USB轉(zhuǎn)串口線來(lái)實(shí)現(xiàn)與外部設(shè)備的串口通信。
DB9接口定義以及各引腳功能說(shuō)明如圖所示,我們一般只用到其中的2(RXD)、3 (TXD)、5(GND)引腳,其他引腳在普通串口模式下一般不使用:
2.2、UART協(xié)議
UART 在發(fā)送或接收過(guò)程中的一幀數(shù)據(jù)由4部分組成,起始位、數(shù)據(jù)位、奇偶校驗(yàn)位和停止位,如圖所示。
其中,起始位標(biāo)志著一幀數(shù)據(jù)的開(kāi)始,停止位標(biāo)志著一幀數(shù)據(jù)的結(jié)束,數(shù)據(jù)位是一幀數(shù)據(jù)中的有效數(shù)據(jù)。
校驗(yàn)位分為奇校驗(yàn)和偶校驗(yàn),用于檢驗(yàn)數(shù)據(jù)在傳輸過(guò)程中是否出錯(cuò)。
奇校驗(yàn)時(shí),發(fā)送方應(yīng)使數(shù)據(jù)位中1的個(gè)數(shù)與校驗(yàn)位中1的個(gè)數(shù)之和為奇數(shù);接收方在接收數(shù)據(jù)時(shí), 對(duì)1的個(gè)數(shù)進(jìn)行檢查.
若不為奇數(shù),則說(shuō)明數(shù)據(jù)在傳輸過(guò)程中出了差錯(cuò)。同樣,偶校驗(yàn)則檢查1的個(gè)數(shù)是否為偶數(shù)。關(guān)于奇偶校驗(yàn)可參考:Verilgo實(shí)現(xiàn)的FPGA奇偶校驗(yàn)
UART通信過(guò)程中的數(shù)據(jù)格式及傳輸速率是可設(shè)置的,為了正確的通信,收發(fā)雙方應(yīng)約定并遵循同樣的設(shè)置。
數(shù)據(jù)位可選擇為5、6、7、8位,其中8位數(shù)據(jù)位是最常用的,在實(shí)際應(yīng)用中一般都選擇8位數(shù)據(jù)位;校驗(yàn)位可選擇奇校驗(yàn)、偶校驗(yàn)或者無(wú)校驗(yàn)位;停止位可選擇1位(默認(rèn)), 1.5或2位。
串口通信的速率用波特率表示,它表示每秒傳輸二進(jìn)制數(shù)據(jù)的位數(shù),單位是bps(位 /秒),常用的波特率有9600、19200、38400、57600以及115200等。
如波特率9600則代表每秒傳輸9600bit數(shù)據(jù),以串口發(fā)送1個(gè)字節(jié)10bit算(起始位1bit+數(shù)據(jù)8bit+停止位1bit+NO校驗(yàn)位),則傳輸1個(gè)字節(jié)需要的時(shí)間是1*10/9600秒。
3、串口發(fā)送模塊
3.1、接口定義與整體設(shè)計(jì)
發(fā)送模塊整體框圖、輸入輸出信號(hào)如下所示:
其中信號(hào)端口如下:
需要說(shuō)明的是,uart_tx_data為需要發(fā)送的一個(gè)字節(jié)的數(shù)據(jù),uart_tx_en為發(fā)送使能位,當(dāng)其拉高,則代表此時(shí)通過(guò)串口發(fā)送數(shù)據(jù)線發(fā)送數(shù)據(jù)uart_tx_data。
3.2、設(shè)計(jì)思路
該模塊支持任意波特率(理論上)的發(fā)送,但需要在使用該模塊時(shí)使用參數(shù)將其例化,數(shù)據(jù)位8位,起始位和停止位各1位,無(wú)奇偶校驗(yàn)
當(dāng)使能信號(hào)有效后拉高發(fā)送標(biāo)志信號(hào),標(biāo)志模塊進(jìn)入發(fā)送過(guò)程;當(dāng)發(fā)送完10個(gè)bit后,拉低發(fā)送標(biāo)志信號(hào),標(biāo)志發(fā)送過(guò)程結(jié)束。使能信號(hào)有效時(shí)將要發(fā)送的數(shù)據(jù)寄存。
假設(shè)波特率為9600,則發(fā)送一個(gè)bit的時(shí)間為1s/9600,一個(gè)數(shù)據(jù)的傳輸共10bit(數(shù)據(jù)位8位,起始位和停止位各1位),則共需要1s/9600;
假設(shè)系統(tǒng)時(shí)鐘為50MHz(參數(shù)化以便適應(yīng)不同的系統(tǒng)頻率),則其周期為20ns,那么發(fā)送一個(gè)bit所需要的系統(tǒng)周期數(shù)為(1s/9600)/ 20ns ≈ 5208(個(gè))。
在發(fā)送過(guò)程中使用一個(gè)計(jì)數(shù)器計(jì)數(shù),計(jì)數(shù)區(qū)間為(0~5208-1),這樣的區(qū)間一共10個(gè)(一個(gè)字節(jié)需要發(fā)送10個(gè)bit);
此外還需一個(gè)計(jì)數(shù)器對(duì)發(fā)送的bit數(shù)計(jì)數(shù)(每當(dāng)上一個(gè)計(jì)數(shù)器計(jì)數(shù)到5207則表示發(fā)送完了一個(gè)bit),計(jì)數(shù)區(qū)間(0~9)
在發(fā)送過(guò)程,根據(jù)計(jì)數(shù)器的值(發(fā)送bit計(jì)數(shù)器),對(duì)發(fā)送數(shù)據(jù)線進(jìn)行操作。
若發(fā)送bit計(jì)數(shù)器 = 0,則代表此時(shí)需要發(fā)送起始位;
若發(fā)送bit計(jì)數(shù)器 = 1,則代表此時(shí)需要發(fā)送發(fā)送數(shù)據(jù)的最低位LSB(數(shù)據(jù)的發(fā)送總是低位在前,高位在后);
······
若發(fā)送bit計(jì)數(shù)器 = 8,則代表此時(shí)需要發(fā)送發(fā)送數(shù)據(jù)的最高位MSB;
若發(fā)送bit計(jì)數(shù)器 = 9,則代表此時(shí)需要發(fā)送停止位;
發(fā)送數(shù)據(jù)線在不處于發(fā)送狀態(tài)時(shí)需拉高,以滿足UART時(shí)序的空閑狀態(tài)
3.3、Verilg代碼
根據(jù)上述設(shè)計(jì)思路,部分發(fā)送模塊代碼如下:
// ******************************************************************************************************* // ** 作者 : 孤獨(dú)的單刀 // ** 郵箱 : zachary_wu93@163.com // ** 博客 : https://blog.csdn.net/wuzhikaidetb // ** 日期 : 2022/07/31 // ** 功能 : 1、基于FPGA的串口發(fā)送驅(qū)動(dòng)模塊; // 2、可設(shè)置波特率BPS、主時(shí)鐘CLK_FRE; // 3、起始位1bit,數(shù)據(jù)位8bit,停止位1bit,無(wú)奇偶校驗(yàn); // 4、每發(fā)送1個(gè)字節(jié)后拉高uart_tx_done一個(gè)周期,可用于后續(xù)發(fā)送多字節(jié)模塊。 // ******************************************************************************************************* module uart_tx #( parameter integer BPS = 9_600 , //發(fā)送波特率 parameter integer CLK_FRE = 50_000_000 //主時(shí)鐘頻率 ) ( //系統(tǒng)接口 input sys_clk , //系統(tǒng)時(shí)鐘 input sys_rst_n , //系統(tǒng)復(fù)位,低電平有效 //用戶接口 input [7:0] uart_tx_data , //需要通過(guò)UART發(fā)送的數(shù)據(jù),在uart_tx_en為高電平時(shí)有效 input uart_tx_en , //發(fā)送有效,當(dāng)其為高電平時(shí),代表此時(shí)需要發(fā)送的數(shù)據(jù)有效 //UART發(fā)送 output reg uart_tx_done , //成功發(fā)送1BYTE數(shù)據(jù)后拉高一個(gè)周期 output reg uart_txd //UART發(fā)送數(shù)據(jù)線tx ); //當(dāng)發(fā)送使能信號(hào)到達(dá)時(shí),寄存待發(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ā)送使能信號(hào)到達(dá)時(shí),進(jìn)入發(fā)送過(guò)程 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ā)送信號(hào)有效則進(jìn)入發(fā)送過(guò)程 ?//發(fā)送完了最后一個(gè)數(shù)據(jù)則退出發(fā)送過(guò)程 ? ? ?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ā)送完畢信號(hào)一個(gè)周期,指示一個(gè)字節(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ā)送完畢信號(hào)一個(gè)周期 ? ? ?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 //進(jìn)入發(fā)送過(guò)程后,啟動(dòng)時(shí)鐘計(jì)數(shù)器與發(fā)送個(gè)數(shù)bit計(jì)數(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 ? ? ? ? ? ?//一個(gè)bit數(shù)據(jù)沒(méi)有發(fā)送完 ? ? ?clk_cnt <= clk_cnt + 1'b1; ? ? ? ? ? ? ?//時(shí)鐘計(jì)數(shù)器+1 ? ? ?bit_cnt <= bit_cnt; ? ? ? ? ? ? ? ? ?//bit計(jì)數(shù)器不變 ? ?end ? ? ? ? ? ? ?else begin ? ? ? ? ? ? ? ? ? ? ? ?//一個(gè)bit數(shù)據(jù)發(fā)送完了 ? ? ? ?clk_cnt <= 32'd0; ? ? ? ? ? ? ? ? ?//清空時(shí)鐘計(jì)數(shù)器,重新開(kāi)始計(jì)時(shí) ? ? ?bit_cnt <= bit_cnt+1'b1; ? ? ? ? ? ? ?//bit計(jì)數(shù)器+1,表示發(fā)送完了一個(gè)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è)計(jì)如下:
設(shè)定波特率230400(這樣的目的是為了更方便的觀察發(fā)送使能信號(hào)uart_tx_en,節(jié)約時(shí)間)
3000ns后,拉高發(fā)送使能信號(hào)uart_tx_en一個(gè)周期,同時(shí)生成1個(gè)8bit的隨機(jī)數(shù)據(jù)給uart_tx_data作為要發(fā)送的數(shù)據(jù)
觀察UART上TX線的時(shí)序是否滿足要求
// ******************************************************************************************************* // ** 作者 : 孤獨(dú)的單刀 // ** 郵箱 : zachary_wu93@163.com // ** 博客 : https://blog.csdn.net/wuzhikaidetb // ** 日期 : 2022/07/29 // ** 功能 : 1、對(duì)基于FPGA的串口發(fā)送驅(qū)動(dòng)模塊的測(cè)試testbench // 2、發(fā)送一個(gè)8bit的隨機(jī)數(shù)據(jù),觀測(cè)其波形是否符合UART時(shí)序 // ******************************************************************************************************* `timescale 1ns/1ns //定義時(shí)間刻度 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 ; //計(jì)算出傳輸每個(gè)bit所需要的時(shí)間 initial begin sys_clk <=1'b0; ? ?sys_rst_n <=1'b0; ? ? ?uart_tx_en <=1'b0; ?uart_tx_data <=8'd0; ? ? ? ? ?#80 ? ? ? ? ? ? ? ? ? ? //系統(tǒng)開(kāi)始工作 ? ?sys_rst_n <=1'b1; ? ? ?#200 ? ?@(posedge sys_clk); ? ?uart_tx_en <=1'b1; ? ? ?uart_tx_data <= ({$random} % 256); ? ?//發(fā)送8位隨機(jī)數(shù)據(jù) ?#20 ? ? ?uart_tx_en <=1'b0; ? ?#(BIT_TIME * 10) ? ? ? ? ? ? ?//發(fā)送1個(gè)BYTE需要10個(gè)bit ?#200 $finish; ? ? ? ? ? ? ? ?//結(jié)束仿真 end always #10 sys_clk=~sys_clk; ? ? ? ? ?//定義主時(shí)鐘,周期20ns,頻率50M //例化發(fā)送驅(qū)動(dòng)模塊 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é)果如下圖(注釋很詳細(xì)):
下圖中可以看到發(fā)送模塊發(fā)送了1個(gè)數(shù)據(jù)8'h24,一段時(shí)間后發(fā)送結(jié)束,并無(wú)法直接觀察發(fā)送線TX上的時(shí)序。
整體仿真時(shí)序
下圖中可以看到發(fā)送模塊發(fā)送了1個(gè)數(shù)據(jù)8'h24,一段時(shí)間后發(fā)送結(jié)束,且可以看到發(fā)送線TX在以符合UART時(shí)序的方式發(fā)送數(shù)據(jù)00100100(低位在前、高位在后),即8'h24。
單次發(fā)送時(shí)序
可以看到仿真結(jié)果是符合預(yù)期設(shè)計(jì)要求的。
3.6、上板實(shí)測(cè)
至此已經(jīng)順利完成了發(fā)送模塊的仿真驗(yàn)證,接下來(lái)使用一塊Altera Cyclone IV E的開(kāi)發(fā)板上板實(shí)測(cè)。
編寫(xiě)一個(gè)發(fā)送模塊測(cè)試模塊,該模塊調(diào)用串口發(fā)送模塊,并按一定間隔(默認(rèn)1s)拉高發(fā)送使能信號(hào)和生成發(fā)送數(shù)據(jù),發(fā)送數(shù)據(jù)從0x01開(kāi)始累加1,直到0xFF(溢出到0x00)。
同時(shí)在電腦上使用串口調(diào)試軟件接收發(fā)送過(guò)來(lái)的數(shù)據(jù)。根據(jù)串口調(diào)試軟件接收到的數(shù)據(jù)判斷串口發(fā)送模塊是否能成功工作。
發(fā)送模塊驗(yàn)證模塊代碼如下:
// ******************************************************************************************************* // ** 作者 : 孤獨(dú)的單刀 // ** 郵箱 : zachary_wu93@163.com // ** 博客 : https://blog.csdn.net/wuzhikaidetb // ** 日期 : 2022/07/29 // ** 功能 : 1、基于FPGA的串口發(fā)送驅(qū)動(dòng)模塊的測(cè)試模塊; // 2、每個(gè)1s發(fā)送1個(gè)遞增1的數(shù)據(jù)到上位機(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)其為高電平時(shí),代表此時(shí)需要發(fā)送數(shù)據(jù) reg [7:0] uart_tx_data; //需要通過(guò)UART發(fā)送的數(shù)據(jù),在uart_tx_en為高電平時(shí)有效 //1s計(jì)數(shù)模塊,每隔1s發(fā)送一個(gè)數(shù)據(jù)和拉高發(fā)送使能信號(hào)一次;數(shù)據(jù)從0開(kāi)始遞增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······。說(shuō)明我們的發(fā)送模塊工作正常。
4、串口接收模塊
4.1、接口定義與整體設(shè)計(jì)
接收模塊整體框圖、輸入輸出信號(hào)如下所示:
其中信號(hào)描述如下:
需要說(shuō)明的是,uart_rx_data為接收的一個(gè)字節(jié)的數(shù)據(jù),uart_rx_done為接收完成標(biāo)志位,當(dāng)其拉高,則代表此時(shí)接收到的串口數(shù)據(jù)uart_rx_data有效。
4.2、設(shè)計(jì)思路
該模塊支持任意波特率(理論上)的接收,但需要在使用該模塊時(shí)使用參數(shù)將其例化,數(shù)據(jù)位8位,起始位和停止位各1位,無(wú)奇偶校驗(yàn)
串口的傳輸是以起始位開(kāi)始的,而起始位是將數(shù)據(jù)線拉低 ,所以我們需要捕捉數(shù)據(jù)線的下降沿,將接收數(shù)據(jù)線打拍3次,捕捉其下降沿。
當(dāng)捕捉到接收數(shù)據(jù)線的下降沿,拉高接收標(biāo)志信號(hào),標(biāo)志模塊進(jìn)入接收過(guò)程;當(dāng)接收完10個(gè)bit后,拉低接收標(biāo)志信號(hào),標(biāo)志接收過(guò)程結(jié)束
假設(shè)波特率為9600,則傳輸一個(gè)bit的時(shí)間為1s/9600,一個(gè)數(shù)據(jù)的傳輸共10bit(數(shù)據(jù)位8位,起始位和停止位各1位),則共需要1s/960;
假設(shè)系統(tǒng)時(shí)鐘為50MHz(參數(shù)化以便適應(yīng)不同的系統(tǒng)頻率),則其周期為20ns,那么傳輸一個(gè)bit所需要的系統(tǒng)周期數(shù)為(1s/960)/ 20ns ≈ 5208(個(gè))。
在接收過(guò)程中使用一個(gè)計(jì)數(shù)器計(jì)數(shù),計(jì)數(shù)區(qū)間為(0~5208-1),這樣的區(qū)間一共10個(gè)(一個(gè)字節(jié)需要傳輸10個(gè)bit);
此外還需一個(gè)計(jì)數(shù)器對(duì)接收的bit數(shù)計(jì)數(shù)(每當(dāng)上一個(gè)計(jì)數(shù)器計(jì)數(shù)到5207則表示接收完了一個(gè)bit),計(jì)數(shù)區(qū)間(0~9)。
在接收過(guò)程,根據(jù)計(jì)數(shù)器的值(接收bit計(jì)數(shù)器),在每個(gè)bit計(jì)數(shù)器的中間接收數(shù)據(jù),將其移位寄存(在電平中間數(shù)據(jù)最穩(wěn)定)
若接收bit計(jì)數(shù)器 = 0,則代表是起始位,不需要接收
若接收bit計(jì)數(shù)器 = 1,則代表此時(shí)接收到數(shù)據(jù)的最低位LSB(數(shù)據(jù)的傳輸總是低位在前,高位在后),將其賦值給寄存數(shù)據(jù)的最低位;
······
若接收bit計(jì)數(shù)器 = 8,則代表此時(shí)接收到數(shù)據(jù)的最高位MSB,將其賦值給寄存數(shù)據(jù)的最高位;
若接收bit計(jì)數(shù)器 = 9,則代表是停止位,不需要接收
4.3、Verilg代碼
根據(jù)上述設(shè)計(jì)思路,部分代碼如下:
// ******************************************************************************************************* // ** 作者 : 孤獨(dú)的單刀 // ** 郵箱 : zachary_wu93@163.com // ** 博客 : https://blog.csdn.net/wuzhikaidetb // ** 日期 : 2022/08/05 // ** 功能 : 1、基于FPGA的串口接收驅(qū)動(dòng)模塊; // 2、可重新設(shè)置波特率BPS、主時(shí)鐘CLK_FRE; // 3、起始位1bit,數(shù)據(jù)位8bit,停止位1bit,無(wú)奇偶校驗(yàn)。 // ******************************************************************************************************* module uart_rx #( parameter integer BPS = 9_600 , //發(fā)送波特率 parameter integer CLK_FRE = 50_000_000 //輸入時(shí)鐘頻率 ) ( //系統(tǒng)接口 input sys_clk , //50M系統(tǒng)時(shí)鐘 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í),代表接收數(shù)據(jù)有效 output reg [7:0] uart_rx_data //接收到的數(shù)據(jù),在uart_rx_done為高電平時(shí)有效 ); assign neg_uart_rxd = uart_rx_d3 & (~uart_rx_d2); //捕獲數(shù)據(jù)線的下降沿,用來(lái)標(biāo)志數(shù)據(jù)傳輸開(kāi)始 //將數(shù)據(jù)線打3拍,作用1:同步不同時(shí)鐘域信號(hào),防止亞穩(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)后,拉高傳輸開(kāi)始標(biāo)志位,并在第9個(gè)數(shù)據(jù)(終止位)的傳輸過(guò)程正中(數(shù)據(jù)比較穩(wěn)定)再將傳輸開(kāi)始標(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個(gè)數(shù)據(jù)(終止位)將傳輸開(kāi)始標(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止位時(shí),拉高傳輸完成標(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; ? ? ? ? ? ? ? ? ?//僅僅拉高一個(gè)時(shí)鐘周期 ? ?uart_rx_data <= uart_rx_data_reg; ? ?end ? ? ? ? ? ? ? ?else begin ? ? ? ? ? ? ?uart_rx_done <= 1'b0; ? ? ? ? ? ? ? ? ?//僅僅拉高一個(gè)時(shí)鐘周期 ? ?uart_rx_data <= uart_rx_data; ?end end //時(shí)鐘每計(jì)數(shù)一個(gè)BPS_CNT(傳輸一位數(shù)據(jù)所需要的時(shí)鐘個(gè)數(shù)),即將數(shù)據(jù)計(jì)數(shù)器加1,并清零時(shí)鐘計(jì)數(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 ? ? ? ? ? ? ? ? //一個(gè)bit數(shù)據(jù)沒(méi)有接收完 ? ? ?clk_cnt <= clk_cnt + 1'b1; ? ? ? ? ? ? ? ? ? ?//時(shí)鐘計(jì)數(shù)器+1 ? ? ?bit_cnt <= bit_cnt; ? ? ? ? ? ? ? ? ? ? ? ? ? //bit計(jì)數(shù)器不變 ? ?end ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?else begin ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//一個(gè)bit數(shù)據(jù)接收完了 ? ? ? ?clk_cnt <= 32'd0; ? ? ? ? ? ? ? ? ? ? ? ? ? ? //清空時(shí)鐘計(jì)數(shù)器,重新開(kāi)始計(jì)時(shí) ? ? ?bit_cnt <= bit_cnt + 1'b1; ? ? ? ? ? ? ? ? ? ?//bit計(jì)數(shù)器+1,表示接收完了一個(gè)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è)計(jì)如下:
設(shè)定波特率230400(這樣的目的是為了更方便的觀察發(fā)送使能信號(hào)uart_tx_en)
定義一個(gè)任務(wù)task,該任務(wù)將輸入使用波特率230400一個(gè)bit一個(gè)bit的輸出,模擬上位機(jī)發(fā)送數(shù)據(jù)給FPGA
3000ns后,發(fā)送第1個(gè)隨機(jī)數(shù)據(jù)
發(fā)送完了第1個(gè)隨機(jī)數(shù)據(jù)后發(fā)送第2個(gè)隨機(jī)數(shù)據(jù),一共發(fā)送4個(gè)隨機(jī)數(shù)據(jù)
// ******************************************************************************************************* // ** 作者 : 孤獨(dú)的單刀 // ** 郵箱 : zachary_wu93@163.com // ** 博客 : https://blog.csdn.net/wuzhikaidetb // ** 日期 : 2022/07/29 // ** 功能 : 1、對(duì)基于FPGA的串口接收驅(qū)動(dòng)模塊的測(cè)試testbench // 2、通過(guò)構(gòu)建一個(gè)task來(lái)模擬上位機(jī)時(shí)序發(fā)送數(shù)據(jù)給串口接收驅(qū)動(dòng),觀察該模塊能否成功接收數(shù)據(jù)。 // 3、依次發(fā)送4個(gè)隨機(jī)的8bit數(shù)據(jù) // ******************************************************************************************************* `timescale 1ns/1ns //定義時(shí)間刻度 //模塊、接口定義 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 ; //計(jì)算出傳輸每個(gè)bit所需要的時(shí)間,單位:ns //初始時(shí)刻定義 initial begin $timeformat(-9, 0, " ns", 10); //定義時(shí)間顯示格式 sys_clk =1'b0; sys_rst_n <=1'b0; ? ? ?uart_rxd <=1'b1; ? ?#20 //系統(tǒng)開(kāi)始工作 ?sys_rst_n <=1'b1; ? ?#3000 ?rx_byte({$random} % 256); ? ?//生成8位隨機(jī)數(shù)1 ?rx_byte({$random} % 256); ? ?//生成8位隨機(jī)數(shù)2 ?rx_byte({$random} % 256); ? ? ? //生成8位隨機(jī)數(shù)3 ?rx_byte({$random} % 256); ? ? ? //生成8位隨機(jī)數(shù)4 ? ?#60 ?$finish(); end //每當(dāng)成功接收一個(gè)BYTE的數(shù)據(jù),就在測(cè)試端窗口打印出來(lái) 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; //定義一個(gè)常量 ?//用 for 循環(huán)產(chǎn)生一幀數(shù)據(jù),for 括號(hào)中最后執(zhí)行的內(nèi)容只能寫(xiě) 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ù)延時(shí) ?end ? ? endtask ? ? ? ? ? ? ? //任務(wù)結(jié)束 //設(shè)置主時(shí)鐘 always #10 sys_clk <= ~sys_clk; ? ?//時(shí)鐘20ns,50M //例化被測(cè)試的串口接收驅(qū)動(dòng) 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é)果如下圖(注釋很詳細(xì)):
下圖中分別發(fā)送了4個(gè)數(shù)據(jù)8'h24--8'h81--8'h09--8'h63;接收模塊分別接收到了4個(gè)數(shù)據(jù)8'h24--8'h81--8'h09--8'h63。發(fā)送、接收數(shù)據(jù)一致。
接收總體時(shí)序
下圖是第1次接收數(shù)據(jù)(8'h24,即00100100)是的時(shí)序圖。
單個(gè)字節(jié)接收時(shí)序
4.6、上板測(cè)試
至此已經(jīng)順利完成了接收模塊的仿真驗(yàn)證,接下來(lái)使用一塊Altera Cyclone IV E的開(kāi)發(fā)板上板測(cè)試。
首先生成一個(gè)IP核--ISSP(In-System Sources and Probes),這個(gè)IP核可以提供一個(gè)輸出用來(lái)在線輸出,相當(dāng)于一個(gè)簡(jiǎn)單的信號(hào)發(fā)生器--Source,此外還可以提供探針Probes來(lái)在線監(jiān)控信號(hào)的輸出。
在本次設(shè)計(jì)中,我們使用Probes來(lái)觀察串口接收數(shù)據(jù)。ISSP調(diào)用如下:
編寫(xiě)一個(gè)接收模塊驗(yàn)證模塊,該模塊調(diào)用接收模塊,ISSP IP核。同時(shí)在電腦上使用串口調(diào)試軟件發(fā)送數(shù)據(jù),根據(jù)接收到的數(shù)據(jù)判斷串口接收模塊是否能成功工作。
接收模塊驗(yàn)證模塊uart_rx_test代碼如下:
// ******************************************************************************************************* // ** 作者 : 孤獨(dú)的單刀 // ** 郵箱 : zachary_wu93@163.com // ** 博客 : https://blog.csdn.net/wuzhikaidetb // ** 日期 : 2022/07/29 // ** 功能 : 1、基于FPGA的串口接收驅(qū)動(dòng)模塊的測(cè)試模塊; // 2、例化串口接收驅(qū)動(dòng)與ISSP IP核; // 3、使用上位機(jī)發(fā)送隨機(jī)數(shù)據(jù)到FPGA,通過(guò)觀察ISSP監(jiān)測(cè)到的接收驅(qū)動(dòng)接收的數(shù)據(jù)來(lái)進(jìn)行測(cè)試。 // ******************************************************************************************************* 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作為觀測(cè)手段 issp_uart_rx issp_uart_rx_inst ( .probe (uart_rx_data ), //觀測(cè)接收數(shù)據(jù) .source ( ) ); endmodule
下載程序后,在Quartus II中打開(kāi)In-System Sources and Probes Editor,然后使用串口調(diào)試軟件發(fā)送數(shù)據(jù)0x55--0xaa--0x88(隨機(jī)選的3個(gè)),觀察 In-System Sources and Probes Editor中寄存器的值,分別如下:
5、總結(jié)
串口作為一種常用的通信協(xié)議與調(diào)試手段,請(qǐng)一定要熟練掌握!
基于FPGA的串口實(shí)現(xiàn)不難,只要注意根據(jù)波特率合理設(shè)計(jì)計(jì)數(shù)器即可,在此計(jì)數(shù)器的調(diào)動(dòng)下可以實(shí)現(xiàn)數(shù)據(jù)的發(fā)送與接收。
來(lái)源:CSDN博主「孤獨(dú)的單刀」的原創(chuàng)文章
審核編輯:湯梓紅
-
FPGA
+關(guān)注
關(guān)注
1629文章
21729瀏覽量
602977 -
接口
+關(guān)注
關(guān)注
33文章
8575瀏覽量
151014 -
計(jì)數(shù)器
+關(guān)注
關(guān)注
32文章
2256瀏覽量
94476 -
串口
+關(guān)注
關(guān)注
14文章
1551瀏覽量
76421 -
uart
+關(guān)注
關(guān)注
22文章
1235瀏覽量
101352
原文標(biāo)題:串口(UART)的FPGA實(shí)現(xiàn)(含源碼工程)
文章出處:【微信號(hào):mcu168,微信公眾號(hào):硬件攻城獅】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論