从单片机基础到程序框架(全集 2019pdf版).pdf - 第442页
住“退 格按 键[Bac kspace]”不 松手 ,就 会“ 连续 均匀 ”的 触发 “删 除”的 功能 ,自动 逐个 把整 行文 字删除 清空, 这就 是 “按住不松手 的连续均匀 触发” 应用 案例之一。除此之外, 在很 多需要人机 交互的项目中 都有 这样的功能, 为了 快速加减某个 数值, 按住 某个按 键不松手, 某个 数值有节奏地 快速往上加或 者快速 往下减。 这种 “按住不松手 连续均匀触发 ” 的按键识别, 在程序 …

第九十七节: 独立按键按住不松手的连续均匀触发。
【97.1 按住不松手的连续均匀触发。】
上图 97.1.1 独立按键电路
上图 97.1.2 灌入式驱动 8 个 LED
上图 97.1.3 有源蜂鸣器电路
在电脑上删除某个文件某行文字的时候,单击一次“退格按键[Backspace]”,就删除一个文字,如果按

住“退格按键[Backspace]”不松手,就会“连续均匀”的触发“删除”的功能,自动逐个把整行文字删除
清空,这就是“按住不松手的连续均匀触发”应用案例之一。除此之外,在很多需要人机交互的项目中都有
这样的功能,为了快速加减某个数值,按住某个按键不松手,某个数值有节奏地快速往上加或者快速往下减。
这种“按住不松手连续均匀触发”的按键识别,在程序上有“3 个时间”需要留意,第 1 个是按键单击的“滤
波”时间,第 2 个是按键“从单击进入连击”的间隔时间(此时间是“单击”与“连击”的分界线),第 3
个是按键“连击”的间隔时间,
本节例程实现的功能如下:(1)8 个受按键控制的跑马灯在某一时刻只有 1 个 LED 亮,每触发一次 K1
按键,“亮的 LED”就“往左边跑一步”;相反,每触发一次 K2 按键,“亮的 LED”就“往右边跑一步”。如果
按住 K1 或者 K2 不松手就连续触发,“亮的 LED”就“连续跑”,一直跑到左边或者右边的尽头。(2)按键每
“单击”一次蜂鸣器就鸣叫一次,但是,当按键“从单击进入连击”后,蜂鸣器就不鸣叫。
#include "REG52.H"
#define KEY_VOICE_TIME 50
#define KEY_SHORT_TIME 25 //按键单击的“滤波”时间 25ms
#define KEY_ENTER_CONTINUITY_TIME 300 //按键“从单击进入连击”的间隔时间 300ms
#define KEY_CONTINUITY_TIME 80 //按键“连击”的间隔时间 80ms
#define BUS_P0 P0 //8 个 LED 灯一一对应单片机的 P0 口总线
void T0_time();
void SystemInitial(void) ;
void Delay(unsigned long u32DelayTime) ;
void PeripheralInitial(void) ;
void BeepOpen(void);
void BeepClose(void);
void VoiceScan(void);
void KeyScan(void);
void KeyTask(void);
void DisplayTask(void); //显示的任务函数(LED 显示状态)
sbit P3_4=P3^4; //蜂鸣器
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 灯的状态
unsigned char Gu8DisplayUpdate=1; //显示的刷新标志

volatile unsigned char vGu8KeySec=0; //按键的触发序号
volatile unsigned char vGu8ShieldVoiceFlag=0; //屏蔽声音的标志
void main()
{
SystemInitial();
Delay(10000);
PeripheralInitial();
while(1)
{
KeyTask(); //按键的任务函数
DisplayTask(); //显示的任务函数(LED 显示状态)
}
}
/* 注释一:
* Gu8DisplayUpdate 这类“显示刷新变量”在“显示框架”里是很常见的,而且屡用屡爽。
* 目的是,既能及时刷新显示,又能避免主函数“不断去执行显示代码”而影响程序效率。
*/
void DisplayTask(void) //显示的任务函数(LED 显示状态)
{
if(1==Gu8DisplayUpdate) //需要刷新一次显示
{
Gu8DisplayUpdate=0; //及时清零,避免主函数“不断去执行显示代码”而影响程序效率
//Gu8LedStatus 是左移的位数,范围(0 至 7),决定了跑马灯的显示状态。
BUS_P0=~(1<<Gu8LedStatus); //“左移<<”之后的“取反~”,因为 LED 电路是灌入式驱动方式。
}
}
/* 注释二:
* 按键“连续均匀触发”的识别过程:
* 第一步:平时只要 K1 没有被按下,按键的自锁标志 Su8KeyLock1、去抖动延时计数器 Su16KeyCnt1、
* 连击计数器 Su16KeyContinuityCnt1,一直被清零。
* 第二步:一旦 K1 按键被按下,去抖动延时计数器 Su16KeyCnt1 开始在定时中断函数里累加,在还没
* 累加到阀值 KEY_SHORT_TIME 时,如果在这期间由于受外界干扰或者按键抖动,
* 而使 IO 口突然瞬间触发成高电平,这个时候马上把延时计数器 Su16KeyCnt1 清零,
* 这个过程非常巧妙,非常有效地去除瞬间的杂波干扰。
* 第三步:如果 K1 按键按下的时间超过了阀值 KEY_SHORT_TIME,则触发一次“单击”, 同时,马上把自锁
* 标志 Su8KeyLock1 置 1 防止按住按键不松手后一直触发,并且把计数器 Su16KeyCnt1 清零为了下
* 一步用来累加“从单击进入连击的间隔时间 1000ms”。如果此时还没有松手,直到发现按下的时