本文基于上一篇MP3的基础上,讲解一下核心的消抖技术,首先声明,该技术完全原创,目前没有看到任何一个人采用类似的方法。
按键消抖一直是一个令人头大的问题,如果采用了最佳的硬件消抖自然是最好,但很多时候却不得不面对没有硬件消抖的按键,所以就用到了软件消抖。
目前主流的按键相应分为定时器扫描与外部中断,其中定时器扫描用的最多,外部中断其实理论上更优,但是直接在中断里延时消抖实在糟糕至极,相较之下扫描会好很多。但是扫描的坏处是占用定时器资源,且需要不断检测,变相增加了系统开销,那么有没有一种更优的办法呢?既能尽可能的提升消抖效果,又能减少资源占用与CPU开销。
事实上,你在CSDN搜索消抖,没有任何一个令人满意的技术,不论是低通滤波还是其他,都不够好,所以我写了一个专门的消抖,基于FREERTOS,保证中断及时处理又能按键消抖。
下面是任务代码,实现多级菜单翻页,其中当检测次数不够时挂起自己这个任务,等待下次中断唤醒。
void MyTask4(void *argument)
{
//line1: /* USER CODE BEGIN MyTask4 */
uint16_t GPIO_Pin;
static Pin4_cnt=0;
/* Infinite loop */
for(;;)
{
if(xQueueReceive(Exit_Queue,&GPIO_Pin,100)==pdTRUE)
{
int cnt;
cnt = 0;
line1:
while(HAL_GPIO_ReadPin(KEY1_GPIO_Port,GPIO_Pin) == RESET)
{
cnt++;
osDelay(10);
}
if(cnt<3)
{
vTaskSuspend(EXIT_CHECKHandle);
goto line1;
}
switch(GPIO_Pin)
{
case GPIO_PIN_1:
func_index = table[func_index].up;// 向上翻
break;
case GPIO_PIN_2:
func_index = table[func_index].down; //向下翻
break;
case GPIO_PIN_3:
func_index = table[func_index].enter;//确认
break;
case GPIO_PIN_4:
Pin4_cnt++;
if(Pin4_cnt == 1)
{
vTaskDelete( Print_CPUHandle );
}
if(Pin4_cnt >1)
{
Pin4_cnt = 0;
taskENTER_CRITICAL(); /* 进入临界区 */
Print_CPUHandle = osThreadNew(StartTask07, NULL, &Print_CPU_attributes);
taskEXIT_CRITICAL(); /* 退出临界区 */
}
break;
}
}
vTaskSuspend(EXIT_CHECKHandle);
}
/* USER CODE END MyTask4 */
}
//本处不采用任何硬件消抖,以此减少中断时间,并且方便后期接入RTOS
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
static uint16_t GPIO_PIN_USE;
//确保是否产生了EXTI Line中断
uint32_t ulReturn;
// /* 进入临界段,临界段可以嵌套 */
ulReturn = taskENTER_CRITICAL_FROM_ISR();
GPIO_PIN_USE = GPIO_Pin;
xQueueOverwriteFromISR(Exit_Queue,&GPIO_PIN_USE,&pxHigherPriorityTaskWoken);
xTaskResumeFromISR(EXIT_CHECKHandle);//任务恢复
//如果需要的话进行一次任务切换
// portYIELD_FROM_ISR(pxHigherPriorityTaskWoken);
/* 退出临界段 */
taskEXIT_CRITICAL_FROM_ISR( ulReturn );
}
相关代码:
osThreadId_t EXIT_CHECKHandle;
const osThreadAttr_t EXIT_CHECK_attributes = {
.name = "EXIT_CHECK",
.stack_size = 256 * 4,
.priority = (osPriority_t) osPriorityLow3,
};
Exit_Queue = xQueueCreate(1,2);
在电平跳变时进入外部中断,写入队列传递GPIO使用引脚信息,然后唤醒按键处理任务并退出中断,等到再次检测到电平跳变时再进入,通过优先级设置可以保证响应及时性,同时将任务分为两个部分,耗时的在中断以外处理,这样可以确保其他中断进行,同时及时的中断响应处理简单任务,确保电平检测及时。
其余设计想法:外部中断设计双跳变沿检测后通过时间戳记录按键时长,此处仍然采用链式任务进行按键消抖,同时判断是否为长按从而实现长按不同功能
除此以外,也可以采用对按键时长进行CNT计数,但是缺点是长时间需要进行任务,不够高效
如果仍然有更优策略可以交流