1. 程式人生 > >【黑金原創教程】【FPGA那些事兒-驅動篇I 】實驗十一:PS/2模組⑤ — 擴充套件滑鼠

【黑金原創教程】【FPGA那些事兒-驅動篇I 】實驗十一:PS/2模組⑤ — 擴充套件滑鼠

實驗十一:PS/2模組⑤ — 擴充套件滑鼠

當普通滑鼠即三鍵滑鼠再也無法滿足需求的時候,擴充套件滑鼠即滾輪滑鼠就誕生了,然而實驗十一的實驗目的就是實現滾輪滑鼠的驅動。不過,進入整體之前,先讓我們來了解一下滑鼠的常用命令。

clip_image002

圖11.1 命令F3,設定取樣頻率。

命令F3也是Set Sample Rate,主要是用來設定採集頻率。筆者曾經說過,採集頻率就是滑鼠採集按鍵狀況還有位置狀況的間隔時間,預設下是100次/秒。如圖11.1所示,FPGA先發送命令資料8’hF3,事後滑鼠會反饋8’hFA以示接收成功,餘下FPGA再發送引數資料8’d200,滑鼠接收成功後也會反饋 8’hFA。如此一來,滑鼠的採集頻率從原本的 100次/秒,變成 200次/秒。

clip_image004

圖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

clip_image006

圖11.3 命令F6,使用預設引數。

假設筆者手癢,不小心打亂滑鼠內部的引數資料,此刻筆者可以傳送命令F6,即Set Defaults將引數資料回覆成原來的預設值。如圖11.3所示,FPGA先發送命令資料8’hF6

,滑鼠完成接收以後便會反饋8’hFA。

clip_image008

圖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,讓滑鼠再度唱歌。

clip_image010

圖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鍵滑鼠而已 ... 反之,如果完成暗語核對,擴充套件滑鼠才會發揮滾輪功能。

clip_image012

圖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。

完後,滑鼠便成為擴充套件滑鼠,內部也自動初始化,然後進入預設模式。

clip_image014

圖11.7 擴充套件滑鼠標示的位置。

普通滑鼠相較擴充套件滑鼠,它多了滾輪功能,即滑鼠除了標示左鍵,中鍵,右鍵,X還有Y以外,擴充套件還會標示Z。如圖11.7所示,X與Y可以看成面積,至於Z則可以看成上下。當滑鼠向西移動,X呈現正直,反之負值;當滑鼠向北移動,Y呈現正直,反之負值;當滾動向下活動,Z呈現正直,反之負值。

clip_image016

圖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位,內容用補碼錶示。

clip_image018

圖11.9 擴充套件滑鼠的位置範圍。

圖11.9表示擴充套件滑鼠的位置範圍,X與Y與普通滑鼠一樣,Z比較畸形一點,因為Z向上不是正直而是負值,反之亦然。Z的有效範圍是 4’b1001~4’b0111或者 -7~7,也就是說滾輪向下活動,暫存器Z就遞增,向上滾動,暫存器Z就遞減。

上述內容理解完畢以後,我們便可以開始建模了:

clip_image020

圖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

clip_image022

圖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

clip_image024

圖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讀取功能模組負責兩種滑鼠的讀取操作之際,裡邊好比有一對兄弟,一個人負責擴充套件滑鼠的讀取操作,另一個人則針對普通滑鼠的讀取操作。期間,偽函式就是某種操作資源,也可以看成是湯匙。為了不讓兩位兄弟爭用一把湯匙而吵架,身為設計者的我們,應該為每個人分配一把湯匙。

對此,我們必須多花一些錢買另一把湯匙,這樣做我們可能多消耗一些邏輯資源。不過,家和為貴,為使模組可以和諧共處以致提高表達能力,要筆者多消耗一些邏輯資源,筆者也覺得值得。

細節二:完整的個體模組

clip_image026

圖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作為一介儲存資源(儲存器),它的好處就是大容量空間,壞處則就是麻煩的控制規