【黑金原創教程】【FPGA那些事兒-驅動篇I 】實驗二十八:TFT模組
實驗二十八:TFT模組 - 觸屏
讀者在上一個實驗所玩弄過的 TFT LCD模組,除了顯示大小為 320 × 240,顏色為16位RGB的影象資訊以外,它還支援觸屏。所謂觸屏就是滑鼠還有鍵盤以外的輸入手段,例如現在流行平板還有智慧手機,觸屏輸入對我們來說,已經成為日常的一部分。描述語言一門偏向硬體的語言,面對觸屏,它頂多只能做做一些驅動的工作,其餘如濾波,還有畫素轉換等計算,它必須交由高階語言去負責。
面向無能為力的描述語言,筆者不禁聯想過去的自己 ... 好痛苦,好難受。話雖如此,天生我才必有用,描述語言雖然完成不了觸屏的偉業,不過只要做做驅動,平穩過著日子,它便心滿意足了。
圖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讀取。好奇的同學可能會繼續好奇什麼是物理畫素?這個問題說來話長 ... 假設螢幕是一個容器,藏在容器裡邊的畫素就稱為螢幕畫素,然而圍繞容器外邊的畫素就稱為物理畫素。
比喻來講,同是人形生物,住在地球的人形生物就稱為地球人,住在地球以外的人形生物就稱為外星人。結果而言,地球人並不等價外星人,雖然兩者都有相似的外表。所以說,物理畫素還有螢幕畫素,它們聽起來都是畫素,不過實際上是不等價的東西。對此,物理畫素為了成為螢幕畫素,它們必須經過“轉換演算法”才能成為如假包換的螢幕畫素。不過,“轉換演算法”對描述語言來說,這項工作實在太巨大了。
好了好了,筆者也差不多要進入主題了,廢話再說下去,口水就會把電腦淹沒了。
圖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則是從機。
圖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行則是由高至低逐位賦值。
圖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哪裡暫存結果。
圖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訊號。
圖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
準備知識理解完畢以後,我們便可以開始建模了。
圖28.7 Touch基礎模組的建模圖。
圖28.7是Touch基礎模組,內容包含控制模組還有功能模組。功能模組左邊有兩位寬的Call/Done,其中[1]為讀取物理畫素X,[0]為讀取物理畫素Y。至於右邊則是一些頂層訊號。控制模組的左邊,除了頂層訊號IRQ是輸入以外,其餘訊號都呈現輸出狀態。16位寬的Data對應物理畫素X還有物理畫素Y,而Done則是溝通作用的觸發訊號。
整體來說,一旦觸碰觸屏,IRQ拉低,控制模組利用功能模組讀取物理畫素X與Y,然後再產生完成訊號以示一次性的觸屏結果。
touch_funcmod.v
圖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
圖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基礎模組的連線部署,讀者自己看著辦吧。
圖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基礎模組充其量還是半身熟的雞蛋,因為當中預設重要的環節,然而這個環節卻是描述語言難以負擔的重任。
圖28.11 物理畫素轉換為螢幕畫素的概念圖。
如圖28.11所示,原始的物理畫素一般都包含噪聲,對此物理畫素必須預先經過一層濾波。濾波以後的物理畫素才會開始進入轉換階段,轉換階段必須借用轉換演算法的力量,論道演算法,想必有些同學會不經意縮緊眉心,因為我們必須向數學打交道不可。那怕物件是簡單乘法或者除法,讀過《時序篇》的朋友一定會曉得演算法都很麻煩。
物理畫素成功轉換為螢幕畫素以後,它還不能立即納入使用,因為轉換演算法有可能存在細節上的缺失。對此,螢幕畫素必須經過校正 ... 校正期間不僅有出現演算法,我們還要和螢幕發生互動,實在煩死人了。現階段而言,如果描述語言如果不及順序語言那麼便捷,什麼物理畫素轉換螢幕畫素,什麼校正 ... 我們最好想也不要想,不然結果只有自討苦吃。
雖然筆者有可能被指責為膽小鬼,不負責任之類的渣渣,對此筆者不否認,理性而言,那些危及生命的事情,筆者另可揹負屈辱也要逃跑,因為沒有什麼東西比生命更可貴。此刻,只要承認懦弱,我們才會安全成長,未來的事情就讓未來的自己去打算吧!少年少女們!
細節二:時序引數
老實說,筆者也覺得懦弱的自己太沒出息了 ... 不過,作為補償,讓我們來瞧瞧硬體XTP2046的時序引數吧。
圖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 |
圖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的死粉,讀者隨時都可以駕駛藤原豆腐車超頻。
圖28.12 CS相關的時序引數。
接下來讓我們看看 TCSS 還有 TCSH,前者可以視為CS拉低所需的最小時間,後者則是CS拉高所需的最小時間。前者需要100ns,後者則是10ns,如果無法滿足它們,CS的有效性就會存在風險。話雖如此,因為手冊比較怕死,所以引數略顯保險(誇張),我們只要隨便應付就好。
圖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都無法完全覆蓋資料,結果資料的有效性是得到人頭擔保的。
圖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.