多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
【95.1 “电脑键盘式”组合按键。】 ![](https://img.kancloud.cn/a2/3d/a23df87ac21f61d2182864f67461b009_359x103.png) 上图95.1.1 独立按键电路 ![](https://img.kancloud.cn/68/91/6891d9a9e89ee7345b1505221de5c26b_252x282.png) 上图95.1.2 LED电路 ![](https://img.kancloud.cn/89/70/8970513a066fe0726b2997dcb0329ce0_194x190.png) 上图95.1.3 有源蜂鸣器电路 上一节也讲了由K1和K2构成的组合按键,但是这种组合按键是普通的组合按键,因为它们的K1和K2是不分先后顺序的,你先按住K1然后再按K2,或者你先按住K2然后再按K1,效果都一样。本节讲的组合按键是分先后顺序的,比如,像电脑的复制快捷键(Ctrl+C),你必须先按住Ctrl再按住C此时“复制快捷键”才有效,如果你先按住C再按住Ctrl此时“复制快捷键”无效。本节讲的例程就是要实现这个功能,用K1代表C这类“字符数字键”,用K2代表Ctrl这类“辅助按键”,因此,要触发组合键(K2+K1),必须先按住K2再按K1才有效。本节讲的例程功能如下:(1)K1每单击一次,LED要么从“灭”变成“亮”,要么从“亮”变成“灭”,在两种状态之间切换。(2)如果先按住K2再按K1,就认为构造了“电脑键盘式”组合键,蜂鸣器发出“嘀”的一声。代码如下: \#include "REG52.H" \#define KEY\_VOICE\_TIME 50 //组合按键触发后发出的声音长度 50ms \#define KEY\_FILTER\_TIME 25 //按键滤波的“稳定时间”25ms void T0\_time(); void SystemInitial(void) ; void Delay(unsigned long u32DelayTime) ; void PeripheralInitial(void) ; void BeepOpen(void); void BeepClose(void); void LedOpen(void); void LedClose(void); void VoiceScan(void); void KeyScan(void); //按键识别的驱动函数,放在定时中断里 void SingleKeyTask(void); //单击按键任务函数,放在主函数内 void CombinationKeyTask(void); //组合按键任务函数,放在主函数内 sbit P3\_4=P3^4; //蜂鸣器 sbit P1\_4=P1^4; //LED sbit KEY\_INPUT1=P2^2; //K1按键识别的输入口。 sbit KEY\_INPUT2=P2^1; //K2按键识别的输入口。 volatile unsigned char vGu8BeepTimerFlag=0; volatile unsigned int vGu16BeepTimerCnt=0; unsigned char Gu8LedStatus=0; //记录LED灯的状态,0代表灭,1代表亮 volatile unsigned char vGu8SingleKeySec=0; //单击按键的触发序号 volatile unsigned char vGu8CombinationKeySec=0; //组合按键的触发序号 void main() { SystemInitial(); Delay(10000); PeripheralInitial(); while(1) { CombinationKeyTask(); //组合按键任务函数 SingleKeyTask(); //单击按键任务函数 } } void T0\_time() interrupt 1 { VoiceScan(); KeyScan(); //按键识别的驱动函数 TH0=0xfc; TL0=0x66; } void SystemInitial(void) { TMOD=0x01; TH0=0xfc; TL0=0x66; EA=1; ET0=1; TR0=1; } void Delay(unsigned long u32DelayTime) { for(;u32DelayTime>0;u32DelayTime--); } void PeripheralInitial(void) { if(0==Gu8LedStatus) { LedClose(); } else { LedOpen(); } } void BeepOpen(void) { P3\_4=0; } void BeepClose(void) { P3\_4=1; } void LedOpen(void) { P1\_4=0; } void LedClose(void) { P1\_4=1; } void VoiceScan(void) { static unsigned char Su8Lock=0; if(1==vGu8BeepTimerFlag&&vGu16BeepTimerCnt>0) { if(0==Su8Lock) { Su8Lock=1; BeepOpen(); } else { vGu16BeepTimerCnt--; if(0==vGu16BeepTimerCnt) { Su8Lock=0; BeepClose(); } } } } /\* 注释一: \* “电脑键盘式”组合按键扫描的详细过程: \* 第一步:K2与K1构成的组合按键触发是融合在K1单击按键程序里的,只需稍微更改一下K1单击的程序 \* ,就可以兼容到K2与K1构成的“电脑键盘式”组合按键。平时只要K1没有被按下时,按 \* 键的自锁标志Su8KeyLock1和去抖动延时计数器Su16KeyCnt1一直被清零。 \* 第二步:一旦K1按键被按下,去抖动延时计数器Su16KeyCnt1开始在定时中断函数里累加,在还没 \* 累加到阀值KEY\_FILTER\_TIME时,如果在这期间由于受外界干扰或者按键抖动,而使 \* IO口突然瞬间触发成高电平,这个时候马上把延时计数器Su16KeyCnt1清零了, \* 这个过程非常巧妙,非常有效地去除瞬间的杂波干扰。 \* 第三步:如果K1按键按下的时间超过了阀值KEY\_FILTER\_TIME,马上把自锁标志Su8KeyLock1置1, \* 防止按住按键不松手后一直触发,此时才开始判断一次K2按键的电平状态,如果K2为低电 \* 平就认为是组合按键,并给按键编号vGu8CombinationKeySec赋值,否则,就认为是K1的单击 \* 按键,并给按键编号vGu8SingleKeySec赋值。 \* 第四步:等K1按键松开后,自锁标志Su8KeyLock1及时清零,为下一次自锁做准备。 \*/ void KeyScan(void) //此函数放在定时中断里每1ms扫描一次 { static unsigned char Su8KeyLock1; static unsigned int Su16KeyCnt1; //K1的单击,或者K2与K1构成的“电脑键盘式组合按键”。 if(0!=KEY\_INPUT1)//单个K1按键没有按下,及时清零一些标志。 { Su8KeyLock1=0; //按键解锁 Su16KeyCnt1=0; //去抖动延时计数器清零,此行非常巧妙,是全场的亮点。 } else if(0==Su8KeyLock1)//单个按键K1被按下 { Su16KeyCnt1++; //累加定时中断次数 if(Su16KeyCnt1>=KEY\_FILTER\_TIME) //滤波的“稳定时间”KEY\_FILTER\_TIME。 { if(0==KEY\_INPUT2) //此时才开始判断一次K2的电平状态,为低电平则是组合按键。 { Su8KeyLock1=1; vGu8CombinationKeySec=1; //组合按键的触发 } else { Su8KeyLock1=1; vGu8SingleKeySec=1; //K1单击按键的触发 } } } } void CombinationKeyTask(void) //组合按键任务函数,放在主函数内 { if(0==vGu8CombinationKeySec) { return; //按键的触发序号是0意味着无按键触发,直接退出当前函数,不执行此函数下面的代码 } switch(vGu8CombinationKeySec) //根据不同的按键触发序号执行对应的代码 { case 1: //K1与K2的组合按键任务 vGu8BeepTimerFlag=0; vGu16BeepTimerCnt=KEY\_VOICE\_TIME; //触发一次组合按键后,发出“嘀”一声 vGu8BeepTimerFlag=1; vGu8CombinationKeySec=0; //响应按键服务处理程序后,按键编号必须清零,避免一直触发 break; } } void SingleKeyTask(void) //单击按键任务函数,放在主函数内 { if(0==vGu8SingleKeySec) { return; //按键的触发序号是0意味着无按键触发,直接退出当前函数,不执行此函数下面的代码 } switch(vGu8SingleKeySec) //根据不同的按键触发序号执行对应的代码 { case 1: //K1单击任务 if(0==Gu8LedStatus) { Gu8LedStatus=1; LedOpen(); //LED亮 } else { Gu8LedStatus=0; LedClose(); //LED灭 } vGu8SingleKeySec=0; //响应按键服务处理程序后,按键编号必须清零,避免一直触发 break; } }