【Kevin原创】《基于FPGA的简易计算器设计》第二章:矩阵键盘按键检测
2.1矩阵键盘检测原理说明
首先,我们的这个设计为了能够满足我们的数字输入,采用4×4矩阵键盘。大家可能听过独立按键,但是在咱们这里只对矩阵键盘进行讲解。
咱们先看一下矩阵键盘的电路原理图:
电路图中,总共有8根线,ROW[3:0]和COL[3:0]。
接着,咱们来说下这个矩阵键盘电路的工作原理。首先,ROW默认接收到的电平为4’b1111。但是如果有按键按下,那我们怎么来确定是哪个按键按下了呢?我们先一步一步来,假设当COL=4’b1110时,“0”按键按下,那我们接收到的ROW应该是4’b1110。这样一来,首先我们由COL=4’b1110确定了按键是在第一列,ROW=4’b1110确定了按键在第一行,所以确定按键的结果就是“0”按键。确定其余按键也是这样的,所以当我们确定某一个按键时,相应的COL值就应该为低电平,但是由于我们按下的按键是随机的,所以我们就需要使COL进行扫描,即从4’b1110,4’b1101,4’b1011,4’0111循环变化,当然变化的时间肯定是有间隔的。然后我们再通过接收到的ROW值来确定具体是哪个按键按下。这就是4×4矩阵按键的检测原理,当然4×4的原理搞清楚了,5×5或6×6那就不成问题了,对于独立按键检测那就更是小菜一碟了。
2.2按键消抖介绍
虽然矩阵键盘的原理讲清楚了,但是咱们怎么来写代码呢?这应该困扰过很多朋友。
在写代码之前,Kevin再给大家普及一个常识,对于机械键盘,在按下或松开时都是会有机械振动的。
而对于抖动的时间,一般为5ms~10ms,为了是按键检测更稳定,这里我们把抖动时间定义为10ms。过了10ms之后,我们再去检测按键的值,这个操作就是我们通常说的“消抖”。
2.2矩阵按键检测逻辑框图
对于我们的这个设计,我们先对矩阵键盘编号,然后对应的按键按下之后,我们需要将按键的值显示到数码管上。
2.3矩阵键盘按键检测时序设计
在矩阵键盘检测时,我们必须要知道是否有按键按下,检测到有按键按下之后,先进行10ms的消抖,再来检测处于稳定的按键值。
那咱们怎么来确定是否有按键按下了呢?这里我们可以用这样的一条语句“&row”,咱们在这里使用的是按位与的运算,即row[3]&row[2]&row[1]&row[0]。只要我们检测到“&row”的值为0,那我们就能够确定此时一定有按键按下。
对于这个时序图,Kevin在这里向大家解释一下:
Falg_col:当检测到按键按下后立即拉高,当按键的后消抖结束后再拉低
Col:根据flag_col信号的低电平来产生列扫描信号(时间间隔为20us)
Row_and:寄存&row的结果
Row_and_t:row_and 的延拍
Flag_neg:前消抖标志信号,前消抖计数器计满10ms时拉低
Cnt_pos:前消抖计数器,当前消抖结束后,继续保持计数器的值,直到后消抖结束后才清零
Flag_pos:后消抖标志
Cnt_pos:后消抖计数器
Col_lock:在检测到有按键按下时,寄存col的值,直到下次有按键按下时才变化
Row_lock:与col_lock作用相似
下面,咱们就来写代码了,写代码之前再说一句,col的扫描信号变换的时间为20us:
/*********************************************************************** * Module Name : keyscan * Engineer : Kevin * Function: : 矩阵键盘检测 * Blog Website : http://dengkanwen.com * Version : 1.0 ************************************************************************/ module keyscan( input wire sclk, input wire s_rst_n, input wire [3:0] row, //接收到的行信号 output reg [3:0] col, //按键的列扫描 output reg [3:0] key_val ); parameter CNT_END = 10'd999, //20us计数 CNT_F = 19'd499_999; //10ms计数 wire row_and; //row按位与结果 reg row_and_r; reg [9:0] cnt; //20us计时,用于col扫描 reg flag_col; //col扫描标志位 reg flag_neg; reg [18:0] cnt_neg; //10ms计数, reg flag_pos; reg [18:0] cnt_pos; reg [3:0] col_lock; //有按键按下时,col的值 reg [3:0] row_lock; //有按键按下时,row的值 //===========================记录row_and的值================================= assign row_and = &row; always @(posedge sclk) row_and_r <= row_and; //================================20us计数================================= always @(posedge sclk or negedge s_rst_n) if(s_rst_n == 1'b0) cnt <= 10'd0; else if(flag_col == 1'b0 && cnt == CNT_END) cnt <= 10'd0; else if(flag_col == 1'b1) cnt <= 10'd0; else cnt <= cnt + 1'b1; //=============================产生列扫描col信号=========================== always @(posedge sclk or negedge s_rst_n) if(s_rst_n == 1'b0) col <= 4'b1110; else if(flag_col == 1'b1) col <= col; else if(cnt == CNT_END) col <= {col[2:0], col[3]}; always @(posedge sclk or negedge s_rst_n) if(s_rst_n == 1'b0) flag_col <= 1'b0; else if(cnt_pos == CNT_F && cnt_neg == CNT_F) flag_col <= 1'b0; else if(row_and == 1'b0 && row_and_r == 1'b1) flag_col <= 1'b1; //==============================进行前消抖================================= always @(posedge sclk or negedge s_rst_n) if(s_rst_n == 1'b0) cnt_neg <= 19'd0; else if(flag_col == 1'b0) cnt_neg <= 19'd0; else if(cnt_neg == CNT_F && flag_col == 1'b1) cnt_neg <= CNT_F; else if(flag_neg == 1'b1) cnt_neg <= cnt_neg + 1'b1; always @(posedge sclk or negedge s_rst_n) if(s_rst_n == 1'b0) flag_neg <= 1'b0; else if(cnt_neg == CNT_F) flag_neg <= 1'b0; else if(row_and == 1'b0 && row_and_r == 1'b1) flag_neg <= 1'b1; //==============================进行后消抖================================== always @(posedge sclk or negedge s_rst_n) if(s_rst_n == 1'b0) flag_pos <= 1'b0; else if(cnt_pos == CNT_F) flag_pos <= 1'b0; else if(row_and == 1'b1 && row_and_r == 1'b0 && cnt_neg == CNT_F) flag_pos <= 1'b1; always @(posedge sclk or negedge s_rst_n) if(s_rst_n == 1'b0) cnt_pos <= 19'd0; else if(flag_pos == 1'b0) cnt_pos <= 19'd0; else cnt_pos <= cnt_pos + 1'b1; //==========================col与row的值进行锁存============================= always @(posedge sclk or negedge s_rst_n) if(s_rst_n == 1'b0) col_lock <= 4'b1111; else if(row_and == 1'b0 && row_and_r == 1'b1 && cnt_neg == 22'd0) col_lock <= col; always @(posedge sclk or negedge s_rst_n) if(s_rst_n == 1'b0) row_lock <= 4'b1111; else if(row_and == 1'b0 && row_and_r == 1'b1 && cnt_neg == 22'd0) row_lock <= row; //=========================================================================== always @(posedge sclk or negedge s_rst_n) if(s_rst_n == 1'b0) key_val <= 4'd0; else case({col_lock, row_lock}) 8'b1110_1110: key_val <= 4'd0; 8'b1101_1110: key_val <= 4'd1; 8'b1011_1110: key_val <= 4'd2; 8'b0111_1110: key_val <= 4'd3; 8'b1110_1101: key_val <= 4'd4; 8'b1101_1101: key_val <= 4'd5; 8'b1011_1101: key_val <= 4'd6; 8'b0111_1101: key_val <= 4'd7; 8'b1110_1011: key_val <= 4'd8; 8'b1101_1011: key_val <= 4'd9; 8'b1011_1011: key_val <= 4'd10; 8'b0111_1011: key_val <= 4'd11; 8'b1110_0111: key_val <= 4'd12; 8'b1101_0111: key_val <= 4'd13; 8'b1011_0111: key_val <= 4'd14; 8'b0111_0111: key_val <= 4'd15; default: key_val <= 4'd0; endcase endmodule
下面来说下这个矩阵按键怎么进行仿真,当然如果你对自己的设计有足够的自信,也可以不仿真。
(未完,明天继续)
BZH星
2017年3月28日 下午6:24
请问电子书哪里可以看到
Kevin
2017年3月28日 下午10:44
这套电子书没有写完哦,已经写的部分,都已经发布在博客中了