1. 程式人生 > >【黑金原創教程】【FPGA那些事兒-驅動篇I 】實驗二十八:TFT模組

【黑金原創教程】【FPGA那些事兒-驅動篇I 】實驗二十八:TFT模組

實驗二十八:TFT模組 - 觸屏

讀者在上一個實驗所玩弄過的 TFT LCD模組,除了顯示大小為 320 × 240,顏色為16位RGB的影象資訊以外,它還支援觸屏。所謂觸屏就是滑鼠還有鍵盤以外的輸入手段,例如現在流行平板還有智慧手機,觸屏輸入對我們來說,已經成為日常的一部分。描述語言一門偏向硬體的語言,面對觸屏,它頂多只能做做一些驅動的工作,其餘如濾波,還有畫素轉換等計算,它必須交由高階語言去負責。

面向無能為力的描述語言,筆者不禁聯想過去的自己 ... 好痛苦,好難受。話雖如此,天生我才必有用,描述語言雖然完成不了觸屏的偉業,不過只要做做驅動,平穩過著日子,它便心滿意足了。

clip_image002

圖28.1 觸屏的硬體部署。

如圖28.1所示,那是觸屏的硬體部署。根據理論,TFT顯示屏上面佈滿一層敏感的電磁層(觸屏層)作為輸入或者感測,而且感測的強弱會隨著位置不同而不同。如果筆者不小心觸碰任何一點,控制器 SSD 1289 會經由差分的 X+/-訊號 還有 Y+/- 訊號,將感測結果傳送出去。在此,TFT顯示屏的工作已經結束。

差分是什麼?估計失憶的同學會非常好奇。假設筆者不小心觸碰位置X20,然後畫素X20所對應的模擬訊號,例如1.2v,其中正電壓 +1.2v會經由X+ 訊號傳送出去,至於負電壓 -1.2v 則會經由 X- 訊號後傳送出去。差分的目的一般都為了使電壓更穩定更準確,讀者只要這樣理解即可。

事後,差分的感測結果便會傳入A/D硬體的耳中,如果輸入源是差分,A/D硬體理應長出一對接收差分的耳朵,對此也稱為差分A/D硬體。如果讀者不知道A/D是什麼,讀者必須抓去槍斃了 ... A/D全名是 Analogue to Digital Convert,即模擬轉為數字。這隻命運般的A/D硬體就是鼎鼎大名的 XPT2046 硬體。

硬體XPT2046就是差分的A/D硬體,同時也是本實驗的主角。如圖28.1所示,它將TFT顯示屏哪裡發來的感測結果轉換為數字訊號,然後再經由 FPGA讀取。好奇的同學可能會繼續好奇什麼是物理畫素?這個問題說來話長 ... 假設螢幕是一個容器,藏在容器裡邊的畫素就稱為螢幕畫素,然而圍繞容器外邊的畫素就稱為物理畫素。

比喻來講,同是人形生物,住在地球的人形生物就稱為地球人,住在地球以外的人形生物就稱為外星人。結果而言,地球人並不等價外星人,雖然兩者都有相似的外表。所以說,物理畫素還有螢幕畫素,它們聽起來都是畫素,不過實際上是不等價的東西。對此,物理畫素為了成為螢幕畫素,它們必須經過“轉換演算法”才能成為如假包換的螢幕畫素。不過,“轉換演算法”對描述語言來說,這項工作實在太巨大了。

好了好了,筆者也差不多要進入主題了,廢話再說下去,口水就會把電腦淹沒了。

clip_image004

圖28.2 硬體XTP2046連結FPGA。

圖28.2顯示硬體XTP2046連結FPGA所需要的連線,由於硬體XTP2046是SPI傳輸協議的信奉者,所以這些訊號多少也有SPI的影子。CS訊號也是拉低有效的使能訊號,CLK訊號是序列時鐘,IRQ是拉低有效的溝通訊號,DI與DO是讀寫作用的序列資料訊號,BY是BUSY的縮寫,亦即表達狀態的狀態訊號。如圖28.2所示,因為FPGA控制 CLK訊號還有CS訊號,所以FPGA是主機,硬體XTP2046則是從機。

clip_image006

圖28.3 寫時序/寫命令(主機視角)。

首先讓我們來瞧瞧XTP2046的寫時序,如圖28.3所示,寫時序也稱為寫命令,因為主機訪問從機只有寫命令而沒有寫資料。我們知道SPI都是下降沿設定資料,上升沿鎖存資料 ... 對此,寫一個位元組命令需要用到8下降沿,期間CS必須一直拉低。如圖28.3所示,一位元組命令當中卻有各個不同的位作用,結果如表28.1所示:

表28.1 命令位說明。

位寬

名稱

說明

Bit 7

Call

起始位,值1為起始。

Bit 6~4

A2~A0

通道地址。X通道 = 3’b001,Y通道 3’b101。

Bit 3

Mode

12位/8位解析度選擇。值0為12位解析度,反之亦然。

Bit 2

SER/DFR

輸入源選擇。值0為差分輸入,值1為單端輸入。

Bit 1~0

PD1~PD0

功率選擇。2’b00為低功率,2’b11為平常。

