1. 程式人生 > >【黑金原創教程】【FPGA那些事兒-驅動篇I 】實驗九:PS/2模組③ — 鍵盤與多組合鍵

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

實驗九:PS/2模組③ — 鍵盤與多組合鍵

筆者曾經說過,通碼除了單位元組以外,也有雙位元組通碼,而且雙位元組通碼都是 8’hE0開頭,別名又是 E0按鍵。常見的的E0按鍵有,<↑>,<↓>,<←>,<→>,<HOME>,<PRTSC> 等編輯鍵。除此之外,一些組合鍵也是E0按鍵,例如 <RCtrl> 或者 <RAlt>

。所以說,當我們設計組合鍵的時候,除了考慮“左邊”的組合鍵以外,我們也要考慮“右邊”的組合鍵。<Ctrl> 為例:

<LCtrl> 通碼是 8’h14;

<RCtrl> 通碼則是 8’hE0 8’h14。

E0按鍵除了通碼攜帶 8’hE0位元組以外,E0按鍵的斷碼同樣也會攜帶 8’hE0位元組。<Ctrl>繼續為例:

<LCtrl> 斷碼是 8’hF0 8’h14;

<RCtrl> 斷碼是 8’hE0 8’F0 8’h14。

至於時序方面呢 ...

clip_image002

圖9.1 含有E0的通碼與斷碼。

如圖9.1所示,當筆者按下 <RCtrl>,緊接著PS/2鍵盤會發送 8’hE0 8’h14的通碼,完後isCtrl立旗。假設筆者立即釋放 <RCtrl>,那麼PS/2鍵盤會發送 8’hE0 8’hF0 8’h14的斷碼,事後isCtrl就會消除立旗狀態。

clip_image004

圖9.2 E0按鍵與組合鍵①。

假設筆者按下 <RCtrl> 又按下 <A>,那麼 <RCtrl> 通碼會導致 isCtrl立旗,<A> 通碼則會導致 isDone產生高脈衝,此刻組合鍵 <Ctrl> + <A> 完成。假設筆者手癢,先釋放 <A> 再釋放 <RCtrl>,<A> 斷碼沒有異常,反之 <RCtrl> 斷碼則會消除 isCtrl的立旗狀態。

clip_image006

圖9.3 E0按鍵與組合鍵②。

假設頑皮的筆者先按下 <RCtrl> 又按下 <LCtrl> 然後釋放 <LCtrl>。首先 <RCtrl> 通碼會導致 isCtrl 立旗,不過 <LCtrl> 通碼會驅使 isCtrl 重複立旗,但是 <LCtrl> 斷碼則會消除 isCtrl的立旗狀態。如果此刻筆者按下 <A>,雖然 <A> 通碼使產生isDone的高脈衝,但是組合鍵 <Ctrl> + <A> 則沒有成立。心灰意冷的筆者,於是便釋放 <A>又釋放 <RCtrl>,期間 <A> 斷碼與 <RCtrl> 斷碼都沒有異樣。

clip_image008

圖9.4 E0按鍵與組合鍵③。

為了解決這個問題,我們必須把 isCtrl 旗標區分為 isLCtrl 與 isRCtrl 為兩種旗標。如圖9.4所示,同樣的按鍵過程,不過卻有不同的按鍵結果。期間,<RCtrl> 通碼立旗 isRCtrl,換之 <LCtrl> 通碼立旗 isLCtrl。雖然 <LCtrl> 斷碼消除 isLCtrl的立旗狀態,但是 <A> 通碼還有isRCtrl 立旗因為合作無間,結果造就組合鍵 <Ctrl> + <A> 完成。 事後 <RCtrl> 斷碼再消除 isRCtrl 的立旗狀態。

為此,我們 isLCtrl 與 isRCtrl 之間的關係可以這樣表示:

wire isCtrl = isLCtrl | isRCtrl;

除此之外, isLShift,isRShift,isLAlt 與 isRAlt也是同樣的道理。

我們雖然已經解決 E0按鍵還有組合鍵之間的問題,但是還有根本性的問題在等待我們。實驗七~八之際,解讀一幀資料,資料要麼就是通碼,資料要麼就是斷碼 ... 換句話說,檢測資料的時候,我們只要檢測1×2等兩種可能性而已,即8’hF0或者非 8’hF0。如果資料是 8’hF0,那麼資料就是斷碼,否則就是通碼。

一旦 E0按鍵亂入攪局,檢測的可能性也從原來的 1×2等兩種可能性,變成 1×2×3 等8種可能性,這個事實無疑會加劇Verilog的描述難度。簡言之就是實驗七,還有實驗八的思路卻不適合實驗九,為此我們需要更換一下思路。

