【黑金原創教程】【FPGA那些事兒-驅動篇I 】實驗十三:串口模塊② — 接收


實驗十三:串口模塊② — 接收

我們在實驗十二實現了串口發送,然而這章實驗則要實現串口接收 ... 在此,筆者也會使用其它思路實現串口接收。

clip_image002

圖13.1 模塊之間的數據傳輸。

假設我們不考慮波特率,而且一幀數據之間的傳輸也只是發生在FPGA之間,即兩只模塊之間互轉,並且兩塊模塊都使用相同的時鍾頻率,結果如圖13.1所示。只要成立上述的假設成立,串口傳輸不過是簡單的數據傳輸活動而已,圖中的發送模塊經由TXD將一幀11位的數據發送至接收模塊。

clip_image004

圖13.2 發送與接收一幀數據。

至於兩者之間的時序過程,則如圖13.2所示 ... 發送方經由TXD,從T0~T10總共發送一幀為11位的數據,反之接收方則從T2~T9讀取其中的8位數據而已(D為寄存器的暫存內容)。從圖13.2當中,我們可以看見發送方,即TXD都是經由上升沿發送未來值,接收方D則是經由上升沿讀取過去值。對此,Verilog可以這樣描述,結果如代碼13.1所示:

      //發送方
      reg [10:0]rTXD;
     always @(posedge CLOCK)
         case(i)
            0,1,2,3,4,5,6,7,8,9,10:      
            begin TXD <= rTXD[i]; i <= i + 1'b1; end
            ......
         endcase
 
      //接收方
      reg [7:0]D1;
     always @(posedge CLOCK)
         case(i)
           2,3,4,5,6,7,8,9:      
            begin D1[i] <= TXD; i <= i + 1'b1; end
            ......
         endcase

代碼13.1

如代碼13.1所示,發送方在步驟0~10一共發送一幀為11位的數據 ... 反之接收方,則在步驟2~9讀取其中的數據[7:0]。心機重的朋友的一定會疑惑道,為什么筆者要換個角度去思考串口怎樣接收呢?原因其實很簡單,目的就是為了簡化理解,腦補時序,實現精密控制。

對此,FPGA與其它設備互轉數據,其實可以反映成兩只模塊正在互轉數據,然而理想時序就是關鍵。因為Verilog無法描述理想以外的時序,對此所有時序活動都必須看成理想時序。

clip_image006

圖13.3 FPGA接收一幀波特率為115200的數據。

當FPGA接收一幀數據為波特率115200之際,情況差不多如圖13.3所示。50Mhz是FPGA的時鍾源,也是一幀數據的采集時鍾,RXD則是一幀數據的輸入端。波特率為115200的一位數據經過50Mhz的時鍾量化以后,每一位數據大約保持8.68us,即434個時鍾。

串口傳輸沒有自己的時鍾信號,所以我們必須利用FPGA的時鍾源“跟蹤”每一位數據。對此,FPGA只能借用計數器“同步跟蹤”而已,至於Verilog則可以這樣描述,結果如代碼13.2所示:

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

代碼13.2

如代碼13.2所示,所謂同步跟蹤,就是利用計數器估計每一位數據 ... 期間,步驟0~10表示每一位數據,至於C1計數434個時鍾則是同步跟蹤中。其中 -1 考慮了步驟之間的跳轉所耗掉的時鍾。

clip_image008

圖13.4 讀取起始位。

我們都知道串口的一幀數據都是從拉低的起始位開始,然而為了完美尾行,亦即實現精密控時,起始位的讀取往往都是關鍵。如圖13.4所示,當我們在第一個時鍾讀取(采集)起始位的時候,由於Verilog的讀取只能經過讀取過去值而已,余下起始位還有433個時鍾需要我們跟蹤,為此Verilog可以這樣描述,結果如代碼13.3所示:

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

代碼13.3

如代碼13.3所示,步驟0用來檢測起始位,如果RXD的電平為拉低狀態,C1立即遞增以示同步跟蹤已經用掉一個時鍾,同樣也可以看成i進入下一個步驟用掉一個時鍾。然而步驟1是用來跟蹤余下的433個時鍾,但是計數器C1不是從0開始計數,而是從1開始計算,因為C1在步驟已經遞增的緣故。

