【黑金原創教程】【FPGA那些事兒-驅動篇I 】實驗十一:PS/2模組⑤ — 擴充套件滑鼠
實驗十一:PS/2模組⑤ — 擴充套件滑鼠
當普通滑鼠即三鍵滑鼠再也無法滿足需求的時候,擴充套件滑鼠即滾輪滑鼠就誕生了,然而實驗十一的實驗目的就是實現滾輪滑鼠的驅動。不過,進入整體之前,先讓我們來了解一下滑鼠的常用命令。
圖11.1 命令F3,設定取樣頻率。
命令F3也是Set Sample Rate,主要是用來設定採集頻率。筆者曾經說過,採集頻率就是滑鼠採集按鍵狀況還有位置狀況的間隔時間,預設下是100次/秒。如圖11.1所示,FPGA先發送命令資料8’hF3,事後滑鼠會反饋8’hFA以示接收成功,餘下FPGA再發送引數資料8’d200,滑鼠接收成功後也會反饋 8’hFA。如此一來,滑鼠的採集頻率從原本的 100次/秒,變成 200次/秒。
圖11.2 命令E8,設定解析度。
命令E8也是 Set Resolution,主要是用來設定解析度。所謂解析度就是位置對應暫存器計數的單位,預設下是4計數/mm,亦即 1mm 的距離,滑鼠計數4下。如圖11.2所示,FPGA先發送命令資料 8’hE8,滑鼠接收以後便反饋 8’hFA,FPGA隨之也會發送引數資料 8’h01,滑鼠接收以後也會反饋資料 8’hFA。完後,滑鼠的分辨從原本的 4計數/mm 變成 2計數/mm。
引數資料所對應的解析度如表11.1所示:
表11.1 引數資料所對應的解析度。
引數資料 |
解析度 |
8’h00 |
1計數/mm |
8’h01 |
2計數/mm |
8’h02 |
4計數/mm |
8’h03 |
8計數/mm |
圖11.3 命令F6,使用預設引數。
假設筆者手癢,不小心打亂滑鼠內部的引數資料,此刻筆者可以傳送命令F6,即Set Defaults將引數資料回覆成原來的預設值。如圖11.3所示,FPGA先發送命令資料8’hF6
,滑鼠完成接收以後便會反饋8’hFA。
圖11.4 命令F4使能報告,命令F5關閉報告。
PS/2滑鼠不像PS/2鍵盤,上電並且完成初始化以後它便會陷入發呆狀態,如果不傳送命令資料8’hF4(即Enable Data Report)手動開啟滑鼠的水龍頭,滑鼠是不會發送報告(即夾雜按鍵狀況與位置狀況的資料)。如圖11.4所示,FPGA先發送命令資料8’hF4,滑鼠接收以後便會反饋8’hFA,事後滑鼠立即處於就緒狀態,一旦按鍵狀況或者位置狀況發生改變,滑鼠就會發送報告。
假設讀者覺得滑鼠太嘮叨,什麼大事小事都報告,筆者可以傳送命令資料 8’hF5(即 Disable Data Report)為了使其閉嘴。如圖11.4所示,FPGA先發送命令資料 8’hF4,滑鼠接收完畢以後便會反饋8’hFA,事後滑鼠就成為閉嘴狀態,大事小事再也不會煩人。如果讀者覺得寂寞,讀者可以再度傳送命令資料 8’hF4,讓滑鼠再度唱歌。
圖11.5 命令F2,讀取滑鼠ID。
為了區分滑鼠是普通滑鼠還是擴充套件滑鼠,期間我們必須使用命令8’hF2,即 Get Device ID。如圖11.5所示,FPGA傳送命令資料 8’hF2,滑鼠接收以後先反饋 8’hFA,再來便傳送滑鼠ID。如果內容是8’h00,則表示該滑鼠只是普通滑鼠 ... 反之,如果內容是 8’h03,那麼該滑鼠就是擴充套件滑鼠。因為如此,我們需要更改一下偽函式,結果如程式碼11.1所示:
1. 32: // Press low PS2_CLK 100us
2. if( C1 == T100US -1 ) begin C1 <= 13'd0; i <= i + 1'b1; end
3. else begin isQ1 = 1'b1; rCLK <= 1'b0; C1 <= C1 + 1'b1; end
4.
5. 33: // release PS2_CLK and set in ,PS2_DAT set out
6. begin isQ1 <= 1'b0; rCLK <= 1'b1; isQ2 <= 1'b1; i <= i + 1'b1; end
7.
8. 34: // start bit 1
9. begin rDAT <= 1'b0; i <= i + 1'b1; end
10.
11. 35,36,37,38,39,40,41,42,43: // data bit 9
12. if( isH2L ) begin rDAT <= T[ i-35 ]; i <= i + 1'b1; end
13.
14. 44: // stop bit 1
15. if( isH2L ) begin rDAT <= 1'b1; i <= i + 1'b1; end
16.
17. 45: // Ack bit
18. if( isH2L ) begin i <= i + 1'b1; end
19.
20. 46: // PS2_DAT set in
21. begin isQ2 <= 1'b0; i <= i + 1'b1; end
22.
23. /***********/ // Receive 1st Frame
24.
25. 47,48,49,50,51,52,53,54,55,56,57: // Ingnore
26. if( isH2L ) i <= i + 1'b1;
27.
28. 58: // Check comd F2
29. if( T[7:0] == 8'hF2 ) i <= i + 1'b1;
30. else i <= Go;
31.
32. /***********/ // Receive 2nd Frame
33.
34. 59: // Start bit 1
35. if( isH2L ) i <= i + 1'b1;
36.
37. 60,61,62,63,64,65,66,67,68: // Data bit 9
38. if( isH2L ) begin T[i-60] <= PS2_DAT; i <= i + 1'b1; end
39.
40. 69: // Stop bit 1
41. if( isH2L ) i <= Go;
程式碼11.1
如程式碼11.1所示,步驟32~57則是傳送一幀資料又忽略一幀反饋,基本上與實驗十一模一樣。至於第58行則是用來判斷,FPGA所傳送的命令是否是 8’hF2即 Get Device ID
?如果是,步驟則繼續讀取操作,因為命令8’hF2令滑鼠反饋8’hFA之餘,還會導致滑鼠會發送一幀ID資料。否則的話,即表示其他命令,步驟返回。步驟59~69是用來讀取下一幀ID資料,期間步驟60~68用來讀取 8位資料位,還有1位校驗位。完後,步驟便返回。
小時候的筆者很愛假扮刺客,筆者與近鄰的小孩就總是瞎著玩,其它小朋友則扮演祕密商人。刺客為了與祕密商人進行交易,兩者之間必須經過暗語核對,例如:
“陽光的男孩赤裸裸 ... ”,對方問道。
“對面的女來看過來 ... ”,筆者答道。
滾輪滑鼠也是擴充套件滑鼠,上電以後也不會立即變成擴充套件滑鼠,如果擴充套件滑鼠不經過核對暗語,擴充套件滑鼠也是一隻普通的3鍵滑鼠而已 ... 反之,如果完成暗語核對,擴充套件滑鼠才會發揮滾輪功能。
圖11.6 設定擴充套件滑鼠的暗語。
如圖11.6所示,那是設定擴充套件滑鼠的暗語:
傳送命令資料 8’hF3,接收反饋8’hFA,再發送引數資料 8’hC8,在接收反饋8’hFA;
傳送命令資料 8’hF3,接收反饋8’hFA,再發送引數資料 8’h64,在接收反饋8’hFA;
傳送命令資料 8’hF3,接收反饋8’hFA,再發送引數資料 8’h50,在接收反饋8’hFA;
傳送命令資料 8’hF2,接收反饋8’hFA,再接收滑鼠ID8’h03。
完後,滑鼠便成為擴充套件滑鼠,內部也自動初始化,然後進入預設模式。
圖11.7 擴充套件滑鼠標示的位置。
普通滑鼠相較擴充套件滑鼠,它多了滾輪功能,即滑鼠除了標示左鍵,中鍵,右鍵,X還有Y以外,擴充套件還會標示Z。如圖11.7所示,X與Y可以看成面積,至於Z則可以看成上下。當滑鼠向西移動,X呈現正直,反之負值;當滑鼠向北移動,Y呈現正直,反之負值;當滾動向下活動,Z呈現正直,反之負值。
圖11.8 擴充套件滑鼠的報告長度。
為此,擴充套件滑鼠相較普通滑鼠,報告長度則多了一個位元組。如圖11.8所示,當滑鼠察覺變化以後,滑鼠便會發送4個位元組長度的報告,然而位元組之間的位分配如表11.1所示:
表11.1 Device ID 為 8’h03 的報告內容。
位元組/位 |
[7] |
[6] |
[5] |
[4] |
[3] |
[2] |
[1] |
[0] |
位元組一 |
Y溢位位 |
X溢位位 |
Y[8]符號位 |
X[8]符號位 |
保留 |
中鍵 |
右鍵 |
左鍵 |
位元組二 |
X[7:0] |
|||||||
位元組三 |
Y[7:0] |
|||||||
位元組四 |
保留 |
保留 |
保留 |
保留 |
Z[3]符號位 |
Z[2] |
Z[1] |
Z[0] |
筆者需要補充一下 ... 由於早期Intel 稱王,所以擴充套件滑鼠標準都是Intel說話算話,Device ID 為 8’h03 就是其中一種擴充套件標準。如表11.1所示,位元組一至位元組三基本上變化不大,反之位元組四則稍微不同。位元組四的[2..0]位是 Z[2:0],位元組四的[3]是Z[3],也是Z的符號位。換句話說,暫存器Z有4位,內容用補碼錶示。
圖11.9 擴充套件滑鼠的位置範圍。
圖11.9表示擴充套件滑鼠的位置範圍,X與Y與普通滑鼠一樣,Z比較畸形一點,因為Z向上不是正直而是負值,反之亦然。Z的有效範圍是 4’b1001~4’b0111或者 -7~7,也就是說滾輪向下活動,暫存器Z就遞增,向上滾動,暫存器Z就遞減。
上述內容理解完畢以後,我們便可以開始建模了:
圖11.10 實驗十一的建模圖。
如圖11.10所示,組合模組 ps2_demo 包含的內容與實驗十相差不了多少,不過卻少了正直化的即時操作。期間,PS/2初始化功能模組的 oEn 有兩位,oEn[1] 拉高表示滑鼠為擴充套件滑鼠,oEn[0] 拉高表示滑鼠為普通滑鼠。PS/2讀取功能模組的 oData[2:0] 直接驅動LED資源, oData[27:4]則驅動數碼管基礎模組的 iData。
ps2_init_funcmod.v
圖11.11 PS/2初始化功能模組的建模圖。
如圖11.11所示,PS/2初始化功能模組有兩位oEn,[1]拉高表示滑鼠為擴充套件滑鼠,[0]拉高則表示滑鼠為普通滑鼠。
1. module ps2_init_funcmod
2. (
3. input CLOCK, RESET,
4. inout PS2_CLK,
5. inout PS2_DAT,
6. output [1:0]oEn
7. );
8. parameter T100US = 13'd5000;
9. parameter FF_Write = 7'd32;
以上內容為相關的出入端宣告。第8行是100us的常量宣告,第9行則是偽函式的入口地址。
11. /*******************************/ // sub1
12.
13. reg F2,F1;
14.
15. always @ ( posedge CLOCK or negedge RESET )
16. if( !RESET )
17. { F2,F1 } <= 2'b11;
18. else
19. { F2, F1 } <= { F1, PS2_CLK };
20.
21. /*******************************/ // core
22.
23. wire isH2L = ( F2 == 1'b1 && F1 == 1'b0 );
以上內容是用來檢測電平變化的周邊操作,第23行則是下降沿的即時宣告。
24. reg [8:0]T;
25. reg [6:0]i,Go;
26. reg [12:0]C1;
27. reg rCLK,rDAT;
28. reg isQ1,isQ2,isEx;
29. reg [1:0]isEn;
30.
31. always @ ( posedge CLOCK or negedge RESET )
32. if( !RESET )
33. begin
34. T <= 9'd0;
35. C1 <= 13'd0;
36. { i,Go } <= { 7'd0,7'd0 };
37. { rCLK,rDAT } <= 2'b11;
38. { isQ1,isQ2,isEx } <= 3'b000;
39. isEn <= 2'b00;
40. end
41. else
以上內容是相關的暫存器宣告,第33~39行則是這群暫存器的復位操作。其中isEn有兩位,isEx為擴充套件滑鼠的立旗。
42. case( i )
43.
44. /***********/ // INIT Mouse
45.
46. 0: // Send F3 1111_0011
47. begin T <= { 1'b1, 8'hF3 }; i <= FF_Write; Go <= i + 1'b1; end
48.
49. 1: // Send C8 1100_1000
50. begin T <= { 1'b0, 8'hC8 }; i <= FF_Write; Go <= i + 1'b1; end
51.
52. 2: // Send F3 1111_0011
53. begin T <= { 1'b1, 8'hF3 }; i <= FF_Write; Go <= i + 1'b1; end
54.
55. 3: // Send 64 0110_1000
56. begin T <= { 1'b0, 8'h64 }; i <= FF_Write; Go <= i + 1'b1; end
57.
58. 4: // Send F3 1111_0011
59. begin T <= { 1'b1, 8'hF3 }; i <= FF_Write; Go <= i + 1'b1; end
60.
61. 5: // Send 50 0101_0000
62. begin T <= { 1'b1, 8'h50 }; i <= FF_Write; Go <= i + 1'b1; end
63.
64. 6: // Send F2 1111_0010
65. begin T <= { 1'b0, 8'hF2 }; i <= FF_Write; Go <= i + 1'b1; end
66.
67. 7: // Check Mouse ID 00(normal), 03(extend)
68. if( T[7:0] == 8'h03 ) begin isEx <= 1'b1; i <= i + 1'b1; end
69. else if( T[7:0] == 8'h00 ) begin isEx <= 1'b0; i <= i + 1'b1; end
70.
71. 8: // Send F4 1111_0100
72. begin T <= { 1'b0, 8'hF4 }; i <= FF_Write; Go <= i + 1'b1; end
73.
74. 9:
75. if( isEx ) isEn[1] <= 1'b1;
76. else if( !isEx ) isEn[0] <= 1'b1;
77.
以上內容是核心操作。步驟0~9是主操作,步驟0~6則是傳送用來開啟擴充套件滑鼠的暗語,步驟7用來判斷滑鼠返回的 Device ID 是否為 8’h03,如果是 isEx 立旗,否則 isEx 消除立旗。步驟8用來使能滑鼠。步驟9根據 isEx 的狀態再來決定 isEn的結果, 如果isEx為1 isEn[1] 便拉高,否則 isEx 拉高,完後步驟停留。
78. /****************/ // PS2 Write Function
79.
80. 32: // Press low PS2_CLK 100us
81. if( C1 == T100US -1 ) begin C1 <= 13'd0; i <= i + 1'b1; end
82. else begin isQ1 = 1'b1; rCLK <= 1'b0; C1 <= C1 + 1'b1; end
83.
84. 33: // release PS2_CLK and set in ,PS2_DAT set out
85. begin isQ1 <= 1'b0; rCLK <= 1'b1; isQ2 <= 1'b1; i <= i + 1'b1; end
86.
87. 34: // start bit 1
88. begin rDAT <= 1'b0; i <= i + 1'b1; end
89.
90. 35,36,37,38,39,40,41,42,43: // data bit 9
91. if( isH2L ) begin rDAT <= T[ i-35 ]; i <= i + 1'b1; end
92.
93. 44: // stop bit 1
94. if( isH2L ) begin rDAT <= 1'b1; i <= i + 1'b1; end
95.
96. 45: // Ack bit
97. if( isH2L ) begin i <= i + 1'b1; end
98.
99. 46: // PS2_DAT set in
100. begin isQ2 <= 1'b0; i <= i + 1'b1; end
101.
102. /***********/ // Receive 1st Frame
103.
104. 47,48,49,50,51,52,53,54,55,56,57: // Ingnore
105. if( isH2L ) i <= i + 1'b1;
106.
107. 58: // Check comd F2
108. if( T[7:0] == 8'hF2 ) i <= i + 1'b1;
109. else i <= Go;
110.
以上內容是部分核心操作。步驟32~58是部分偽函式,內容則是傳送一幀資料,再讀取一幀反饋,完後便進入步驟58判斷,傳送的命令是否為 8’hF2,如果是便繼續步驟,否則便返回步驟。
111. /***********/ // Receive 2nd Frame
112.
113. 59: // Start bit 1
114. if( isH2L ) i <= i + 1'b1;
115.
116. 60,61,62,63,64,65,66,67,68: // Data bit 9
117. if( isH2L ) begin T[i-60] <= PS2_DAT; i <= i + 1'b1; end
118.
119. 69: // Stop bit 1
120. if( isH2L ) i <= Go;
121.
122. endcase
123.
以上內容是部分核心操作。步驟59~69也是部分偽函式,主要用來讀取下一幀資料的位元組內容,在此是針對命令8’hF2,也就是Device ID。讀完一幀資料以後便返回步驟。
124. assign PS2_CLK = isQ1 ? rCLK : 1'bz;
125. assign PS2_DAT = isQ2 ? rDAT : 1'bz;
126. assign oEn = isEn;
127.
128. endmodule
以上內容為驅動輸出宣告。
ps2_read_funcmod.v
圖11.12 PS/2讀功能模組的建模圖。
實驗十一的PS/2讀功能模組與實驗十相比,左邊的 iEn出入多出一位以外,右邊的oData也多出一個位元組。
1. module ps2_read_funcmod
2. (
3. input CLOCK, RESET,
4. input PS2_CLK,PS2_DAT,
5. input [1:0]iEn,
6. output oTrig,
7. output [31:0]oData
8. );
9. parameter FF_Read = 7'd32;
以上內容是相關的出入端宣告。第9行是偽函式的入口。
10.
11. /*******************************/ // sub1
12.
13. reg F2,F1;
14.
15. always @ ( posedge CLOCK or negedge RESET )
16. if( !RESET )
17. { F2,F1 } <= 2'b11;
18. else
19. { F2, F1 } <= { F1, PS2_CLK };
20.
21. /*******************************/ // core
22.
23. wire isH2L = ( F2 == 1'b1 && F1 == 1'b0 );
以上內容是檢測電平變化的周邊操作,第23行則是下降沿的即時宣告。
24. reg [31:0]D1;
25. reg [7:0]T;
26. reg [6:0]i,Go;
27. reg isDone;
28.
29. always @ ( posedge CLOCK or negedge RESET )
30. if( !RESET )
31. begin
32. D1 <= 32'd0;
33. T <= 8'd0;
34. { i,Go } <= { 7'd0,7'd0 };
35. isDone <= 1'b0;
36. end
以上內容為相關的暫存器宣告以及復位操作。
37. else if( iEn[1] )
38. case( i )
39.
40. /***********/ // Extend Mouse Read Data
41.
42. 0: // Read Data 1st byte
43. begin i <= FF_Read; Go <= i + 1'b1; end
44.
45. 1: // Store Data 1st byte
46. begin D1[7:0] <= T; i <= i + 1'b1; end
47.
48. 2: // Read Data 2nd byte
49. begin i <= FF_Read; Go <= i + 1'b1; end
50.
51. 3: // Store Data 2nd byte
52. begin D1[15:8] <= T; i <= i + 1'b1; end
53.
54. 4: // Read Data 3rd byte
55. begin i <= FF_Read; Go <= i + 1'b1; end
56.
57. 5: // Store Data 3rd byte
58. begin D1[23:16] <= T; i <= i + 1'b1; end
59.
60. 6: // Read Data 4rd byte
61. begin i <= FF_Read; Go <= i + 1'b1; end
62.
63. 7: // Store Data 4rd byte
64. begin D1[31:24] <= T; i <= i + 1'b1; end
65.
66. 8:
67. begin isDone <= 1'b1; i <= i + 1'b1; end
68.
69. 9:
70. begin isDone <= 1'b0; i <= 7'd0; end
以上內容為部分核心操作。第37行的 if( iEn[1] ) 表示下面所有內容都是擴充套件滑鼠的核心操作。步驟0~7則是讀取4個位元組的資料,步驟8~9用來產生完成訊號以示一次性的報告已經接收完畢。
71.
72. /****************/ // PS2 Write Function
73.
74. 32: // Start bit
75. if( isH2L ) i <= i + 1'b1;
76.
77. 33,34,35,36,37,38,39,40: // Data byte
78. if( isH2L ) begin T[i-33] <= PS2_DAT; i <= i + 1'b1; end
79.
80. 41: // Parity bit
81. if( isH2L ) i <= i + 1'b1;
82.
83. 42: // Stop bit
84. if( isH2L ) i <= Go;
85.
86. endcase
以上內容為部分核心操作。步驟32~42是讀取一幀資料的偽函式。
87. else if( iEn[0] )
88. case( i )
89.
90. /***********/ // Normal Mouse Read Data
91.
92. 0: // Read Data 1st byte
93. begin i <= FF_Read; Go <= i + 1'b1; end
94.
95. 1: // Store Data 1st byte
96. begin D1[7:0] <= T; i <= i + 1'b1; end
97.
98. 2: // Read Data 2nd byte
99. begin i <= FF_Read; Go <= i + 1'b1; end
100.
101. 3: // Store Data 2nd byte
102. begin D1[15:8] <= T; i <= i + 1'b1; end
103.
104. 4: // Read Data 3rd byte
105. begin i <= FF_Read; Go <= i + 1'b1; end
106.
107. 5: // Store Data 3rd byte
108. begin D1[23:16] <= T; i <= i + 1'b1; end
109.
110. 6:
111. begin isDone <= 1'b1; i <= i + 1'b1; end
112.
113. 7:
114. begin isDone <= 1'b0; i <= 7'd0; end
115.
以上內容為部分核心操作。第87行的 if( iEn[0] ) 表示下面的內容均為普通滑鼠的核心操作。步驟0~5用來讀取3個位元組的內容,步驟6~7則用來產生完成訊號以示一次性的報告已經讀取完畢。
116. /****************/ // PS2 Write Function
117.
118. 32: // Start bit
119. if( isH2L ) i <= i + 1'b1;
120.
121. 33,34,35,36,37,38,39,40: // Data byte
122. if( isH2L ) begin T[i-33] <= PS2_DAT; i <= i + 1'b1; end
123.
124. 41: // Parity bit
125. if( isH2L ) i <= i + 1'b1;
126.
127. 42: // Stop bit
128. if( isH2L ) i <= Go;
129.
130. endcase
131.
以上內容為部分核心操作。步驟32~42是讀取一幀資料的偽函式。
132. assign oTrig = isDone;
133. assign oData = D1;
134.
135. endmodule
以上內容是輸出驅動宣告。
ps2_demo.v
筆者就不重複貼上實驗十一的建模圖了,具體內容我們還是來看程式碼吧。
1. module ps2_demo
2. (
3. input CLOCK, RESET,
4. inout PS2_CLK, PS2_DAT,
5. output [7:0]DIG,
6. output [5:0]SEL,
7. output [2:0]LED
8. );
9. wire [1:0]EnU1;
10.
11. ps2_init_funcmod U1
12. (
13. .CLOCK( CLOCK ),
14. .RESET( RESET ),
15. .PS2_CLK( PS2_CLK ), // < top
16. .PS2_DAT( PS2_DAT ), // < top
17. .oEn( EnU1 ) // > U2
18. );
19.
20. wire [31:0]DataU2;
21.
22. ps2_read_funcmod U2
23. (
24. .CLOCK( CLOCK ),
25. .RESET( RESET ),
26. .PS2_CLK( PS2_CLK ), // < top
27. .PS2_DAT( PS2_DAT ), // < top
28. .iEn( EnU1 ), // < U1
29. .oTrig(),
30. .oData( DataU2 ) // > U3
31. );
32.
33. smg_basemod U3
34. (
35. .CLOCK( CLOCK ),
36. .RESET( RESET ),
37. .DIG( DIG ), // > top
38. .SEL( SEL ), // > top
39. .iData( { 2'd0,DataU2[5],DataU2[4],DataU2[27:24],DataU2[23:16],DataU2[15:8] }) // < U2
40. );
41.
42. assign LED = {DataU2[1], DataU2[2], DataU2[0]};
43.
44. endmodule
上訴內容的連線部署基本上與圖11.10差不了多少,期間第39行的 2’d0,DataU2[5],DataU2[4] 表示數碼管的第一位顯示 X 與 Y的符號位;DataU2[27:24] 表示數碼管的第二位顯示 Z的內容;DataU2[23:16] 表示數碼管的第三至第四位顯示 Y 的內容;DataU2[15:8] 表示數碼管的第五至第六位顯示 X 的內容。第42行則表示 LED[2]顯示右鍵,LED[1]顯示中鍵,LED[0]顯示左鍵。
編譯完畢並且下載程式。當滑鼠向西南方移動的時候,第一位數碼管便會顯示 4’h3,即 4’b0011,也就是說 X 與 Y 的符號位都是拉高狀態(負值)。當滾輪向上滾動的時候,第二位數碼管便會顯示 4’hF,即4’b1111,也就是Z為負值 -1(只要滾動速度夠快,負值還能更小)。至於數碼管第3~4顯示Y的內容(補碼形式),數碼管5~6則顯示X的內容(補碼形式)。
細節一: 兩個人,兩把湯匙
1. else if( iEn[1] )
2. case( i )
3. 擴充套件滑鼠的核心操作;
4. 偽函式;
5. endcase
6. else if(isEn[0])
7. case(i)
8. 普通滑鼠的核心操作;
9. 偽函式;
10. endcase
程式碼11.2
PS/2 讀取功能模組有一個有趣的現象,即資源多義性的問題。如程式碼11.2所示,PS/2讀取功能模組用 if( iEn[1] ) 與 if( iEn[0] ) 表示該模組針對兩種滑鼠的讀取操作。這種感覺好比一對兄弟在吃飯 ... 正常情況下,當然是一個人一把湯匙才對,這種比喻完全對應程式碼11.2的內容。
PS/2讀取功能模組負責兩種滑鼠的讀取操作之際,裡邊好比有一對兄弟,一個人負責擴充套件滑鼠的讀取操作,另一個人則針對普通滑鼠的讀取操作。期間,偽函式就是某種操作資源,也可以看成是湯匙。為了不讓兩位兄弟爭用一把湯匙而吵架,身為設計者的我們,應該為每個人分配一把湯匙。
對此,我們必須多花一些錢買另一把湯匙,這樣做我們可能多消耗一些邏輯資源。不過,家和為貴,為使模組可以和諧共處以致提高表達能力,要筆者多消耗一些邏輯資源,筆者也覺得值得。
細節二:完整的個體模組
圖11.13 PS/2滑鼠基礎模組的建模圖。
圖11.13是PS/2滑鼠基礎模組的建模圖。
ps2mouse_basemod.v
1. module ps2mouse_basemod
2. (
3. input CLOCK, RESET,
4. inout PS2_CLK, PS2_DAT,
5. output oTrig,
6. output [31:0]oData
7. );
8. wire [1:0]EnU1;
9.
10. ps2_init_funcmod U1
11. (
12. .CLOCK( CLOCK ),
13. .RESET( RESET ),
14. .PS2_CLK( PS2_CLK ), // < top
15. .PS2_DAT( PS2_DAT ), // < top
16. .oEn( EnU1 ) // > U2
17. );
18.
19. ps2_read_funcmod U2
20. (
21. .CLOCK( CLOCK ),
22. .RESET( RESET ),
23. .PS2_CLK( PS2_CLK ), // < top
24. .PS2_DAT( PS2_DAT ), // < top
25. .iEn( EnU1 ), // < U1
26. .oTrig( oTrig ), // > top
27. .oData( oData ) // > top
28. );
29.
30. endmodule
相關推薦
【黑金原創教程】【FPGA那些事兒-驅動篇I 】實驗十一:PS/2模組⑤ — 擴充套件滑鼠
實驗十一:PS/2模組⑤ — 擴充套件滑鼠 當普通滑鼠即三鍵滑鼠再也無法滿足需求的時候,擴充套件滑鼠即滾輪滑鼠就誕生了,然而實驗十一的實驗目的就是實現滾輪滑鼠的驅動。不過,進入整體之前,先讓我們來了解一下滑鼠的常用命令。 圖11.1 命令F3,設定取樣頻率。 命令F3也是Set Sample Rat
【黑金原創教程】【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作為一介儲存資源(儲存器),它的好處就是大容量空間,壞處則就是麻煩的控制規