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