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  

在这里插入图片描述

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