假設實驗九所針對的組合鍵有:

<LShift> 與 <RShift>
<LCtrl> 與 <RCtrl>
<LAlt> 與 <RAlt>

然後,我們必須事先考慮所有可能性,包括這些組合鍵的通碼與斷碼,然後用常量表達出來,結果如程式碼9.1所示:

      parameter MLSHIFT = 24'h00_00_12, MLCTRL = 24'h00_00_14, MLALT = 24'h00_00_11;
     parameter BLSHIFT = 24'h00_F0_12, BLCTRL = 24'h00_F0_14, BLALT = 24'h00_F0_11;
     parameter MRSHIFT = 24'h00_00_59, MRCTRL = 24'hE0_00_14, MRALT = 24'hE0_00_11;
     parameter BRSHIFT = 24'h00_F0_59, BRCTRL = 24'hE0_F0_14, BRALT = 24'hE0_F0_11;

程式碼9.1

如程式碼9.1所示,M××表示通碼,B××表示斷碼 ... 如果算計位元組 8’hF0與 8’hE0,

所有組合鍵的通碼與斷碼都可以使用3位元組來表達。期間<RCtrl> 與 <RAlt> 的通碼與斷碼都有8’hE0的字眼。此外,我們知道低階建模II是追求表達能力的建模技巧,凡事力求直觀 ... 為此,我們必須建立3個步驟,而且每個步驟處理單一情況,結果如程式碼9.2所示:

