💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
【104.1 “一键两用”的短按与长按。】 ![](https://img.kancloud.cn/89/70/8970513a066fe0726b2997dcb0329ce0_194x190.png) 上图104.1.1 有源蜂鸣器电路 ![](https://img.kancloud.cn/68/91/6891d9a9e89ee7345b1505221de5c26b_252x282.png) 上图104.1.2 LED电路 ![](https://img.kancloud.cn/c1/8a/c18ad9232965b2a0699e388df49ac7b9_341x221.png) 上图104.1.3 3\*3矩阵按键的电路 矩阵按键与前面章节独立按键的“短按与长按”的处理思路是一样的,本节讲矩阵按键的“短按与长按”,也算是重温之前章节讲的内容。“短按与长按”的原理是依赖“按键按下的时间长度”来区分识别。“短按”是指从按下的“下降沿”到松手的“上升沿”时间,“长按”是指从按下的“下降沿”到一直按住不松手的“低电平持续时间”。本节的例程功能如下:(1)S1每“短按”一次,LED要么从“灭”变成“亮”,要么从“亮”变成“灭”,在两种状态之间切换。(2)S1每“长按”一次,蜂鸣器发出“嘀”的一声。代码如下: \#include "REG52.H" \#define KEY\_VOICE\_TIME 50 \#define KEY\_SHORT\_TIME 20 //按键的“短按”兼“滤波”的“稳定时间” \#define KEY\_LONG\_TIME 400 //按键的“长按”兼“滤波”的“稳定时间” void T0\_time(); void SystemInitial(void) ; void Delay(unsigned long u32DelayTime) ; void PeripheralInitial(void) ; void BeepOpen(void); void BeepClose(void); void LedOpen\_P1\_4(void); void LedClose\_P1\_4(void); void VoiceScan(void); void KeyScan(void); void KeyTask(void); sbit P3\_4=P3^4; sbit P1\_4=P1^4; sbit ROW\_INPUT1=P2^2; //第1行输入口。 sbit ROW\_INPUT2=P2^1; //第2行输入口。 sbit ROW\_INPUT3=P2^0; //第3行输入口。 sbit COLUMN\_OUTPUT1=P2^5; //第1列输出口。 sbit COLUMN\_OUTPUT2=P2^4; //第2列输出口。 sbit COLUMN\_OUTPUT3=P2^3; //第3列输出口。 volatile unsigned char vGu8BeepTimerFlag=0; volatile unsigned int vGu16BeepTimerCnt=0; unsigned char Gu8LedStatus\_P1\_4=0; volatile unsigned char vGu8KeySec=0; //短按与长按共用一个全局变量vGu8KeySec来传递按键信息 void main() { SystemInitial(); Delay(10000); PeripheralInitial(); while(1) { KeyTask(); } } /\* 注释一: \* 本节破题的关键: \* 矩阵按键涉及的按键数量很多,但是实际项目上一般只需要少数个别按键具备这种 \* “短按”与“长按”的特殊技能,因此,在代码上,必须把这类“特殊技能按键”与 \* “大众按键”区分开来,才能相互清晰互不干扰。本节的“特殊技能按键”是S1。 \*/ void KeyScan(void) //此函数放在定时中断里每1ms扫描一次 { static unsigned char Su8KeyLock=0; static unsigned int Su16KeyCnt=0; static unsigned char Su8KeyStep=1; static unsigned char Su8ColumnRecord=0; static unsigned char Su8KeyShortFlag\_S1=0; //S1按键专属的“短按”触发标志 switch(Su8KeyStep) { case 1: if(0==Su8ColumnRecord) { COLUMN\_OUTPUT1=0; COLUMN\_OUTPUT2=1; COLUMN\_OUTPUT3=1; } else if(1==Su8ColumnRecord) { COLUMN\_OUTPUT1=1; COLUMN\_OUTPUT2=0; COLUMN\_OUTPUT3=1; } else { COLUMN\_OUTPUT1=1; COLUMN\_OUTPUT2=1; COLUMN\_OUTPUT3=0; } Su16KeyCnt=0; Su8KeyStep++; break; case 2: //等待列输出稳定,但不是去抖动延时 Su16KeyCnt++; if(Su16KeyCnt>=2) { Su16KeyCnt=0; Su8KeyStep++; } break; case 3: if(1==ROW\_INPUT1&&1==ROW\_INPUT2&&1==ROW\_INPUT3) { Su8KeyStep=1; Su8KeyLock=0; Su16KeyCnt=0; if(1==Su8KeyShortFlag\_S1) //松手的时候,如果“短按”标志有效就触发一次“短按” { Su8KeyShortFlag\_S1=0; //先清零“短按”标志避免一直触发。 vGu8KeySec=1; //触发S1的“短按” } Su8ColumnRecord++; if(Su8ColumnRecord>=3) { Su8ColumnRecord=0; } } else if(0==Su8KeyLock) { //以下第1行,直接把S1按键单独扣出来,用“&&0==Su8ColumnRecord”作为筛选条件 if(0==ROW\_INPUT1&&1==ROW\_INPUT2&&1==ROW\_INPUT3&&0==Su8ColumnRecord) { Su16KeyCnt++; if(Su16KeyCnt>=KEY\_SHORT\_TIME) //“短按”兼“滤波”的“稳定时间” { //注意,这里不能“自锁”。后面“长按”触发的时候才“自锁”。 Su8KeyShortFlag\_S1=1; //S1的“短按”标志有效,待松手时触发。 } if(Su16KeyCnt>=KEY\_LONG\_TIME) //“长按”兼“滤波”的“稳定时间” { Su8KeyLock=1; //此时“长按”触发才“自锁” Su8KeyShortFlag\_S1=0; //既然此时“长按”有效,那么就要废除潜在的“短按”。 vGu8KeySec=21; //触发S1的“长按” } } else if(0==ROW\_INPUT1&&1==ROW\_INPUT2&&1==ROW\_INPUT3) { Su16KeyCnt++; if(Su16KeyCnt>=KEY\_SHORT\_TIME) { Su8KeyLock=1; //既然S1按键已经被上面几行代码单独扣出来,这里就直接从S2按键开始判断 if(1==Su8ColumnRecord) { vGu8KeySec=2; } else if(2==Su8ColumnRecord) { vGu8KeySec=3; } } } else if(1==ROW\_INPUT1&&0==ROW\_INPUT2&&1==ROW\_INPUT3) { Su16KeyCnt++; if(Su16KeyCnt>=KEY\_SHORT\_TIME) { Su8KeyLock=1; if(0==Su8ColumnRecord) { vGu8KeySec=4; } else if(1==Su8ColumnRecord) { vGu8KeySec=5; } else if(2==Su8ColumnRecord) { vGu8KeySec=6; } } } else if(1==ROW\_INPUT1&&1==ROW\_INPUT2&&0==ROW\_INPUT3) { Su16KeyCnt++; if(Su16KeyCnt>=KEY\_SHORT\_TIME) { Su8KeyLock=1; if(0==Su8ColumnRecord) { vGu8KeySec=7; } else if(1==Su8ColumnRecord) { vGu8KeySec=8; } else if(2==Su8ColumnRecord) { vGu8KeySec=9; } } } } break; } } void KeyTask(void) { if(0==vGu8KeySec) { return; } switch(vGu8KeySec) { case 1: //S1按键的“短按”任务,更改P1.4所在的LED灯的显示状态 if(0==Gu8LedStatus\_P1\_4) { Gu8LedStatus\_P1\_4=1; LedOpen\_P1\_4(); } else { Gu8LedStatus\_P1\_4=0; LedClose\_P1\_4(); } vGu8KeySec=0; break; //以下S1按键的“长按”直接选择case 21的“21”,是为了不占用前排其它按键的编号。 case 21: //S1按键的“长按”任务,蜂鸣器发出“嘀”一声 vGu8BeepTimerFlag=0; vGu16BeepTimerCnt=KEY\_VOICE\_TIME; //蜂鸣器发出“嘀”一声 vGu8BeepTimerFlag=1; vGu8KeySec=0; break; default: vGu8KeySec=0; break; } } 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\_P1\_4) { LedClose\_P1\_4(); } else { LedOpen\_P1\_4(); } } void BeepOpen(void) { P3\_4=0; } void BeepClose(void) { P3\_4=1; } void LedOpen\_P1\_4(void) { P1\_4=0; } void LedClose\_P1\_4(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(); } } } }