【STM32】HAL库开发之定时器作按键检测
目录
一、按键检测
本次是在前文《【STM32】HAL库开发之通用定时器中断》的基础上进一步开发
首先贴上板子上面的按键电路原理图
以KEY0为例,在按键没有按下的时候,GPIO的电平为高电平;当KEY0按下,此时GPIO直接与GND相连,电平变为低电平;当KEY0再次松开,GPIO的电平再次为高电平。
在按键按下或松开过程中,电平状态会出现抖动状态。在很多的例程中,都是使用延时函数来进行按键消抖。但是由于使用延时函数,占用cpu资源,会导致部分程序在延时的过程中发生暂停的情况,所以本次将使用定时器来进行按键消抖。
本次基于STM32L4VET6开发板,使用HAL库进行开发。
二、定时器消抖原理
以上述按键原理图为例,在本次实验中,一共有四个独立按键,我使用定时器每隔1ms读取一次按键的状态,然后进行判断。
如果连续8次按键都是按下状态,则判断按键确实按下了;
如果连续8次按键都是松开状态,则判断按键确实松开了;
如果连续8次内,既存在按下状态,也存在松开状态,则判断按键发生抖动,不进行处理。
原理很简单,实现起来也不难。
三、STM32CubeMX软件配置
本次是在前文《【STM32】HAL库开发之通用定时器中断》的基础上进一步开发。
在定时器的配置基础上,仅需增加按键的GPIO配置
将与几个按键相连的GPIO设置为输入模式
设置完成如下图所示
接下来将进行程序设计
四、程序设计
按键检测有关的函数将在 gpio.c 文件中添加
首先在 gpio.h 中添加几个宏定义,用于读取按键的电平状态以及操作LED
// 操作LED电平状态
#define LEDR_ON HAL_GPIO_WritePin(LED_R_GPIO_Port, LED_R_Pin, GPIO_PIN_RESET)
#define LEDR_OFF HAL_GPIO_WritePin(LED_R_GPIO_Port, LED_R_Pin, GPIO_PIN_SET)
#define LEDG_ON HAL_GPIO_WritePin(LED_G_GPIO_Port, LED_G_Pin, GPIO_PIN_RESET)
#define LEDG_OFF HAL_GPIO_WritePin(LED_G_GPIO_Port, LED_G_Pin, GPIO_PIN_SET)
#define LEDB_ON HAL_GPIO_WritePin(LED_B_GPIO_Port, LED_B_Pin, GPIO_PIN_RESET)
#define LEDB_OFF HAL_GPIO_WritePin(LED_B_GPIO_Port, LED_B_Pin, GPIO_PIN_SET)
#define LEDR_FLIP HAL_GPIO_TogglePin(LED_R_GPIO_Port, LED_R_Pin)
#define LEDG_FLIP HAL_GPIO_TogglePin(LED_G_GPIO_Port, LED_G_Pin)
#define LEDB_FLIP HAL_GPIO_TogglePin(LED_B_GPIO_Port, LED_B_Pin)
// 获取按键的GPIO电平状态
#define KEY0_STATE HAL_GPIO_ReadPin(KEY0_GPIO_Port, KEY0_Pin)
#define KEY1_STATE HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin)
#define KEY2_STATE HAL_GPIO_ReadPin(KEY2_GPIO_Port, KEY2_Pin)
#define WK_UP_STATE HAL_GPIO_ReadPin(WK_UP_GPIO_Port, WK_UP_Pin)
// 定义按键按下的标志位
#define NO_KEY_PRES 0
#define KEY0_PRES 1
#define KEY1_PRES 2
#define KEY2_PRES 3
#define WK_UP_PRES 4
然后在 gpio.c 中的用户代码段添加
/* USER CODE BEGIN 1 */
static uint8_t KeySta[] = {1, 1, 1, 1};
/* USER CODE END 1 */
/* USER CODE BEGIN 2 */
/**
* @Function : Key_Scan
* @Brief : 按键状态扫描函数
* @Input : void
* @Return : void
* @Others : 放在定时器中断回调函数中调用
*/
void Key_Scan(void)
{
uint8_t i;
static uint8_t KeyBuff[] = {0xff, 0xff, 0xff, 0xff};
// 读取按键值,存入数组
KeyBuff[0] = (KeyBuff[0] << 1 | (KEY0_STATE & 0x01));
KeyBuff[1] = (KeyBuff[1] << 1 | (KEY1_STATE & 0x01));
KeyBuff[2] = (KeyBuff[2] << 1 | (KEY2_STATE & 0x01));
KeyBuff[3] = (KeyBuff[3] << 1 | (!WK_UP_STATE & 0x01));
for(i = 0; i < 4; i ++)
{
// 连续扫描8次都是1,8ms内都是弹起状态,按键已松开
if(KeyBuff[i] == 0xff)
{
KeySta[i] = 1;
}
// 连续扫描8次都是0,8ms内都是按下状态,按键已按下
else if(KeyBuff[i] == 0x00)
{
KeySta[i] = 0;
}
else
{}
}
}
/**
* @Function : KeyPress
* @Brief : 按键检测函数
* @Input : void
* @Return : void
* @Others : 放在while(1)中调用
*/
uint8_t KeyPress(void)
{
static uint8_t KeyBackup[] = {1, 1, 1, 1};
uint8_t i, Key_Value = 0;
for(i = 0; i < 4; i ++)
{
// 检测到按键状态发生变化
if(KeySta[i] != KeyBackup[i])
{
// 按键按下时操作
if(KeySta[i] == 0)
// Key_Value = KeyDrive(i);
Key_Value = i + 1;
KeyBackup[i] = KeySta[i];
}
}
return Key_Value;
}
/**
* @Function : KeyDrive
* @Brief : 读取按键键值函数
* @Input : uint8_t KeyValue
* @Return : uint8_t Key_Temp
* @Others :
*/
uint8_t KeyDrive(uint8_t KeyValue)
{
uint8_t Key_Temp = 0;
switch(KeyValue)
{
case 0: Key_Temp = KEY0_PRES; break;
case 1: Key_Temp = KEY1_PRES; break; // KEY1 开启录音
case 2: Key_Temp = KEY2_PRES; break; // KEY2 停止录音
case 3: Key_Temp = WK_UP_PRES; break;
default: Key_Temp = NO_KEY_PRES; break;
}
return Key_Temp;
}
/**
* @Function : KeyActivity
* @Brief : 按键活动函数
* @Input : uint8_t KeyValue
* @Return : uint8_t Key_Temp
* @Others : 根据不同的按键,执行相应操作
*/
void KeyActivity(void)
{
uint8_t key;
key = KeyPress();
switch(key)
{
case KEY0_PRES: LEDR_FLIP; break; // KEY0按下,翻转LEDR电平
case KEY1_PRES: LEDG_FLIP; break; // KEY1按下,翻转LEDG电平
case KEY2_PRES: LEDB_FLIP; break; // KEY2按下,翻转LEDB电平
case WK_UP_PRES: LEDR_OFF; LEDG_OFF; LEDB_OFF; break; // 关闭所有LED
default : break;
}
}
/* USER CODE END 2 */
在 tim.c 中编写定时器中断回调函数,并且在回调函数中调用按键扫描函数 Key_Scan();
注:默认的tim.c并没有包含gpio.h,请添加上,否则程序会报错
/* USER CODE BEGIN 1 */
// 定时器更新中断回调函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
// 判断是定时器3发生的中断
if(htim->Instance == TIM3)
{
// 定时器3每中断一次执行一次按键扫描
Key_Scan();
}
}
/* USER CODE END 1 */
在 main.c 中,调用按键活动函数 KeyActivity();
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
KeyActivity();
}
/* USER CODE END 3 */
至此,按键检测的全部程序编写完成,可以烧录至开发板进行验证。
所实现的功能为:
初始状态为LED_R、LED_G、LED_B全部熄灭状态;
按下KEY0,LED_R点亮,再次按下LED_R熄灭;
按下KEY1,LED_G点亮,再次按下LED_G熄灭;
按下KEY2,LED_B点亮,再次按下LED_B熄灭;
按下WK-UP,所有LED全部熄灭。
下一篇: web前端-2.HTML的基础知识点