如表28.1所示,那是命令位的說明。Call為起始,值1表示開始命令,A2~A0為通道地址,常用有X通道與Y通道。Mode為解析度選擇,即轉換結果是8位還是12位,常見為12位解析度。SER/DFR為輸入源選擇,雖然XTP2046是差分A/D,不過它可以選擇輸入源。PD1~PD0為功率選擇,預設為低功率。

表28.1雖然令人眼花繚亂,不過常見的命令只有以下兩個:

讀取X通道轉化結果,12位解析度,差分輸入,低功率,8’b1_001_0000或者8’h90。

讀取Y通道轉換結果,12位解析度,差分輸入,低功率,8’b1_101_0000或者8’hD0。

此外,Verilog可以這樣描述圖28.3,結果如程式碼28.1所示:

1.        0,1,2,3,4,5,6,7:
2.        begin
3.            rDI <= T1[7-i];
4.                             
5.            if( C1 == 0 ) rCLK <= 1'b0;
6.            else if( C1 == FHALF ) rCLK <= 1'b1;
7.                            
8.            if( C1 == FCLK -1) begin C1 <= 8'd0; i <= i + 1'b1; end
9.            else begin C1 <= C1 + 1'b1; end
10.        end

程式碼28.1

如程式碼28.1所示,第1行表示相同的操作有8次,第8~9行表示一個步驟所停留的時間

,其中的FCLK表示一個時鐘週期。第5~6行表示,C1為0拉低CLK,C1為半個週期(FHALF)則拉高時鐘。第3行則是由高至低逐位賦值。

clip_image008

圖28.4 讀時序/讀資料(主機視角)。

如圖28.4所示,那是XTP2046的讀時序或者說為讀資料。我們知道SPI利用時鐘訊號的上升沿鎖存資料,如果資料的解析度設為12位(Mode為0值),那麼一次性的讀資料需要12個上升沿,期間CS必須拉低。根據手冊,雖然一次性的讀資料有12位,不過僅有當中的高八位有利用價值,亦即 Bit 11~Bit 4,結果低四位被無視。對此,Verilog可以這樣表示,結果如程式碼28.2所示:

1.    0,1,2,3,4,5,6,7,8,9,10,11:
2.    begin 
3.        if( C1 == FHALF ) T2[11-i] <= TP_DO;
4.         
5.        if( C1 == 0 ) rCLK <= 1'b0;
6.        else if( C1 == FHALF ) rCLK <= 1'b1;
7.                            
8.        if( C1 == FCLK -1) begin C1 <= 8'd0; i <= i + 1'b1; end
9.        else begin C1 <= C1 + 1'b1; end
10.    end

程式碼28.2

如程式碼28.2所示,第1行表示該操作重複12次,第8~9行表示該步驟停留FCLK週期

。第5~6行表示,C1為0拉低CLK,C1為FHALF則拉高CLK。第3行表示,C1為HALF就從TP_DO哪裡暫存結果。

clip_image010

圖28.5 一次性寫命令與讀資料的時序圖(主機視角)。

圖28.5是一次性寫命令與讀資料的時序圖。操作期間,CS必須持續拉低,首先主機利用下降沿向從機發送一位元組的寫命令,事後從機會拉高BY一個時鐘以示忙狀態,期間主機必須給足一個空時鐘。從機忙完以後便會拉低BY訊號,緊接著便會發送12位寬的轉換結果,期間主機必須利用上升沿讀取資料。對此,Verilog 可以這樣描述,結果如程式碼28.3所示:

1.     1: // Write byte
2.     begin rCS <= 1'b0; T1 <= Command; i <= FF_Write; Go <= i + 1'b1; end
3.                        
4.     2:  // Wait Busy 
5.     begin
6.         if( C1 == 0 ) rCLK <= 1'b0;
7.          else if( C1 == FHALF ) rCLK <= 1'b1;
8.                            
9.         if( C1 == FCLK -1) begin C1 <= 8'd0; i <= i + 1'b1; end
10.         else begin C1 <= C1 + 1'b1; end
11.     end
12.                        
13.     3: // Read 12 bit
14.     begin i <= FF_Read; Go <= i + 1'b1; end
15.                        
16.     4:
17.     begin D1 <= T2[11:4]; rCS <= 1'b1; i <= i + 1'b1; end

程式碼28.3

如程式碼28.3所示,步驟0拉低CS之餘,它也賦值寫命令,然後i指向寫命令的偽函式(程式碼28.1)。寫命令完成以後,主機必須給足一個時鐘,對此步驟1發呆一個時鐘週期。步驟2將i指向讀資料的偽函式(程式碼28.2),隨後在步驟4儲存高8位的結果,最後也順便拉高一下CS訊號。

clip_image012

圖28.6 IRQ訊號。

初期階段(放手階段)IRQ呈現拉高狀態,如果我們不小心觸碰螢幕,XTP2046便會察覺輸入源發生變化,然後便會拉低IRQ訊號以示觸屏事件發生了,結果如圖28.6所示。

對此,Verilog 可以這樣描述,結果如程式碼28.4所示:

1.    0:
2.    if( !TP_IRQ ) begin ...; i <= i + 1’b1; end
3.     
4.    1:
5.    ......

程式碼28.4

準備知識理解完畢以後,我們便可以開始建模了。

clip_image014

圖28.7 Touch基礎模組的建模圖。

