欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

【STM32】HAL库开发之定时器作按键检测

程序员文章站 2022-03-13 17:19:29
...

目录

一、按键检测

二、定时器消抖原理

三、STM32CubeMX软件配置

四、程序设计


一、按键检测

本次是在前文《【STM32】HAL库开发之通用定时器中断》的基础上进一步开发

首先贴上板子上面的按键电路原理图

【STM32】HAL库开发之定时器作按键检测

以KEY0为例,在按键没有按下的时候,GPIO的电平为高电平;当KEY0按下,此时GPIO直接与GND相连,电平变为低电平;当KEY0再次松开,GPIO的电平再次为高电平。

在按键按下或松开过程中,电平状态会出现抖动状态。在很多的例程中,都是使用延时函数来进行按键消抖。但是由于使用延时函数,占用cpu资源,会导致部分程序在延时的过程中发生暂停的情况,所以本次将使用定时器来进行按键消抖。

本次基于STM32L4VET6开发板,使用HAL库进行开发。

二、定时器消抖原理

以上述按键原理图为例,在本次实验中,一共有四个独立按键,我使用定时器每隔1ms读取一次按键的状态,然后进行判断。

如果连续8次按键都是按下状态,则判断按键确实按下了;

如果连续8次按键都是松开状态,则判断按键确实松开了;

如果连续8次内,既存在按下状态,也存在松开状态,则判断按键发生抖动,不进行处理。

原理很简单,实现起来也不难。

三、STM32CubeMX软件配置

本次是在前文《【STM32】HAL库开发之通用定时器中断》的基础上进一步开发。

在定时器的配置基础上,仅需增加按键的GPIO配置

将与几个按键相连的GPIO设置为输入模式

【STM32】HAL库开发之定时器作按键检测

设置完成如下图所示

【STM32】HAL库开发之定时器作按键检测

接下来将进行程序设计

四、程序设计

按键检测有关的函数将在 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全部熄灭。