进阶项目(11) 矩阵键盘程序设计讲解

一世执手 提交于 2019-11-29 15:39:06

写在前面的话

在使用按键的时候,如果按键不多的话,我们可以直接让按键与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都没有发生改变。

 

 

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!