之前也写过一篇利用定时器实现按键检测及消抖的文章,但是那篇代码量太大,而且不便于移植,还有一堆不明变量,简直就是屎山代码,这次使用结构体数组优化一下,原理不变
一、基本原理
这是一个按键从按下到松开的过程
传统用while死循环实现按键的松手检测,但是这样会使程序卡死,浪费CPU资源,在延时函数里只进行+1+1+1+1的操作,对程序运行的实时性很不友好
我们使用定时器每5ms扫描一次按键,当检测到按键被按下时,我们的按键计数器(LongPressCount)就开始计数,每5ms增加一次,当检测到按键被松开时停止计数,接着通过判断LongPressCount的值来判断按键按下的状态(没按下、短按和长按),当LongPressCount的值在0-4之间时就意味着按键在0-20ms之间就松开了,就判定为没有按键按下,计数器继续计数,当LongPressCount的值在4-200之间,意味着按键在20-1000ms之间松开,判定为短按,将按键的状态设置为短按并清除LongPressCount,那长按就是LongPressCount大于200了。
现在假如我们的按键没有抖动,我们要实现按键按下一次a++但是不许使用delay和while,最简单的逻辑是这样的(伪代码)
while (1)
{
if(GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_14) == 0)
{
a++;
}
}
但是问题也随之而来,当我按键按下的时候a会一直++,所以我们要定义一个变量Key_flag,默认为0,当按键按下后flag变为1,不再执行a++,逻辑是这样的
while (1)
{
if(Key_flag == 0)
{
if(GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_14) == 0)
{
a++;
Key_flag = 1;
}
}
}
这样的话第一次按下正常,第二次按下就没反应,因为按键松开的时候还要恢复标志位,当标志位还是被置位(flag==1)时,如果按键是松开的就清除标志位
while (1)
{
if(Key_flag == 0)
{
if(GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_14) == 0)
{
a++;
Key_flag = 1;
}
}
if(Key_flag == 1)
{
if(GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_14) == 1)
{
Key_flag = 0;
}
}
}
以上就是一个不使用while死循环实现松手检测的例子,记住这种思想,下面要用
二、程序设计
1、定时器中断
void Timer_Init(uint16_t Psc, uint16_t Per)
{
/*开启时钟*/
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);
TIM_InternalClockConfig(TIM6);
/*时基单元初始化*/
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = Per - 1;
TIM_TimeBaseInitStructure.TIM_Prescaler = Psc - 1;
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM6, &TIM_TimeBaseInitStructure);
TIM_ClearFlag(TIM6, TIM_FLAG_Update);
TIM_ITConfig(TIM6, TIM_IT_Update, ENABLE);
/*中断配置*/
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM6_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority =