進階項目(11) 矩陣鍵盤程序設計講解


寫在前面的話

在使用按鍵的時候,如果按鍵不多的話,我們可以直接讓按鍵與FPGA相連接,但是如果按鍵比較多的時候,如果還繼續使用直接讓按鍵與FPGA相連接的話,會大量增加FPGA端口的消耗,為了減少FPGA端口的消耗,我們可以把按鍵設計成矩陣的形式。接下來,夢翼師兄將和大家一起學習掃描鍵盤的電路原理以及驅動方式。

項目需求

設計4*4矩陣鍵盤按鍵掃描模塊正確解析按鍵值

矩陣鍵盤的原理

由上圖可以知道,矩陣鍵盤的行row(行)與col(列)的交點,都是通過一個按鍵相連接。傳統的一個按鍵一個端口的方法,若要實現16個按鍵,則需要16個端口,而現在這個矩陣鍵盤的設計,16個按鍵,僅僅需要8個端口,如果使用16個端口來做矩陣鍵盤的話,可以識別64個按鍵,端口的利用率遠遠比傳統的設計高好多,所以如果需要的按鍵少的話,可以選擇傳統的按鍵設計,如果需要的按鍵比較多的話,可以采用這種矩陣鍵盤的設計。而我們現在就以掃描法為例來介紹矩陣鍵盤的工作原理。

首先col(列)是FPGA給矩陣鍵盤輸出的掃描信號,而row(行)是矩陣鍵盤反饋給FPGA的輸入信號,用於檢測哪一個按鍵被按下,示意圖如下:

 如上圖所示,FPGA給出掃描信號COL[3:0],COL = 4’b0111,等下一個時鍾周期COL = 4’b1011,再等下一個時鍾周期COL = 4’b1101,再等下一個時鍾周期COL = 4’b1110,再等下一個時鍾周期COL = 4’b0111,COL就是這樣不斷循環,給矩陣鍵盤一個低電平有效的掃描信號,當FPGA給矩陣鍵盤COL掃描信號的同時,FPGA也要在檢測矩陣鍵盤給FPGA的的反饋信號ROW,舉個例子,假若矩陣鍵盤中的9號按鍵被按下了:

當COL = 4’b0111,ROW = 4’b1111;

當COL = 4’b1011,ROW = 4’b1111;

當COL = 4’b1101,ROW = 4’b1011;

當COL = 4’b1110,ROW = 4’b1111;

有人問,為什么當COL = 4’b1101的時候,ROW = 4’b1011呢?我們現在就以矩陣鍵盤的電路來分析一下這個原因,如上圖所示:

當9號按鍵被按下的時候,9號按鍵的電路就會被導通,掃描電路COL開始掃描,當掃描到COL[1]的時候,由於9號按鍵的電路被導通了,COL[1]的電壓等於ROW[2]的電壓,所以會出現當COL = 4’b1101的時候ROW = 4’b1011(掃描信號的頻率大概1K左右)。

通常的按鍵所用開關為機械彈性開關,當機械觸點斷開、閉合時,由於機械觸點的彈性作用,一個按鍵開關在閉合時不會馬上穩定地接通,在斷開時也不會一下子斷開。因而在閉合及斷開的瞬間均伴隨有一連串的抖動,為了不產生這種現象而作的措施就是按鍵消抖。

抖動時間的長短由按鍵的機械特性決定,一般為5ms~10ms。這是一個很重要的時間參數,在很多場合都要用到。按鍵穩定閉合時間的長短則是由操作人員的按鍵動作決定的,一般為零點幾秒至數秒(按鍵按下的時間一般都會大於20ms)。鍵抖動會引起一次按鍵被誤讀多次。為確保CPU對按鍵的一次閉合僅作一次處理,必須去除按鍵抖動。在按鍵閉合穩定時讀取按鍵的電平狀態,並且必須判別到按鍵釋放穩定后再作處理。

然后我們就可以利用這些現象,來設計一個識別按鍵的電路。

架構設計

根據原理分析,我們設計出架構圖如下:

 

模塊功能介紹

模塊名

功能描述

key_scan

檢測按鍵值

頂層模塊端口描述

端口名

端口說明

clk

系統時鍾輸入

rst_n

系統復位

row

矩陣鍵盤的行線

data

按鍵值

flag

按鍵值有效(尖峰脈沖)

col

矩陣鍵盤的列線

 

 代碼解釋

Key_scan模塊代碼

/****************************************************          

 *   Engineer      :   夢翼師兄

 *   QQ             :   761664056

 *   The module function: 檢測出鍵盤矩陣的按鍵值

*****************************************************/