圖28.7是Touch基礎模組,內容包含控制模組還有功能模組。功能模組左邊有兩位寬的Call/Done,其中[1]為讀取物理畫素X,[0]為讀取物理畫素Y。至於右邊則是一些頂層訊號。控制模組的左邊,除了頂層訊號IRQ是輸入以外,其餘訊號都呈現輸出狀態。16位寬的Data對應物理畫素X還有物理畫素Y,而Done則是溝通作用的觸發訊號。

整體來說,一旦觸碰觸屏,IRQ拉低,控制模組利用功能模組讀取物理畫素X與Y,然後再產生完成訊號以示一次性的觸屏結果。

touch_funcmod.v

clip_image016

圖28.8 Touch功能模組的建模圖。

圖28.8是Touch功能模組的建模圖,具體內容我們還是來看程式碼吧。

1.    module touch_funcmod
2.    (
3.         input CLOCK,RESET,
4.         output TP_CS_N,
5.         output TP_CLK,
6.         output TP_DI,
7.         input TP_DO,
8.         
9.         input [1:0]iCall,
10.         output oDone,
11.         output [7:0]oData
12.    );

以上內容為相關的出入端宣告。

13.         parameter FCLK = 8'd20, FHALF = 8'd10; // 2.5Mhz 
14.         parameter FF_Write = 6'd16, FF_Read = 6'd32;

以上內容為常量宣告,內容包括2.5Mhz週期,半週期,寫命令還有讀資料入口地址。

16.         reg [5:0]i,Go;
17.         reg [11:0]D1;
18.         reg [7:0]C1;
19.         reg rCS,rCLK,rDI;
20.         reg isDone;
21.         
22.         always @ ( posedge CLOCK or negedge RESET )
23.            if( !RESET )
24.                 begin
25.                        { i,Go } <= { 6'd0, 6'd0 };
26.                        D1 <= 12'd0;
27.                        C1 <= 8'd0;
28.                        { rCS,rCLK,rDI } <= 3'b111;
29.                        isDone <= 1'b0;
30.                  end

以上內容為相關的暫存器宣告還有復位操作。

31.             else if( iCall )
32.                 case( i )
33.                  
34.                        0: // [1] Read 12bit X, [0],Read 12 bit Y
35.                        if( iCall[1] ) begin D1[7:0] <= 8'h90; i <= i + 1'b1; end
36.                        else if( iCall[0] ) begin D1[7:0] <= 8'hd0; i <= i + 1'b1; end
37.                        
38.                        1: // Write byte
39.                        begin rCS <= 1'b0; i <= FF_Write; Go <= i + 1'b1; end
40.                        
41.                        2:  // Wait Busy 
42.                        begin
43.                             if( C1 == 0 ) rCLK <= 1'b0;
44.                             else if( C1 == FHALF ) rCLK <= 1'b1;
45.                            
46.                            if( C1 == FCLK -1) begin C1 <= 8'd0; i <= i + 1'b1; end
47.                             else begin C1 <= C1 + 1'b1; end
48.                        end
49.                        
50.                        3: // Read 12 bit
51.                        begin i <= FF_Read; Go <= i + 1'b1; end
52.                        
53.                        4:
54.                        begin rCS <= 1'b1; i <= i + 1'b1; end
55.                        
56.                        5:
57.                        begin isDone <= 1'b1; i <= i + 1'b1; end
58.                        
59.                        6:
60.                        begin isDone <= 1'b0; i <= 6'd0; end
61.                     

以上內容為該功能模組的核心操作。步驟0會根據 iCall 為 D1賦值 8’h90(讀X),還是 8’hD0(讀Y)。步驟1拉低CS之餘也寫命令,步驟2則是給足一個空時間。步驟3為讀資料,步驟4拉低CS,步驟5~6則產生完成訊號。

62.                        /********************/
63.                        
64.                        16,17,18,19,20,21,22,23:
65.                        begin
66.                            rDI <= D1[23-i];
67.                             
68.                            if( C1 == 0 ) rCLK <= 1'b0;
69.                             else if( C1 == FHALF ) rCLK <= 1'b1;
70.                            
71.                            if( C1 == FCLK -1) begin C1 <= 8'd0; i <= i + 1'b1; end
72.                             else begin C1 <= C1 + 1'b1; end
73.                        end
74.                        
75.                        24:
76.                        i <= Go;
77.                        

步驟16~23為寫一個位元組。

78.                        /********************/
79.                        
80.                        32,33,34,35,36,37,38,39,40,41,42,43:
81.                        begin 
82.                            if( C1 == FHALF ) D1[43-i] <= TP_DO;
83.                             
84.                             if( C1 == 0 ) rCLK <= 1'b0;
85.                             else if( C1 == FHALF ) rCLK <= 1'b1;
86.                            
87.                            if( C1 == FCLK -1) begin C1 <= 8'd0; i <= i + 1'b1; end
88.                             else begin C1 <= C1 + 1'b1; end
89.                        end
90.                        
91.                        44:
92.                        i <= Go;
93.                  
94.                  endcase
95.                  

步驟32~92為讀12位資料。

96.            assign TP_CS_N = rCS;
97.            assign TP_CLK = rCLK;
98.            assign TP_DI = rDI;
99.            assign oDone = isDone;
100.            assign oData = D1[11:4];
101.    
102.    endmodule

