从单片机基础到程序框架(全集 2019pdf版).pdf - 第413页

第九十三节: 独立按键鼠标式的单击与双击。 【93.1 鼠标式的单击与双击。 】 上图 93.1.1 独立 按键电路 上图 93.1.2 L ED 电路 上图 93.1.3 有源 蜂鸣器电路 鼠标的左键 , 可以 触发单击, 也可 以触发双击 。 双击的规则 是这样的 , 两次单击 , 如果第 1 次单击与第 2 次单 击的时 间比 较“短” 的 时候, 则这 两次 单击就 构成 双击。 编写 这个程 序的 最大 亮点 是如 何控制 好…

100%1 / 836
锁标志位,一旦按键被触发后,这个标志位会 1,防止按键按住不松手的时候不断触发按键。这样,按
只能按一次触发一次,松开手后再按一次,又触发一次。
【92.3 专题分析:if(0!=KEY_INPUT1)。
疑问:为什么不用 if(1==KEY_INPUT1)而用 if(0!=KEY_INPUT1)?
if(0!=KEY_INPUT1),是因为考虑到了代码在不同单片机平台上的可移植性和兼容性。很多 32 位的单片机
供的是库函数,库函数返回的按键状态是一个字节变量来表示,当被按下的时候是 0,但是,当没有按下的
时候并不一定等于 1,而是一个“非 0”的数值。
【92.4 专题分析:把 KeyScan 函数放在定时器中断里。
疑问:为什么把 KeyScan 数放在定时器中断里?
解答:中断函数里放的函数或者代码越少越好,但 KeyScan 函数是特殊的函数,是涉及到 IO 口输
信号的滤波,滤波就涉及到时间的及时性与均匀性,放在定时中断函数里更加能保证时间的一致性。比如,
蜂鸣器驱动,动态数码管驱动,按键扫描驱动,我个人都习惯放在定时中断函数里。
【92.5 专题分析:if(0==vGu8KeySec)return。
疑问:if(0==vGu8KeySec)return 是不是多此一举?
解答:在 KeyTask 函数这里,if(0==vGu8KeySec)return 这行代码删掉,对程序功能是没有影响的,这
里之所以多插入这行判断语句,是因为,当按键多达几十个的时候,避免主函数每次进入 KeyTask 函数,
挨个扫描判断 switch 的状态进行多次判断,如果增加这行 if(0==vGu8KeySec)return 代码,就可以直
退出省事,在理论上感觉更加运行高效。其实,不同单片机不同 C 编译器可能 switch 语句的翻译不一
样,因此,这里的是不是更加高效我不敢保证。但是可以保证的是,加了这行代码也没有其它副作用。
第九十三节: 独立按键鼠标式的单击与双击。
【93.1 鼠标式的单击与双击。
上图 93.1.1 独立按键电路
上图 93.1.2 LED 电路
上图 93.1.3 有源蜂鸣器电路
鼠标的左键可以触发单击,也可以触发双击双击的规则是这样的两次单击如果第 1 次单击与第
2 次单击的时间比较“短”时候,则这两次单击就构成双击。编写这个程序的最大亮点是如何控制好第 1
次单击与第 2 次单击的时间间隔。
程序例程要实现的功能是:(1)单击改变 LED 灯的显示状态,单击一次 LED 从原来“灭”的状态变成“亮
的状态,或者从原来“亮”的状态变成“灭”的状态,依次循环切换。(2)双击则蜂鸣器发“嘀”的一声。
代码如下:
#include "REG52.H"
#define KEY_VOICE_TIME 50 //按键触发后发出的声音长度
#define KEY_FILTER_TIME 25 //按键滤波的“稳定时间”25ms
#define KEY_INTERVAL_TIME 250 //连续两次单击之间的最大有效时 250ms
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 DoubleKeyTask(void); //双击按键任务函数,放在主函数
sbit P3_4=P3^4; //蜂鸣
sbit P1_4=P1^4; //LED
sbit KEY_INPUT1=P2^2; //K1 按键识别的输入口
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 vGu8DoubleKeySec=0; //双击按键的触发序号
void main()
{
SystemInitial();
Delay(10000);
PeripheralInitial();
while(1)