2.3 基於FPGA的UART協議實現(三)簡單UART傳輸FPGA實現

2.3.3 簡單UART傳輸FPGA實現

在這裏插入圖片描述

              圖2 34 FPGA發送一幀串口數據(考慮波特率)
  如果圖2 34考慮 115200 的波特率,結果如圖2 34所示,每一位數據都保持 434 個時鐘,爲此 Verilog 可以這樣表示,如代碼2 11所示:
                      代碼2 11

1.	reg [10:0]D1;  
2.	reg [8:0]C1;  
3.	always @( posedge CLOCK)  
4.	      case( i)  
5.	      0,1,2,3,4,5,6,7,8,9,10:  
6.	      if( C1 == 9’ d434 -1 ) begin C1 <= 9’ d0; i <= i + 1’ b1; endelse begin TXD <= D1[i]; C1 <= C1 + 1'b1; end  
7.	......  
8.	endcase  

  如代碼2 11所示,步驟 1~8 不再保持一個時鐘,換之每個步驟都保持 434 個時鐘,因此每位 TXD 的發送數據也保持 8.68us。
  除此此外,串口傳輸協議不僅可以自定義波特率,串口傳輸協議也可以自定義一幀數據的位寬,自定義內容如表2 8所示:
                    表2 8 自定義一幀數據

在這裏插入圖片描述
  如表2 8所示,可以自定義的數據其中便包含數據位,默認下爲 1 字節,自定義內容則是 5~9 位,校驗位也可以設置爲有或者無( 默認下是有),停止位也可以增至 2 位(默認下是 1 位)。
在這裏插入圖片描述
                圖2 35 TX 功能模塊的建模圖
  如圖2 35所示,該模塊的左方有問答信號,還有 8 位的 iData,至於右方則是 TXD 頂層信號。此外,一幀數據的波特率爲 115200 bps。
                  代碼 2 12 TX 功能模塊代碼

1.	//****************************************************************************//  
2.	//# @Author: 碎碎思  
3.	//# @Date:   2019-04-21 21:14:51  
4.	//# @Last Modified by:   zlk  
5.	//# @WeChat Official Account: OpenFPGA  
6.	//# @Last Modified time: 2019-08-04 02:48:17  
7.	//# Description:   
8.	//# @Modification History: 2014-05-10 13:42:27  
9.	//# Date                By             Version             Change Description:   
10.	//# ========================================================================= #  
11.	//# 2014-05-10 13:42:27  
12.	//# ========================================================================= #  
13.	//# |                                                                       | #  
14.	//# |                                OpenFPGA                               | #  
15.	//****************************************************************************//  
16.	module tx_funcmod  
17.	(  
18.	    input CLOCK, RESET,  
19.	     output TXD,  
20.	     input iCall,  
21.	     output oDone,  
22.	     input [7:0]iData  
23.	);  
24.	     parameter B115K2 = 9'd434; // formula : ( 1/115200 )/( 1/50E+6 )      
25.	  
26.	     reg [3:0]i;  
27.	     reg [8:0]C1;  
28.	     reg [10:0]D1;  
29.	     reg rTXD;  
30.	     reg isDone;  
31.	       
32.	     always @( posedge CLOCK or negedge RESET )  
33.	         if( !RESET )  
34.	              begin  
35.	                  i <= 4'd0;  
36.	                  C1 <= 9'd0;  
37.	                   D1 <= 11'd0;  
38.	                     rTXD <= 1'b1;   
39.	                     isDone <= 1'b0;  
40.	              end  
41.	          else if( iCall )  
42.	              case( i )  
43.	                  
44.	                    0:  
45.	                     begin D1 <= { 2'b11 , iData , 1'b0 }; i <= i + 1'b1; end  
46.	                       
47.	                     1,2,3,4,5,6,7,8,9,10,11:       
48.	                     if( C1 == B115K2 -1 ) begin C1 <= 8'd0; i <= i + 1'b1; end  
49.	                     else begin rTXD <= D1[i - 1]; C1 <= C1 + 1'b1; end  
50.	  
51.	                    12:  
52.	                     begin isDone <= 1'b1; i <= i + 1'b1; end  
53.	                       
54.	                     13:  
55.	                     begin isDone <= 1'b0; i <= 4'd0; end  
56.	                  
57.	                endcase  
58.	      
59.	    assign TXD = rTXD;  
60.	    assign oDone = isDone;  
61.	      
62.	endmodule 

  第16~24行爲相關的出入端聲明,第 9 行則是波特率爲 115200 的常量聲明。
  第26~40行爲相關的寄存器聲明以及復位操作。
  第41~62行爲部分核心操作。第 41 行的 if( iCall ) 表示該模塊不使能就不工作。步驟 0 用來準備發送數據,其中 2’b11 是停止位與校驗位(隨便填),1’b0 則是起始位。步驟 1~11用來發送一幀數據。步驟 12~13 用來反饋完成信號並返回步驟。
  這樣發送模塊就完成了,理想時序如下圖所示:
