【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
这套电子书没有写完哦,已经写的部分,都已经发布在博客中了