【黑金原創教程】【FPGA那些事兒-驅動篇I 】實驗七:PS/2模組① — 鍵盤
實驗七:PS/2模組① — 鍵盤
實驗七依然也是熟爛的PS/2鍵盤。相較《建模篇》的PS/2鍵盤實驗,實驗七實除了實現基本的驅動以外,我們還要深入解PS/2時序,還有PS/2鍵盤的行為。不過,為了節省珍貴的頁數,怒筆者不再重複有關PS/2的基礎內容,那些不曉得的讀者請複習《建模篇》或者自行谷歌一下。
市場上常見的鍵盤都是應用第二套掃描碼,各種掃描碼如圖7.2所示。《建模篇》之際,筆者也只是擦邊一下PS/2鍵盤,簡單讀取單位元組通碼與斷碼而已。所謂單位元組通碼,就是有效的按下內容,例如 <A> 鍵被按下的時候會輸出 1C。所謂單位元組斷碼,就是有效的釋放內容,例如 <A> 鍵被釋放的時候會輸出 F0 1C。
除了單位元組的通碼以外,PS/2鍵盤也有雙位元組通碼與斷碼。所謂雙位元組通碼,例如 <R CTRL>鍵被按下時候會輸出 E0 14;反之,所謂雙位元組斷碼,例如 <R CTRL> 鍵被釋放時候會輸出 E0 F0 14。不管是單位元組還是雙位元組,斷碼都包含F0。
除了上述的要求以外,筆者還要實現雙組合鍵,例如 <Ctrl> + <A>。不僅而已,筆者也要實現三組合鍵,例如 <Ctrl> + <Alt > + <A>。常識上,這些任性的要求都是軟體的工作,然而這種認識也僅侷限小氣的腦袋而已。換做筆者,筆者就算霸王硬上弓,筆者也要使用Verilog實現這些任性的要求。
未進入實驗之前,筆者需要強調一下!Verilog究竟如何驅動PS/2裝置,然後又如何實現軟體的工作,這一切Verilog自有方法。不管C語言還有微控制器這對活寶,驅動PS/2裝置再怎麼神,它們也沒有資格在旁指指點點。讀者千萬也別嘗試用借用它們的思路去思考Verilog,否則後果只有撞牆而已。
圖7.1 PS/2鍵盤傳送資料(主機視角)。
PS/2傳輸協議與一般的傳輸協議一樣,除了主從之分之餘,它也有“讀寫”兩個訪問的方向。除非有特殊的需要,不然從機(FPGA)是不會訪問PS/2鍵盤的內部。換之,從機只要不停從PS/2鍵盤哪裡讀取資料即可 ... 換句話說,驅動PS/2鍵盤僅有讀資料這一環而已,然而PS/2鍵盤是主機,FPGA是從機。(主機的定義是時鐘訊號的擁有者)
不管是何種傳輸協議,只要協議當中存有時鐘訊號,那麼“什麼時鐘沿,怎樣對待資料”這個鐵則是不會改變的。如圖7.1所示,那是PS/2鍵盤傳送資料的時序圖,亦即上升沿設定資料(輸出資料)。根據主機視角,除了開始位以外,PS/2鍵盤一共利用10個上升沿輸出10位資料。
圖7.2 第二套鍵盤的掃描碼。
圖7.3 PS/2鍵盤傳送資料,FPGA讀取資料(從機視角)。
PS/2傳輸資料一般都是一幀一幀互相往來,一幀有11位資料。Bit 0位為拉低的開始位,Bit 1~8 是由低自高的資料位,Bit 9為校驗位,Bit 10為拉高的結束位。根據從機視角,如圖7.3所示,PS/2鍵盤在傳送資料的時候,FPGA是下降沿鎖存資料(讀取資料)。PS2_CLK訊號一共產生了11個下降沿,FPGA也根據這11次下降沿鎖存11位資料。
圖7.4 檢測PS2_CLK的電平變化。
為了察覺下降沿,我們可以借用F2~F1的力量,對此Verilog可以這樣表示:
reg F2,F1;
always @ ( posedge CLOCK )
{ F2,F1 } <= { F1,KEY };
然後下降沿宣告為即時:
wire isH2L = ( F2 == 1 && F1 == 0 );
那麼,從機接收1幀11位資料的操作可以這樣描述,結果如程式碼7.1所示:
1. case( i )
2.
3. 0:
4. if(isH2L) i <= i + 1’b1;
5. 1,2,3,4,5,6,7,8:
6. if(isH2L) D1[i-1] <= PS2_DAT; i <= i + 1’b1; end
7. 9:
8. if(isH2L) i <= i + 1’b1;
9. 10:
10. if(isH2L) i <= 4‘d0;
11.
12. endcase
程式碼7.1
不過,為了方便控制程式碼7.1,筆者設法將程式碼7.1設定為偽函式,結果如程式碼7.2所示:
1. parameter RDFUNC = 4’d4;
2. ......
3. case( i )
4. ......
5. /*********************/
6. 4:
7. if(isH2L) i <= i + 1’b1;
8. 5,6,7,8,9,10,11,12:
9. if(isH2L) D1[i-5] <= PS2_DAT; i <= i + 1’b1; end
10. 13:
11. if(isH2L) i <= i + 1’b1;
12. 14:
13. if(isH2L) i <= Go;
14.
15. endcase
程式碼7.2
理解這些以後,我們就要開始認識PS/2鍵盤的按鍵行為了。
圖7.5 PS/2鍵盤,按一下又釋放。
假設筆者輕按一下<A>然後又釋放,如圖7.5所示,PS/2鍵盤先會發送一幀8’h1C的通碼,然後又傳送兩幀8’hF0 8’h1C的斷碼,這是PS/2鍵盤最常見的按鍵行為。
圖7.6 PS/2鍵盤,長按又釋放。
如果筆者長按 <A> 鍵不放,如圖7.6所示,PS/2鍵盤會不停傳送通碼,直至釋放才傳送斷碼。至於長按期間,通碼的傳送間隔大約是100ms,亦即1秒內傳送10個通碼。
圖7.7 PS/2鍵盤,有效通碼。
一般而言,我們都會選擇通碼放棄斷碼,為了表示一次性,而且也是有效性的通碼。每當一幀通碼完成接收,isDone就會產生一個高脈衝,以示一次性而且有效的通碼已經接收完畢。
圖7.8 實驗七的建模圖。
如圖7.8所示,那是實驗七的建模圖,其中名為 ps2_demo的組合模組,內容包含實驗六的 smg_basemod 以外,該組合模組也包含 ps2_funcmod。PS/2功能模組接收來PS/2鍵盤傳送過來的資料,然後再經由oData驅動smg_basemod的iData,最後並將通碼顯示在數碼管上。
ps2_funcmod.v
圖7.9 PS/2功能模組。
圖7.9是PS/2功能模組的建模圖,左方是PS2_CLK與PS2_DAT頂層訊號的輸入,右方則是1位oTrig與8位oData。具體內容,讓我們來瞧瞧程式碼:
1. module ps2_funcmod
2. (
3. input CLOCK, RESET,
4. input PS2_CLK, PS2_DAT,
5. output oTrig,
6. output [7:0]oData
7. );
8. parameter BREAK = 8'hF0;
9. parameter FF_Read = 5'd4;
以上內容為相關的出入端宣告以及常量宣告。第8行是斷碼的常量宣告(第一幀),第9行則是偽函式的入口。
10.
11. /******************/ // sub
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 );
24. reg [7:0]D1;
25. reg [4:0]i,Go;
26. reg isDone;
27.
28. always @ ( posedge CLOCK or negedge RESET )
29. if( !RESET )
30. begin
31. D1<= 8'd0;
32. i <= 5'd0;
33. Go <= 5'd0;
34. isDone <= 1'b0;
35. end
36. else
以上內容是周邊操作以及相關暫存器宣告,還有它們的復位操作。周邊操作主要用來檢測PS2_CLK的電平變化。第23行是下降沿的即時宣告。第24~26行是相關的暫存器宣告,第30~34行則是這些暫存器的復位操作。
37. case( i )
38.
39. 0:
40. begin i <= FF_Read; Go <= i + 1'b1; end
41.
42. 1:
43. if( D1 == BREAK ) begin i <= FF_Read; Go <= 5'd0; end
44. else i <= i + 1'b1;
45.
46. 2:
47. begin isDone <= 1'b1; i <= i + 1'b1; end
48.
49. 3:
50. begin isDone <= 1'b0; i <= 5'd0; end
51.
52. /*************/ // PS2 read function
53.
54. 4: // Start bit
55. if( isH2L ) i <= i + 1'b1;
56.
57. 5,6,7,8,9,10,11,12: // Data byte
58. if( isH2L ) begin D1[ i-5 ] <= PS2_DAT; i <= i + 1'b1; end
59.
60. 13: // Parity bit
61. if( isH2L ) i <= i + 1'b1;
62.
63. 14: // Stop bit
64. if( isH2L ) i <= Go;
65.
66. endcase
以上內容為核心操作。其中步驟4~14(第54~64行)是讀取1幀資料的偽函式,入口地址是4。步驟0~3則是主要操作,過程如下:
步驟0,進入偽函式準備讀取第一幀資料。讀完第一幀資料以後便返回步驟1。
步驟1,判斷第一幀資料是否為斷碼?是,進入偽函式,完整第二幀資料的讀取,然後返回步驟指向為0。否,繼續步驟。
步驟2~3,產生完成的觸發訊號,然後返回步驟0。
67.
68. /************************************/
69.
70. assign oTrig = isDone;
71. assign oData = D1;
72.
73. /*************************************/
74.
75. endmodule
以上內容為輸出驅動的宣告。
ps2_demo.v
組合模組 ps2_demo的聯絡部署請複習圖7.8。
1. module ps2_demo
2. (
3. input CLOCK, RESET,
4. input PS2_CLK, PS2_DAT,
5. output [7:0]DIG,
6. output [5:0]SEL
7. );
8.
9. wire [7:0]DataU1;
10.
11. ps2_funcmod U1
12. (
13. .CLOCK( CLOCK ),
14. .RESET( RESET ),
15. .PS2_CLK( PS2_CLK ), // < top
16. .PS2_DAT( PS2_DAT ), // < top
17. .oData( DataU1 ), // > U2
18. .oTrig()
19. );
20.
21. smg_basemod U2
22. (
23. .CLOCK( CLOCK ),
24. .RESET( RESET ),
25. .DIG( DIG ), // > top
26. .SEL( SEL ), // > top
27. .iData( { 16'h0000, DataU1 } ) // < U1
28. );
29.
30. endmodule
上述程式碼沒有什麼特別,除了第18行,無視觸發訊號的輸出以外,還有第27行其 16’h0000 則表示數碼管的前四位皆為0,後兩位則是通碼。編譯完後便下載程式。
如果筆者按下 <A> 鍵,數碼管便會顯示1C;如果筆者釋放 <A> 鍵,數碼管也是顯示1C,期間也會發生一絲的閃耀。由於ps2_funcmod的暫存空間D直切驅動oData,所以數碼管事實反映ps2_funcmod的讀取狀況。從演示上來看的確如此,不過在時序身上,唯有通碼讀取成功以後,才會產生觸發訊號。
細節一:主操作與偽函式的距離
1. case( i )
2.
3. 0:
4. begin i <= FF_Read; Go <= i + 1'b1; end
5. 1:
6. if( D1 == BREAK ) begin i <= FF_Read; Go <= 5'd0; end
7. else i <= i + 1'b1;
8. 2:
9. begin isDone <= 1'b1; i <= i + 1'b1; end
10. 3:
11. begin isDone <= 1'b0; i <= 5'd0; end
12. /*************/ // PS2 read function
13. 4: // Start bit
14. if( isH2L ) i <= i + 1'b1;
15. 5,6,7,8,9,10,11,12: // Data byte
16. if( isH2L ) begin D1[ i-5 ] <= PS2_DAT; i <= i + 1'b1; end
17. 13: // Parity bit
18. if( isH2L ) i <= i + 1'b1;
19. 14: // Stop bit
20. if( isH2L ) i <= Go;
21.
22. endcase
程式碼7.3
如程式碼7.3所示,步驟0~3是主操作,步驟4~14則是偽函式,期間主操作的下任步驟直接連線偽函式的入口。一般而言,如果模組的核心操作是小功能的話,這樣做倒沒有什麼問題。反之,如果遇上覆雜功能的核心操作,主操作與偽函式之間必須隔空一段距離。根據筆者的習慣,預設下都會設為16或者32,不過也有例外的情況。
23. case( i )
24.
25. 0:
26. begin i <= FF_Read; Go <= i + 1'b1; end
27. 1:
28. if( D1 == BREAK ) begin i <= FF_Read; Go <= 5'd0; end
29. else i <= i + 1'b1;
30. 2:
31. begin isDone <= 1'b1; i <= i + 1'b1; end
32. 3:
33. begin isDone <= 1'b0; i <= 5'd0; end
34. /*************/ // PS2 read function
35. 16: // Start bit
36. if( isH2L ) i <= i + 1'b1;
37. 17,18,19,20,21,22,23,24: // Data byte
38. if( isH2L ) begin D1[ i-5 ] <= PS2_DAT; i <= i + 1'b1; end
39. 25: // Parity bit
40. if( isH2L ) i <= i + 1'b1;
41. 26: // Stop bit
42. if( isH2L ) i <= Go;
43.
44. endcase
程式碼7.4
如程式碼7.4所示,偽函式的入口地址已經設為16,為此主操作與偽函式之間有16個步驟的距離。如此一來,主操作擁有更多的步驟空間。
細節二:完整的個體模組
圖7.10 PS/2鍵盤功能模組。
圖7.10是PS/2鍵盤功能模組,內容基本上與PS/2功能模組一模一樣,至於區別就是穿上其它馬甲而已,所以怒筆者不再重複貼上了。
相關推薦
【黑金原創教程】【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.