以上內容為輸出驅動宣告。注意第100行,oData賦值D1的高八位。

touch_ctrlmod.v

clip_image018

圖28.9 Touch控制模組的建模圖。

圖28.9是Touch控制模組的建模圖,基本上也沒有什麼好說的,具體內容讓我們來看程式碼吧。

1.    module touch_ctrlmod
2.    (
3.          input CLOCK,RESET,
4.          input TP_IRQ,
5.          output oDone,
6.          output [15:0]oData,
7.          
8.          output [1:0]oCall,
9.          input iDone,
10.          input [7:0]iData
11.    );

以上內容為相關的出入端宣告。

12.         reg [5:0]i;
13.         reg [7:0]D1,D2;
14.         reg [1:0]isCall;
15.         reg isDone;
16.         
17.         always @ ( posedge CLOCK or negedge RESET )
18.             if( !RESET )
19.                  begin
20.                         i <= 6'd0;
21.                         { D1,D2 } <= { 8'd0,8'd0 };
22.                         isCall <= 2'd0;
23.                         isDone <= 1'b0;
24.                    end

以上內容為相關的暫存器宣告還有復位操作。

25.              else
26.                  case( i )
27.                    
28.                         0:
29.                         if( !TP_IRQ ) i <= i + 1'b1; 
30.                         
31.                         1: // Read X
32.                         if( iDone ) begin isCall[1] <= 1'b0; D1 <= iData; i <= i + 1'b1; end
33.                         else begin  isCall[1] <= 1'b1; end
34.                         
35.                         2: // Read Y
36.                         if( iDone ) begin isCall[0] <= 1'b0; D2 <= iData; i <= i + 1'b1; end
37.                         else begin  isCall[0] <= 1'b1; end
38.                         
39.                         3:
40.                         begin isDone <= 1'b1; i <= i + 1'b1; end
41.                         
42.                         4:
43.                         begin isDone <= 1'b0; i <= 6'd0; end
44.                    
45.                    endcase

以上內容為該控制模組的核心內容。步驟0用來檢測 IRQ訊號拉低,步驟1讀取物理畫素X,步驟2讀取物理畫素Y。步驟3~4則是用來產生完成訊號。

47.         assign oDone = isDone;
48.         assign oData = {D1,D2};
49.         assign oCall = isCall;
50.                    
51.    endmodule

以上內容為相關的輸出驅動宣告。

touch_basemod.v

有關這個模組的連線部署請參考圖28.7。

1.    module touch_basemod
2.    (
3.         input CLOCK,RESET,
4.         output TP_CS_N,
5.         output TP_CLK,
6.         input TP_IRQ,
7.         output TP_DI,
8.         input TP_DO,
9.         
10.         output oDone,
11.         output [15:0]oData
12.    );
13.        wire [1:0]CallU1;
14.    
15.        touch_ctrlmod U1
16.         (
17.              .CLOCK( CLOCK ),
18.              .RESET(  RESET ),
19.              .TP_IRQ( TP_IRQ ),  // < top
20.              .oDone( oDone ),        // > top
21.              .oData( oData ),         // > top
22.              .oCall( CallU1 ),         // > U2
23.              .iDone( DoneU2 ),     // < U1
24.              .iData( DataU2 )      // < U1
25.         );
26.         
27.         wire DoneU2;
28.         wire [7:0]DataU2;
29.         
30.         touch_funcmod U2
31.         (
32.             .CLOCK( CLOCK ),
33.              .RESET( RESET ),
34.              .TP_CS_N( TP_CS_N ),      // > top
35.              .TP_CLK( TP_CLK ),        // > top
36.              .TP_DI( TP_DI ),          // > top
37.              .TP_DO( TP_DO ),          // < top
38.              .iCall( CallU1 ),           // < U1
39.              .oDone( DoneU2 ),        // > U1
40.              .oData( DataU2 )          // > U1
41.         );
42.    
43.    endmodule

以上內容為Touch基礎模組的連線部署,讀者自己看著辦吧。

clip_image020

圖28.10 實驗二十八的建模圖。

圖28.10是實驗二十八的建模圖,首先核心操作會初始化還有清屏TFT基礎模組,然後觸屏資訊會經由Touch基礎模組傳至核心操作,隨後核心操作再將觸屏資訊作為螢幕畫素寫入TFT屏當中。此外,筆者也更動基礎模組的繪圖功能,它會將32位寬的畫素X,畫素Y,以及顏色資訊寫入其中。廢話少說,讓我們看看該繪圖功能究竟發生什麼改變呢?

tft_ctrlmod.v

6. input [7:0]iData,

首先是tft控制模組多了兩個出入端的宣告。

