在单片机系统中应用按键的时候,如果只需要按下一次按键加 1 或减 1,那用第 8 章学到的知识就可以完成了,但如果想连续加很多数字的时候,要一次次按下这个按键确实有点不方便,这时我们会希望一直按住按键,数字就自动持续增加或减小,这就是所谓的长短按键应用。
当检测到一个按键产生按下动作后,马上执行一次相应的操作,同时在程序里记录按键按下的持续时间,该时间超过 1 秒后(主要是为了区别短按和长按这两个动作,因短按的时间通常都达到几百 ms),每隔 200ms(如果你需要更快那就用更短的时间,反之亦然)就自动再执行一次该按键对应的操作,这就是一个典型的长按键效果。
对此,我们做了一个模拟定时炸弹效果的实例,提供给大家作为参考。打开开关后,数码管显示数字 0,按向上的按键数字加 1,按向下的按键数字减 1,长按向上按键 1 秒后,数字会持续增加,长按向下按键 1 秒后,数字会持续减小。设定好数字后,按下回车按键,时间就会进行倒计时,当倒计时到 0 的时候,用蜂鸣器和板子上的 8 个 LED 小灯做炸弹效果,蜂鸣器持续响,LED 小灯全亮。
- #include <reg52.h>
-
- sbit BUZZ = P1^6;
- sbit ADDR3 = P1^3;
- sbit ENLED = P1^4;
- sbit KEY_IN_1 = P2^4;
- sbit KEY_IN_2 = P2^5;
- sbit KEY_IN_3 = P2^6;
- sbit KEY_IN_4 = P2^7;
- sbit KEY_OUT_1 = P2^3;
- sbit KEY_OUT_2 = P2^2;
- sbit KEY_OUT_3 = P2^1;
- sbit KEY_OUT_4 = P2^0;
-
- unsigned char code LedChar[] = {
- 0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8,
- 0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E
- };
- unsigned char LedBuff[7] = {
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
- };
- unsigned char code KeyCodeMap[4][4] = {
- { 0x31, 0x32, 0x33, 0x26 },
- { 0x34, 0x35, 0x36, 0x25 },
- { 0x37, 0x38, 0x39, 0x28 },
- { 0x30, 0x1B, 0x0D, 0x27 }
- };
- unsigned char KeySta[4][4] = {
- {1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}
- };
- unsigned long pdata KeyDownTime[4][4] = {
- {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}
- };
-
- bit enBuzz = 0;
- bit flag1s = 0;
- bit flagStart = 0;
- unsigned char T0RH = 0;
- unsigned char T0RL = 0;
- unsigned int CountDown = 0;
-
- void ConfigTimer0(unsigned int ms);
- void ShowNumber(unsigned long num);
- void KeyDriver();
-
- void main(){
- EA = 1;
- ENLED = 0;
- ADDR3 = 1;
- ConfigTimer0(1);
- ShowNumber(0);
-
- while (1){
- KeyDriver();
- if (flagStart && flag1s){
- flag1s = 0;
- if (CountDown > 0){
- CountDown--;
- ShowNumber(CountDown);
- if (CountDown == 0){
- enBuzz = 1;
-
- LedBuff[6] = 0x00;
- }
- }
- }
- }
- }
-
- void ConfigTimer0(unsigned int ms){
- unsigned long tmp;
- tmp = 11059200 / 12;
- tmp = (tmp * ms) / 1000;
- tmp = 65536 - tmp;
- tmp = tmp + 28;
- T0RH = (unsigned char)(tmp>>8);
- T0RL = (unsigned char)tmp;
- TMOD &= 0xF0;
- TMOD |= 0x01;
- TH0 = T0RH;
- TL0 = T0RL;
- ET0 = 1;
- TR0 = 1;
- }
-
- void ShowNumber(unsigned long num){
- signed char i;
- unsigned char buf[6];
- for (i=0; i<6; i++){
- buf[i] = num % 10;
- num = num / 10;
- }
- for (i=5; i>=1; i--){
- if (buf[i] == 0){
- LedBuff[i] = 0xFF;
- }else{
- break;
- }
- }
-
- for ( ; i>=0; i--){
- LedBuff[i] = LedChar[buf[i]];
- }
- }
-
- void KeyAction(unsigned char keycode){
- if (keycode == 0x26){
- if (CountDown < 9999){
- CountDown++;
- ShowNumber(CountDown);
- }
- }else if (keycode == 0x28){
- if (CountDown > 1){
- CountDown--;
- ShowNumber(CountDown);
- }
- }else if (keycode == 0x0D){
- flagStart = 1;
- }else if (keycode == 0x1B){
- enBuzz = 0;
- LedBuff[6] = 0xFF;
- flagStart = 0;
- CountDown = 0;
- ShowNumber(CountDown);
- }
- }
-
- void KeyDriver(){
- unsigned char i, j;
- static unsigned char pdata backup[4][4] = {
- {1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}
- };
- static unsigned long pdata TimeThr[4][4] = {
- {1000, 1000, 1000, 1000}, {1000, 1000, 1000, 1000},
- {1000, 1000, 1000, 1000}, {1000, 1000, 1000, 1000}
- };
-
- for (i=0; i<4; i++){
- for (j=0; j<4; j++){
- if (backup[i][j] != KeySta[i][j]){
- if (backup[i][j] != 0){
- KeyAction(KeyCodeMap[i][j]);
- }
- }
- backup[i][j] = KeySta[i][j];
- if (KeyDownTime[i][j] > 0){
- if (KeyDownTime[i][j] >= TimeThr[i][j]){
- KeyAction(KeyCodeMap[i][j]);
- TimeThr[i][j] += 200;
- }
- }else{
- TimeThr[i][j] = 1000;
- }
- }
- }
- }
-
- void KeyScan(){
- unsigned char i;
- static unsigned char keyout = 0;
-
- static unsigned char keybuf[4][4] = {
- {0xFF, 0xFF, 0xFF, 0xFF}, {0xFF, 0xFF, 0xFF, 0xFF},
- {0xFF, 0xFF, 0xFF, 0xFF}, {0xFF, 0xFF, 0xFF, 0xFF}
- };
-
-
- keybuf[keyout][0] = (keybuf[keyout][0] << 1) | KEY_IN_1;
- keybuf[keyout][1] = (keybuf[keyout][1] << 1) | KEY_IN_2;
- keybuf[keyout][2] = (keybuf[keyout][2] << 1) | KEY_IN_3;
- keybuf[keyout][3] = (keybuf[keyout][3] << 1) | KEY_IN_4;
-
- for (i=0; i<4; i++){
-
- if ((keybuf[keyout][i] & 0x0F) == 0x00){
- KeySta[keyout][i] = 0;
- KeyDownTime[keyout][i] += 4;
-
- }else if ((keybuf[keyout][i] & 0x0F) == 0x0F){
- KeySta[keyout][i] = 1;
- }
- }
- KeyDownTime[keyout][i] = 0;
-
- keyout++;
- keyout &= 0x03;
- switch (keyout){
- case 0: KEY_OUT_4 = 1; KEY_OUT_1 = 0; break;
- case 1: KEY_OUT_1 = 1; KEY_OUT_2 = 0; break;
- case 2: KEY_OUT_2 = 1; KEY_OUT_3 = 0; break;
- case 3: KEY_OUT_3 = 1; KEY_OUT_4 = 0; break;
- default: break;
- }
- }
-
- void LedScan(){
- static unsigned char i = 0;
- P0 = 0xFF;
- P1 = (P1 & 0xF8) | i;
- P0 = LedBuff[i];
- if (i < 6){
- i++;
- }else{
- i = 0;
- }
- }
-
- void InterruptTimer0() interrupt 1{
- static unsigned int tmr1s = 0;
- TH0 = T0RH;
- TL0 = T0RL;
- if (enBuzz){
- BUZZ = ~BUZZ;
- }else{
- BUZZ = 1;
- }
- LedScan();
- KeyScan();
- if (flagStart){
- tmr1s++;
- if (tmr1s >= 1000){
- tmr1s = 0;
- flag1s = 1;
- }
- }else{
- tmr1s = 0;
- }
- }
长按键功能实现的重点有两个:第一,是在原来的矩阵按键扫描函数 KeyScan 内,当检测到按键按下后,持续的对一个时间变量进行累加,其目的是用这个时间变量来记录按键按下的时间;第二,是在按键驱动函数 KeyDriver 里,除了原来的检测到按键按下这个动作时执行按键动作函数 KeyAction 外,还监测表示按键按下时间的变量,根据它的值来完成长按时的连续快速按键动作功能。