STM32框架之按键扫描新思路
STM32框架之按键扫描新思路
- 引入
- 代码展示
- 思路分析
我们学习了定时器实现毫秒级/秒级任务框架,这期我们基于任务框架学习按键扫描新思路。
引入
在按键扫描的过程中,最重要的一步就是按键消抖,解决的方法最简单粗暴的就是先扫描一次按键状态,判断按键按下后,延时,再次判断按键状态。就像这样:
if(1-HAL_GPIO_ReadPin(KEY0_GPIO_Port,KEY0_Pin)) //判断按键0是否按下
{
HAL_Delay(20); //延时消抖
if(1-HAL_GPIO_ReadPin(KEY0_GPIO_Port,KEY0_Pin)) //再次判断按键0是否按下
{
while(1-HAL_GPIO_ReadPin(KEY0_GPIO_Port,KEY0_Pin)) //等待按键0松开
{
}
Key_Num = 1; //赋值按键值
}
}
这是最笨的方法,当然还可以
多次调用按键扫描函数,当按键按下的时候开始计数/计时。当数字/时间到达一定值时,判断按键按下,当有一次扫描到按键为按下时,计数/计时清零,代码如下:
int Key_Scan(void)
{
int Key_Num = 0; //定义按键值
int temp = 0; //定义临时变量
static int Key0_Count=0,Key1_Count=0,Key2_Count=0,WKUP_Count=0 ; //定义不同按键的按键值
if(1-HAL_GPIO_ReadPin(KEY0_GPIO_Port,KEY0_Pin)) temp = 1; //当某一个按键按下是,临时变量值为对应的按键值加一
if(1-HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin)) temp = 2; //加一是因为temp为0时无法进入switch中,所以案件之都加一
if(1-HAL_GPIO_ReadPin(KEY2_GPIO_Port,KEY2_Pin)) temp = 3; //
if(HAL_GPIO_ReadPin(WKUP_GPIO_Port,WKUP_Pin)) temp = 4;
switch(temp)
{
case 1: Key0_Count++; Key1_Count=0;Key2_Count=0;WKUP_Count=0 ; //通过temp变量的值,将对应按键计数值加一
break; //同时将其他按键计数值清零,
case 2: Key1_Count++; Key0_Count=0;Key2_Count=0;WKUP_Count=0 ;
break;
case 3: Key2_Count++; Key0_Count=0;Key1_Count=0;WKUP_Count=0 ;
break;
case 4: WKUP_Count++; Key0_Count=0;Key1_Count=0;Key2_Count=0 ;
break;
default: Key0_Count=0; Key1_Count=0;Key2_Count=0;WKUP_Count=0 ;
break;
}
if(Key0_Count >= 500) //当temp到达一定值时,则可判断按键按下
{
Key_Num = 1; //按键按下,将所有的按键计数值清零
Key0_Count=0; Key1_Count=0;Key2_Count=0;WKUP_Count=0 ;
}
if(Key1_Count >= 500)
{
Key_Num = 2;
Key0_Count=0; Key1_Count=0;Key2_Count=0;WKUP_Count=0 ;
}
if(Key2_Count >= 500)
{
Key_Num = 3;
Key0_Count=0; Key1_Count=0;Key2_Count=0;WKUP_Count=0 ;
}
if(WKUP_Count >= 500)
{
Key_Num = 4;
Key0_Count=0; Key1_Count=0;Key2_Count=0;WKUP_Count=0 ;
}
return Key_Num; //返回按键值,当没有按键按下,或者存在抖动时,返回值为0
这种方法相对于第一种,逻辑上更严谨,但是不好控制计数值。并且多次进入函数,占用大量CPU资源,所以我们引入第三种方法,规定时间间隔,进行扫描,通过数组判断结果。代码如下:
代码展示
- main.c
/* USER CODE BEGIN PFP */
void Proc2msTask(void) //2ms任务
{
static uint8_t i=0;
if(Get_2ms_Flag() == 1) //获取2ms标志位
{
Clear_2ms_Flag(); //清除2ms标志位
//2mstask code
i++;
if(i==4) //1s扫描一处按键状态
{
i=0;
Key_One_Scan(Key_Name_Key0,Key0_Up_Task,Key0_Down_Task); //扫描Key0状态
Key_One_Scan(Key_Name_Key1,Key1_Up_Task,Key1_Down_Task); //扫描Key1状态
Key_One_Scan(Key_Name_Key2,Key2_Up_Task,Key2_Down_Task); //扫描Key2状态
Key_One_Scan(Key_Name_WKUP,WWKUP_Up_Task,WKUP_Down_Task); //扫描WKUP状态
}
}
}
void Proc1sTask(void) //1s任务
{
if(Get_1s_Flag() == 1) //获取1s标志位
{
Clear_1s_Flag(); //清除1s标志位
//1stask code
// HAL_GPIO_TogglePin(LED0_GPIO_Port,LED0_Pin);
}
}
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
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_TIM10_Init();
/* USER CODE BEGIN 2 */
HAL_TIM_Base_Start_IT(&htim10);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
Proc2msTask(); //调用2ms任务
Proc1sTask(); //调用1s任务
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
- Key.c
/* USER CODE BEGIN 2 */
void Key0_Down_Task(void)
{
HAL_GPIO_WritePin(LED0_GPIO_Port,LED0_Pin,GPIO_PIN_RESET); //按下亮灯
}
void Key0_Up_Task(void)
{
HAL_GPIO_WritePin(LED0_GPIO_Port,LED0_Pin,GPIO_PIN_SET); //松开关闭
}
void Key1_Down_Task(void)
{
HAL_GPIO_WritePin(LED1_GPIO_Port,LED1_Pin,GPIO_PIN_RESET); //按下亮灯
}
void Key1_Up_Task(void)
{
HAL_GPIO_WritePin(LED1_GPIO_Port,LED1_Pin,GPIO_PIN_SET); //松开关闭
}
void Key2_Down_Task(void)
{
HAL_GPIO_WritePin(LED2_GPIO_Port,LED2_Pin,GPIO_PIN_RESET); //按下亮灯
}
void Key2_Up_Task(void)
{
HAL_GPIO_WritePin(LED2_GPIO_Port,LED2_Pin,GPIO_PIN_SET); //松开关闭
}
void WKUP_Down_Task(void)
{
HAL_GPIO_WritePin(LED3_GPIO_Port,LED3_Pin,GPIO_PIN_RESET); //按下亮灯
}
void WWKUP_Up_Task(void)
{
HAL_GPIO_WritePin(LED3_GPIO_Port,LED3_Pin,GPIO_PIN_SET); //松开关闭
}
void Key_One_Scan(uint8_t KeyName ,void(*OnKeyOneUp)(void), void(*OnKeyOneDown)(void))
{
static uint8_t Key_Val[Key_Name_Max]; //按键值的存放位置
static uint8_t Key_Flag[Key_Name_Max]; //KEY0~2为0时表示按下,为1表示松开,WKUP反之
Key_Val[KeyName] = Key_Val[KeyName] <<1; //每次扫描完,将上一次扫描的结果左移保存
switch(KeyName)
{
case Key_Name_Key0: Key_Val[KeyName] = Key_Val[KeyName] | (HAL_GPIO_ReadPin(KEY0_GPIO_Port, KEY0_Pin)); //读取Key0按键值
break;
case Key_Name_Key1: Key_Val[KeyName] = Key_Val[KeyName] | (HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin)); //读取Key1按键值
break;
case Key_Name_Key2: Key_Val[KeyName] = Key_Val[KeyName] | (HAL_GPIO_ReadPin(KEY2_GPIO_Port, KEY2_Pin)); //读取Key2按键值
break;
case Key_Name_WKUP: Key_Val[KeyName] = Key_Val[KeyName] | (HAL_GPIO_ReadPin(WKUP_GPIO_Port, WKUP_Pin)); //读取WKUP按键值
break;
default:
break;
}
if(KeyName == Key_Name_WKUP) //WKUP的电路图与其他按键不同,所以需要特殊处理
{
//WKUP特殊情况
//当按键标志为1(松开)是,判断是否按下,WKUP按下时为0xff
if(Key_Val[KeyName] == 0xff && Key_Flag[KeyName] == 1)
{
(*OnKeyOneDown)();
Key_Flag[KeyName] = 0;
}
//当按键标志位为0(按下),判断按键是否松开,WKUP松开时为0x00
if(Key_Val[KeyName] == 0x00 && Key_Flag[KeyName] == 0)
{
(*OnKeyOneUp)();
Key_Flag[KeyName] = 1;
}
}
else //Key0~2按键逻辑判断
{
//Key0~2常规判断
//当按键标志为1(松开)是,判断是否按下
if(Key_Val[KeyName] == 0x00 && Key_Flag[KeyName] == 1)
{
(*OnKeyOneDown)();
Key_Flag[KeyName] = 0;
}
//当按键标志位为0(按下),判断按键是否松开
if(Key_Val[KeyName] == 0xff && Key_Flag[KeyName] == 0)
{
(*OnKeyOneUp)();
Key_Flag[KeyName] = 1;
}
}
}
/* USER CODE END 2 */
- Key.h
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* USER CODE BEGIN Private defines */
typedef enum
{
Key_Name_Key0 = 0,
Key_Name_Key1,
Key_Name_Key2,
Key_Name_WKUP,
Key_Name_Max
}EnumKeyOneName;
/* USER CODE END Private defines */
void MX_GPIO_Init(void);
/* USER CODE BEGIN Prototypes */
void Key0_Down_Task(void);
void Key0_Up_Task(void);
void Key1_Down_Task(void);
void Key1_Up_Task(void);
void Key2_Down_Task(void);
void Key2_Up_Task(void);
void WKUP_Down_Task(void);
void WWKUP_Up_Task(void);
void Key_One_Scan(uint8_t KeyName ,void(*OnKeyOneUp)(void), void(*OnKeyOneDown)(void));
/* USER CODE END Prototypes */
#ifdef __cplusplus
}
#endif
#endif /*__ GPIO_H__ */
思路分析
按键按下的过程中,难免会有抖动,但是抖动的时间通常在10~20ms之间,而按键按下的过程往往会持续100ms以上,所以我们可以每10ms扫描一次按键状态,如果连续八次都是按下,则认为按键已经按下,想要的执行认为即可。反之,如果八次都是松开,则认为按键松开了,也可以执行按键松开的相关任务。
具体细节,大家看代码吧,这个只是大体思路,代码还有很多精彩的地方。