进阶项目(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都没有发生改变。

原文地址:https://www.cnblogs.com/mengyi1989/p/11521086.html

时间: 2024-10-02 12:45:15

进阶项目(11) 矩阵键盘程序设计讲解的相关文章

【思库教育】2017PHP项目实战基础+进阶+项目之基础篇

下载链接: [思库教育]2017PHP项目实战基础+进阶+项目之基础篇 小白变大牛,您的专属资源库! 小白变大牛,您的专属资源库! 内容非常充实,可以看目录,设计的面多,项目多,技能多,如果掌握好,找一份PHP的工作,易如反掌!学完后可以到PHP小白变大牛精华区查找更加符合你的资源或者项目! [思库教育]2017PHP项目实战基础+进阶+项目之基础篇[思库教育]2017PHP项目实战基础+进阶+项目之进阶篇[思库教育]2017PHP项目实战基础+进阶+项目之项目篇小白变大牛!Python小白,J

STM32 4x4矩阵键盘

可同时识别两个键值,并分别显示,用了自己的12864显示,有些注释的地方有乱码请忽略.基于STM32f103vct6. 主函数: #include "Rect_key.h" #include "12864.h" int main() { u16 keynum=0; delay_init(); lcd_gpio_init(); lcd_init(); Rect_key_Init(); lcd_6x8(0,0,"STM32 4x4 KEY Test :&quo

疑问----单片机矩阵键盘行列反转扫描法

学到矩阵键盘这一块对网上别人写的C代码有些疑问,希望有能看到的大牛帮助解答. 矩阵键盘和独立键盘原理图: 原理:对于图右侧的矩阵键盘,把P3口的低四位(P3.0 ~ P3.3)和高四位(P3.4 ~ P3.7)分别置1,只要有按键被按下,那么与之对应的P3口某一位将由高电平被拉低,两次分别测得了行值和列值即确定了哪一个按键被按下了. 比如:先把低四位赋高电平即 P3 = 0000_1111 假如 S15 被按下了,那么 P3.2 就被拉成低电平即 P3 = 0000_1011 ,此时再把高四位赋

矩阵键盘 数码管

开始时数码管不显示,顺序按下矩阵键盘后,在数码管上依次显示0~F,6个数码管同时静态显示. 数码管和矩阵键盘 首先关闭所有的数码管的段选不让数码管显示任何数字,然后位选中所有的数码管,接下来只需要选中所有的数码管,以后的操作的只需要送段选数据就行了,接着进入while()大循环不停的扫描键盘是否有被按下 #include<reg52.h> #define uchar unsigned char #define uint unsigned int sbit dula=P2^6; sbit wel

单片机入门-矩阵键盘控制数码管显示

任务简述:通过按下4X4矩阵键盘实现数码管对应显示数字 实现方式:通过行列分接法,可以节省使用单片机的I/O口,扫描矩阵键盘是否有按下,判断P2端口得到的值. protues 硬件系统 单片机晶振复位电路: 锁存器.上拉电阻: 4x4矩阵键盘: 代码实现: void key44scan(void)//键盘扫描函数 { uint t; P2=0xfe; t=P2; t=t&0xf0; if(t!=0xf0) { delay(); t=P2; switch(t) { case 0xee:keynum

4x4矩阵键盘扫描

4x4矩阵键盘扫描 Windows 10 IoT Core 是微软针对物联网市场的一个重要产品,与以往的Windows版本不同,是为物联网设备专门设计的,硬件也不仅仅限于x86架构,同时可以在ARM架构上运行. 上一章我们讲了 Win10 IoT 如何对本地 IoT 设备内嵌 SQLite 数据库进行 CURD 操作 ,这章我们来学习如何使用 GPIO Pin 扫描4x4矩阵键盘按键状态.如果对安装部署过程还不熟悉可以参考前几篇文章,Raspberry安装 IoT系统及搭建开发环境(http:/

51单片机实现矩阵键盘行扫描

-------------------------------------------- 分类: 按结构原理分: 触点式开关按键 无触点开关按键 接入方式 独立式按键 矩阵式键盘 -------------------------------------------- 矩阵式键盘识别方法(行扫描法) 检测列线的状态: 列线Y4~Y7置高电平,行线Y0~Y3置低电平.只要有一列的电平为低,则表示键盘该列有一个或多个按键被按下.若所有列线全为高电平,则键盘中无按键按下. 判断闭合按键所在的位置: 行

十天学会单片机Day2键盘检测(独立键盘、矩阵键盘)

1.键盘的分类 编码键盘:键盘上闭合键的识别由专用的硬件编码器实现,并产生键编码号或键值的称为编码键盘,如计算机键盘 非编码键盘:靠软件编程来识别的称为非编码键盘.独立键盘.矩阵键盘 2.按键消抖              如图,理想波形与实现波形之间是有区别的.实际波形在按下和释放的瞬间都有抖动现象,抖动事件的长短与按键的机械特性有关,一般为5~10ms.通常我们手动按下键然后立即释放,这个动作中稳定闭合时间超过20ms.因此检测键盘是否按下时,需加上去抖动操作. 一般有两种消抖方式: 硬件消

矩阵键盘实验

电路原理图如下:其中ROW[3:0]为处理器输入,COL[3:0]为处理器输出 矩阵键盘实验代码一共包括四个部分:1.按键检测(matrix_key_scan) 2.读取数据(led_input_display) 3.74HC595显示(led_74595_driver) 4.顶层模块(KEY_Scan_Design).整体RTL视图如下: 按键检测部分(matrix_key_scan)检测原理如下: 按键检测(matrix_key_scan)状态机如下: 1 //===============