STM32使用HAL库实现按键的单击、双击、长按

STM32使用HAL库实现按键的单击、双击、长按


前言

编程开发环境:STM32CubeIDE编程

         ~~~~~~~~         按键的单击、双击、长按等在MCU编程中是比较常见且经常使用的事件,本文章基于STM32来实现,具体思路用在其余MCU也是如此。ui

具体思路

  • 初始化一个全局标记
  • 按键中断事件发生后置位标记
  • while死循环中一直检测这个标记,若是被置位那么进行消抖,而后再次检测链接KEY的IO是否处于按下状态,如是则认为本次按键有效
  • 第一次按键事件有效后,启动定时器定时300ms,在此定时期间内若是有二次按下那么就是双击,若是没有按下,等到300ms定时时间到后读取IO电平,若是处于松开状态那么本次就是单击事件,若是仍是按下状态那么就再次启动700定时器,700ms事后再次读取IO电平是否处于按下状态,如是那么就是长按。

工程配置

我是使用STM32CUBEMX配置生成的工程,配置按键:spa

在这里插入图片描述
STM32的定时器有向上、向下计数模式,向上计数就是从0计数到N而后触发中断,向下计数模式就是倒计时到0,因为咱们须要二次启动定时器来检测长按,因此再次配置为向下计数模式,分频为7199,计数3000就是定时300ms:
在这里插入图片描述
中断配置:
在这里插入图片描述



code

代码实现

/* * key_state位说明: * * bit[15]:按键事件完成标志位 * bit[14]:按键中断触发标志位 * bit[13]:长按事件触发须要启动第二次定时器标志位 * bit[12~0]: 按键计数,=1:单击 =2:双击 =3:长按 */
__IO uint16_t key_state = 0;

int __io_putchar(int ch)
{ 
	HAL_UART_Transmit(&huart1, (uint8_t*) &ch, 1, 100);
	return ch;
}

void TIM4_Stop(void)
{ 
	HAL_TIM_Base_Stop_IT(&htim4);
}

void TIM4_Start(uint16_t timeout)
{ 
	__HAL_TIM_SET_COUNTER(&htim4, timeout);
	HAL_TIM_Base_Start_IT(&htim4);
}

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{ 
	if(htim->Instance == htim4.Instance)
	{ 
		TIM4_Stop();
		if(key_state & 0X2000)    // 若是是700ms的超时
		{ 
			// 再次检测按键若是是按下状态,那么就是长按事件,不然事件无效
			if(HAL_GPIO_ReadPin(KEY_GPIO_Port, KEY_Pin) == GPIO_PIN_RESET)
			{ 
				key_state = 0X8003;
			}else
			{ 
				key_state = 0X8000;
			}
		}
		else  // 若是是300ms的超时
		{ 
			 // 300ms时间到再次检测按键状态若是仍处于按下状态,那么就启动700ms的超时,
			 // 700ms时间到后再次检测按键若是是按下状态,那么就是长按事件了。
			if(HAL_GPIO_ReadPin(KEY_GPIO_Port, KEY_Pin) == GPIO_PIN_RESET)  // 按键处于按下状态
			{ 
				key_state |= 0X2000;  // 标记启动了700ms的超时定时
				TIM4_Start(7000);     // 启动700ms超时
			}
			else  // 按键处于松开状态
			{ 
				if((key_state & 0X1FFF) == 1)        // 按键按下计数=1,是单击事件
				{ 
					key_state = 0X8001;
				}else if((key_state & 0X1FFF) >= 2)  // 按键按下计数>=2,是双击事件
				{ 
					key_state = 0X8002;
				}else                                // 按键按下计数为其余值,本次按键事件无效
				{ 
					key_state = 0;
				}
			}
		}
	}
}

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{ 
	switch(GPIO_Pin)
	{ 
	case KEY_Pin:
		if((key_state & 0X8000) == 0)
		{ 
			key_state |= 0X4000;    // 标记按键中断触发,在while死循环中进行消抖和的启动定时器
		}
		break;
	default:break;
	}
}

int main(void)
{ 
	HAL_Init();
	SystemClock_Config();
	MX_GPIO_Init();
	MX_USART1_UART_Init();
	MX_TIM4_Init();
	__HAL_TIM_CLEAR_IT(&htim4, TIM_IT_UPDATE);    // STM32的定时器初始化完毕以后若是不清理中断标记为会有直接进入中断的问题

	while (1)
	{ 
		if(key_state & 0X4000)     // 按键中断触发
		{ 
			key_state &= ~0x4000;  // 清除事件
			HAL_Delay(60);         // 消抖延时

			// 消抖完毕以后再次检测按键是否为按下状态,如是则本次按键有效,不然视为无效并清除标识。
			if(HAL_GPIO_ReadPin(KEY_GPIO_Port, KEY_Pin) == GPIO_PIN_RESET)
			{ 
				if((key_state & 0X1FFF) == 0)  // 首次按下时启动300ms超时定时器
				{ 
					TIM4_Start(3000);
				}
				key_state++;                    // 按键按下计数增长
			}
		}

		if(key_state & 0X8000)  // 按键事件发成了
		{ 
			switch(key_state)
			{ 
			case 0X8001:
				printf("single\r\n");
				break;
			case 0X8002:
				printf("double\r\n");
				break;
			case 0X8003:
				printf("hold\r\n");
				break;
			default:break;
			}
			key_state = 0;  // 清除事件
		}
	}
}

实验效果

按键单击、双击、长按实验效果以下:blog

在这里插入图片描述

相关文章
相关标签/搜索