STM32HAL库利用实现单击、双击、长按

要实现单片机的单击、双击、长按操作则需要理解一下他们的区别是什么

        可从图中观察到单击在一定周期里有一个低电平双击则是两个低电平长按则是整个周期都是低电平。

本次进行的软件       STM32CubeMX   +   Keil5

采用的方法是中断(按键)+  定时器  来实现单击、双击、长按操作

设计思路是这样的

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

开始STM32CubeMX的配置

最先开始配置时钟,因为需要通过频率来计算定时时间,配置时钟树如下:

 我采用的是内部时钟,将HCLK直接输入64M(最大值),这时定时器从内部获取的频率是64M.

配置定时器,输出

查看时钟树我们知道APB2当前频率为64MHz,把预分频系数设置为640-1,自动重载值为2-1,得到的计时器更新中断频率即64,000,000/640/2=50KHz。通过调节这个频率可以控制时间,如果发现自己按下之后延时严重,就可以调节频率找到刚好的位置。

然后进行中断的配置,这里选择向下模式,并且选择上拉

 配置完中断和定时器后,开启中断,设置中断优先级

 这里面用到了不定长串口接收,如需了解前往:

STM32关于UART的接收方式_啵啵520520的博客-CSDN博客

到这里就配置完STM32CubeMX,下一步进行代码编写

代码

/* USER CODE BEGIN 0 */
__IO uint16_t key_state = 0;
/*
* key_state位说明:
*
* bit[15]:按键事件完成标志位
* bit[14]:按键中断触发标志位
* bit[13]:长按事件触发需要启动第二次定时器标志位
* bit[12~0]: 按键计数,=1:单击 =2:双击 =3:长按
*/
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)
	{
		HAL_TIM_Base_Stop_IT(&htim4);
		if(key_state & 0x2000){
			if(HAL_GPIO_ReadPin(GPIOA,KEY1_Pin) == GPIO_PIN_RESET){
				key_state = 0x8003;
			}
			else{
				key_state = 0x8000;
			}
		}
		else{
			if(HAL_GPIO_ReadPin(GPIOA,KEY1_Pin) == GPIO_PIN_RESET) // 按键处于按下状态
			{
				key_state |= 0X2000; // 标记启动了700ms的超时定时
				TIM4_Start(700); // 启动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 KEY1_Pin:
			if((key_state & 0X8000) == 0){
				key_state |= 0X4000; // 标记按键中断触发,在while死循环中进行消抖和的启动定时器
			}
			break;
		case KEY2_Pin:
			HAL_GPIO_WritePin(GPIOB,LED1_Pin,GPIO_PIN_SET);
			HAL_GPIO_WritePin(GPIOB,LED2_Pin,GPIO_PIN_SET);
				break;
		default:break;
	}
}
/* USER CODE END 0 */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  MX_TIM4_Init();
  /* USER CODE BEGIN 2 */
	__HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE);	//开启空闲中断
	HAL_UART_Receive_IT(&huart1,(uint8_t *)buf,256);
	
	__HAL_TIM_CLEAR_IT(&htim4, TIM_IT_UPDATE); // STM32的定时器初始化完毕之后如果不清理中断标记为会有直接进入中断的问题
	HAL_UART_Transmit(&huart1,(uint8_t *)"开始\n",6,50);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
	  if(key_state & 0X4000) // 按键中断触发
	  {
		  key_state &= ~0x4000; // 清除事件
		  HAL_Delay(60); // 消抖延时
		  // 消抖完毕之后再次检测按键是否为按下状态,如是则本次按键有效,否则视为无效并清除标识。
		  if(HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin) == GPIO_PIN_RESET)
		  {
			  if((key_state & 0X1FFF) == 0) // 首次按下时启动250ms超时定时器
			  {
					TIM4_Start(2500);
			  }
			  key_state++; // 按键按下计数增加
		  }
	  }
	  if(key_state & 0X8000) // 按键事件发成了
	  {
		  switch(key_state)
		  {
		    case 0X8001:
			    HAL_GPIO_TogglePin(GPIOB,LED1_Pin);
				HAL_UART_Transmit(&huart1,(uint8_t *)"单击\n",6,50);
			    break;
			case 0X8002:
				HAL_GPIO_TogglePin(GPIOB,LED2_Pin);
				HAL_UART_Transmit(&huart1,(uint8_t *)"双击\n",6,50);
				break;
			case 0X8003:
				HAL_GPIO_WritePin(GPIOB,LED1_Pin,GPIO_PIN_RESET);
				HAL_GPIO_WritePin(GPIOB,LED2_Pin,GPIO_PIN_RESET);
				HAL_UART_Transmit(&huart1,(uint8_t *)"长按\n",6,50);
				break;
				default:break;
		  }
		key_state = 0; // 清除事件
	  }
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

在其中用到了

__IO        将该变量变成一个程序每次运行都必须去检测这个变量,不能做优化

还有两个中断回调函数

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)        //中断回调函数

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)        //定时器回调函数

你自己使用,则需要修改主循环中的处理函数(switch那里)

以后有问题在加载这后面

  • 3
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
好的,我可以为您提供一些关于 stm32 按键按短按双击实现方法。 对于单片机按键处理,常见的方法是轮询法和中断法。轮询法是在主循环中不断读取按键状态,判断按键是否按下或者释放,并进行相应的处理;中断法则是通过外部中断或者定时器中断来响应按键事件。 对于按、短按和双击事件的处理,一种常见的方法是使用状态机。状态机包括多种状态和转移条件,用于描述按键事件的不同状态和状态之间的转移过程。通过按键状态的变化,可以判断出按键事件的类型,并进行相应的处理。 具体实现方法可以参考以下步骤: 1. 读取按键状态,并判断按键是否按下或者释放。 2. 根据按键状态的变化,更新状态机的状态,并记录按键的按下时间和释放时间。 3. 根据状态机的状态,判断按键事件的类型,并进行相应的处理。例如,当状态机处于“按下”状态时,如果按键持续按下的时间超过一定阈值,则判断为按事件;如果按键持续按下的时间不足阈值,则判断为短按事件;如果在一定时间内连续按下两次按键,则判断为双击事件。 4. 根据按键事件的类型,执行相应的操作。例如,按事件可以用于开启或关闭某个功能;短按事件可以用于切换不同的模式;双击事件可以用于执行快速操作。 总之,实现按键按短按双击需要结合具体的硬件平台和软件环境进行综合考虑,根据实际需求选择合适的方法。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

啵啵520520

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值