最佳按键检测消抖与外部中断策略 基于FREERTOS

本文基于上一篇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计数,但是缺点是长时间需要进行任务,不够高效

如果仍然有更优策略可以交流

  • 7
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
FreeRTOS 中实现按键消抖,可以通过以下步骤进行: 1. 确定按键的引脚和相关参数:首先,确定用于连接按键的引脚。确保正确配置引脚的输入/输出模式、上拉/下拉电阻等参数。 2. 创建一个任务或使用现有任务:你可以为按键处理创建一个专用任务,或者将按键处理与其他任务结合在一起。在这个任务中,我们将执行按键消抖的逻辑。 3. 实现按键消抖算法按键消抖是通过软件算法来实现的。常见的按键消抖算法是基于状态机的方法。 a. 定义状态:定义两个状态,例如按下状态和释放状态。 b. 检测按键状态变化:在任务中不断检测按键的输入状态。当按键被按下时,记录下当前时间戳,并将状态设置为按下状态。然后继续检测按键状态,直到按键被释放。 c. 检测时间间隔:当按键被释放后,检测当前时间戳与之前记录的时间戳之间的时间间隔。如果时间间隔小于设定的消抖时间阈值(例如10ms),则可以认为是噪声引起的抖动,忽略该事件。如果时间间隔大于等于设定的时间阈值,说明按键的状态已经稳定,可以进行后续的处理。 d. 处理按键事件:根据按键的状态进行相应的处理,比如触发一个事件、执行某个功能等。 4. 使用延时函数:在任务中,可以使用 FreeRTOS 提供的延时函数来控制按键状态的检测频率。通过调整延时时间,可以适应不同的应用场景和系统性能要求。 需要注意的是,按键消抖算法可能因硬件和应用场景的不同而有所差异。上述步骤仅提供了一种常见的实现方式,具体实现还需根据实际需求进行调整。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值