2.4 基於FPGA的UART協議實現(四)實用UART傳輸FPGA實現(二)

2.3.4.2 UART發送模塊

  發送模塊的時序及相關介紹前面都已經寫的很清楚了,就是將波特率模塊分開,這樣方便後期維護及修改,同時也爲了下一節接收模塊的設計做統一處理。
  整個模塊要實現的結構如下:
在這裏插入圖片描述
        圖2 47 發送模塊(tx_module.v)整體結構圖
  首先,有一個頂層模塊(tx_module.v),這個模塊會包含(例化)成下面的兩個子模塊(bps_module.v和tx_control_module.v)。
  bps_module.v波特率產生模塊,簡單說就是分頻模塊。
  tx_control_module.v發送控制模塊,就是利用狀態機(簡化狀態機)進行發送開始位、數據位和停止位。
        表2 11 發送模塊(tx_module.v)模塊間信號定義
在這裏插入圖片描述
        表2 12 發送模塊(tx_module.v)頂層模塊信號定義
在這裏插入圖片描述
  從根本上說,tx_module.v 是實現“定時發送”的功能。假設我配置的波特率爲9600 bps,那麼每隔0.000104166666666667s bps_module.v 這個模塊就會產生一個高脈衝給tx_control_module.v,該控制模塊會按這個節拍將數據一位一位的數據發送出去。假設配 置的波特率爲115200 bps,那麼每隔0.000086805555555555555555555555555556s tx_bps_module.v 這個模塊就會產生一個高脈衝給tx_control_module.v,該控制模塊會按這個節拍將數據一位一位的數據發送出去。一幀數據有 11 位,那麼 bps_module.v 需要產生 12 次定時。
  首先看下bps_module.v模塊,波特率產生模塊設計和之前任意波形發生器一樣。bps_module.v 是作爲“定時”的功能。當 TX_En_Sig/En_Sig 拉低的時候,它是處於隨眠的狀態。一旦 TX_En_Sig/En_Sig 拉高電平,那麼 bps_module.v 就開始計數。然後定時產生一個高脈衝經 BPS_CLK 給 tx_control_module.v。整體結構如下:
在這裏插入圖片描述
        圖2 48 波特率產生模塊(bps_module)整體結構圖
        表2 13 波特率產生模塊(bps_module)引腳說明
在這裏插入圖片描述

  按照1.5.5任意分頻器設計進行bps_module模塊設計。
  

第一步,相位計數器-32位K步計數器,如代碼2 19所示。

          代碼2 19 相位計數器-32位K步計數器

1.	//------------------------------------------------------  
2.	//RTL1: Precise fractional frequency for uart bps clock   
3.	reg [31:0]  cnt;  
4.	always@(posedge CLOCK or negedge RST_n)  
5.	begin  
6.	    if(!RST_n)  
7.	        cnt <= 0;  
8.	    else if(En_Sig)  
9.	        cnt <= cnt + BPS_CNT;          
10.	end  
11.	  
12.	//------------------------------------------------------  

  代碼2 19中,在全局時鐘CLOCK驅動下,進行K步計數,cnt爲0~232-1的地址。BPS_CNT是一個16倍波特率(16*115200)的宏定義的分頻參數,方便例化或者維護。
  

第二步,合成頻率方波的生成。

  在第一步完成了0~232-1尋址後,需要對地址進行比較、劃分,得到一個方波信號。如代碼2 20所示。
            代碼2 20 合成頻率方波的生成

