基于STM32HAL库的独立按键驱动程序,支持消抖,识别长短按,裸机和FreeRTOS,实测稳定消除抖动
部署使用
使用FreeRTOS,新建一个按键任务,延时10ms循环执行:
/* USER CODE END Header_KeyTask */
void KeyTask(void const * argument)
{
/* USER CODE BEGIN KeyTask */
/* Infinite loop */
for(;;)
{
key_scan(); /* 按键扫描 */
osDelay(10);
}
/* USER CODE END KeyTask */
}
使用裸机,定时器中断模式,10ms一次定时器中断,cubemx已配置好多定时器中断周期
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) /* 定时器重装载中断回调函数 */
{
if(htim == &htim7)
{
key_scan(); /* 按键扫描 */
}
}
代码部分
key.c 全部代码
/**
****************************************************************************************************
* @file key.c
* @author 盾迷
* @version V1.0
* @date 2024-11-1
* @brief 独立按键扫描驱动代码,支持长按短按
****************************************************************************************************
*/
#include "key.h"
/* 定义按键端口(只有这部分需要修改)
例如这样,一共三个按键,PA0 PA1 PC3,则需要填写为:
GPIO_TypeDef* ports[] = {GPIOA, GPIOA, GPIOC};
uint16_t pins[] = {GPIO_PIN_0, GPIO_PIN_1, GPIO_PIN_3};
上下一一对应,按键顺序从左到右
*/
GPIO_TypeDef* ports[] = {USER_KEY_GPIO_Port, KEY1_GPIO_Port, KEY2_GPIO_Port};
uint16_t pins[] = {USER_KEY_Pin, KEY1_Pin, KEY2_Pin};
/* 程序部分 */
#define KEYS_COUNT (sizeof(pins) / sizeof(pins[0])) /* 读取按键数量 */
keys key[KEYS_COUNT]; /* 定义结构体数组存放数据 */
void shortkey(uint8_t key) /* 短按事件(需要更多按键直接添加case) */
{
switch(key)
{
case 0:/* 按键1短按事件 */
{
HAL_GPIO_TogglePin(LED_R_GPIO_Port,LED_R_Pin);
}
break;
case 1:/* 按键2短按事件 */
{
HAL_GPIO_TogglePin(LED_G_GPIO_Port,LED_G_Pin);
}
break;
case 2:/* 按键3短按事件 */
{
HAL_GPIO_TogglePin(LED_B_GPIO_Port,LED_B_Pin);
}
break;
}
}
void longkey(uint8_t key) /* 长按事件(需要更多按键直接添加case) */
{
switch(key)
{
case 0:/* 按键1长按事件 */
{
HAL_GPIO_TogglePin(LED_R_GPIO_Port,LED_R_Pin);
}
break;
case 1:/* 按键2长按事件 */
{
HAL_GPIO_TogglePin(LED_G_GPIO_Port,LED_G_Pin);
}
break;
case 2:/* 按键3长按事件 */
{
HAL_GPIO_TogglePin(LED_B_GPIO_Port,LED_B_Pin);
}
break;
}
}
/* 按键扫描部分 */
void key_scan()
{
uint8_t i=0;
/* 数据位循环读取GPIO引脚高低电平 */
for(i=0;i<KEYS_COUNT;i++)
{
key[i].key_dat=HAL_GPIO_ReadPin(ports[i],pins[i]);
}
/* 每一轮开始判断短按是否触发 */
for(i=0;i<KEYS_COUNT;i++)
{
if(key[i].key_judge ==0 && key[i].key_flag==1)
{
/* 触发短按事件并清除标志位 */
shortkey(i);
key[i].key_flag=0;
}
}
/* 状态位判断 */
for(i=0;i<KEYS_COUNT;i++)
{
switch(key[i].key_judge) /* 按键判断位,判断按键是否按下,总共判断三次消除抖动 */
{
case 0:
{
if(key[i].key_dat==0) /* 如果按键按下 */
{
key[i].key_judge=1; /* 按键按下判断位配置为1,进入下一次判断 */
key[i].key_time=0; /* 时间清零 */
}
}
break;
case 1:
{
if(key[i].key_dat==0) /* 重复判断按键按下,消除抖动 */
{
key[i].key_judge=2;
}
else
{
key[i].key_judge=0; /* 如果按键未按下,判断位归零。再重复消抖操作 */
}
}
break;
case 2:
{
if(key[i].key_dat==1) /* 按键未按下,判断位归零。再重复消抖操作 */
{
key[i].key_judge=0;
}
else
{
key[i].key_time++; /* 按键按下,时间位不断自加1 */
if(key[i].key_time<100)
{
key[i].key_flag=1; /* 时间位小于100,短按标志置1 */
}
else
{
key[i].key_longflag=1; /* 时间位大于100,长按标志置1 */
/* 触发长按事件并清除标志位 */
longkey(i);
key[i].key_longflag=0;
key[i].key_time=0;
key[i].key_judge=3;
}
}
}
break;
case 3: /* 执行长按后第一轮扫描,清除短按标志位,防止误触发 */
{
if(key[i].key_dat==1)
{
key[i].key_flag=0; /* 清除短按标志位 */
key[i].key_judge=0; /* 复位状态 */
key[i].key_time=0; /* 时间清零 */
}
}
break;
default: /* 出错时清除短按标志位,防止误触发 */
if(key[i].key_dat==1)
{
key[i].key_flag=0; /* 清除短按标志位 */
key[i].key_judge=0; /* 复位状态 */
key[i].key_time=0; /* 时间清零 */
}
}
}
}
key.h 全部代码
/**
****************************************************************************************************
* @file key.h
* @author 盾迷
* @version V1.0
* @date 2024-11-1
* @brief 独立按键扫描驱动代码,支持长按短按
****************************************************************************************************
*/
#ifndef _KEY_H_
#define _KEY_H_
#include "main.h"
typedef struct
{
unsigned char key_dat;//按键数据标志位
unsigned char key_judge;//按键按下的判断位
unsigned char key_flag;//按下标志位
unsigned char key_longflag;//长按标志位
unsigned char key_time;//按键时间标志位
}keys;
void key_scan(void);
#endif
test
我也test
test