在這裏插入圖片描述
              圖2 36 串口發送模塊功能邏輯示意圖
  由上圖可知,串口發送模塊是“定時發送”的過程。波特率模塊產生的時間間隔是通過計數器實現的,由圖2 37可知,每隔一定時間波特率模塊就會產生一個高脈衝給TX_Pin_Out引腳。
  串口發送的框圖如圖2 37所示:
在這裏插入圖片描述
                  圖2 37 串口模塊RTL框圖
  對於FPGA實現UART的RX模塊功能主要就是電平採集。那麼它到底是如何實現採集的呢?
在這裏插入圖片描述
                  圖 2 38 RX模塊電平採集示意圖
  說到底,在發送模塊中一位數據發送的時間間隔是通過波特率進行控制的,同理,在接受模塊中採集一位數據的間隔同樣也要通過波特率進行控制,具體如上圖所示。
  上圖中,數據採集都是在“每位數據的中間”進行着。RX_Pin_In 輸入一幀數據,當檢測到低電平(起始位), 在第 0 位數據,採取忽略的態度,然後接下來的 8 位數據位都被採集,最後校驗位和停止位,卻是採取了忽略的操作。有一點必須好好注意,串口傳輸數據“從最低位開始,到最高位結束”。
  因爲 Verilog 無法描述理想以外的時序, 對此所有時序活動都必須看成理想時序。
在這裏插入圖片描述
              圖2 39 FPGA接收一幀波特率爲115200的數據
  當FPGA接收一幀數據爲波特率115200之際,情況差不多如圖2 41所示。 50Mhz是FPGA的時鐘源,也是一幀數據的採集時鐘, RXD 則是一幀數據的輸入端。波特率爲 115200的一位數據經過 50Mhz 的時鐘量化以後,每一位數據大約保持 8.68us,即 434 個時鐘。串口傳輸沒有自己的時鐘信號,所以我們必須利用 FPGA 的時鐘源“跟蹤”每一位數據。對此, FPGA 只能借用計數器“同步跟蹤”而已,至於 Verilog 則可以這樣描述,結果如代碼2 13所示:
                      代碼2 13

1.	0,1,2,3,4,5,6,7,8,9,10: //同步跟蹤中 ...  
2.	      if( C1 == 434 -1 ) begin C1 <= 8'd0; i <= i + 1'b1; end  
3.	      else C1 <= C1 + 1'b1;  

  如代碼2 13所示,所謂同步跟蹤,就是利用計數器估計每一位數據 … 期間,步驟 0~10表示每一位數據,至於 C1 計數 434 個時鐘則是同步跟蹤中。其中-1考慮了步驟之間的跳轉所耗掉的時鐘。
在這裏插入圖片描述
                  圖2 40 讀取起始位
  知道串口的一幀數據都是從拉低的起始位開始,然而爲了完美尾行,亦即實現精密控時,起始位的讀取往往都是關鍵。如圖2 40所示,當我們在第一個時鐘讀取(採集)起始位的時候,由於 Verilog 的讀取只能經過讀取過去值而已,餘下起始位還有 433 個時鐘需要我們跟蹤,爲此 Verilog 可以這樣描述,結果如代碼2 14所示:
                    代碼2 14

1.	0:  
2.	    if( RXD == 1'b0 ) begin i <= i + 1'b1; C1 <= C1 + 4'd1; end  
3.	1: // stalk start bit  
4.	    if( C1 == BPS115K2 -1 ) begin C1 <= 8'd0; i <= i + 1'b1; end  
5.	    else C1 <= C1 + 1'b1;

  如代碼2 14所示,步驟 0 用來檢測起始位,如果 RXD 的電平爲拉低狀態, C1 立即遞增以示同步跟蹤已經用掉一個時鐘,同樣也可以看成 i 進入下一個步驟用掉一個時鐘。然而步驟 1 是用來跟蹤餘下的 433 個時鐘,但是計數器 C1 不是從 0 開始計數,而是從 1開始計算,因爲 C1 在步驟已經遞增的緣故。
在這裏插入圖片描述
                圖2 41 讀取一幀數據當中的數據位
  一幀數據的跟蹤結果與讀取結果如圖2 41所示 … 除了起始位,我們使用了兩個步驟採集並跟蹤之餘,接下來便用 8 個步驟數據一邊跟蹤一邊採集所有數據位,然而採集的時候則是 1/4 週期,即每位數據的第 108 個時鐘。最後的校驗位及結束位則是跟蹤而已。
  對此, Verilog 可以這樣表示,結果如代碼2 15所示:
                    代碼2 15

1.	0:   
2.	if( RXD == 1'b0 ) begin i <= i + 1'b1; C1 <= C1 + 4'd1; end   
3.	  
4.	1: // start bit  
5.	if( C1 == BPS115K2 -1 ) begin C1 <= 8'd0; i <= i + 1'b1; end   
6.	else C1 <= C1 + 1'b1;  
7.	  
8.	2,3,4,5,6,7,8,9: //stalk and count 1~8 data's bit , sample data at 1/2 for bps  
9.	begin  
10.	if( C1 == SAMPLE ) D1[i-2] <= RXD;  
11.	   if( C1 == BPS115K2 -1 ) begin C1 <= 8'd0; i <= i + 1'b1; end  
12.	      else C1 <= C1 + 1'b1;          
13.	end  
14.	  
15.	10,11: // parity bit & stop bit  
16.	if( C1 == BPS115K2 -1 ) begin C1 <= 8'd0; i <= i + 1'b1; end  
17.	else C1 <= C1 + 1'b1;  
18.	  
19.	12:  
20.	begin isDone <= 1'b1; i <= i + 1'b1; end  
21.	  
22.	13:  
23.	begin isDone <= 1'b0; i <= 4'd0; end  

  如代碼2 15所示,步驟 0~1 用來採集與跟蹤起始位,步驟 2~9 則用來跟蹤數據位,並且採集爲 1/4 週期。步驟 10~11 則用來跟蹤校驗位於結束位。
在這裏插入圖片描述
                圖2 42 RX功能模塊的建模圖
  圖2 42是 RX 功能模塊的建模圖,左方鏈接至頂層信號 RXD,右方則是問答信號還有 8位的 oData。
                代碼2 16 RX模塊實現代碼

1.	//****************************************************************************//  
2.	//# @Author: 碎碎思  
3.	//# @Date:   2019-04-21 22:46:15  
4.	//# @Last Modified by:   zlk  
5.	//# @WeChat Official Account: OpenFPGA  
6.	//# @Last Modified time: 2019-08-04 03:21:38  
7.	//# Description:   
8.	//# @Modification History: 2014-05-10 13:44:28  
9.	//# Date                By             Version             Change Description:   
10.	//# ========================================================================= #  
11.	//# 2014-05-10 13:44:28  
12.	//# ========================================================================= #  
13.	//# |                                                                       | #  
14.	//# |                                OpenFPGA                               | #  
15.	//****************************************************************************//  
16.	module rx_funcmod  
17.	(  
18.	    input CLOCK, RESET,   
19.	     input RXD,  
20.	     input iCall,  
21.	     output oDone,   
22.	     output [7:0]oData  
23.	);  
24.	    parameter BPS115K2 = 9'd434, SAMPLE = 9'd108;  
25.	        
26.	    reg [3:0]i;  
27.	     reg [8:0]C1;  
28.	     reg [7:0]D1;  
29.	     reg isDone;  
30.	       
31.	     always @ ( posedge CLOCK or negedge RESET )  
32.	         if( !RESET )  
33.	              begin  
34.	                    i <= 4'd0;  
35.	                     C1 <= 9'd0;  
36.	                     D1 <= 8'd0;  
37.	                     isDone <= 1'b0;  
38.	                end  
39.	          else if( iCall )  
40.	              case( i )  
41.	                       
42.	                     0:   
43.	                     if( RXD == 1'b0 ) begin i <= i + 1'b1; C1 <= C1 + 4'd1; end   
44.	                       
45.	                     1: // start bit  
46.	                     if( C1 == BPS115K2 -1 ) begin C1 <= 8'd0; i <= i + 1'b1; end   
47.	                     else C1 <= C1 + 1'b1;  
48.	                       
49.	                     2,3,4,5,6,7,8,9: //stalk and count 1~8 data's bit , sample data at 1/2 for bps  
50.	                     begin  
51.	                        if( C1 == SAMPLE ) D1[i-2] <= RXD;  
52.	                        if( C1 == BPS115K2 -1 ) begin C1 <= 8'd0; i <= i + 1'b1; end  
53.	                        else C1 <= C1 + 1'b1;            
54.	                     end  
55.	                       
56.	                     10,11: // parity bit & stop bit  
57.	                     if( C1 == BPS115K2 -1 ) begin C1 <= 8'd0; i <= i + 1'b1; end  
58.	                     else C1 <= C1 + 1'b1;  
59.	                       
60.	                     12:  
61.	                     begin isDone <= 1'b1; i <= i + 1'b1; end  
62.	                       
63.	                     13:  
64.	                     begin isDone <= 1'b0; i <= 4'd0; end  
65.	                  
66.	                endcase  
67.	                  
68.	    assign oDone = isDone;  
69.	    assign oData = D1;  
70.	      
71.	endmodule  

  第 16~24 行是相關的出入端聲明,第 24 行則是波特率爲 115200 的常量聲明,其外還有采集的週期。
  第 26~38 行是相關的寄存器聲明,第 32~38 行則是這些寄存器的復位操作。
  第 39~46 行是核心操作。第 39 行的 if( iCall ) 表示該模塊不使能便不工作。步驟 0~1 用來判斷與跟蹤起始位;

  步驟 2~9 用來跟蹤並且讀取當中的數據位;步驟 10 至 11 則是用來跟蹤校驗位與停止位而已。步驟 12~13 則用來反饋完成信號,以示一次性的接收工作已經完成。
  第 68~69 行是輸出驅動聲明。
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章