clip_image010

圖13.5 讀取一幀數據當中的數據位。

一幀數據的跟蹤結果與讀取結果如圖13.5所示 ... 除了起始位,我們使用了兩個步驟采集並跟蹤之余,接下來便用8個步驟數據一邊跟蹤一邊采集所有數據位,然而采集的時候則是1/4周期,即每位數據的第108個時鍾。最后的校驗位及結束位則是跟蹤而已。對此,Verilog 可以這樣表示,結果如代碼13.4所示:

    0: 
    if( RXD == 1'b0 ) begin i <= i + 1'b1; C1 <= C1 + 4'd1; end 
                     
    1: // start bit
    if( C1 == 434 -1 ) begin C1 <= 8'd0; i <= i + 1'b1; end 
    else C1 <= C1 + 1'b1;
                     
    2,3,4,5,6,7,8,9: 
    begin
        if( C1 == 108 ) D1[i-2] <= RXD;
        if( C1 == 434 -1 ) begin C1 <= 8'd0; i <= i + 1'b1; end
        else C1 <= C1 + 1'b1;                           
    end
                 
    10,11: // parity bit & stop bit
    if( C1 == 434 -1 ) begin C1 <= 8'd0; i <= i + 1'b1; end
    else C1 <= C1 + 1'b1;

代碼13.4

如代碼13.4所示,步驟0~1用來采集與跟蹤起始位,步驟2~9則用來跟蹤數據位,並且采集為1/4周期。步驟10~11則用來跟蹤校驗位於結束位。理解完畢以后,我們便可以開始建模了。

clip_image012

圖13.6 實驗十三的建模圖。

圖13.6是實驗十三的建模圖,rx_demo組合模塊包含RX功能模塊與核心操作。RX功能模塊的左方鏈接至RXD頂層信號,它主要是負責一幀數據的接收,然后反饋給核心操作。核心操作則負責RX功能模塊的使能工作,當它領取完成信號以后,變槳回收回來的數據再經由TXD頂層信號發送出去。

rx_funcmod.v

clip_image014

圖13.7 RX功能模塊的建模圖。

圖13.7是RX功能模塊的建模圖,左方鏈接至頂層信號RXD,右方則是問答信號還有8位的oData。

1.    module rx_funcmod
2.    (
3.         input CLOCK, RESET, 
4.         input RXD,
5.         input iCall,
6.         output oDone, 
7.         output [7:0]oData
8.    );
9.        parameter BPS115K2 = 9'd434, SAMPLE = 9'd108;
10.          

以上內容是相關的出入端聲明,第9行則是波特率為115200的常量聲明,其外還有采集的周期。

11.         reg [3:0]i;
12.         reg [8:0]C1;
13.         reg [7:0]D1;
14.         reg isDone;
15.         
16.         always @ ( posedge CLOCK or negedge RESET )
17.             if( !RESET )
18.                  begin
19.                         i <= 4'd0;
20.                         C1 <= 9'd0;
21.                         D1 <= 8'd0;
22.                         isDone <= 1'b0;
23.                    end

以上內容行是相關的寄存器聲明,第17~22行則是這些寄存器的復位操作。

24.              else if( iCall )
25.                  case( i )
26.                         
27.                         0: 
28.                         if( RXD == 1'b0 ) begin i <= i + 1'b1; C1 <= C1 + 4'd1; end 
29.                         
30.                         1: // start bit
31.                         if( C1 == BPS115K2 -1 ) begin C1 <= 8'd0; i <= i + 1'b1; end 
32.                         else C1 <= C1 + 1'b1;
33.                         
34.                         2,3,4,5,6,7,8,9: //stalk and count 1~8 data's bit , sample data at 1/2 for bps
35.                         begin
36.                            if( C1 == SAMPLE ) D1[i-2] <= RXD;
37.                            if( C1 == BPS115K2 -1 ) begin C1 <= 8'd0; i <= i + 1'b1; end
38.                            else C1 <= C1 + 1'b1;          
39.                         end
40.                         
41.                         10,11: // parity bit & stop bit
42.                         if( C1 == BPS115K2 -1 ) begin C1 <= 8'd0; i <= i + 1'b1; end
43.                         else C1 <= C1 + 1'b1;
44.                         
45.                         12:
46.                         begin isDone <= 1'b1; i <= i + 1'b1; end
47.                         
48.                         13:
49.                         begin isDone <= 1'b0; i <= 4'd0; end
50.                    
51.                    endcase
52.                    

以上內容是核心操作。第24行的 if( iCall ) 表示該模塊不使能便不工作。步驟0~1用來判斷與跟蹤起始位;步驟2~9用來跟蹤並且讀取當中的數據位;步驟10至11則是用來跟蹤校驗位與停止位而已。步驟12~13則用來反饋完成信號,以示一次性的接收工作已經完成。

53.        assign oDone = isDone;
54.        assign oData = D1;
55.        
56.    endmodule

以上內容是輸出驅動聲明。

rx_demo.v

rx_demo的連線布局請瀏覽回圖13.6,至於核心操作的內容請瀏覽代碼。

1.    module rx_demo
2.    (
3.         input CLOCK, RESET, 
4.         input RXD,
5.         output TXD
6.    );

以上內容是相關的出入端聲明。

7.         wire DoneU1;
8.         wire [7:0]DataU1;
9.        
10.        rx_funcmod U1
11.         (
12.             .CLOCK( CLOCK ),
13.              .RESET( RESET ),
14.              .RXD( RXD ),    // < top
15.              .iCall( isRX ),    // < core
16.              .oDone( DoneU1 ),  // > core
17.              .oData( DataU1 )   // > core
18.         );
19.         

以上內容為是RX功能模塊的實例化,第7~8是連線聲明。

20.         parameter B115K2 = 9'd434, TXFUNC = 5'd16;
21.         
22.         reg [4:0]i,Go;
23.         reg [8:0]C1;
24.         reg [10:0]D1;
25.         reg rTXD;
26.         reg isRX;
27.         
28.         always @ ( posedge CLOCK or negedge RESET )
29.             if( !RESET )
30.                  begin 
31.                         i <= 5'd0;
32.                         C1 <= 9'd0;
33.                         D1 <= 11'd0;
34.                         rTXD <= 1'b1;
35.                         isRX<= 1'b0;
36.                    end

以上內容為相關的寄存器聲明以及復位操作。第20行是波特率為115200常量聲明之余還有偽函數的入口地址。第22~26行是相關的寄存器聲明,第29~33行則是這些寄存器的復位操作。

37.              else
38.                  case( i )
39.                    
40.                         0:
41.                         if( DoneU1 ) begin isRX <= 1'b0; D1 <= { 2'b11,DataU1,1'b0 }; i <= TXFUNC; Go <= 5'd0; end
42.                         else isRX <= 1'b1; 
43.                         
44.                         /**********/
45.                         
46.                         16,17,18,19,20,21,22,23,24,25,26:
47.                         if( C1 == B115K2 -1 ) begin C1 <= 8'd0; i <= i + 1'b1; end
48.                         else begin rTXD <= D1[i - 16]; C1 <= C1 + 1'b1; end
49.                         
50.                         27:
51.                         i <= Go;
52.                        
53.                    endcase
54.                    

以上內容為核心操作。步驟16~27是發送一幀數據的偽函數,筆者直接將TX功能整合進來。步驟0則是用來接收完成反饋,並且准備好發送輸數,然后i指向偽函數。

55.        assign TXD = rTXD;
56.                               
57.    endmodule

以上內容是相關的輸出驅動聲明。編譯完畢便下載程序,串口調試助手設置為 115200 波特率,8位數據位,奇偶校驗位隨便,停止位1。事后,每當串口調試助手想FPGA發送什么數據,FPGA也會回饋串口調試助手,不過僅限於一幀又有間隔的數據而已。目前是實驗十三還不能支持數據流的接收,因為實驗十三沒有空間緩沖數據流 ... 此外,核心操作沒發送一幀數據也有一定的時間耽誤。

細節一:完整的個體模塊

實驗十三的RX功能模塊已經是完整的個體模塊,可以直接拿來調用。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM