1.按键消抖
通常的按键所用开关为机械弹性开关,当机械触点断开、 闭合时, 电压信号如下图所示
由于机械点的弹性作用,按键开关在闭合时不会马上稳定的接通,在断开时也不会一下子断开。在闭合和断开的瞬间均伴随着一连串的抖动。抖动时间的长短由按键的机械特性决定的, 一般为 5ms 到 10ms。 按键稳定闭合时间的长短则由操作人员的按键动作决定的, 一般为零点几秒至数秒。 按键抖动会引起按键被误读多次。 为了确保 CPU 对按键的一次闭合仅作一次处理, 必须进行消抖。
按键消抖有两种方式, 一种是硬件消抖(在按键管脚上串联RC电路,通过延时相当于把抖动的波滤除掉), 另一种是软件消抖。 为了使电路更加简单, 通常采用软件消抖。一个简单的按键消抖就是先读取按键的状态, 如果得到按键按下之后, 延时 10ms, 再次读取按键的状态, 如果按键还是按下状态, 那么说明按键已经按下。其中延时10ms 就是软件消抖处理。
2.矩阵键盘工作原理及检测
3X3 矩阵键盘将 9 个按键排成 3行 3 列, 第一行将每个按键的一端连接在一起构成行线, 第一列将每个按键的另一端连接在一起构成列线, 这样便一共有 3 行 3 列共 6 根线, 我们将这 6
根线连接到6 个 I/O 口上, 通过程序扫描键盘就可检测 9 个键。 用这种方法我们也可实现 4 行 4 列 16 个键、 5 行 5 列 25 个键、 6 行 6 列36 个键甚至更多。
矩阵键盘两端都与 I/O 口相连, 因此在检测时需编程通过一侧的 I/O 口送出低电平。 检测方法有多种,最常用的是行列扫描和线翻转法。
行列扫描法检测时, 先送一列为低电平, 其余几列全为高电平(此时我们确定了列数), 然后立即轮流检测一次各行是否有低电平, 若检测到某一行为低电平(这时我们又确定了行数),则我们便可确认当前被按下的键是哪一行哪一列的, 用同样方法轮流送各列一次低电平,再轮流检测一次各行是否变为低电平,这样即可检测完所有的按键, 当有键被按下时便可判断出按下的键是哪一个键。当然我们也可以将行线置低电平, 扫描列是否有低电平。 从而达到整个键盘的检测。
线翻转法, 就是使所有行线为低电平时, 检测所有列线是否有低电平, 如果有, 就记录列线值; 然后再翻转, 使所有列线都为低电平, 检测所有行线的值,由于有按键按下, 行线的值也会有变化, 记录行线的值。 从而就可以检测到全部按键。
3.程序
按键驱动程序源文件
#include "key.h"
//按键初始化函数
void KEY_Init(void)
{
EALLOW;
SysCtrlRegs.PCLKCR3.bit.GPIOINENCLK=1;//开启GPIO时钟
//KEY端口配置
GpioCtrlRegs.GPAMUX1.bit.GPIO12=0;//设置为普通IO口
GpioCtrlRegs.GPADIR.bit.GPIO12=0;//设置为输入
GpioCtrlRegs.GPAPUD.bit.GPIO12=0;//默认上拉
GpioCtrlRegs.GPAMUX1.bit.GPIO13=0;//设置为普通IO口
GpioCtrlRegs.GPADIR.bit.GPIO13=0;//设置为输入
GpioCtrlRegs.GPAPUD.bit.GPIO13=0;//默认上拉
GpioCtrlRegs.GPAMUX1.bit.GPIO14=0;//设置为普通IO口
GpioCtrlRegs.GPADIR.bit.GPIO14=0;//设置为输入
GpioCtrlRegs.GPAPUD.bit.GPIO14=0;//默认上拉
GpioCtrlRegs.GPBMUX2.bit.GPIO48=0;//设置为普通IO口
GpioCtrlRegs.GPBDIR.bit.GPIO48=1;//设置为输出
GpioCtrlRegs.GPBPUD.bit.GPIO48=0;//默认上拉
GpioCtrlRegs.GPBMUX2.bit.GPIO49=0;//设置为普通IO口
GpioCtrlRegs.GPBDIR.bit.GPIO49=1;//设置为输出
GpioCtrlRegs.GPBPUD.bit.GPIO49=0;//默认上拉
GpioCtrlRegs.GPBMUX2.bit.GPIO50=0;//设置为普通IO口
GpioCtrlRegs.GPBDIR.bit.GPIO50=1;//设置为输出
GpioCtrlRegs.GPBPUD.bit.GPIO50=0;//默认上拉
EDIS;
GpioDataRegs.GPBSET.bit.GPIO48=1;//设置输出高电平
GpioDataRegs.GPBSET.bit.GPIO49=1;//设置输出高电平
GpioDataRegs.GPBSET.bit.GPIO50=1;//设置输出高电平
//即使按键按下也不影响判断
}
//按键检测函数
char KEY_Scan(char mode)
{
static char keyl1=1;//三个静态变量(如不是静态变量,在执行过程中,每次进入该函数变量值为1.
static char keyl2=1;
static char keyl3=1;
//第一列扫描
KEY_L1_SetL;//第一列为低电平,其他为高电平
KEY_L2_SetH;//
KEY_L3_SetH;//
if(keyl1==1&&(KEY_H1==0||KEY_H2==0||KEY_H3==0))//有一个为0即按键按下
{
DELAY_US(10000);//软件消抖
keyl1=0;//如该列有按键按下,则变为0。方便下一次再判断(按键松开后,可能还有抖动,有了这个松开按键后不会再执行。其实是松开按键的消抖
if(KEY_H1==0)
{
return KEY1_PRESS;
}
else if(KEY_H2==0)
{
return KEY4_PRESS;
}
else if(KEY_H3==0)
{
return KEY7_PRESS;
}
}
else if(KEY_H1==1&&KEY_H2==1&&KEY_H3==1)
{
keyl1=1;//松开后按键要为1,为下次判断做准备
}
if(mode)//mode为0相当于按键按一下执行一下不能连续按。但当mode为1(连续扫描)时则可以连续按,像音量加减那样
keyl1=1;//始终
//第2列扫描
KEY_L1_SetH;//第2列为低电平,其他为高电平
KEY_L2_SetL;//
KEY_L3_SetH;//
if(keyl2==1&&(KEY_H1==0||KEY_H2==0||KEY_H3==0))//有一个为0即按键按下
{
DELAY_US(10000);//软件消抖
keyl2=0;
if(KEY_H1==0)
{
return KEY2_PRESS;
}
else if(KEY_H2==0)
{
return KEY5_PRESS;
}
else if(KEY_H3==0)
{
return KEY8_PRESS;
}
}
else if(KEY_H1==1&&KEY_H2==1&&KEY_H3==1)
{
keyl2=1;
}
if(mode)
keyl2=1;
//第三列扫描
KEY_L1_SetH;//第3列为低电平,其他为高电平
KEY_L2_SetH;//
KEY_L3_SetL;//
if(keyl3==1&&(KEY_H1==0||KEY_H2==0||KEY_H3==0))//有一个为0即按键按下
{
DELAY_US(10000);//软件消抖
keyl3=0;
if(KEY_H1==0)
{
return KEY3_PRESS;
}
else if(KEY_H2==0)
{
return KEY6_PRESS;
}
else if(KEY_H3==0)
{
return KEY9_PRESS;
}
}
else if(KEY_H1==1&&KEY_H2==1&&KEY_H3==1)
{
keyl3=1;
}
if(mode)
keyl3=1;
return KEY_UNPRESS;
}
按键驱动程序头文件
#ifndef KEY_H_
#define KEY_H_
#include "DSP2833x_Device.h"//包含所有的头文件,常用的宏定义 、F2833x 内核、 外设驱动的源文件对应的头文件
#include "DSP2833x_Examples.h"// 包含F2833x 通用文件的头文件。 比如中断、全局变量及函数声明、 IQmath 库等
#define KEY_L1_SetL (GpioDataRegs.GPBCLEAR.bit.GPIO48=1)
#define KEY_L2_SetL (GpioDataRegs.GPBCLEAR.bit.GPIO49=1)
#define KEY_L3_SetL (GpioDataRegs.GPBCLEAR.bit.GPIO50=1)
#define KEY_L1_SetH (GpioDataRegs.GPBSET.bit.GPIO48=1)
#define KEY_L2_SetH (GpioDataRegs.GPBSET.bit.GPIO49=1)
#define KEY_L3_SetH (GpioDataRegs.GPBSET.bit.GPIO50=1)
#define KEY_H1 (GpioDataRegs.GPADAT.bit.GPIO12)
#define KEY_H2 (GpioDataRegs.GPADAT.bit.GPIO13)
#define KEY_H3 (GpioDataRegs.GPADAT.bit.GPIO14)
#define KEY1_PRESS 1
#define KEY2_PRESS 2
#define KEY3_PRESS 3
#define KEY4_PRESS 4
#define KEY5_PRESS 5
#define KEY6_PRESS 6
#define KEY7_PRESS 7
#define KEY8_PRESS 8
#define KEY9_PRESS 9
#define KEY_UNPRESS 0
void KEY_Init(void);
char KEY_Scan(char mode);
#endif /* KEY_H_ */
主程序
#include "DSP2833x_Device.h"//包含所有的头文件,常用的宏定义 、F2833x 内核、 外设驱动的源文件对应的头文件
#include "DSP2833x_Examples.h"// 包含F2833x 通用文件的头文件。 比如中断、全局变量及函数声明、 IQmath 库等
#include "leds.h"
#include "key.h"
void main()
{
int i=0;
char key=0;
InitSysCtrl();//系统时钟初始化
LED_Init();
KEY_Init();
while(1)//不断的检测按键扫描
{
key=KEY_Scan(0);
switch(key)
{
case KEY1_PRESS: LED2_TOGGLE;break;
case KEY2_PRESS: LED3_TOGGLE;break;
case KEY3_PRESS: LED4_TOGGLE;break;
case KEY4_PRESS: LED5_TOGGLE;break;
case KEY5_PRESS: LED6_TOGGLE;break;
case KEY6_PRESS: LED7_TOGGLE;break;
}
i++;
if(i%2000==0)
{
LED1_TOGGLE;//设置GPIO输出翻转信号
i=0;
}
DELAY_US(100);
}
}