本次实验使用的是蓝桥杯嵌入式大赛的指定开发板,STM32G431R8T6。
下面为CubeMX和代码部分
CubeMX配置
将按键引脚设置为双边沿中断触发模式,并设置上拉电阻。
定时器本次选择TIM3,选择内部时钟源,设置PSC为80-1,COUNTER Period为1000-1,实现1ms定时(频率为80MHz)
并打开定时器中断。
Key.c
#include "key.h"
/* 3个按键的结构体定义 */
key_data keys[3]={0};
/* 按键中断回调函数 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if(GPIO_Pin == KEY1_Pin) //此时按键1被按下
{
static unsigned int old_time = 0;
unsigned int new_time = HAL_GetTick();
/* 消抖 */
if(new_time - old_time < DEBOUNCE)
return ;
old_time = new_time;
/* 按键被按下 */
if(HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin) == GPIO_PIN_RESET)
{
if(keys[0].key_state == FREE) /* 如果现在按键未被按下 */
{
keys[0].key_state = PRESSED; /* 现在已经第一次按下,等待松手 */
keys[0].waitetime = new_time; /* 保存当前情况的时间节点 */
}
else if(keys[0].key_state == WAIT_DOUBLE){ /* 按键现在状态处于等待第二次按下时候 */
keys[0].key_result = TWICE; /* 按键第二次按下,按键最终状态为双击 */
keys[0].key_state = FREE;
}
}
else{ /* 按键因上升沿产生的中断,按键松开 */
if(keys[0].key_state == PRESSED)
{
keys[0].key_state = WAIT_DOUBLE; /* 将当前状态值设置为等待第二次按下 */
keys[0].waitetime = new_time;
}
}
}
else if(GPIO_Pin == KEY2_Pin) //此时按键2被按下
{
}
else if(GPIO_Pin == KEY3_Pin) //此时按键3被按下
{
}
}
/* 按键状态判断函数,每1ms调用一次 */
void key_process(void)
{
unsigned char i;
unsigned int time;
time = HAL_GetTick();
for(i=0 ; i<3 ; i++)
{
switch(keys[i].key_state){
case PRESSED: /* 当按键当前处于按下的时候进行对应操作 */
if(time - keys[i].waitetime > LONGWAITETIME) /* 当按键保持按下状态1s时 */
{
keys[i].key_result = LONG; /* 按键最终的结果为长按 */
keys[i].key_state = FREE;
}
break;
case WAIT_DOUBLE: /* 当按键处于等待第二次按下的时候进行操作 */
if(time - keys[i].waitetime > DOUBLEWAITETIME)
{
keys[i].key_result = ONCE; /* 按键最终的结果为单击 */
keys[i].key_state = FREE;
}
break;
default:
break;
}
}
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM3)
{
key_process(); /* 每1ms进入一次 */
}
}
key.h
#ifndef __KEY_H_
#define __KEY_H_
#include "main.h"
#define DEBOUNCE 20
#define LONGWAITETIME 1000
#define DOUBLEWAITETIME 400
/* 按键最后的状态:未按下,单击,双击,空闲 */
typedef enum{
IDLE = 0,
ONCE,
TWICE,
LONG
}keyresult_t;
/* 当前按键的三个状态,未被按下,按下,等待第二次按下 */
typedef enum{
FREE = 0,
PRESSED,
WAIT_DOUBLE
}keystate_t;
/* 按键的信息:按键最后的结果,按键当前的值,等待时间存储 */
typedef struct{
keyresult_t key_result;
keystate_t key_state;
unsigned int waitetime; /* 用于判断长按的时间,和第二次按下的间隔 */
}key_data;
extern key_data keys[3];
/* 函数声明 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin);
void key_process(void);
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim);
#endif
main函数验证
while (1)
{
switch(keys[0].key_result){
case IDLE:
LCD_DisplayStringLine(Line1, (unsigned char *)" KEY1 IDLE ");
break;
case ONCE:
LCD_DisplayStringLine(Line1, (unsigned char *)" KEY1 ONCE ");
break;
case TWICE:
LCD_DisplayStringLine(Line1, (unsigned char *)" KEY1 TWIC ");
break;
case LONG:
LCD_DisplayStringLine(Line1, (unsigned char *)" KEY1 LONG ");
break;
}
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
主要在main中还需要打开定时器的中断模式,
/*使能定时器1中断*/
HAL_TIM_Base_Start_IT(&htim2);