矩阵式键盘识别lsl.docx
《矩阵式键盘识别lsl.docx》由会员分享,可在线阅读,更多相关《矩阵式键盘识别lsl.docx(12页珍藏版)》请在冰豆网上搜索。
![矩阵式键盘识别lsl.docx](https://file1.bdocx.com/fileroot1/2023-4/24/68a82a7a-f27c-433d-b5b4-9989d13c9915/68a82a7a-f27c-433d-b5b4-9989d13c99151.gif)
矩阵式键盘识别lsl
矩阵式键盘识别lsl
FPGA实现矩阵式键盘识别
摘要:
按键是电子系统中普遍使用的人机交互方式。
根据需要按键的个数我们通常有独立按键、矩阵式键盘、编码式键盘等按键的接口方式。
本文介绍了用行扫描法和行反转法进行矩阵式键盘识别的原理并且用VerilogHDL实现了行扫描法矩阵式键盘的识别及键码显示。
关键字:
FPGA键盘VerilogHDL
在现实的电子系统中,往往我们希望能够对系统进行一定的控制,按键就是一种常用的实现人机交互的方式。
从独立按键到多个按键再到更复杂的键盘甚至是触摸屏等等形式各异的交互手段为我们实现对电子系统的控制了多种选择。
虽然实现手段看起来多种多样但是都不管怎样都需要对按键进行识别。
对于单个按键或者少数个按键,我们可以单独用一个或几个I/O口对其进行检测,但是如果需要的按键数偏多的话,直接采用一个按键占用一个I/O口的方式对按键进行识别的话,占用的I/O口资源就会明显增加,是对I/O口资源的严重浪费。
于是聪明的工程师们发明了采用尽量少的I/O资源实现尽量多的按键的检测的接口方式和识别方法。
常用的有矩阵式,编码式,还有一些个人独创的可以实现更多按键识别的方式,但是检测方式比较复杂。
通常情况下,一个电子系统的按键不会非常多,采用矩阵式键盘来实现已经完全可以满足需要了。
如果需要的键盘特别多,如PC键盘等,一般会采用专门的键盘检测编码芯片来实现,可以简化系统的设计。
所以我们这里就讨论矩阵式键盘的识
别。
如图一所示是一个四行五列的矩阵式
键盘,有20个按键。
我们对按键从左到右,
从上至下由0开始编码。
我们这里要实现
的是对每个按键的按键动作进行识别,并
且将键码显示在7段数码管上,以验证我
们按键进行了正确的识别。
对于矩阵式键盘的识别,通常我们又有两种方法。
一种是行扫描,另一种是行反转法。
拿图一所示的原理图来说,四行五列,行扫描法需要四个I/O口作为行扫
1
描信号输出,需要五个I/O口作为列信号反馈信号输入。
列线接了高电平,按键未按下时,可以检测到读进来的行信号都为高电平‘1’。
行扫描法就是轮流让其中一个行输出I/O为低电平‘0’,而其他行输出I/O输出高电平,如此循环变化,如果行扫描信号足够快的话,当其中某个按键按下的时候,读入的列信号中就有一个I/O为低电平‘0’,而其他的为高电平‘1’,这样,根据检测到列的低电平‘0’时的输出扫描行码和读入的列码就可以对按键进行唯一编码,从而实现对按键的识别。
对于另一种矩阵键盘识别方法——行反转法,它的工作原理是这样的:
将行列I/O口都上拉,行I/O口先做为输出口,所有I/O口输出低电平‘0’,而列I/O口做为输入口,对列I/O进行检测,如果没有按键按下的话,读入的列信号应该都为高电平‘1’,而当检测到读入的列信号中有一个变为低电平‘0’时,说明在该列至少有一个按键按下了。
此时,对行列进行反转,将行I/O置成输入口,将列I/O置成输出口,输出低电平‘0’,读取行I/O的电平信号。
如果该行没有按键按下,那么读进来的就是被上拉的高电平‘1’,如果该行中后按键按下,那么对应行I/O就为低电平‘0’。
根据第一次检测到列出现低电平时的列码和反转后读得的行码,就可以对按键进行唯一编码了,从而实现了按键的识别。
从上述两种矩阵式键盘的扫描原理来看,我们可以知道,无论是行扫描法还是行反转法,都可以实现矩阵式键盘的识别,而且识别方法都不复杂。
对比上面两种方法,我们可以看到,行扫描法固定I/O口的输入输出方式,在以后的检测中,I/O口的输入输出方式将保持不变,可以说这是行扫描法对于行反转法的一个小小的优势吧。
对于早期的CPU或MCU如i8086/8088等,一般要使用I/O口扩展芯片,如8255A等。
将行列接在8255A上时,需要在汇编代码中写入状态字来配置I/O口的输入输出,所以说行扫描法的固定I/O口的方向相对于行反转法中需要对行列I/O口的方向进行频繁变化存在这么一个小小的优点。
然而对
MCU也罢,还有可编程器件FPGA/CPLD等,它们的pin于现在的CPU也好,
都是双向I/O口,可读入也可以输出,所以行反转法实现起来也是很简便的。
行反转法和行扫描法相比,有一个很大的优点就是,行扫描法需要对行I/O实时的输出扫描信号和读取列信号,而行反转法则不需要。
行反转法只需读列信号,但发现有按键按下时才进行反转。
很明显,行反转法的工作效率要比行扫描法要
2
高。
这对于现在电子系统要求的高速,高效,低耗来说,无疑是一个重要优点。
尽管如此,针对本次采用可编程器件FPGA/CPLD实现矩阵式键盘接口,我们采用的是上述的第一种方法,即行扫描方法来实现矩阵式键盘的按键识别的。
对于可编程器件来说,内部的门资源很多,实现组合逻辑电路和时序逻辑电路非常的简单,但是不管它内部资源如何的丰富,在它没有被综合成一个处理器时,它的控制能力个人认为还是没有一个通用或专用的处理器、控制器强的。
对于上述的两种矩阵式键盘的按键识别,可以针对不同的接口电路采用不同的识别方式。
如果采用行反转法,通过前述的分析,我们知道它不需要实时提供一个键盘检测扫描信号,而是只需要实时检测列的电平变化,更需要的是一种控制。
而采用行扫描法的话,由于需要让行I/O口循环输出低电平的扫描信号,采用MCU实现的话,需要定时的让行I/O变化,这样会很大的限制MCU的控制作用,而采用FPGA实现时,就不会存在这种限制,因为可以从系统时钟中分出一个时钟专门用于按键的识别。
分频,对于可编程逻辑器件来说,是一个基本的功能。
所以,采用行扫描法实现矩阵式键盘的按键识别可以充分显示FPGA强大的组合逻辑和时序逻辑功能。
矩阵式键盘按键识别显示系统的实现框图如图二所示。
从图二我们可以更直观的用FPGA
实现行扫描法识别矩阵式按键的过程。
图中实际上有四个部分:
分频电路,扫
描电路,列码输入按键编码和键码的译
码输出。
分频电路是为了产生一个行扫
描输出时钟,实现按键的实时检测。
扫
描电路时为了循环输出0111、1011、
1101、1110……的行扫描信号,而列码读
入和按键编码则是实时读入列I/O的状
态,对每一个按键进行唯一编码。
最后
为了将按键的编码用七段数码管显示出来,以方便检测我们的按键识别是否准确,还用了一个译码器,将按键编码译成七段数码显示编码,同时输出数码管使能信号en,这样就可以在有按键按下时,显示出其键码来。
3
以下是用FPGA实现行扫描法识别矩阵式按键的VerilogHDL程序及必要注释:
VerilogHDL程序
//////////////////////////////////////////////////////////////////////////////////
//Company:
//Engineer:
lishiliang_lsl//CreateDate:
13:
19:
1610/24/2011
//DesignName:
key_scan//ModuleName:
key_scan//ProjectName:
key_scan//TargetDevices:
//Toolversions:
QuartusII9.0
键盘扫描模块symbol//////////////////////////////////////////////////////////////////////////////////
modulekey_scan(clk_sys,rst_n,c,r,data,en);
inputclk_sys,rst_n;
input[4:
0]c;
output[3:
0]r;
output[7:
0]data;
output[1:
0]en;
reg[7:
0]data;
reg[26:
0]count1;
regclk_scan;
reg[3:
0]r_tmp;
reg[4:
0]c_tmp;
reg[7:
0]data_shi;
reg[7:
0]data_ge;
reg[1:
0]en_tmp;
parametercnt=27'd10000;//为了便于仿真,仿真时将cnt改为了cnt=27'b10;
parameterzero=27'd0000;
//初始化
initial
begin
r_tmp<=4'b1110;
en_tmp<=4'b1110;
data_shi<=8'hff;
data_ge<=8'hff;
count1<=cnt;
end
//分频给行扫描
always@(posedgeclk_sys)
begin
4
if(!
rst_n)
begin
count1<=cnt;
end
if(count1==zero)
begin
clk_scan<=~clk_scan;
count1<=cnt;
end
else
begin
count1<=count1-1'b1;
end
end
//c0-c3按照扫描时钟输出扫描信号
always@(posedgeclk_scan)
begin
r_tmp<={r_tmp[2:
0],r_tmp[3]};//实现循环移位
end
//输出行扫描信号
assignr=r_tmp[3:
0];
//读取列扫描信号
always@(negedgeclk_scan)
begin
c_tmp<=c;
end
//根据行列信号,判定按键的位置,给数码管赋值
always@(negedgeclk_scan)
begin
case({r_tmp,c_tmp})
9'b1110_11110:
begindata_shi<=8'h03;data_ge<=8'h03;end//00
9'b1110_11101:
begindata_shi<=8'h03;data_ge<=8'h9f;end//01
9'b1110_11011:
begindata_shi<=8'h03;data_ge<=8'h25;end//02
9'b1110_10111:
begindata_shi<=8'h03;data_ge<=8'h0d;end//03
9'b1110_01111:
begindata_shi<=8'h03;data_ge<=8'h99;end//04
9'b1101_11110:
begindata_shi<=8'h03;data_ge<=8'h49;end//05
9'b1101_11101:
begindata_shi<=8'h03;data_ge<=8'h41;end//06
9'b1101_11011:
begindata_shi<=8'h03;data_ge<=8'h1f;end//07
5
9'b1101_10111:
begindata_shi<=8'h03;data_ge<=8'h01;end//08
9'b1101_01111:
begindata_shi<=8'h03;data_ge<=8'h09;end//09
11110:
begindata_shi<=8'h9f;data_ge<=8'h03;end//109'b1011_
9'b1011_11101:
begindata_shi<=8'h9f;data_ge<=8'h9f;end//11
9'b1011_11011:
begindata_shi<=8'h9f;data_ge<=8'h25;end//12
9'b1011_10111:
begindata_shi<=8'h9f;data_ge<=8'h0d;end//13
9'b1011_01111:
begindata_shi<=8'h9f;data_ge<=8'h99;end//14
9'b0111_11110:
begindata_shi<=8'h9f;data_ge<=8'h49;end//15
9'b0111_11101:
begindata_shi<=8'h9f;data_ge<=8'h41;end//16
9'b0111_11011:
begindata_shi<=8'h9f;data_ge<=8'h1f;end//17
9'b0111_10111:
begindata_shi<=8'h9f;data_ge<=8'h01;end//18
9'b0111_01111:
begindata_shi<=8'h9f;data_ge<=8'h09;end//19
endcase
end
//输出数码管使能信号EN
always@(negedgeclk_scan)
begin
en_tmp<={en_tmp[0],en_tmp[1]};//实现循环移位
end
assignen=en_tmp;
//显示按键值
always@(negedgeclk_scan)
begin
case(en_tmp)
2'b10:
data<=data_shi;
2'b01:
data<=data_ge;
default:
data<=8'hff;
endcase
end
endmodule
为了验证程序的正确与否,我们编写了测试程序key_scan_tb,同时还用QuartusII的波
形仿真功能,对程序进行了验证,一下是测试程序的内容。
6
/////////////////////////////////////////////
/*矩阵式键盘行扫描法识别测试程序*/
/*此程序是在把.v程序中的分频常数cnt改为10*//*实现的.目的是为了便于得到仿真结果*///2011-12-10///////////////////////////////
//////////////////////////////////////////////
`timescale1ns/100p;modulekey_scan_tb;
wiredata;
wire[1:
0]en;
wire[3:
0]r;
reg[4:
0]c;
regclk_sys;
regrst_n;
key_scanu1(.clk_sys(clk_sys),.rst_n(rst_n),
.c(c),.r(r),.en(en),.data(data));
always
begin
#50clk_sys=~clk_sys;
end
initial
begin
#50rst_n=1'b0;
#300rst_n=1'b1;
end
always
begin
//模拟输入一个列码,得到对应的
//按键码验证程序结果的正确性
#500c<=5'b11110;
#100c<=5'b11111;
#500c<=5'b11101;
#100c<=5'b11111;
#500c<=5'b11011;
#100c<=5'b11111;
7
#500c<=5'b10111;
#100c<=5'b11111;
#500c<=5'b01111;
#100c<=5'b11111;
这里只验证了这几种情况,结果还跟r有关//
end
always@(c)
begin
$monitor("time=%d,r=%d,c=%d,data=%h\n",$time,r,c,data);
end
endmodule
在QuartusII下进行波形仿真,仿真结果如下:
8
从仿真结果可以看到,当有按键按下时,输出的data值与程序设计的一致,从而可以证明程序设计的正确性。
此外,本程序在实际的硬件中也试验过,是完全合理的。
以上完成了本次矩阵式按键识别的全部内容,程序的设计虽然简单,但是为我们进行FPGA/verilog的设计仿真提供了较完整参考的过程,同时也阐明了键盘识别的原理,有一定的实用和启发作用。
参考文献:
VerilogHDL程序设计与实践云创工作室著
9