1.	//RTL2: Equal division of the Frequency division clock  
2.	reg cnt_equal;  
3.	always@(posedge CLOCK or negedge RST_n)  
4.	begin  
5.	    if(!RST_n)  
6.	        cnt_equal <= 0;  
7.	    else if(En_Sig)  
8.	    begin  
9.	        if(cnt < 32'h7FFF_FFFF)  
10.	            cnt_equal <= 0;  
11.	        else  
12.	            cnt_equal <= 1;  
13.	    end  
14.	end  
15.	  
16.	//------------------------------------------------------  

  代碼2 20中,第9行,32‘h7FFF_FFFF爲232-1的中點,因此它可以作爲一種“門限電壓”來實現合成頻率的方波輸出。

第四步,分頻時鐘使能信號的生成

  主要利用“邊沿檢測技術”生成使能時鐘信號,如代碼2 21所示。
            代碼2 21 分頻時鐘使能信號的生成

1.	//RTL3: Generate enable clock for clock  
2.	reg cnt_equal_r;  
3.	always@(posedge CLOCK or negedge RST_n)  
4.	begin  
5.	    if(!RST_n)  
6.	        cnt_equal_r <= 0;  
7.	    else if(En_Sig)  
8.	        cnt_equal_r <= cnt_equal;  
9.	end  
10.	  
11.	assign  BPS_CLKen = (~cnt_equal_r & cnt_equal) ? 1'b1 : 1'b0;   
12.	assign  BPS_CLK = cnt_equal_r;  

  通過邊沿檢測技術,捕獲了合成時鐘的上升沿作爲時鐘使能信號,同時輸出cut_equel_r信號作爲分頻時鐘BPS_CLK。
  完整的代碼如下:
        代碼2 22 波特率產生模塊(bps_module)完整代碼

1.	//****************************************************************************//  
2.	//# @Author: 碎碎思  
3.	//# @Date:   2019-05-03 01:32:40  
4.	//# @Last Modified by:   zlk  
5.	//# @WeChat Official Account: OpenFPGA  
6.	//# @Last Modified time: 2019-05-06 21:22:18  
7.	//# Description:   
8.	//# @Modification History: 2019-05-06 21:22:18  
9.	//# Date                By             Version             Change Description:   
10.	//# ========================================================================= #  
11.	//# 2019-05-06 21:22:18  
12.	//# ========================================================================= #  
13.	//# |                                                                       | #  
14.	//# |                                OpenFPGA                               | #  
15.	//****************************************************************************//  
16.	/*********************************************************************** 
17.	    fc  :   Refrence clock = 50MHz = 50*10^6 
18.	    fo  :   Output  clock 
19.	    K   :   Counter Step 
20.	    fo  =   fc*[K/(2^32)] 
21.	100MHz  K   =   fo*(2^32)/fc = fo*(2^32)/(100*10^6) = 42.94967296 * fo 
22.	50MHz   K   =   fo*(2^32)/fc = fo*(2^32)/(50*10^6)  = 85.89934592 * fo 
23.	***********************************************************************/  
24.	`timescale 1ns/1ps  
25.	module  bps_module  
26.	#(  
27.	    //BPS_CNT = 85.89934592 * fo    for 50Mhz  
28.	//  parameter       BPS_CNT = 32'd21990233  //256000bps 21990233  
29.	//  parameter       BPS_CNT = 32'd10995116  //128000bps 10995116  
30.	//  parameter       BPS_CNT = 32'd9895605   //115200bps 9895605    
31.	    parameter       BPS_CNT = 32'd824634//9600bps 824634      
32.	  //BPS_CNT = 42.949 67296 * fo    for 100Mhz  
33.	//  parameter       BPS_CNT = 32'd175921860 //256000bps   
34.	//  parameter       BPS_CNT = 32'd87960930  //128000bps   
35.	//  parameter       BPS_CNT = 32'd79164837  //115200bps   
36.	//  parameter       BPS_CNT = 32'd412317    //9600bps   
37.	)  
38.	(  
39.	    //global clock  
40.	    input           CLOCK,  
41.	    input           RST_n,  
42.	      
43.	    //user interface  
44.	    input          En_Sig,  
45.	    output          BPS_CLK,  
46.	    output          BPS_CLKen  
47.	);  
48.	  
49.	  
50.	/********************************/  
51.	  
52.	//------------------------------------------------------  
53.	//RTL1: Precise fractional frequency for uart bps clock   
54.	reg [31:0]  cnt;  
55.	always@(posedge CLOCK or negedge RST_n)  
56.	begin  
57.	    if(!RST_n)  
58.	        cnt <= 0;  
59.	    else if(En_Sig)  
60.	        cnt <= cnt + BPS_CNT;          
61.	end  
62.	  
63.	//------------------------------------------------------  
64.	//RTL2: Equal division of the Frequency division clock  
65.	reg cnt_equal;  
66.	always@(posedge CLOCK or negedge RST_n)  
67.	begin  
68.	    if(!RST_n)  
69.	        cnt_equal <= 0;  
70.	    else if(En_Sig)  
71.	    begin  
72.	        if(cnt < 32'h7FFF_FFFF)  
73.	            cnt_equal <= 0;  
74.	        else  
75.	            cnt_equal <= 1;  
76.	    end  
77.	end  
78.	  
79.	//------------------------------------------------------  
80.	//RTL3: Generate enable clock for clock  
81.	reg cnt_equal_r;  
82.	always@(posedge CLOCK or negedge RST_n)  
83.	begin  
84.	    if(!RST_n)  
85.	        cnt_equal_r <= 0;  
86.	    else if(En_Sig)  
87.	        cnt_equal_r <= cnt_equal;  
88.	end  
89.	  
90.	assign  BPS_CLKen = (~cnt_equal_r & cnt_equal) ? 1'b1 : 1'b0;   
91.	assign  BPS_CLK = cnt_equal_r;  
92.	  
93.	endmodule  

  波特率產生後會將時鐘信號送給tx_control_module模塊,模塊的整體結構如下:
在這裏插入圖片描述
      圖2 49 發送控制模塊(tx_control_module)整體結構圖
       表2 14 發送控制模塊(tx_control_module)引腳說明
在這裏插入圖片描述
  tx_control_module.v 控制模塊是最爲中心的一部分,當 TX_En_Sig 拉高電平,同時間bps_module.v 也會開始計數。tx_control_module.v 將 TX_Data 的值,按bps_module.v 產生的定時,有節奏的往 TXD 發送。當一幀數據發送完畢後,就反饋一個 TX_Done_Sig 的高脈衝。
      代碼2 23 發送控制模塊(tx_control_module)完整代碼

1.	//****************************************************************************//  
2.	//# @Author: 碎碎思  
3.	//# @Date:   2019-05-04 00:47:08  
4.	//# @Last Modified by:   zlk  
5.	//# @WeChat Official Account: OpenFPGA  
6.	//# @Last Modified time: 2019-05-05 22:30:21  
7.	//# Description:   
8.	//# @Modification History: 2019-05-05 22:30:21  
9.	//# Date                By             Version             Change Description:   
10.	//# ========================================================================= #  
11.	//# 2019-05-05 22:30:21  
12.	//# ========================================================================= #  
13.	//# |                                                                       | #  
14.	//# |                                OpenFPGA                               | #  
15.	//****************************************************************************//  
16.	/************************************************************************** 
17.	..IDLE...Start...............UART DATA........................End...IDLE... 
18.	________                                                     ______________ 
19.	        |____< D0 >< D1 >< D2 >< D3 >< D4 >< D5 >< D6 >< D7 > 
20.	        Bit0  Bit1  Bit2  Bit3  Bit4  Bit5  Bit6  Bit7  Bit8  Bit9 
21.	**************************************************************************/  
22.	module tx_control_module  
23.	(  
24.	    CLOCK, RST_n,  
25.	    TX_En_Sig,   
26.	    TX_Data,   
27.	    BPS_CLK,   
28.	    TX_Done_Sig,   
29.	    TXD  
30.	       
31.	);  
32.	  
33.	    input CLOCK;  
34.	    input RST_n;  
35.	       
36.	    input TX_En_Sig;  
37.	    input [7:0]TX_Data;  
38.	    input BPS_CLK;  
39.	       
40.	    output TX_Done_Sig;  
41.	    output TXD;  
42.	       
43.	    /********************************************************/  
44.	  
45.	     reg [3:0]i;  
46.	     reg rTX;  
47.	     reg isDone;  
48.	      
49.	     always @ ( posedge CLOCK or negedge RST_n )  
50.	         if( !RST_n )  
51.	              begin  
52.	                 i <= 4'd0;  
53.	                    rTX <= 1'b1;  
54.	                    isDone  <= 1'b0;  
55.	                end  
56.	          else if( TX_En_Sig )  
57.	              case ( i )  
58.	                  
59.	                   4'd0 :  
60.	                     if( BPS_CLK ) begin i <= i + 1'b1; rTX <= 1'b1; end  
61.	                     4'd1 :  
62.	                     if( BPS_CLK ) begin i <= i + 1'b1; rTX <= 1'b0; end  
63.	                       
64.	                     4'd2, 4'd3, 4'd4, 4'd5, 4'd6, 4'd7, 4'd8, 4'd9 :  
65.	                     if( BPS_CLK ) begin i <= i + 1'b1; rTX <= TX_Data[ i - 2 ]; end  
66.	                       
67.	                     4'd10 :  
68.	                     if( BPS_CLK ) begin i <= i + 1'b1; rTX <= 1'b1; end  
69.	                                   
70.	                     4'd11 :  
71.	                     if( BPS_CLK ) begin i <= i + 1'b1; rTX <= 1'b1; end  
72.	                       
73.	                     4'd12 :  
74.	                     if( BPS_CLK ) begin i <= i + 1'b1; isDone <= 1'b1; end  
75.	                       
76.	                     4'd13 :  
77.	                     begin i <= 4'd0; isDone <= 1'b0; end  
78.	                       
79.	                     //default:i <= 4'd0;  
80.	                   
81.	                endcase  
82.	                  
83.	    /********************************************************/  
84.	       
85.	     assign TXD = rTX;  
86.	     assign TX_Done_Sig = isDone;  
87.	       
88.	     /*********************************************************/  
89.	       
90.	endmodule  

  代碼2 23中的設計的核心思想是狀態機,狀態機部分在1.6節已經介紹過了,只不過在寫代碼的時候將狀態機精簡。
  當 TX_En_Sig 被拉高(36行)該控制模塊就會開始工作了(同時間bps_module.v 也會開始計數)。每當bps_module.v 產生一個定時的高脈衝 BPS_CLKen, tx_control_module.v 都會發送一位數據。
  第 0 位數據是起始位,所以 rTX 寄存器被賦值與邏輯 0(40 行)。接下來的八位都是數據位, tx_control_module.v 按 TX_Data 的值從最低位到最高位,將數據賦值給 rTX 寄存器(43 行)。最後兩位數據則是校驗位和停止位,如果沒有什麼特別需求,就隨便填上邏輯 1 就行了(41~45 行)。最後產生一個 TX_Done_Sig 的高脈衝(51~55 行)。在 62 行 TX_Done_Sig 的輸出是被 isDone 這個標誌寄存器驅動着,然而 TXD的輸出是由 rTX 這個寄存器驅動。
  接下來是將上兩個模塊進行例化,完成整個發送模塊的設計。電路的整體結構如圖2 49所示。頂層模塊只需要按照整體結構圖進行“連線”就可以了。代碼如下:
        代碼2 24 發送頂層模塊(tx_module.v)完整代碼

1.	//****************************************************************************//  
2.	//# @Author: 碎碎思  
3.	//# @Date:   2019-05-03 02:18:37  
4.	//# @Last Modified by:   zlk  
5.	//# @WeChat Official Account: OpenFPGA  
6.	//# @Last Modified time: 2019-05-06 22:13:07  
7.	//# Description:   
8.	//# @Modification History: 2019-05-06 22:13:07  
9.	//# Date                By             Version             Change Description:   
10.	//# ========================================================================= #  
11.	//# 2019-05-06 22:13:07  
12.	//# ========================================================================= #  
13.	//# |                                                                       | #  
14.	//# |                                OpenFPGA                               | #  
15.	//****************************************************************************//  
16.	module tx_module  
17.	(  
18.	    CLOCK, RST_n,  
19.	    TX_Data,   
20.	    TX_En_Sig,  
21.	    TX_Done_Sig,   
22.	    TXD  
23.	);  
24.	   
25.	   input CLOCK;  
26.	    input RST_n;  
27.	    input [7:0]TX_Data;  
28.	    input TX_En_Sig;  
29.	    output TX_Done_Sig;  
30.	    output TXD;  
31.	        
32.	/********************************/  
33.	        
34.	    wire BPS_CLOCK;  
35.	    wire BPS_CLOCKen;   
36.	      
37.	bps_module //BPS   
38.	#(  
39.	    //BPS_CNT = 85.89934592 * fo    for 50Mhz  
40.	//  .BPS_CNT(32'd21990233)  //256000bps  
41.	//  .BPS_CNT(32'd10995116)  //128000bps  
42.	//  .BPS_CNT(32'd9895605)   //115200bps  
43.	    .BPS_CNT(32'd824634)    //9600bps * 16 824634  
44.	  //BPS_CNT = 42.949 67296 * fo    for 100Mhz  
45.	//  .BPS_CNT(32'd10995116)  //256000bps 10995116  
46.	//  .BPS_CNT(32'd5497558)   //128000bps 5497558  
47.	//  .BPS_CNT(32'd4947802)   //115200bps 4947802  
48.	//  .BPS_CNT(32'd412317)    //9600bps   412317  
49.	)  
50.	U1_bps_module  
51.	(  
52.	       .CLOCK( CLOCK ),  
53.	        .RST_n( RST_n ),  
54.	        .En_Sig( TX_En_Sig ),    // input - from U2  
55.	        .BPS_CLK( BPS_CLOCK ),  // output - to X  
56.	        .BPS_CLKen( BPS_CLOCKen ) // output - to U2  
57.	);    
58.	/*********************************/  
59.	        
60.	tx_control_module U2_tx_control_module  
61.	    (  
62.	       .CLOCK( CLOCK ),  
63.	        .RST_n( RST_n ),  
64.	        .TX_En_Sig( TX_En_Sig ),    // input - from top  
65.	        .TX_Data( TX_Data ),        // input - from top  
66.	        .BPS_CLK( BPS_CLOCKen ),  // input - from U2  
67.	        .TX_Done_Sig( TX_Done_Sig ), // output - to top  
68.	        .TXD( TXD )     // output - to top  
69.	    );  
70.	/***********************************/  
71.	  
72.	endmodule  

完成後的RTL如下:
在這裏插入圖片描述
            圖2 50 UART發送模塊RTL
  和圖2 49整體設計一樣。
  上述代碼的ModelSim仿真和2.3.3節仿真代碼一樣,結果如下:
在這裏插入圖片描述
            圖2 51 ModelSim仿真
  爲了測試代碼的實用性及使用方法,編寫代碼2 25。
          代碼2 25 UART發送實例代碼

1.	//****************************************************************************//  
2.	//# @Author: 碎碎思  
3.	//# @Date:   2019-05-03 02:18:37  
4.	//# @Last Modified by:   zlk  
5.	//# @WeChat Official Account: OpenFPGA  
6.	//# @Last Modified time: 2019-05-06 21:38:04  
7.	//# Description:   
8.	//# @Modification History: 2019-05-06 21:38:04  
9.	//# Date                By             Version             Change Description:   
10.	//# ========================================================================= #  
11.	//# 2019-05-06 21:38:04  
12.	//# ========================================================================= #  
13.	//# |                                                                       | #  
14.	//# |                                OpenFPGA                               | #  
15.	//****************************************************************************//  
16.	module tx_module_demo  
17.	(  
18.	    CLOCK, RST_n,  
19.	    TXD  
20.	);  
21.	  
22.	   input CLOCK;  
23.	    input RST_n;  
24.	    output TXD;  
25.	       
26.	/***************************/  
27.	    reg [7:0]rData;   
28.	    reg isTX;   
29.	    wire DoneU1;  
30.	/****************************/  
31.	  
32.	  
33.	      
34.	tx_module dut  
35.	    (  
36.	        .CLOCK( CLOCK),  
37.	        .RST_n( RST_n ),  
38.	        .TX_Data( rData ),  
39.	        .TX_En_Sig( isTX ),  
40.	        .TX_Done_Sig( DoneU1 ),  
41.	        .TXD( TXD )  
42.	    );  
43.	      
44.	/*******************************/  
45.	  
46.	    reg [3:0]i;  
47.	  
48.	  
49.	/**************************/  
50.	       
51.	     parameter T1S = 26'd49_999_999;  
52.	       
53.	/*************************** 
54.	      
55.	     reg [25:0]Count_Sec; 
56.	      
57.	always @ ( posedge CLOCK or negedge RST_n ) 
58.	         if( !RST_n ) 
59.	              Count_Sec <= 26'd0; 
60.	          else if( Count_Sec == T1S ) 
61.	              Count_Sec <= 26'd0; 
62.	          else 
63.	              Count_Sec <= Count_Sec + 1'b1; 
64.	                 
65.	/******************************** 
66.	 
67.	 
68.	always @ ( posedge CLOCK or negedge RST_n ) 
69.	        if( !RST_n ) 
70.	             begin 
71.	               isTX <= 1'b0; 
72.	                rData <= 8'h00; 
73.	             end     
74.	          else if( DoneU1 ) 
75.	             begin 
76.	                  isTX <= 1'b0; 
77.	                  rData <= 8'hAB;                 
78.	              end 
79.	          else if( Count_Sec == T1S ) 
80.	              isTX <= 1'b1; 
81.	/******************************/  
82.	      
83.	      
84.	/******************************/  
85.	  
86.	always @ ( posedge CLOCK or negedge RST_n )  
87.	         if( !RST_n )  
88.	              begin  
89.	                     i <= 4'd0;  
90.	                     rData <= 8'd0;  
91.	                     isTX <= 1'b0;  
92.	                end  
93.	          else  
94.	              case( i )  
95.	                  
96.	                    0:  
97.	                     if( DoneU1 ) begin isTX <= 1'b0; i <= i + 1'b1; end  
98.	                     else begin isTX <= 1'b1; rData <= 8'hAB; end  
99.	                       
100.	                     1:  
101.	                     if( DoneU1 ) begin isTX <= 1'b0; i <= i + 1'b1; end  
102.	                     else begin isTX <= 1'b1; rData <= 8'hCD; end  
103.	                       
104.	                     2:  
105.	                     if( DoneU1 ) begin isTX <= 1'b0; i <= i + 1'b1; end  
106.	                     else begin isTX <= 1'b1; rData <= 8'hEF; end  
107.	                       
108.	                     3: // Stop  
109.	                     i <= i;  
110.	                  
111.	                endcase  
112.	/******************************/  
113.	  
114.	endmodule  

在這裏插入圖片描述

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