000 module key_scan (

001                     clk, //系統時鍾輸入

002                     rst_n, //系統復位

003                     row,//矩陣鍵盤的行線

004                     flag,//輸出值有效標志(尖峰脈沖)

005                     data,//按鍵值

006                     col//矩陣鍵盤的列線

007                 );

008     //系統輸入

009     input clk;//系統時鍾輸入

010     input rst_n;//系統復位

011     input [3:0] row;//矩陣鍵盤的行線

012     //系統輸出

013     output reg flag;//輸出值有效標志(尖峰脈沖)

014     output reg [3:0] data;//按鍵值

015     output reg [3:0] col;//矩陣鍵盤的列線

016

017     reg clk_1K;//1K的時鍾

018     reg [20:0] count;//計數器

019     

020     always @ (posedge clk or negedge rst_n)

021         begin

022             if (!rst_n)

023                 begin

024                     clk_1K <= 1;

025                     count <= 0;

026                 end

027             else

028                 if (count < 24999) // 50000分頻,得出1K的時鍾

029                     count <= count + 1;

030                 else

031                     begin

032                         count <= 0;

033                         clk_1K <= ~clk_1K;

034                     end

035         end

036         

037     reg [4:0] cnt_time;//按鍵按下的時間

038     reg [1:0] state;//狀態寄存器

039     reg [7:0] row_col;//按鍵對應的行列值

040     

041     always @ (posedge clk_1K or negedge rst_n)

042         begin

043             if (!rst_n)//復位時,將中間寄存器和輸出置0

044                 begin

045                     flag <= 0;

046                     state <= 0;

047                     cnt_time <= 0;

048                     row_col <= 0;

049                     col <= 4'b0000;

050                 end

051             else

052                 begin

053                     case (state)

054                     0 : begin

