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

*/ void KeySc an(void) //此函 数放在定时中 断里每 1 ms 扫描一次 { static unsig ned char Su8KeyL ock=0; static unsig ned int Su1 6KeyCnt=0; static unsig ned char Su8KeyS tep=1; static unsig ned char Su8Colu mnRecord=0; //用来 切换当前列的 输出 st…

100%1 / 836
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)
{
SingleKeyTask(); //单击按键任务函数
DoubleKeyTask(); //双击按键任务函数
}
}
/* 注释一:
* 矩阵按键扫描的详细过程
* 先输出某 1 列低电平,其它 2 列输出高电平,延时等待 2ms 后(等此 3 列输出同步稳定)
* 再分别判断 3 行的输入 IO 口, 如果发现哪一行是低电平,就说明对应的某个按键被触发。
* 依次循环切换输出的 3 种状态,并且分别判断输入的 3 行,就可以检测完 9 个按键。矩阵按键
* 去抖动处理方法跟我前面讲的独立按键去抖动方法是一样的
*/
/* 注释二:
* 双击按键扫描的详细过程
* 第一步:平时没有按键被触发时,按键的自锁标志,去抖动延时计数器一直被清零。
* 如果之前已经有按键触发 1 次单击,那么启动时间间隔计数器 Su16KeyIntervalCnt1,
* KEY_INTERVAL_TIME 这个允许的时间差范围内,如果一直没有第 2 次单击触发,
* 则把累加按键触发的次数 Su8KeyTouchCnt1 也清零,上一次累计的单击数被清零,
* 就意味着下一次新的双击必须重新开始累加两次单击数。
* 第二步:一旦有按键被按下,去抖动延时计数器开始在定时中断函数里累加,在还没累加到
* 阀值 KEY_SHORT_TIME 时,如果在这期间由于受外界干扰或者按键抖动,而使
* IO 口突然瞬间触发成高电平,这个时候马上把延时计数 Su16KeyCnt
* 清零了,这个过程非常巧妙,非常有效地去除瞬间的杂波干扰,以后凡是用到开关感应器的时候,
* 都可以用类似这样的方法去干扰
* 第三步:如果按键按下的时间超过了阀 KEY_SHORT_TIME,马上把自锁标志 Su8KeyLock 1,
* 防止按住按键不松手后一直触发。与此同时,累加 1 次按键次数,如果按键次数累加 2 次,
* 则认为触发双击按键,并把编号 vGu8DoubleKeySec 赋值。
* 第四步:等按键松开后,自锁标志 Su8KeyLock 及时清零解锁,为下一次自锁做准备。并且累加间隔时间,
* 防止两次按键的间隔时间太长。如果连续 2 次单击的间隔时间太长达到了 KEY_INTERVAL_TIME
* 的长度,立即清零当前按键次数的计数器,这样意味着上一次的累加单击数无效,下一次双击
* 必须重新累加新的单击数
*/
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 Su8KeyTouchCnt1; //S1 按键的次数记录
static unsigned int Su16KeyIntervalCnt1; //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: //延时等待 2ms 后(等此 3 列输出同步稳定。不是按键的去抖动延时
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(Su8KeyTouchCnt1>=1) //之前已经有按键触发过一次,启动间隔时间的计数器
{
Su16KeyIntervalCnt1++; //按键间隔的时间计数器累加
if(Su16KeyIntervalCnt1>=KEY_INTERVAL_TIME) //达到最大允许的间隔时间,溢出无效
{
Su16KeyIntervalCnt1=0; //时间计数器清零
Su8KeyTouchCnt1=0; //清零按键的按下的次数,因为间隔时间溢出无效
}
}
Su8ColumnRecord++; //输出下一列
if(Su8ColumnRecord>=3)
{
Su8ColumnRecord=0; //依次输出完 3 列之后,继续从第 1 开始输出低电平
}
}
else if(0==Su8KeyLock) //有按键按下,且是第一次触发
{
if(0==ROW_INPUT1&&1==ROW_INPUT2&&1==ROW_INPUT3)
{
Su16KeyCnt++; //去抖动延时计数器
if(Su16KeyCnt>=KEY_SHORT_TIME)
{
Su8KeyLock=1;//自锁 1,避免一直触发,只有松开按键,此标志位才会被清零
if(0==Su8ColumnRecord) //第 1 列输出低电
{
Su16KeyIntervalCnt1=0; //按键有效间隔的时间计数器清零
Su8KeyTouchCnt1++; //记录当前单击的次数
if(1==Su8KeyTouchCnt1) //只按了 1
{
vGu8SingleKeySec=1; //单击任务,触发 1 号键 对应 S1
}
else if(Su8KeyTouchCnt1>=2) //连续按了两次以上