29.    else if( iCall[2] )  
30.                  case( i )
31.                    
32.                         0: // X0
33.                         if( iDone ) begin isCall[2] <= 1'b0; i <= i + 1'b1; end
34.                         else begin isCall[2] <= 1'b1; D1 <= 8'h4E; D2 <= { 8'd0, iData[31:24] }; end
35.                         
36.                         1: // Y0
37.                         if( iDone ) begin isCall[2] <= 1'b0; i <= i + 1'b1; end
38.                         else begin isCall[2] <= 1'b1; D1 <= 8'h4F; D2 <= { 8'd0, iData[23:16] }; end
39.                         
40.                         2: // Write data to ram 0x22
41.                         if( iDone ) begin isCall[1] <= 1'b0; i <= i + 1'b1; end
42.                         else begin isCall[1] <= 1'b1; D1 <= 8'h22; end
43.                         
44.                         /**********/
45.                         
46.                         3: // Write color
47.                         if( iDone ) begin isCall[0] <= 1'b0; i <= i + 1'b1; end
48.                         else begin isCall[0] <= 1'b1; D2 <= [15:0]; end
49.                        
50.                         /**********/
51.                         
52.                         4:
53.                         begin isDone <= 1'b1; i <= i + 1'b1; end
54.                         
55.                         5:
56.                         begin isDone <= 1'b0; i <= 6'd0; end
57.                         
58.                    endcase

緊接著是受更動的繪圖功能。步驟0根據 Data[31:24] 設定X畫素,步驟1根據Data[23:16]設定Y畫素,然後步驟2鎖定資料該寫入的地址,最後步驟3再將Data[15:0]的影象資訊寫進去。步驟4~5則是用來產生完成訊號。讀者是不是覺得很簡單呢?

touch_demo.v
1.    module touch_demo
2.    (
3.         input CLOCK, RESET,
4.         output TFT_RST,
5.         output TFT_RS,     
6.         output TFT_CS_N,  
7.         output TFT_WR_N,
8.         output TFT_RD_N,
9.         output [15:0]TFT_DB,
10.         
11.         output TP_CS_N,
12.         output TP_CLK,
13.         input TP_IRQ,
14.         output TP_DI,
15.         input TP_DO
16.    );

以上內容為相關的出入端宣告。

17.         wire DoneU1;
18.         wire [15:0]DataU1;
19.        
20.         touch_basemod U1
21.         (
22.              .CLOCK( CLOCK ),
23.              .RESET( RESET ),
24.              .TP_CS_N( TP_CS_N ),
25.              .TP_CLK( TP_CLK ),
26.              .TP_IRQ( TP_IRQ ),
27.              .TP_DI( TP_DI ),
28.              .TP_DO( TP_DO ),
29.              .oDone( DoneU1 ),
30.              .oData( DataU1 ),
31.         );
32.         

以上內容為Touch基礎模組的例項化。

33.         wire DoneU2; 
34.         
35.         tft_basemod U2
36.         (
37.              .CLOCK( CLOCK ),
38.              .RESET( RESET ),
39.              .TFT_RST( TFT_RST ),
40.              .TFT_RS( TFT_RS ),
41.              .TFT_CS_N( TFT_CS_N ),
42.              .TFT_WR_N( TFT_WR_N ),
43.              .TFT_RD_N( TFT_RD_N ),
44.              .TFT_DB( TFT_DB ),
45.              .iCall( isCall ),
46.              .oDone( DoneU2 ),
47.              .iData( {D1,D2,D3} ),
48.         );
49.          

以上內容為TFT基礎模組的例項化。

50.         reg [5:0]i,Go;
51.         reg [2:0]isCall;
52.         reg [7:0]D1;
53.         reg [8:0]D2;
54.         reg [15:0]D3;
55.         
56.         always @ ( posedge CLOCK or negedge RESET )
57.             if( !RESET )
58.                  begin
59.                        {i,Go} <= { 6'd0,6'd0 };
60.                         isCall <= 3'd0;
61.                         { D1,D2,D3 } <= { 8'd0,9d0,16'd0 };
62.                    end
63.              else

以上內容為相關的暫存器宣告還有復位操作。

64.                  case( i )
65.                    
66.                         0: // Inital TFT
67.                         if( DoneU2 ) begin isCall[0] <= 1'b0; i <= i + 1'b1; end
68.                         else begin isCall[0] <= 1'b1; end
69.                         
70.                         1: // Clear Screen
71.                         if( DoneU2 ) begin isCall[1] <= 1'b0; i <= i + 1'b1; end
72.                         else begin isCall[1] <= 1'b1; end
73.                         
74.                         2:
75.                         if( DoneU1 ) begin i <= i + 1'b1; end
76.                         
77.                         3:
78.                         if( DoneU2 ) begin isCall[2] <= 1'b0; i <= i + 1'b1;  end
79.                         else begin isCall[2] <= 1'b1; D1 <= DataU1[15:8]; D2 <= { 1'b0, DataU1[7:0] }; D3 <= 16'd0; end
80.                         
81.                         4:
82.                         i <= 2;
83.    
84.                    endcase
85.       
86.    endmodule

以上內容為核心操作。步驟0初始化TFT顯示屏,步驟1則是清屏。步驟2等待Touch基礎模組反饋完成,然後繼續步驟。步驟3將反饋過來的X與Y以及黑色寫入其中。完後,步驟步驟2~3不停來回重複。

綜合完畢便下載程式,然後我們便可以用手指在螢幕上塗鴉了,雖然方向還有塗鴉大小有點暴走,那是因為物理畫素對螢幕畫素是不同性質的東西,結果還是視為實驗成功。

細節一:完整的個體模組

本實驗的Touch基礎模組充其量還是半身熟的雞蛋,因為當中預設重要的環節,然而這個環節卻是描述語言難以負擔的重任。

clip_image022

圖28.11 物理畫素轉換為螢幕畫素的概念圖。

如圖28.11所示,原始的物理畫素一般都包含噪聲,對此物理畫素必須預先經過一層濾波。濾波以後的物理畫素才會開始進入轉換階段,轉換階段必須借用轉換演算法的力量,論道演算法,想必有些同學會不經意縮緊眉心,因為我們必須向數學打交道不可。那怕物件是簡單乘法或者除法,讀過《時序篇》的朋友一定會曉得演算法都很麻煩。

物理畫素成功轉換為螢幕畫素以後,它還不能立即納入使用,因為轉換演算法有可能存在細節上的缺失。對此,螢幕畫素必須經過校正 ... 校正期間不僅有出現演算法,我們還要和螢幕發生互動,實在煩死人了。現階段而言,如果描述語言如果不及順序語言那麼便捷,什麼物理畫素轉換螢幕畫素,什麼校正 ... 我們最好想也不要想,不然結果只有自討苦吃。

雖然筆者有可能被指責為膽小鬼,不負責任之類的渣渣,對此筆者不否認,理性而言,那些危及生命的事情,筆者另可揹負屈辱也要逃跑,因為沒有什麼東西比生命更可貴。此刻,只要承認懦弱,我們才會安全成長,未來的事情就讓未來的自己去打算吧!少年少女們!

細節二:時序引數

老實說,筆者也覺得懦弱的自己太沒出息了 ... 不過,作為補償,讓我們來瞧瞧硬體XTP2046的時序引數吧。

clip_image024

圖28.11 XTP2046 的物理時序圖。

圖28.11是從官方手冊哪裡拷貝過來的物理時序圖,這張圖雖然有可能是嚇跑小朋友的虎姑婆 ... 不過,我們只要習慣以後,它也可能是弱小的小虎貓。換之,表28.2是各個時序引數的詳細資訊。

表28.2 時序引數的詳細資訊。

引數

說明

最大

最小

單位

tDS

DIN在 DCLK上升沿前生效

100

ns

tDH

DIN 保持在DCLK高電平後

50

ns

tDO

DCLK 下降沿到 DOUT生效

200

ns

tDV

CS 下降沿到 DOUT 使能

200

ns

tTR

CS 上升沿到 DOUT禁止

200

ns

tCSS

CS 下降沿到第一個 DCLK 上升沿

100

ns

tCSH

CS 上升沿到 DCLK被忽略

10

ns

tCH

DCLK 高電平

200

ns

tCL

DCLK低電平

200

ns

tBD

DCLK下降沿到 BUSY上升/下降

200

ns

tBDV

CS 下降沿到 BUSY 使能

200

ns

tBTR

CS 上升沿到BUSY 禁止

200

ns

clip_image026

圖28.11 DCLK相關的時序引數。

首先是時序第一員的TCH 還有TCL,如圖28.11所示,TBD+TCL+ TBD+TCH造就一個時鐘週期。根據表28.2所示,由於TBD最小為0ns,所以可以無視(TBD可以視為上山訊號還有下上訊號)。對此,造就一個時鐘週期的成分只有TCH+TCL,而且兩者最小皆是200ns,所以最高速率是:

1/(200ns + 200ns) = 2.5Mhz

話雖如此,這僅僅是手冊給出的安全速率,如果讀者是一名頭文字D的死粉,讀者隨時都可以駕駛藤原豆腐車超頻。

clip_image028

圖28.12 CS相關的時序引數。

接下來讓我們看看 TCSS 還有 TCSH,前者可以視為CS拉低所需的最小時間,後者則是CS拉高所需的最小時間。前者需要100ns,後者則是10ns,如果無法滿足它們,CS的有效性就會存在風險。話雖如此,因為手冊比較怕死,所以引數略顯保險(誇張),我們只要隨便應付就好。

clip_image030

圖28.13 資料相關的時序引數(主機視角)。

再者就是TDS還有TDH,前者是典型的setup要求,後者則是hold要求,只要兩者得到滿足,資料打入暫存器才能得到確保。首先,讀者必須注意一下圖28.13是主機視角的寫時序(從機讀資料),所以Data是主機發給從機的食物。餓昏的從機會借用上升沿鎖存時序,此刻只要 TBD+TCL 大於 TDS,又或者 TBD+TCH大於 TDH,資料就會成功被鎖存。

根據表28.2所示,TDS是100ns,TDH則是50ns,換之TBD+TCL是200ns,TBD+TCH則是200ns。簡單來說,TDS還有TDH都無法完全覆蓋資料,結果資料的有效性是得到人頭擔保的。

clip_image032

圖28.14 資料相關的時序引數(主機視角)。

圖28.14還是資料相關的時序引數,不過方向是從機給主機發送資料,當從機藉由下降沿設定資料的時候,必須被TDO拖後腿諾干時間。根據理想時序(左圖),TDO只是單純覆蓋Data而已 ... 反之,右圖的物理時序則會推擠Data。根據表28.2所示,TDO最大有可能拖後腿半個週期,反過來就是不拖任何後腿。活著就要樂觀一點,凡事都往好的方向去想,所以我們可以無視TDO。

最後還有一些僅是與CS訊號扯上關係的小囉嗦,如TDV,TTR等引數。我們僅要讀寫資料之前死拉高CS不放,讀寫資料之間死拉低CS不放,然後讀寫資料之後又死拉高CS不放,我們就能成功打發它們。

相關推薦

黑金原創教程FPGA那些事兒-驅動I 實驗流水燈模組

實驗一:流水燈模組 對於發展商而言,動土儀式無疑是最重要的任務。為此,流水燈實驗作為低階建模II的動土儀式再適合不過了。廢話少說,我們還是開始實驗吧。 圖1.1 實驗一建模圖。 如圖1.1 所示,實驗一有名為 led_funcmod的功能模組。如果無視環境訊號(時鐘訊號還有復位訊號),該功能模組只有

黑金原創教程FPGA那些事兒-驅動I 連載導讀

前言: 無數晝夜的來回輪替以後,這本《驅動篇I》終於編輯完畢了,筆者真的感動到連鼻涕也流下來。所謂驅動就是認識硬體,還有前期建模。雖然《驅動篇I》的硬體都是我們熟悉的老友記,例如UART,VGA等,但是《驅動篇I》貴就貴在建模技巧的昇華,亦即低階建模II。 話說低階建模II,讀過《建模篇》的朋友多少也會面

黑金原創教程FPGA那些事兒-驅動I 實驗按鍵模組② — 點選與長點選

實驗三:按鍵模組② — 點選與長點選 實驗二我們學過按鍵功能模組的基礎內容,其中我們知道按鍵功能模組有如下操作: l 電平變化檢測; l 過濾抖動; l 產生有效按鍵。 實驗三我們也會z執行同樣的事情,不過卻是產生不一樣的有效按鍵: l 按下有效(點選); l 長按下有效(長點選)。 圖3

黑金原創教程FPGA那些事兒-驅動I 實驗按鍵模組

實驗二:按鍵模組① - 消抖 按鍵消抖實驗可謂是經典中的經典,按鍵消抖實驗雖曾在《建模篇》出現過,而且還惹來一堆麻煩。事實上,筆者這是在刁難各位同學,好讓對方的慣性思維短路一下,但是慘遭口水攻擊 ... 面對它,筆者宛如被甩的男人,對它又愛又恨。不管怎麼樣,如今 I’ll be back,筆者再也不會重複一

黑金原創教程FPGA那些事兒-驅動I 實驗數碼管模組

實驗六:數碼管模組 有關數碼管的驅動,想必讀者已經學爛了 ... 不過,作為學習的新儀式,再爛的東西也要溫故知新,不然學習就會不健全。黑金開發板上的數碼管資源,由始至終都沒有改變過,筆者因此由身懷念。為了點亮多位數碼管從而顯示數字,一般都會採用動態掃描,然而有關動態掃描的資訊請怒筆者不再重複。在此,同樣也是

黑金原創教程FPGA那些事兒-驅動I 實驗按鍵模組③ — 單擊與雙擊

實驗四:按鍵模組③ — 單擊與雙擊 實驗三我們建立了“點選”還有“長點選”等有效按鍵的多功能按鍵模組。在此,實驗四同樣也是建立多功能按鍵模組,不過卻有不同的有效按鍵。實驗四的按鍵功能模組有以下兩項有效按鍵: l 單擊(按下有效); l 雙擊(連續按下兩下有效)。 圖4.1 單擊有效按鍵,時序示意圖

黑金原創教程FPGA那些事兒-驅動I 實驗按鍵模組④ — 點選,長點選,雙擊

實驗五:按鍵模組④ — 點選,長點選,雙擊 實驗二至實驗四,我們一共完成如下有效按鍵: l 點選(按下有效) l 點選(釋放有效) l 長擊(長按下有效) l 雙擊(連續按下有效) 然而,不管哪個實驗都是隻有兩項“功能”的按鍵模組而已,如今我們要建立三項“功能”的按鍵模組,亦即點選(按下有效),長

黑金原創教程FPGA那些事兒-驅動I 原創教程連載導讀連載完成,共九章

前言: 無數晝夜的來回輪替以後,這本《驅動篇I》終於編輯完畢了,筆者真的感動到連鼻涕也流下來。所謂驅動就是認識硬體,還有前期建模。雖然《驅動篇I》的硬體都是我們熟悉的老友記,例如UART,VGA等,但是《驅動篇I》貴就貴在建模技巧的昇華,亦即低階建模II。 話說低階建模II,讀過《建模篇》的朋友多少也會面

黑金原創教程FPGA那些事兒-驅動I 實驗SDRAM模組④ — 頁讀寫 β

實驗二十一:SDRAM模組④ — 頁讀寫 β 未進入主題之前,讓我們先來談談一些重要的體外話。《整合篇》之際,筆者曾經比擬Verilog如何模仿for迴圈,我們知道for迴圈是順序語言的產物,如果Verilog要實現屬於自己的for迴圈,那麼它要考慮的東西除了步驟以外,還有非常關鍵的時鐘。 for(

黑金原創教程FPGA那些事兒-驅動I 實驗PS/2模組④ — 普通滑鼠

實驗十:PS/2模組④ — 普通滑鼠 學習PS/2鍵盤以後,接下來就要學習 PS/2 滑鼠。PS/2滑鼠相較PS/2鍵盤,驅動難度稍微高了一點點,因為FPGA(從機)不僅僅是從PS/2滑鼠哪裡讀取資料,FPGA還要往滑鼠裡寫資料 ... 反之,FPGA只要對PS/2鍵盤讀取資料即可。然而,最傷腦筋的地方就在

黑金原創教程FPGA那些事兒-驅動I 實驗SDRAM模組① — 單字讀寫

實驗十八:SDRAM模組① — 單字讀寫 筆者與SDRAM有段不短的孽緣,它作為冤魂日夜不斷糾纏筆者。筆者嘗試過許多方法將其退散,不過屢試屢敗的筆者,最終心情像橘子一樣橙。《整合篇》之際,筆者曾經大戰幾回兒,不過內容都是點到即止。最近它破蠱而出,日夜不停:“好~痛苦!好~痛苦!”地呻吟著,嚇得筆者不敢半夜如

黑金原創教程FPGA那些事兒-驅動I 實驗TFT模組

實驗二十七:TFT模組 - 顯示 所謂TFT(Thin Film Transistor)就是眾多LCD當中,其中一種支援顏色的LCD,相較古老的點陣LCD(12864笑),它可謂高階了。黑金的TFT LCD除了320×240大小以外,內建SSD1289控制器,同時也是獨立模組。事實上,無論是驅動點陣LCD還

黑金原創教程FPGA那些事兒-驅動I 實驗十三串列埠模組② — 接收

實驗十三:串列埠模組② — 接收 我們在實驗十二實現了串列埠傳送,然而這章實驗則要實現串列埠接收 ... 在此,筆者也會使用其它思路實現串列埠接收。 圖13.1 模組之間的資料傳輸。 假設我們不考慮波特率,而且一幀資料之間的傳輸也只是發生在FPGA之間,即兩隻模組之間互轉,並且兩塊模組都使用相同的時

黑金原創教程FPGA那些事兒-驅動I 實驗PS/2模組① — 鍵盤

實驗七:PS/2模組① — 鍵盤 實驗七依然也是熟爛的PS/2鍵盤。相較《建模篇》的PS/2鍵盤實驗,實驗七實除了實現基本的驅動以外,我們還要深入解PS/2時序,還有PS/2鍵盤的行為。不過,為了節省珍貴的頁數,怒筆者不再重複有關PS/2的基礎內容,那些不曉得的讀者請複習《建模篇》或者自行谷歌一下。 市場

黑金原創教程FPGA那些事兒-驅動I 實驗PS/2模組③ — 鍵盤與多組合鍵

實驗九:PS/2模組③ — 鍵盤與多組合鍵 筆者曾經說過,通碼除了單位元組以外,也有雙位元組通碼,而且雙位元組通碼都是 8’hE0開頭,別名又是 E0按鍵。常見的的E0按鍵有,<↑>,<↓>,<←>,<→>,<HOME>,<PRTSC>

黑金原創教程FPGA那些事兒-驅動I 實驗SDHC模組

實驗二十五:SDHC模組 筆者曾經說過,SD卡發展至今已經衍生許多版本,實驗二十四就是針對版本SDV1.×的SD卡。實驗二十四也說過,CMD24還有CMD17會故意偏移地址29,讓原本範圍指向從原本的232 變成 223,原因是SD卡讀寫一次都有512個位元組。為此我們可以這樣計算: SDV1.x = 2

黑金原創教程FPGA那些事兒-驅動I 實驗SDRAM模組③ — 頁讀寫 α

實驗二十:SDRAM模組③ — 頁讀寫 α 完成單字讀寫與多字讀寫以後,接下來我們要實驗頁讀寫。醜話當前,實驗二十的頁讀寫只是實驗性質的東西,其中不存在任何實用價值,筆者希望讀者可以把它當成頁讀寫的熱身運動。 表示20.1 Mode Register的內容。 Mode Register

黑金原創教程FPGA那些事兒-驅動I 實驗TFT模組

實驗二十八:TFT模組 - 觸屏 讀者在上一個實驗所玩弄過的 TFT LCD模組,除了顯示大小為 320 × 240,顏色為16位RGB的影象資訊以外,它還支援觸屏。所謂觸屏就是滑鼠還有鍵盤以外的輸入手段,例如現在流行平板還有智慧手機,觸屏輸入對我們來說,已經成為日常的一部分。描述語言一門偏向硬體的語言

黑金原創教程FPGA那些事兒-驅動I 實驗SDRAM模組⑤ — FIFO讀寫

經過漫長的戰鬥以後,我們終於來到最後。對於普通人而言,頁讀寫就是一名戰士的墓碑(最終戰役) ... 然而,怕死的筆者想透過這個實驗告訴讀者,旅程的終點就是旅程的起點。一直以來,筆者都在煩惱“SDRAM是否應該成為儲存類?”SDRAM作為一介儲存資源(儲存器),它的好處就是大容量空間,壞處則就是麻煩的控制規

黑金原創教程FPGA那些事兒-驅動I 實驗儲存模組

實驗十四比起動手筆者更加註重原理,因為實驗十四要討論的東西,不是其它而是低階建模II之一的模組類,即儲存模組。接觸順序語言之際,“儲存”不禁讓人聯想到變數或者陣列,結果它們好比資料的暫存空間。 1. int main() 2. { 3. int VarA; 4.