文章目录

  • 前言
  • 1. 延时消抖
    • 1.1 硬件电路
    • 1.2 3种工作方式的代码
  • 2. 利用GPIO输入量化实现按键消抖
    • 2.1 理论分析
    • 2.1 代码分析

前言

本文是在学习普中TMS320F28335开发板矩阵键盘的基础上总结得到的。

本文首先利用延时消抖的方法,给出矩阵键盘3种工作方式的代码,即按下工作,按下再抬起工作,按下连续工作。之后根据GPIO的特性,讨论是否能利用GPIO的输入量化实现按键消抖。


1. 延时消抖

1.1 硬件电路

开发板上矩阵键盘的硬件连接如图1所示。

图1 矩阵键盘硬件连接电路

1.2 3种工作方式的代码

对于一个矩阵键盘而言,利用行列扫描法来判断键值是最容易的。本次所给的代码中令GPIO12、GPIO13、GPIO14依次作为输出口,输出低电平;GPIO48、GPIO49、GPIO50作为输入口。

对于一个按键而言,其按下与抬起的瞬间都需要消抖,抖动的时间大约为5~10ms,本次所给的代码中以延时10ms来消抖。

一般来说按键按下后实现的效果主要有3种:

  1. 按下按键后立即返回键值,执行相应的功能。例如大多数开关的开启。
  2. 按下按键并抬起后返回键值,执行相应的功能。例如大多数开关的关闭。
  3. 按下按键后持续返回键值,执行相应的功能。例如遥控器、汽车的喇叭等。

实现代码如下(注意:以下代码并没有给出开发板上LED的初始化程序):
Key.c文件

/** Key.c*/
#include "DSP2833x_Device.h"
#include "DSP2833x_Examples.h"
#include "Key.h"void Key_Init()
{EALLOW;
//  TZ1GpioCtrlRegs.GPAMUX1.bit.GPIO12 = 0;GpioCtrlRegs.GPAPUD.bit.GPIO12 = 0;GpioDataRegs.GPADAT.bit.GPIO12 = 1;GpioCtrlRegs.GPADIR.bit.GPIO12 = 1;
//  TZ2GpioCtrlRegs.GPAMUX1.bit.GPIO13 = 0;GpioCtrlRegs.GPAPUD.bit.GPIO13 = 0;GpioDataRegs.GPADAT.bit.GPIO13 = 1;GpioCtrlRegs.GPADIR.bit.GPIO13 = 1;
//  TZ3GpioCtrlRegs.GPAMUX1.bit.GPIO14 = 0;GpioCtrlRegs.GPAPUD.bit.GPIO14 = 0;GpioDataRegs.GPADAT.bit.GPIO14 = 1;GpioCtrlRegs.GPADIR.bit.GPIO14 = 1;
//  ECAP5GpioCtrlRegs.GPBMUX2.bit.GPIO48 = 0;GpioCtrlRegs.GPBDIR.bit.GPIO48 = 0;GpioCtrlRegs.GPBPUD.bit.GPIO48 = 0;
//  ECAP6GpioCtrlRegs.GPBMUX2.bit.GPIO49 = 0;GpioCtrlRegs.GPBDIR.bit.GPIO49 = 0;GpioCtrlRegs.GPBPUD.bit.GPIO49 = 0;
//  EQEP1AGpioCtrlRegs.GPBMUX2.bit.GPIO50 = 0;GpioCtrlRegs.GPBDIR.bit.GPIO50 = 0;GpioCtrlRegs.GPBPUD.bit.GPIO50 = 0;EDIS;
}/** mode=0表示按下按键后立即返回键值* mode=1表示按下按键并抬起后返回键值* mode=2表示按下按键后持续返回键值*/
char Key_Scan(char mode)
{static char FLAG1=1; //第一行按键可以有效按下的标志位,按下变为0,不按为1static char FLAG2=1; //第二行按键可以有效按下的标志位,按下变为0,不按为1static char FLAG3=1; //第三行按键可以有效按下的标志位,按下变为0,不按为1static char Key_VALUE; //mode=1时使用,用于存储键值char tempt;     //tempt作为消抖的中间变量//第一行扫描Key_H1_SetL;Key_H2_SetH;Key_H3_SetH;//mode=0时的代码if((mode==0) && (FLAG1==1) && (Key_L1==0 || Key_L2 ==0 || Key_L3 ==0)){DELAY_US(10000);FLAG1=0;         //防止重复返回键值if(Key_L1==0) return Key1_PRESS;if(Key_L2==0) return Key2_PRESS;if(Key_L3==0) return Key3_PRESS;}else if((mode==0) && (Key_L1==1 && Key_L2 ==1 && Key_L3 ==1)){FLAG1=1;}//mode=1时的代码if((mode==1) && (FLAG1==1) && (Key_L1==0 || Key_L2 ==0 || Key_L3 ==0)){DELAY_US(10000);FLAG1=0;if(Key_L1==0) Key_VALUE = Key1_PRESS;if(Key_L2==0) Key_VALUE = Key2_PRESS;if(Key_L3==0) Key_VALUE = Key3_PRESS;}else if((mode==1) && (FLAG1==0) && (Key_L1==1 && Key_L2 ==1 && Key_L3 ==1)){FLAG1=1;tempt = Key_VALUE;Key_VALUE = 0;return tempt;}//mode=2时的代码if((mode==2) && (Key_L1==0 || Key_L2 ==0 || Key_L3 ==0)){if(Key_L1==0) return Key1_PRESS;if(Key_L2==0) return Key2_PRESS;if(Key_L3==0) return Key3_PRESS;}//第二行扫描Key_H1_SetH;Key_H2_SetL;Key_H3_SetH;//mode=0时的代码if((mode==0) && (FLAG2==1) && (Key_L1==0 || Key_L2 ==0 || Key_L3 ==0)){DELAY_US(10000);FLAG2=0;         //防止重复返回键值if(Key_L1==0) return Key4_PRESS;if(Key_L2==0) return Key5_PRESS;if(Key_L3==0) return Key6_PRESS;}else if((mode==0) && (Key_L1==1 && Key_L2 ==1 && Key_L3 ==1)){FLAG2=1;}//mode=1时的代码if((mode==1) && (FLAG2==1) && (Key_L1==0 || Key_L2 ==0 || Key_L3 ==0)){DELAY_US(10000);FLAG2=0;if(Key_L1==0) Key_VALUE = Key4_PRESS;if(Key_L2==0) Key_VALUE = Key5_PRESS;if(Key_L3==0) Key_VALUE = Key6_PRESS;}else if((mode==1) && (FLAG2==0) && (Key_L1==1 && Key_L2 ==1 && Key_L3 ==1)){FLAG2=1;tempt = Key_VALUE;Key_VALUE = 0;return tempt;}//mode=2时的代码if((mode==2) && (Key_L1==0 || Key_L2 ==0 || Key_L3 ==0)){if(Key_L1==0) return Key4_PRESS;if(Key_L2==0) return Key5_PRESS;if(Key_L3==0) return Key6_PRESS;}//第三行扫描Key_H1_SetH;Key_H2_SetH;Key_H3_SetL;//mode=0时的代码if((mode==0) && (FLAG3==1) && (Key_L1==0 || Key_L2 ==0 || Key_L3 ==0)){DELAY_US(10000);FLAG3=0;         //防止重复返回键值if(Key_L1==0) return Key7_PRESS;if(Key_L2==0) return Key8_PRESS;if(Key_L3==0) return Key9_PRESS;}else if((mode==0) && (Key_L1==1 && Key_L2 ==1 && Key_L3 ==1)){FLAG3=1;}//mode=1时的代码if((mode==1) && (FLAG3==1) && (Key_L1==0 || Key_L2 ==0 || Key_L3 ==0)){DELAY_US(10000);FLAG3=0;if(Key_L1==0) Key_VALUE = Key7_PRESS;if(Key_L2==0) Key_VALUE = Key8_PRESS;if(Key_L3==0) Key_VALUE = Key9_PRESS;}else if((mode==1) && (FLAG3==0) && (Key_L1==1 && Key_L2 ==1 && Key_L3 ==1)){FLAG3=1;tempt = Key_VALUE;Key_VALUE = 0;return tempt;}//mode=2时的代码if((mode==2) && (Key_L1==0 || Key_L2 ==0 || Key_L3 ==0)){if(Key_L1==0) return Key7_PRESS;if(Key_L2==0) return Key8_PRESS;if(Key_L3==0) return Key9_PRESS;}return Key_UNPRESS; //无按键按下时返回0
}

Key.h文件

/** Key.h*/
#ifndef KEY_H_
#define KEY_H_void Key_Init();
char Key_Scan(char mode);#define Key_H1_SetL (GpioDataRegs.GPACLEAR.bit.GPIO12=1)
#define Key_H2_SetL (GpioDataRegs.GPACLEAR.bit.GPIO13=1)
#define Key_H3_SetL (GpioDataRegs.GPACLEAR.bit.GPIO14=1)#define Key_H1_SetH (GpioDataRegs.GPASET.bit.GPIO12=1)
#define Key_H2_SetH (GpioDataRegs.GPASET.bit.GPIO13=1)
#define Key_H3_SetH (GpioDataRegs.GPASET.bit.GPIO14=1)#define Key_L1 (GpioDataRegs.GPBDAT.bit.GPIO48)
#define Key_L2 (GpioDataRegs.GPBDAT.bit.GPIO49)
#define Key_L3 (GpioDataRegs.GPBDAT.bit.GPIO50)#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#endif /* KEY_H_ */

main.c文件

/** main.c*/
#include "DSP2833x_Device.h"
#include "DSP2833x_Examples.h"
#include "Key.h"
#include "LED_Set.h"
void main()
{char KeyNum;InitSysCtrl();LED_Init();Key_Init();while(1){KeyNum = Key_Scan(1);switch(KeyNum){case 1:{LED1_TOGGLE;break;}case 2:{LED2_TOGGLE;break;}case 3:{LED3_TOGGLE;break;}case 4:{LED4_TOGGLE;break;}case 5:{LED5_TOGGLE;break;}case 6:{LED6_TOGGLE;break;}case 7:{LED7_TOGGLE;break;}case 8:{LED1_TOGGLE;break;}case 9:{LED2_TOGGLE;break;}}}
}

对于Key.c文件中的Key_Scan(char mode)函数有几点要注意:

  1. 当mode=0时,按键按下的消抖延时函数(DELAY_US(10000))也是按键抬起时的消抖延时函数,也就是说这个DELAY_US(10000)同时实现了按下消抖和抬起消抖,因此不需要考虑抬起消抖部分;
  2. 当mode=1时,按键按下的消抖仍然用延时函数,但是此时的延时函数不能再用于按键抬起消抖,因此用temp作为中间变量来实现消抖;
  3. 当mode=2时,此时不设置消抖处理,因为对于连续按的情况如果设置延时消抖可能会影响器件的正常工作。例如,对于无源蜂鸣器来说,当振荡源振荡频率越高时,其声音将会越大,而振荡频率高的实质是振荡周期短;如果设置了延时10ms,那么蜂鸣器接收到有效键值时将延迟10ms,此时振荡源的振荡周期将非常大,蜂鸣器的声音将非常低。

2. 利用GPIO输入量化实现按键消抖

GPIO的输入量化指:当GPIO作为通用输入输出口并配置为输入模式时,它是可以通过GPxCTRL(x=A,B)来设置采样周期,通过GPxQSEL1/2(x=A,B)来设置采样次数。当在采样时间(采样周期×采样次数)内GPIO端口发生电平的波动时,GPxDAT寄存器中的值仍然是采样之前的值,直至整个采样时间(采样周期×采样次数)内GPIO端口电平保持稳定,GPxDAT寄存器中的值才会发生改变。

可见,可以利用GPIO的输入量化来实现按键的消抖。

2.1 理论分析

查阅数据手册中GPxCTRL、GPxQSEL1/2(x=A,B)两个寄存器的说明,可以发现:最大采样周期为510×SYSCLKOUT=510×6.67ns=3.4us,假设采样周期为6(最大),则采样时间T为6×3.4us=20.4us。也就是说GPIO最大量化延时20.4us得到正确的电平,这远远小于10ms,故实现不了消抖!

其次,GPIO设置了输入量化后,其读取端口电平一定具有延时性,而程序的运行速度是很快的,因此这种延时性很可能使得程序连最基本的键值判断函数都进不去!

综上分析,利用GPIO的输入量化来实现按键消抖并不是一件值得推崇的工作!

2.1 代码分析

以矩阵键盘的第一行为例来说明输入量化实现按键消抖出现的问题。

代码如下(以mode=1为例):

void Key_Init()
{EALLOW;
//  TZ1GpioCtrlRegs.GPAMUX1.bit.GPIO12 = 0;GpioCtrlRegs.GPAPUD.bit.GPIO12 = 0;    GpioCtrlRegs.GPADAT.bit.GPIO12 = 1;GpioCtrlRegs.GPADIR.bit.GPIO12 = 1;
//  ECAP5GpioCtrlRegs.GPBMUX2.bit.GPIO48 = 0;GpioCtrlRegs.GPBDIR.bit.GPIO48 = 0;GpioCtrlRegs.GPBPUD.bit.GPIO48 = 0;
//  ECAP6GpioCtrlRegs.GPBMUX2.bit.GPIO49 = 0;GpioCtrlRegs.GPBDIR.bit.GPIO49 = 0;GpioCtrlRegs.GPBPUD.bit.GPIO49 = 0;
//  EQEP1AGpioCtrlRegs.GPBMUX2.bit.GPIO50 = 0;GpioCtrlRegs.GPBDIR.bit.GPIO50 = 0;GpioCtrlRegs.GPBPUD.bit.GPIO50 = 0;
//  设置GPIO48、GPIO49和GPIO50的采样周期为最大510×SYSCLKOUTGpioCtrlRegs.GPBCTRL.bit.QUALPRD2 = 0xFF;
//  设置GPIO48、GPIO49和GPIO50的采样次数为6GpioCtrlRegs.GPBQSEL2.bit.GPIO48 = 2;GpioCtrlRegs.GPBQSEL2.bit.GPIO49 = 2;GpioCtrlRegs.GPBQSEL2.bit.GPIO50 = 2;EDIS;
}/** mode=0表示单次按下(按下即返回键值)*/
char Key_Scan(char mode)
{static char FLAG1=1; //第一行有按键按下的标志位,按下变为0,不按为1//第一行扫描Key_H1_SetL;Key_H2_SetH;Key_H3_SetH;//mode=0时的代码if((mode==0) && (FLAG1==1) && (Key_L1==0 || Key_L2 ==0 || Key_L3 ==0)){FLAG1=0;         //防止重复返回键值if(Key_L1==0) return Key1_PRESS;if(Key_L2==0) return Key2_PRESS;if(Key_L3==0) return Key3_PRESS;}else if((mode==0) && (Key_L1==1 && Key_L2 ==1 && Key_L3 ==1)){FLAG1=1;}return Key_UNPRESS; //无按键按下时返回0
}

实际的情况是:无论按第一行的哪一个按键,LED都不会亮,这是为什么呢?

由于设置了输入量化,所以GPIO48、GPIO49和GPIO50在获取端口电平时都具有20.4us的延时,假设在“ Key_H1_SetL;Key_H2_SetH;Key_H3_SetH;”语句之前按键按下,当执行完“ Key_H1_SetL;Key_H2_SetH;Key_H3_SetH;”语句之后执行“if((mode == 0) && (FLAG1 == 1) && (Key_L1 == 0 || Key_L2 == 0 || Key_L3 == 0))”语句时,由于输入量化的延时,GPIO48、GPIO49和GPIO50的端口电平永远是1,这就使得键值返回部分的程序永远无法执行,这也就解释了为什么LED不会亮的原因。

解决上述问题有两种方法:

一是减小采样周期和采样次数以减小量化时间,从而能有效地判断“if((mode == 0) && (FLAG1 == 1) && (Key_L1 == 0 || Key_L2 == 0 || Key_L3 == 0))”语句。量化时间的缩小虽然可以使得键值返回部分的程序得到执行,但是并不会消除按键抖动带来的影响;

二是在“Key_H1_SetL;Key_H2_SetH;Key_H3_SetH;”语句之后设置一个延时,即:

 ……Key_H1_SetL;Key_H2_SetH;Key_H3_SetH;DELAY_US(10000);//mode=0时的代码if((mode==0) && (FLAG1==1) && (Key_L1==0 || Key_L2 ==0 || Key_L3 ==0))……

以保证输入量化地延时被过滤掉。这种方法的实质就是延时消抖。

综上,利用GPIO的输入量化来实现消抖是不可取的!

F28335矩阵键盘的3种工作方式代码及按键消抖讨论(包括利用GPIO输入量化实现按键消抖)相关推荐

  1. LVS原理详解(3种工作方式8种调度算法)--老男孩

    一.LVS原理详解(4种工作方式8种调度算法) 集群简介 集群就是一组独立的计算机,协同工作,对外提供服务.对客户端来说像是一台服务器提供服务. LVS在企业架构中的位置: 以上的架构只是众多企业里面 ...

  2. apache php 工作模式,PHP Apache中两种工作方式区别(CGI模式、Apache 模块DLL)

    搜索热词 对PHP在Apache中两种工作方式的区别(CGI模式.Apache 模块DLL)感兴趣的小伙伴,下面一起跟随编程之家 jb51.cc的小编两巴掌来看看吧! Windows 下有两种方法使 ...

  3. linux 学习 vi简介; vi下三种工作方式。

    VI Visual Interface 三种工作方式 1.命令方式 应举例举,例我们在查询时 /session 时,其实就是在 命令模式下 按下 / 进行的. 2.输入方式 我们可以在这里对文件进行编 ...

  4. 7. 【可编程定时器8253】:外部引脚、内部结构特点、计数启动方式、6种工作方式、控制字格式、应用

    文章目录 计数与定时 8253芯片特点 1. 外部引脚 2. 内部结构特点(含3个计数器.1个控制寄存器) 2. 计数启动方式(软件启动.硬件启动的定义) 3. 6种工作方式 不能自动重复计数的工作方 ...

  5. 直接存储器存取(DMA)有哪3种工作方式?

    DMA是I/0设备与主存储器之间由硬件组成的直接数据通路,用于高速I/0设备与主存之间的成组数据传送.数据传送是在DMA控制器控制下进行的. DMA的3种工作方式如下: (1)CPU暂停方式主机响应D ...

  6. LVS三种工作方式八种算法

    一.集群简介 什么是集群 计算机集群简称集群是一种计算机系统,它通过一组松散集成的计算机软件和/或硬件连接起来高度紧密地协作完成计算工作.在某种意义上,他们可以被看作是一台计算机.集群系统中的单个计算 ...

  7. 【STM32】GPIO工作原理(八种工作方式超详细分析,附电路图)

    STM32F1xx官方资料: <STM32中文参考手册V10>-第8章通用和复用功能IO(GPIO和AFIO ) 芯片数据手册(datasheet) STM32的GPIO介绍 STM32引 ...

  8. php和stm32,stm32单片机的gpi和gpo分别有几种工作方式

    GPI有四种工作方式,分别为:浮空输入.上拉输入.下拉输入.模拟输入:GPO有四种工作方式,分别为:开漏输出.开漏复用功能.推挽输出.推挽复用功能. GPI有四种工作方式,分别为:浮空输入.上拉输入. ...

  9. IPsec协议的两种工作方式 协议三大部分 简介

    IPsec协议的两种工作方式: 隧道(tunnel)模式:用户的整个IP数据报被用来计算AH或ESP头,AH或ESP头以及ESP加密的用户数据被封装在一个新的IP数据包中.通常,隧道模式应用在两个安全 ...

最新文章

  1. 一行代码将Pandas加速4倍
  2. Bitcoin Cash 交易签名与构造
  3. mybaits四-2:模糊查询
  4. 手工纸盒子_不锈钢水槽如何选购,拉伸水槽与手工槽制造工艺有何区别
  5. MAT之GA:遗传算法(GA)解决M-TSP多旅行商问题
  6. python基础代码技巧_Python 代码优化技巧(二)
  7. 谁说双非本科就一定无缘阿里?H哥粉丝6棉通过,喜提Offer!
  8. 必须在构造函数基/成员初始值设定项列表中初始化
  9. linux 中kafka发送数据,C++ 向kafka中发送数据
  10. python 快速排名发包_SEO快速排名发包技术及原理
  11. 消息模板取数据的高阶使用说明
  12. 用户体验设计5大目标
  13. 开源音乐软件——落雪
  14. 思维模型 帕累托法则
  15. Linux网卡驱动发送超时看门狗,如何在linux嵌入式中更改看门狗定时器
  16. Burst(突发)信号详解
  17. 【DP】【Burnside】【多项式】烷基计数
  18. 用向量求一个点到一条直线垂足的坐标
  19. oracle px execute reply,(转)PX Deq: Execute Reply 案例说明
  20. python画生肖兔

热门文章

  1. 利用h5Blob将页面内容保存为本地文件
  2. AtCoder Beginner Contest 198 A~E题解
  3. 由View的onAttachedToWindow引发的图片轮播问题探究
  4. android调节系统亮度的方法
  5. 工具 | 一款小巧好用的代码对比工具
  6. 年轻小伙依靠刺绣增收致富
  7. img src php 微信 苹果,javascript - file上传的图片,生成到canvas,toDataUrl后在苹果微信上保存下来,是黑色的。...
  8. 2019.7.9 校内测试题 数学问题
  9. VSCode摸鱼教程---我们的口号是什么:摸鱼摸鱼!!!
  10. python 生孩子朋友圈_生孩子朋友圈报喜范文 生孩子报喜微信怎么写