从单片机基础到程序框架(全集 2019pdf版).pdf - 第411页
case 2 : //2 号按 键 vGu8BeepTi merFlag=0 ; vGu16BeepT imerCnt=K EY_VOICE_TIM E; //触发按键后 ,发出固定长 度的声音 vGu8BeepTi merFlag=1 ; vGu8KeySec =0; //响应按键服 务处理程序后 ,按键 编号必须清零 ,避免一直触 发 break; } } 【92.2 专题分析:else if(0==Su8Ke yLock1)。 】…

Su16KeyCnt1++; //累加定时中断次数
if(Su16KeyCnt1>=KEY_FILTER_TIME) //滤波的“稳定时间”KEY_FILTER_TIME,长度是 25ms。
{
Su8KeyLock1=1; //按键的自锁,避免一直触发
vGu8KeySec=1; //触发 1 号键
}
}
//2 号按键
if(0!=KEY_INPUT2)
{
Su8KeyLock2=0;
Su16KeyCnt2=0;
}
else if(0==Su8KeyLock2)
{
Su16KeyCnt2++;
if(Su16KeyCnt2>=KEY_FILTER_TIME)
{
Su8KeyLock2=1;
vGu8KeySec=2; //触发 2 号键
}
}
}
void KeyTask(void) //按键任务函数,放在主函数内
{
if(0==vGu8KeySec)
{
return; //按键的触发序号是 0 意味着无按键触发,直接退出当前函数,不执行此函数下面的代码
}
switch(vGu8KeySec) //根据不同的按键触发序号执行对应的代码
{
case 1: //1 号按键
vGu8BeepTimerFlag=0;
vGu16BeepTimerCnt=KEY_VOICE_TIME; //触发按键后,发出固定长度的声音
vGu8BeepTimerFlag=1;
vGu8KeySec=0; //响应按键服务处理程序后,按键编号必须清零,避免一直触发
break;

case 2: //2 号按键
vGu8BeepTimerFlag=0;
vGu16BeepTimerCnt=KEY_VOICE_TIME; //触发按键后,发出固定长度的声音
vGu8BeepTimerFlag=1;
vGu8KeySec=0; //响应按键服务处理程序后,按键编号必须清零,避免一直触发
break;
}
}
【92.2 专题分析:else if(0==Su8KeyLock1)。】
疑问:
if(0!=KEY_INPUT1)
{
Su8KeyLock1=0;
Su16KeyCnt1=0;
}
else if(0==Su8KeyLock1)//有按键按下,且是第一次被按下。为什么?为什么?为什么?
{
Su16KeyCnt1++;
if(Su16KeyCnt1>KEY_FILTER_TIME)
{
Su8KeyLock1=1;
vGu8KeySec=1;
}
}
解答:
首先,我们要明白 C 语言的语法中,
if(条件 1)
{
}
else if(条件 2)
{
}
以上语句是一对组合语句,不能分开来看。当(条件 1)成立的时候,它是绝对不会判断(条件 2)的。
当(条件 1)不成立的时候,才会判断(条件 2)。
回到刚才的问题,当程序执行到(条件 2) else if(0==Su8KeyLock1)的时候,就已经默认了(条件 1)
if(0!=KEY_INPUT1)不成立,这个条件不成立,就意味着 0==KEY_INPUT1,也就是有按键被按下,因此,这里
的 else if(0==Su8KeyLock1)等效于 else if(0==Su8KeyLock1&&0==KEY_INPUT1),而 Su8KeyLock1 是一个自
锁标志位,一旦按键被触发后,这个标志位会变 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 语句的翻译不一
样,因此,这里的是不是更加高效我不敢保证。但是可以保证的是,加了这行代码也没有其它副作用。