055                        if (row != 4'b1111)//當有按鍵按下時,開始計數,只有

056                             begin    //一直按下20ms才會被當做有效的按鍵

057                                 if (cnt_time < 19)

058                                     cnt_time <= cnt_time + 1;

059                                 else

060                                      begin

061                                          cnt_time <= 0;

062                                          state <= 1;

063                                           col <= 4'b1110;//掃描的初始值

064                                       end

065                                     end

066                                 else

067                                     cnt_time <= 0;

068                             end

069                         

070                         1 : begin

071                                 if (row!=4'b1111)

072                                     begin

073                                         row_col <= {row,col};//當檢測出來時,把行列線的值存起來

074                                         flag <= 1;  //拉高有效標志

075                                         state <= 2;

076                                         col <= 4'b0000;//用於判斷按鍵是否抬起來

077                                     end

078                                 else

079                                     begin

080                                         col <= {col[2:0],col[3]};//沒有檢測出來時,換成下一列

081                                     end                              //掃描

082                             end

083                             

084                         2 : begin

085                                 if (row == 4'b1111)//當按鍵釋放20ms以后才會被當做釋放

086                                     begin  //跳轉到0狀態進行新的按鍵值的檢測

087                                         if (cnt_time < 19)

088                                             begin

089                                                 cnt_time <= cnt_time + 1;

090                                                 flag <= 0;

091                                             end

092                                         else

093                                             begin

094                                                 cnt_time <= 0;

095                                                 state <= 0;

096                                                 col <= 4'b0000;

097                                             end

098                                     end

099                                 else

100                                     begin

101                                         cnt_time <= 0;

102                                         flag <= 0;

103                                     end

104                             end

105                             

106                         default : state <= 0;

107                     endcase 

108                 end

109         end

110

111     always @ (*)

112         begin

113             if(!rst_n)

114                 begin

115                     data =0;

116                 end

117             else

118                 begin

119                     case(row_col)

120                         8'b1110_1110: data =0; 

121                         8'b1110_1101: data =1; //每一個按鍵的位置被行線和列線唯一確定

122                         8'b1110_1011: data =2; //根據行線和列線的值給出對應的按鍵值

123                         8'b1110_0111: data =3; 

124                         8'b1101_1110: data =4; 

125                         8'b1101_1101: data =5; 

126                         8'b1101_1011: data =6; 

127                         8'b1101_0111: data =7; 

128                         8'b1011_1110: data =8; 

129                         8'b1011_1101: data =9; 

130                         8'b1011_1011: data =10;

131                         8'b1011_0111: data =11;

132                         8'b0111_1110: data =12;

133                         8'b0111_1101: data =13;

134                         8'b0111_1011: data =14;

135                         8'b0111_0111: data =15;

136                         default : data = 0;             

137                     endcase

138                 end

139         end 

140         

141 endmodule 

 

測試代碼

/****************************************************          

 *   Engineer      :   夢翼師兄

 *   QQ             :   761664056

 *   The module function:矩陣鍵盤測試代碼

*****************************************************/

000 `timescale 1ns/1ps

001

002 module key_scan_tb;

003 //系統輸入

004 reg clk;//系統時鍾輸入

005 reg rst_n;//系統復位

006 reg [3:0] row;//矩陣鍵盤的行線

007 //系統輸出

008 wire flag;//輸出值有效標志(尖峰脈沖)

009 wire [3:0] data;//按鍵值

010 wire [3:0] col;//矩陣鍵盤的列線

011

012 initial

013 begin

014 clk=0;

015 rst_n=0;

016 # 1000.1 rst_n=1;

017 end

018

019 always #10 clk=~clk;//50M的時鍾

020

021 reg [4:0] pnumber;//按鍵值

022

023 initial

024 begin

025 pnumber=16;//無按鍵按下

026 # 6000000 pnumber=1;

027 # 3000000 pnumber=16;

028 # 6000000 pnumber=1;

029 # 3000000 pnumber=16;

030 # 6000000 pnumber=1;//模仿了一段抖動

031 # 3000000 pnumber=16;

032 # 6000000 pnumber=1;

033 # 3000000 pnumber=16;

034 # 6000000 pnumber=1;

035 # 21000000

036  pnumber=1;//按鍵“1”按下了21ms

037 # 3000000 pnumber=16;

038 # 6000000 pnumber=1;

039 # 3000000 pnumber=16;

040 # 6000000 pnumber=1;//模仿釋放時的抖動

041 # 3000000 pnumber=16;

042 # 6000000 pnumber=1;

043 # 3000000 pnumber=16;

044 # 6000000 pnumber=1;

045 # 3000000 pnumber=16;

046 # 60000000 

047  pnumber=2;

048 # 3000000 pnumber=16;

049 # 6000000 pnumber=2;

050 # 3000000 pnumber=16;

051 # 6000000 pnumber=2;

052 # 3000000 pnumber=16;//按下時的抖動

053 # 6000000 pnumber=2;

054 # 21000000

055  pnumber=2;//按下21ms

056 # 3000000 pnumber=16;

057 # 6000000 pnumber=2;

058 # 3000000 pnumber=16;

059 # 6000000 pnumber=2;

060 # 3000000 pnumber=16;//釋放時的抖動

061 # 6000000 pnumber=2;

062 # 3000000 pnumber=16;

063 # 6000000 pnumber=2;

064 # 3000000 pnumber=16;

065 end

066

067 //當有按鍵按下時,行線和列線的變化

068 always @(*)

069 case (pnumber)

070 0:    row = {1'b1,1'b1,1'b1,col[0]};  

071 1:    row = {1'b1,1'b1,1'b1,col[1]};

072 2:    row = {1'b1,1'b1,1'b1,col[2]};

073 3:    row = {1'b1,1'b1,1'b1,col[3]};

074 4:    row = {1'b1,1'b1,col[0],1'b1}; 

075 5:    row = {1'b1,1'b1,col[1],1'b1};

076 6:    row = {1'b1,1'b1,col[2],1'b1};

077 7:    row = {1'b1,1'b1,col[3],1'b1};

078 8:    row = {1'b1,col[0],1'b1,1'b1}; 

079 9:    row = {1'b1,col[1],1'b1,1'b1};

080 10:   row = {1'b1,col[2],1'b1,1'b1};

081 11:   row = {1'b1,col[3],1'b1,1'b1};

082 12:   row = {col[0],1'b1,1'b1,1'b1}; 

083 13:   row = {col[1],1'b1,1'b1,1'b1};

084 14:   row = {col[2],1'b1,1'b1,1'b1};

085 15:   row = {col[3],1'b1,1'b1,1'b1}; 

086 16:   row = 4'b1111;

087    default:   row = 4'b1111;

088    endcase

089

090 //實例化key_scan模塊

091  key_scan key_scan (

092 .clk(clk), //系統時鍾輸入

093 .rst_n(rst_n), //系統復位

094 .row(row),//矩陣鍵盤的行線

095 .flag(flag),//輸出值有效標志(尖峰脈沖)

096 .data(data),//按鍵值

097 .col(col)//矩陣鍵盤的列線

098 );

099

100 endmodule

在測試模塊的中的68行至88行,描述了矩陣鍵盤的響應方式。假如:“5”被按下,“5”處在row[1]col[1]的位置,只有當col[1]低電平時,row[1]才能檢測到低電平,並且row=4’b1101唯一確定了按鍵的位置。

在測試中,模擬了數字“1”以及數字“2”按下以及釋放時的抖動。

仿真分析

從波形中,我們可以看出:按鍵穩定前,pnumber有一段抖動,穩定之后,data變成了按鍵值,釋放時pnumber又有一段抖動,兩段抖動data都沒有發生改變。

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM