搜索
bottom↓
回复: 40

零耗时键盘各种事件及消抖处理模板裸奔程序详解

[复制链接]

出0入0汤圆

发表于 2006-12-22 19:24:42 | 显示全部楼层 |阅读模式
点击下载完整的程序包KeyScan.rar



/*---------------------------------------------------------

        零耗时键盘各种事件及消抖处理模板裸奔程序详解

雁塔菜农HotPower   2006.12.22 于菜地http://HotPower.21ic.org/

---------------------------------------------------------*/

#include <LPC213xdef.h>//包含213x自定义结构指针写法头文件

//注意:可将LPC213XDEF.H拷贝到Keil\ARM\INC\PHILIPS目录下

/*---------------------------------------------------------

        时钟参数定义

---------------------------------------------------------*/

#define Fosc 11059200

#define Fcclk (Fosc * 5)

#define Fcco (Fcclk * 5)

#define Fpclk (Fcclk / 5) * 1



/*---------------------------------------------------------

        键盘接口定义

---------------------------------------------------------*/

#define KEYPORT   P1//KEY在P1口

#define KEY0      P1_16//

#define KEY1      P1_17//

#define KEY2      P1_18//

#define KEY3      P1_19//





/*---------------------------------------------------------

        申请系统变量

---------------------------------------------------------*/

volatile signed int KeyPressCount[4];//申请压键20mS计数器数组

volatile signed int KeyDblCount[4];//申请键值计数器数组



/*---------------------------------------------------------

        系统函数声明

---------------------------------------------------------*/

void SystemInit(void);//系统初始化

void VicInit(void);//向量中断初始化

void PortInit(void);//IO初始化

void Timer0Init(void);//定时器初始化

void KeyInit(void);//键盘初始化

void VicIntSetup(void);//向量中断设置

void KeyCommandExec(unsigned int, unsigned int);//执行键盘命令

void IRQ_Timer0 (void) __irq;//定时器0中断





/*---------------------------------------------------------

        键盘事件处理函数声明

---------------------------------------------------------*/

void Key00(void);//放键事件

void Key01(void);

void Key02(void);

void Key03(void);



void Key10(void);//短压键事件

void Key11(void);

void Key12(void);

void Key13(void);



void Key20(void);//双击键事件

void Key21(void);

void Key22(void);

void Key23(void);



void Key30(void);//长键事件

void Key31(void);

void Key32(void);

void Key33(void);



void Key0_1(void);//组合键事件

void Key1_2(void);

void Key2_3(void);

void Key3_0(void);





/*----------------------------------------------------------------------

“零耗时键盘”介绍:



    “零耗时”并非不耗时。它主要是将原本需要延时消除键盘抖动的时间转化为

对定时器的计数来替代。这样就可将节约的时间用于对其他事件的处理。

    “零耗时”键盘程序的编写很简单,首先要做到:

1.用总键盘个数除消除键盘抖动的时间20mS.本例有4个键,即20mS/4=5mS.

  所以,定时器0中断时间常数应该定义为5mS.

2.设置1个压键20mS计数器数组KeyPressCount[]。用于对各键盘的压键次数计数。

  由于全部键盘扫描需要20mS,故KeyPressCount[]内的值为20mS的倍数。

3.设置1个键扫描位置计数器KeyCount,用于记录当前键扫描的位置。

  注意键扫描函数KeyScan()每次只扫描1个键(本例即为IRQ_Timer0())。

4.设置1个键扫描键值计数器数组KeyDblCount[],用于记录键值以处理双击状态。

  本例主要讲解“零耗时”键盘程序的编写,一般不主张在MCU系统下用双击键。

  多建议采用长压键来替代双击键。



特别注意:

  “零耗时”键盘程序属于“扫而不描”类型,即每次只扫描1个键而不管其他键

的状态。这样就可在一定的时间范围内“并行”地处理多个键盘事件。

   键扫描位置计数器KeyCount的值就是键盘扫描结果的键值。故也改进了经典的

键扫描函数KeyScan()需要逐次扫描的缺点

  “零耗时”键盘程序只区分键释放,单击键,双击键 和长压键4种基本事件。

区分只简单地判别KeyPressCount[]的个数即可。

1.当无键压下且KeyPressCount[]减到0时,可判别为键释放事件发生。

2.当有键压下且KeyPressCount[]=2时,可认为键已经经过20mS消抖处理,

  即单击键事件发生。

  如果需要双击键处理,则需要附加KeyDblCount[]双击键计数器数组。

3.当有键压下且KeyPressCount[]=3*50时,即3*50*20mS=3S时,认为3S长压键事件发生。



  对“零耗时键盘”的个人应用总结:

在MCU的裸奔中,“零耗时键盘”很容易构成一个基于时间片小型的操作系统。

它可以“并行地”处理多个键盘事件及任务。

它的节拍不是OS常用的10mS,而是20mS消抖时间的1/N份。

由于20mS也做为视觉暂留的时间基准,故在常用的LED+KEY系统中裸奔表现很不错。

如果每个任务都能保证在20mS/N内完成,那么后台程序可以废除,即主程序只是个

死循环。这在低功耗系统中应用很广。

----------------------------------------------------------------------*/





/*----------------------------------------------------------------------

       定时器0中断作为键盘扫描和键盘消抖处理及命令执行

----------------------------------------------------------------------*/

void IRQ_Timer0 (void) __irq

{

const static unsigned int KeyTab[4] = {//定义FLASH数组

  KEY0, KEY1, KEY2, KEY3

};

static unsigned int KeyCount = 0;

unsigned int i;

  KeyCount &= 0x03;//只有4个键KEY0~KEY3(注意每次只扫描1个键)

  if (KEYPORT->IOPIN & (1 << KeyTab[KeyCount])) {//高电平压键无效

    if (KeyPressCount[KeyCount] > 0) {

          KeyPressCount[KeyCount] = -2;//键释放也需消除键盘抖动至少20mS

        }

        else if (KeyPressCount[KeyCount] < 0) {

          KeyPressCount[KeyCount] ++;

          if (KeyPressCount[KeyCount] == 0) {//键释放

        KeyCommandExec(0, KeyCount);//键释放

          }

        }

  }

  else {

    KeyPressCount[KeyCount] ++;//

    if (KeyPressCount[KeyCount] == 2) {//单击键刚满20mS

          if (KeyDblCount[KeyCount] != KeyCount) {

        KeyCommandExec(1, KeyCount);//单击压键

                for (i = 0; i < 4; i ++ ) {

                  if (i == KeyCount) {

                    KeyDblCount = KeyCount;//设置单击标志

                  }

                  else {

                    KeyDblCount = -1;//摧毁其他键单击标志

                  }

                }

          }

          else {

        KeyCommandExec(2, KeyCount);//双击压键

                for (i = 0; i < 4; i ++ ) {

                  if (i == KeyCount) {

                    KeyDblCount = 0x80 + KeyCount;//设置双击标志

                  }

                  else {

                    KeyDblCount = -1;//摧毁其他键双击标志

                  }

                }

          }

        }

        else if (KeyPressCount[KeyCount] >= 3 * 50) {//3S长压键

      KeyCommandExec(3, KeyCount);//长压键

          KeyDblCount[KeyCount] = -1;//清除单击压键

          KeyPressCount[KeyCount] = 3;//避开单击键以实现多次长压键事件处理

        }

  }

  KeyCount ++;

  T0->TIMER_IR = 0x01;                //清除中断标志

  VIC->VectAddr = 0x00;                //通知VIC中断处理结束

}



/*----------------------------------------------------------------------

     系统初始化函数

----------------------------------------------------------------------*/

void SystemInit(void)

{

  VicInit();

  PortInit();

  KeyInit();

  Timer0Init();

  VicIntSetup();

}



/*----------------------------------------------------------------------

     向量中断初始化函数

----------------------------------------------------------------------*/

void VicInit(void)

{

volatile unsigned int start;

  VIC->IntEnable  = 0;//关闭全部中断

  VIC->SoftIntClr = 0xffffffff;//清除所有软中断标志

  VIC->IntSelect  = 0;//全部中断为IRQ中断或默认中断

  for (start = 0; start < 10000; start ++);//系统延时等待接口稳定

}



/*----------------------------------------------------------------------

     IO初始化函数

----------------------------------------------------------------------*/

void PortInit(void)

{

  PINSEL->PIN_SEL0 = 0x00000000;//设置管脚连接GPIO

  PINSEL->PIN_SEL1 = 0x00000000;//设置管脚连接GPIO

  P0->IODIR        = 0x00000000;//设置P0口为输入

  P1->IODIR        = 0x00000000;//设置P1口为输入

}



/*----------------------------------------------------------------------

     定时器初始化函数

----------------------------------------------------------------------*/

void Timer0Init(void)

{

  T0->TIMER_TC   = 0;                        //时器设置为0

  T0->TIMER_PR   = 0;                        //时钟不分频

  T0->TIMER_MCR  = 0x03;                //设置T0MR0匹配后复位T0TC,并产生中断标志

  T0->TIMER_MR0  = Fpclk / 200;        //5mS定时

  T0->TIMER_TCR  = 0x01;                //启动定时器

  T0->TIMER_IR   = 0x01;                //清除中断标志

}



/*----------------------------------------------------------------------

     向量中断设置函数

----------------------------------------------------------------------*/

void VicIntSetup(void)

{

        /* 设置定时器0中断IRQ */

  VIC->VectCntls[0] = VICIntSel_Enable | VICIntSel_Time0;//设置定时器0中断通道分配最高优先级

  VIC->VectAddrs[0] = (unsigned int)IRQ_Timer0;//设置中断服务程序地址

  VIC->IntEnable |= (1 << VICIntSel_Time0);//使能定时器0中断

}





/*----------------------------------------------------------------------

     键盘初始化函数

----------------------------------------------------------------------*/

void KeyInit(void)

{

unsigned int i;

  KEYPORT->IODIR &= ~((1 << KEY0) | (1 << KEY1) | (1 << KEY2) | (1 << KEY3));// 设置KEY控制口为输入       

  for(i = 0; i < 4;i ++) {//4个键KEY0~KEY3

    KeyPressCount = 0;//清除压键20mS计数器数组,默认无键压下

    KeyDblCount = -1;//清除单击压键

  }

}





/*----------------------------------------------------------------------

     执行键盘命令函数

----------------------------------------------------------------------*/

void KeyCommandExec(unsigned int CommMode, unsigned int CommTask)

{

typedef void (* PV)(void);//函数指针

const static PV KeyCommandArray[4][4] = {//二维函数数组指针阵列表(散转命令地址表)

  {Key00, Key01, Key02, Key03},

  {Key10, Key11, Key12, Key13},

  {Key20, Key21, Key22, Key23},

  {Key30, Key31, Key32, Key33}

};

PV func;//声明函数指针

  func = KeyCommandArray[CommMode][CommTask];//从FLASH中取出键盘放事件处理表

  func();//运行KeyX0()~KeyX3()

}





int main(void)

{

  SystemInit();//系统初始化

  while(1) {

    POWER->P_CON = 1;//待机

        __nop();

    __nop();

    __nop();

  }

}







void Key00(void)//键释放事件

{

  if (KeyDblCount[0] == 0x80) {

//在此添加双击键释放事件处理

        __nop();

  }

  else {

//在此添加单击键释放事件处理

        __nop();

  }

}

void Key01(void)

{

//在此添加释放事件处理(不管单双击键)

  __nop();

}

void Key02(void)

{

}

void Key03(void)

{

}



void Key10(void)//单击键事件

{

  if (KeyPressCount[1] >= 2) {//在KEY1也压下时执行组合键事件

    Key0_1();

  }

  else if (KeyPressCount[3] >= 2) {//在KEY3也压下时执行组合键事件

    Key3_0();

  }

}

void Key11(void)

{

  if (KeyPressCount[0] >= 2) {//在KEY0也压下时执行组合键事件

    Key0_1();

  }

  else if (KeyPressCount[2] >= 2) {//在KEY2也压下时执行组合键事件

    Key1_2();

  }

}

void Key12(void)

{

  if (KeyPressCount[1] >= 2) {//在KEY1也压下时执行组合键事件

    Key1_2();

  }

  else if (KeyPressCount[3] >= 2) {//在KEY3也压下时执行组合键事件

    Key2_3();

  }

}

void Key13(void)

{

  if (KeyPressCount[2] >= 2) {//在KEY2也压下时执行组合键事件

    Key2_3();

  }

  else if (KeyPressCount[0] >= 2) {//在KEY0也压下时执行组合键事件

    Key3_0();

  }

}



void Key20(void)//双击键事件

{

}

void Key21(void)

{

}

void Key22(void)

{

}

void Key23(void)

{

}



void Key30(void)//长压键事件

{

}

void Key31(void)

{

}

void Key32(void)

{

}

void Key33(void)

{

}



/*----------------------------------------------------------------------

“零耗时键盘”对组合键的处理方法:

“零耗时键盘”的一个重要的方法是对键盘“扫而不描”。即每次只扫描1个键而不

管其他键的状态。这样就可在一定的时间范围内“并行”地处理多个键盘事件。

        所谓”组合键”即为在1个键压下后再发生其他键压下的键盘事件。最简单的是

有序组合键即有压键顺序的组合键。

    例先压KEY0再压KEY1,这样我们就可只对单击键事件Key11()做出相应处理即可。

void Key11(void)

{

  if (KeyPressCount[0] >= 2) {//在KEY0也压下时执行组合键事件

    Key0_1();//KEY0KEY1组合键事件

  }

}

KeyPressCount[0]内存放的是KEY0的20mS压键次数。若>=2表示KEY0已经压下。

如果是无序组合键,则如例中示例在Key10()中也要做相应处理。

void Key10(void)//单击键事件

{

  if (KeyPressCount[1] >= 2) {//在KEY1也压下时执行组合键事件

    Key0_1();//KEY0KEY1组合键事件

  }

}

零耗时键盘”对组合键的处理方法看上去“很烦琐”,但却带来了更大的灵活性。

而且也减轻了键扫描程序的负担。

----------------------------------------------------------------------*/



void Key0_1(void)//组合键事件KEY0KEY1

{

}

void Key1_2(void)//KEY1KEY2

{

}

void Key2_3(void)//KEY2KEY3

{

}

void Key3_0(void)//KEY3KEY0

{

}

出0入0汤圆

发表于 2006-12-23 14:32:16 | 显示全部楼层
顶!!!

出0入0汤圆

 楼主| 发表于 2006-12-23 17:00:52 | 显示全部楼层
哈哈~~~倒塌了...



去年出版社邀我写51非典,琢磨了半天真不好写...



因为本人对51实在都玩腻了,再玩真要倒塌了...



无奈又让我写ARM之流,这个还带有"好感"...



就当做为"尿童牌"做贡献吧...



没办法昨写了第1个尿童演示程序,望水友泼水~~~



菜农实在没文化,写作比灌水更头晕~~~

出0入0汤圆

发表于 2009-12-7 21:37:33 | 显示全部楼层
顶起来

出0入0汤圆

发表于 2009-12-7 22:12:12 | 显示全部楼层
莫非是21ic那个菜农?
哈哈。我来看楼主。。。

出0入0汤圆

发表于 2009-12-8 00:01:01 | 显示全部楼层
MARK

出0入0汤圆

发表于 2010-1-9 01:53:19 | 显示全部楼层
好资料

出0入0汤圆

发表于 2010-1-9 08:24:21 | 显示全部楼层
顶一个

出0入0汤圆

发表于 2010-1-26 17:06:41 | 显示全部楼层
顶.....

出0入0汤圆

发表于 2010-1-26 17:10:41 | 显示全部楼层
学习下

出0入0汤圆

发表于 2010-1-26 19:14:18 | 显示全部楼层
最近不在状态you点晕,先mark

出0入0汤圆

发表于 2010-1-26 19:21:12 | 显示全部楼层
MARK

出0入0汤圆

发表于 2010-1-27 19:43:02 | 显示全部楼层
mark,3q

出0入0汤圆

发表于 2010-1-27 22:24:05 | 显示全部楼层
好资料,最近在搞低功耗的项目

出0入0汤圆

发表于 2010-1-27 22:28:25 | 显示全部楼层
非常不错。学习了!

出0入0汤圆

发表于 2010-1-27 22:30:47 | 显示全部楼层
mark!

出0入0汤圆

发表于 2010-1-28 09:22:58 | 显示全部楼层
标记

出0入0汤圆

发表于 2010-1-28 10:30:15 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-1-28 12:56:13 | 显示全部楼层
好东西

出0入0汤圆

发表于 2010-1-28 17:42:13 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-1-28 18:46:43 | 显示全部楼层
MARK 按键扫描

出0入0汤圆

发表于 2010-1-29 13:08:10 | 显示全部楼层
双击键不实际, 倒是很有可能的 "组合键" 没有提及哦

出0入0汤圆

发表于 2010-4-9 08:59:10 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-5-21 19:49:15 | 显示全部楼层
不错,暂时还不懂

出0入0汤圆

发表于 2010-11-30 18:57:09 | 显示全部楼层
真正实用的键盘程序都是零耗时的,其实就是去抖动的处理。如果有LED数码管共用扫描电路,我一般是同时操作。
习惯以按键稳定状态作为键的状态。

出0入0汤圆

发表于 2010-11-30 20:12:06 | 显示全部楼层
哗众取宠!!!

出0入0汤圆

发表于 2010-12-14 21:19:50 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-12-14 21:21:05 | 显示全部楼层
cool

出0入0汤圆

发表于 2010-12-14 21:28:51 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-12-14 23:18:23 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-12-14 23:56:37 | 显示全部楼层
MARK

出0入0汤圆

发表于 2011-3-29 16:07:38 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-3-29 22:19:33 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-3-29 22:37:24 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-5-2 13:06:22 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-5-2 14:24:10 | 显示全部楼层
按状态机、timer中断概念处理就行了。

出0入0汤圆

发表于 2012-5-8 20:38:02 | 显示全部楼层
老hot的东西啊

出0入0汤圆

发表于 2012-5-26 17:06:27 | 显示全部楼层
顶                               了               

出0入0汤圆

发表于 2012-11-16 20:52:00 | 显示全部楼层
           mark  

出0入0汤圆

发表于 2012-11-25 21:18:13 | 显示全部楼层
老前辈呵,可好久不见露面了。
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-5-7 19:59

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表