1.      1: // E0_xx_xx & E0_F0_xx Check
2.      if( T == 8'hE0 ) begin D1[23:16] <= T; i <= FF_Read; Go <= i; end
3.      else if( D1[23:16] == 8'hE0 && T == 8'hF0 ) begin D1[15:8] <= T; i <= FF_Read; Go <= i; end
4.      else if( D1[23:8] == 16'hE0_F0 ) begin D1[7:0] <= T; i <= CLEAR; end
5.      else if( D1[23:16] == 8'hE0 && T != 8'hF0 ) begin D1[15:0] <= {8'd0, T}; i <= SET; end
6.      else i <= i + 1'b1;
7.                          
8.      2: // 00_F0_xx Check
9.      if( T == BREAK ) begin D1[23:8] <= {8'd0,T}; i <= FF_Read; Go <= i; end
10.      else if( D1[23:8] == 16'h00_F0 ) begin D1[7:0] <= T; i <= CLEAR; end
11.      else i <= i + 1'b1;
12.             
13.      3: // 00_00_xx Check
14.        begin D1 <= {16'd0,T}; i <= SET; end

程式碼9.2

如程式碼9.2所示:

步驟1處理 E0_××_×× 或者 E0_F0_××,亦即針對E0通碼與E0斷碼。

步驟2處理 00_F0_××,亦即針對一般斷碼。

步驟3處理 00_00_××,亦即針對一般通碼。

接下來,讓讓我們詳細理解一下各個步驟的內容:

步驟1:

第2行 if( T == 8'hE0 ) 表示,如果第一位元組是 8’hE0便將8’hE0暫存在 D1[23:16],即E0按鍵,然後步驟指向偽函式讀取第二位元組,並且Go返回當前步驟。

第3行 if( D1[23:16] == 8'hE0 && T == 8'hF0 ) 表示,如果 D1[23:16] 的內容是 8’hE0並且第二位元組是8’hF0,即E0斷碼。為此,F0暫存在 D1[15:8],然後步驟指向偽函式讀取第三位元組,Go則返回當前步驟。

第4行 if( D1[23:8] == 16'hE0_F0 ) 表示,如果 D1[23 :8] 為 16’hE0_F0,那麼第三位元組也是斷碼。為此,D1[7:0] 暫存第三位元組,然後步驟指向 Clear (消除步驟)。

第5行 if( D1[23:16] == 8'hE0 && T != 8'hF0 ) 表示, D1[23:16] 為 8’hE0,但是第二位元組不是8’hF0,即E0通碼。為此,D1[16:8] 賦值 8’h00,D1[7:0] 則暫存第二位元組,然後步驟指向 SET (設定步驟)。

第6行,當什麼都不是則表示物件不是E0按鍵,i遞增以示下一個步驟。

步驟2:

第9行,if( T == BREAK ) 表示,第一位元組為 8’hF0,即是一般斷碼。為此,D1[23:18] 賦值8’h00,D1[16:8] 則暫存 8’hF0,然後i指向偽函式讀取第二位元組,Go返回當前步驟。

第10行,if( D1[23:8] == 16'h00_F0 ) 表示,第一位元組8’hF0已經讀取完畢,現正準備斷碼的後續位元組。為此,D1[7:0] 暫存第二位元組,然後i指向 Clear(消除步驟)。

第11行,當什麼都是則表示物件只是一般通碼而已。

步驟3:

第14行,D1[23:8] 賦值16’h00_00 然後 D1[7:0] 暫存第一位元組,然後i指向 SET (設定步驟)。

1.      4: // Set state
2.      if( D1 == MRSHIFT ) begin isTag[5] <= 1'b1; D1 <= 24'd0; i <= 5'd0; end
3.      else if( D1 == MRCTRL ) begin isTag[4] <= 1'b1; D1 <= 24'd0; i <= 5'd0; end
4.      else if( D1 == MRALT ) begin isTag[3] <= 1'b1; D1 <= 24'd0; i <= 5'd0; end
5.      else if( D1 == MLSHIFT ) begin isTag[2] <= 1'b1; D1 <= 24'd0; i <= 5'd0; end
6.      else if( D1 == MLCTRL ) begin isTag[1] <= 1'b1; D1 <= 24'd0; i <= 5'd0; end
7.      else if( D1 == MLALT ) begin isTag[0] <= 1'b1; D1 <= 24'd0; i <= 5'd0; end
8.      else i <= DONE;
9.                          
10.      5: // Clear state
11.      if( D1 == BRSHIFT ) begin isTag[5] <= 1'b0; D1 <= 24'd0; i <= 5'd0; end
12.      else if( D1 == BRCTRL ) begin isTag[4] <= 1'b0; D1 <= 24'd0; i <= 5'd0; end
13.      else if( D1 == BRALT ) begin isTag[3] <= 1'b0; D1 <= 24'd0; i <= 5'd0; end
14.      else if( D1 == BLSHIFT ) begin isTag[2] <= 1'b0; D1 <= 24'd0; i <= 5'd0; end
15.      else if( D1 == BLCTRL ) begin isTag[1] <= 1'b0; D1 <= 24'd0; i <= 5'd0; end
16.      else if( D1 == BLALT ) begin isTag[0] <= 1'b0; D1 <= 24'd0; i <= 5'd0; end
17.      else begin D1 <= 24'd0; i <= 5'd0; end

程式碼9.3

當第一至第三位元組經由步驟1~3分析並且整理完畢以後,就會路由步驟4(SET)或者步驟5(CLEAR)。

步驟4,第2~7行是用來立旗,如果D1的內容是組合鍵,那麼相關的標誌位 isTag[n] 就會立旗,D1清空,然後i返回步驟0;否則,物件只是一般字元按鍵的通碼而已,結果i指向 DONE並且產生完成訊號(第8行)。

步驟5,第11~16行是用來消除立旗,如果D的內容是組合鍵,那麼相關的標誌位 isTag[n]就會被消除,D1清空,i返回步驟0。否則,物件只是一般的字元按鍵的斷碼而已,結果D1清零,i則返回步驟0。

理解這些內容以後,我們就可以開始建模了。

clip_image010

圖9.5 實驗九的建模圖。

圖9.5是實驗九的建模圖,相較實驗八,PS/2功能模組的 oTag 則多了3個狀態,餘下都一樣。

ps2_funcmod.v

clip_image012

圖9.6 PS/2功能模組的建模圖。

同樣,PS/2功能模組相較實驗八,oTag增多了3個位寬,此外內容也發生不少改變。

1.    odule ps2_funcmod
2.    (
3.         input CLOCK, RESET,
4.         input PS2_CLK, PS2_DAT,
5.         output oTrig,
6.         output [7:0]oData,
7.         output [5:0]oTag
8.    );

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

9.         parameter MLSHIFT = 24'h00_00_12, MLCTRL = 24'h00_00_14, MLALT = 24'h00_00_11;
10.         parameter BLSHIFT = 24'h00_F0_12, BLCTRL = 24'h00_F0_14, BLALT = 24'h00_F0_11;
11.         parameter MRSHIFT = 24'h00_00_59, MRCTRL = 24'hE0_00_14, MRALT = 24'hE0_00_11;
12.         parameter BRSHIFT = 24'h00_F0_59, BRCTRL = 24'hE0_F0_14, BRALT = 24'hE0_F0_11;
13.         parameter BREAK = 8'hF0;
14.         parameter FF_Read = 5'd8, DONE = 5'd6, SET = 5'd4, CLEAR = 5'd5;

以上內容為組合鍵的常量宣告(三位元組)。第13行是BREAK的常量宣告。第14行是偽函式,SET步驟與CLEAR步驟等入口地址宣告。

16.         /*******************************/ // sub1
17.         
18.        reg F2,F1; 
19.         
20.        always @ ( posedge CLOCK or negedge RESET )
21.             if( !RESET )
22.                  { F2,F1 } <= 2'b11;
23.              else
24.                  { F2, F1 } <= { F1, PS2_CLK };
25.    
26.         /*******************************/ // core
27.         
28.         wire isH2L = ( F2 == 1'b1 && F1 == 1'b0 );

以上內容是檢測電平的周邊操作,第28行則是下降沿的即時宣告。

29.         reg [7:0]T;
30.         reg [23:0]D1;
31.         reg [5:0]isTag; // [5]isRShift, [4]isRCtrl, [3]isRAlt, [2]isLShift, [1]isLCtrl, [0]isLAlt;
32.         reg [4:0]i,Go;
33.         reg isDone;
34.         
35.         always @ ( posedge CLOCK or negedge RESET )
36.             if( !RESET )
37.                  begin
38.                         T <= 8'd0;
39.                         D1 <= 24'd0;
40.                         isTag <= 6'd0;
41.                         i <= 5'd0;
42.                         Go <= 5'd0;
43.                         isDone <= 1'b0;
44.                    end
45.               else

以上內容是是相關暫存器的宣告以及復位操作。T用於偽函式的暫存空間,D1用來暫存按鍵資料,isTag用來標示各個組合按鍵的狀態,i指向步驟,Go返回步驟,isDone則標示有效按鍵。

46.                    case( i )
47.                          
48.                          0: // Read Make
49.                          begin i <= FF_Read; Go <= i + 1'b1; end
50.                          
51.                          1: // E0_xx_xx & E0_F0_xx Check
52.                          if( T == 8'hE0 ) begin D1[23:16] <= T; i <= FF_Read; Go <= i; end
53.                          else if( D1[23:16] == 8'hE0 && T == 8'hF0 ) begin D1[15:8] <= T; i <= FF_Read; Go <= i; end
54.                          else if( D1[23:8] == 16'hE0_F0 ) begin D1[7:0] <= T; i <= CLEAR; end
55.                          else if( D1[23:16] == 8'hE0 && T != 8'hF0 ) begin D1[15:0] <= {8'd0, T}; i <= SET; end
56.                          else i <= i + 1'b1;
57.                          
58.                          2: // 00_F0_xx Check
59.                          if( T == BREAK ) begin D1[23:8] <= {8'd0,T}; i <= FF_Read; Go <= i; end
60.                          else if( D1[23:8] == 16'h00_F0 ) begin D1[7:0] <= T; i <= CLEAR; end
61.                           else i <= i + 1'b1;
62.             
63.                          3: // 00_00_xx Check
64.                          begin D1 <= {16'd0,T}; i <= SET; end
65.                        

以上內容為部分核心操作,過程如下:

步驟0,進入偽函式以致讀取第一位元組資料。

步驟1處理 E0_××_×× 或者 E0_F0_××,亦即針對E0通碼與E0斷碼。

步驟2處理 00_F0_××,亦即針對一般斷碼。

步驟3處理 00_00_××,亦即針對一般通碼。

66.                          4: // Set state
67.                          if( D1 == MRSHIFT ) begin isTag[5] <= 1'b1; D1 <= 24'd0; i <= 5'd0; end
68.                          else if( D1 == MRCTRL ) begin isTag[4] <= 1'b1; D1 <= 24'd0; i <= 5'd0; end
69.                          else if( D1 == MRALT ) begin isTag[3] <= 1'b1; D1 <= 24'd0; i <= 5'd0; end
70.                          else if( D1 == MLSHIFT ) begin isTag[2] <= 1'b1; D1 <= 24'd0; i <= 5'd0; end
71.                          else if( D1 == MLCTRL ) begin isTag[1] <= 1'b1; D1 <= 24'd0; i <= 5'd0; end
72.                          else if( D1 == MLALT ) begin isTag[0] <= 1'b1; D1 <= 24'd0; i <= 5'd0; end
73.                          else i <= DONE;
74.                          
75.                          5: // Clear state
76.                          if( D1 == BRSHIFT ) begin isTag[5] <= 1'b0; D1 <= 24'd0; i <= 5'd0; end
77.                          else if( D1 == BRCTRL ) begin isTag[4] <= 1'b0; D1 <= 24'd0; i <= 5'd0; end
78.                          else if( D1 == BRALT ) begin isTag[3] <= 1'b0; D1 <= 24'd0; i <= 5'd0; end
79.                          else if( D1 == BLSHIFT ) begin isTag[2] <= 1'b0; D1 <= 24'd0; i <= 5'd0; end
80.                          else if( D1 == BLCTRL ) begin isTag[1] <= 1'b0; D1 <= 24'd0; i <= 5'd0; end
81.                          else if( D1 == BLALT ) begin isTag[0] <= 1'b0; D1 <= 24'd0; i <= 5'd0; end
82.                          else begin D1 <= 24'd0; i <= 5'd0; end
83.                          

以上內容為部分核心操作,過程如下:

步驟4,用來立旗組合鍵。

步驟5,則用來消除組合鍵。

84.                          6: // DONE
85.                          begin isDone <= 1'b1; i <= i + 1'b1; end
86.                          
87.                          7:
88.                          begin isDone <= 1'b0; i <= 5'd0; end

以上內容為部分核心操作,步驟6~7用來產生完成訊號。

90.                          /****************/ // PS2 Read Function
91.                          
92.                          8:  // Start bit
93.                          if( isH2L ) i <= i + 1'b1; 
94.                          
95.                          9,10,11,12,13,14,15,16:  // Data byte
96.                          if( isH2L ) begin i <= i + 1'b1; T[ i-9 ] <= PS2_DAT; end
97.                          
98.                          17: // Parity bit
99.                          if( isH2L ) i <= i + 1'b1;
100.                          
101.                          18: // Stop bit
102.                          if( isH2L ) i <= Go;
103.                            
104.                     endcase
105.         

以上內容為部分核心操作。步驟8~18是讀取一幀資料的偽函式。

106.         assign oTrig = isDone;
107.         assign oData = D1[7:0];
108.         assign oTag = isTag;
109.         
110.    endmodule

以上內容是輸出驅動宣告。

ps2_demo.v

組合模組 ps2_demo 的連線部署請瀏覽圖9.5。

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.         wire [7:0]DataU1;
9.         wire [5:0]TagU1;
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.              .oTrig(),
18.              .oData( DataU1 ),  // > U2
19.              .oTag( TagU1 ) // > U2
20.         );
21.         
22.       smg_basemod U2
23.        (
24.           . CLOCK( CLOCK ),
25.            .RESET( RESET ),
26.            .DIG( DIG ),  // > top
27.            .SEL( SEL ),  // > top
28.            .iData( { 8'h00, 1'b0,TagU1[5:3], 1'b0,TagU1[2:0], DataU1 } ) // < U1
29.        );
30.                 
31.    endmodule

上述程式碼基本上沒有什麼難點,除了第28行的聯合驅動。8’h00表示無視第1~2位的數碼管。1’b0 + TagU1[5:3] 表示第3位數碼管顯示 <RCtrl> <RShift> 還有 <RAlt> 等立旗狀態。1’b0 + TagU1[2:0] 表示第4位數碼管顯示 <LCtrl> <LShift> 還有 <LAlt> 等立旗狀態。DataU1則表示第5~6位數碼管顯示通碼。

編譯完畢便下載程式。如果讀者同時按下 <RCtrl> <RShift> 還有 <RAlt>,第3位數碼管就會顯示4’h7,即4’b0111。如果同時按下 <LCtrl> 還有 <LShift> ,第4位數碼管就會顯示4’h6,即4’b0110。如果按下 <A>,第5~6位數碼管則會顯示 8’h1C。注意,千萬不要太貪心,同時按下6個以上的按鍵,PS/2鍵盤會因此而罷工的 ...

本實驗結束之前,讓我們來聊聊一些八卦 ... 實驗九的 PS/2功能模組雖然支援 E0按鍵,但是僅限 E0的組合鍵而已。至於那些 E0 編輯鍵,如 <↑> 或者 <↓>,則需要進一步擴充套件,不過該要求已經超出本書的討論範圍。不管物件是 E0組合鍵,還是E0編輯鍵,設計思路也是一樣的,不過後者比較偏向軟體。對此,讀者只要基於實驗九,再簡單擴充套件一下即可。

細節一:完整的個體模組

clip_image014

圖9.7 PS/2鍵盤功能模組。

圖9.7是PS/2鍵盤功能模組,內容基本上與PS/2功能模組一模一樣,至於區別就是穿上其它馬甲而已,所以怒筆者不再重複貼上了。

相關推薦

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

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

黑金原創教程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 實驗二十五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.