[蓝桥杯嵌入式]hal库 stm32 独立按键单击、双击、长按识别

本文章以蓝桥杯嵌入式开发板为使用案列,使用stm32g431rbt6芯片,使用PB0,PB1,PB2,PA0作为独立按键的输入。

思路:

使用状态机的方式对4个独立按键进行单机,双击以及长按的识别

代码使用前提要求:需要一个10ms调用的定时器(软件定时器,硬件定时器皆可,精度无要求)

要使用本代码,首先你需要配置好如上的引脚为输入模式。

引脚配置:

.h文件:

key.h

/**
————————————————

@copyright Copyright (c) 2023         CSDN原创:believe、悠闲

      原文链接:https://blog.csdn.net/qq_64777806/article/details/136200795

      未经过允许,禁止商用
————————————————

*/
#ifndef _KEY_H_
#define _KEY_H_

typedef struct Key_Str     //按键结构体
{
	unsigned char level;  //当前按键电平
	unsigned char state;	//当前按键状态
	
	unsigned char one_flag;		//单次按下状态
	
	unsigned int long_time;		//长按计数器
	unsigned char long_flag;	//长按状态
	
	unsigned int twice_star;	//单击遗留状态
	unsigned int twice_time;	//双击计数器
	unsigned int twice_flag;	//双击状态
	
} Key_Str;


void Key_Init(void);   //按键引脚初始化,如果使用cubemx已经配置了可不调用
void Key_Scan(void);   //按键扫描函数,10~15ms调用一次


#endif

.c文件
 

/**
————————————————

@copyright Copyright (c) 2023         CSDN原创:believe、悠闲

      原文链接:https://blog.csdn.net/qq_64777806/article/details/136200795

      未经过允许,禁止商用
————————————————

*/
#include "key.h"
#include "gpio.h"


Key_Str key[4];    //按键变量,数组长度4代表4个按键

void Key_Init(void)   //按键引脚初始化函数
{
	GPIO_InitTypeDef GPIO_InitStruct = {0};
	
	__HAL_RCC_GPIOA_CLK_ENABLE();
	__HAL_RCC_GPIOB_CLK_ENABLE();
	
	//GPIO	A 0
	GPIO_InitStruct.Pin = GPIO_PIN_0;
	GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
	GPIO_InitStruct.Pull = GPIO_NOPULL;
	HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
	
	//GPIO  B 0 1 2
	GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2;
	GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
	GPIO_InitStruct.Pull = GPIO_NOPULL;
	HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}

void Key_Scan(void)           //按键扫描函数(建议10ms~20ms调用一次),本函数会自动修改key[4]结构体变量中的对应值,需要时在对应代码extern变量即可。
{
	unsigned char i;
	key[0].level = HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0);
	key[1].level = HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1);
	key[2].level = HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2);
	key[3].level = HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0);
	
	for(i=0;i<4;i++)
	{
		switch(key[i].state)
		{
			case 0:
				if(0 == key[i].level)  //首次按下
				{
					key[i].state = 1;
				}
				
				if(1 == key[i].twice_star) //2次按键判断计数
				{
					key[i].twice_time++;
					if(key[i].twice_time>=100)   //当计数超过100次时解除2次判断状态(此处100的值代表10ms持续100次,可自行修改)
					{
						key[i].twice_star = 0;
						key[i].twice_time = 0;
					}
				}
				break;
			case 1:
				if(0 == key[i].level)	//消抖确认
				{
					key[i].state = 2;   //确认
				}else
				{
					key[i].state = 0;	//抖动
				}
				break;
			case 2:
				if(0 == key[i].level)
				{
					key[i].long_time++;
					if(key[i].long_time >= 50)        //这里是长按触发,当长按达到500ms时直接触发长按标志位,无需等待放手,如需放手生效,则注释此if及其代码块(判断值可自行修改)
					{
						key[i].long_flag = 1;
						key[i].state=3;
					}
					
				}else if(1 == key[i].level)
				{
					if(key[i].long_time < 50)
					{
						key[i].long_time=0;
						if(1 == key[i].twice_star)   
						{
								key[i].twice_flag = 1;
								key[i].twice_time = 0;
								key[i].twice_star = 0;
								key[i].state=0;

						}else if(0 == key[i].twice_star)
							{
								key[i].one_flag = 1;								
								key[i].state=0;
								key[i].twice_star =1;
							}
					}
				}
				break;
			case 3:
				if(1 == key[i].level)
				{
						key[i].long_time=0;
						key[i].state=0;
				}
				break;
		}
	}
}


以上代码实现了按键的单击,双击,以及长按。

业务部分使用列程:

1、确保按键引脚初始化(如已初始化可省略)

Key_Init();   //初始化按键引脚

2、设置一个10ms调用的中断,这里我使用的1ms定时器4中断配合10ms的软定

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	static uint16_t j;
	
    if (htim == (&htim4)) 硬件计时器
	{
		if(++j >= 10)    //软件计时
		{
			j=0;
			Key_Scan();   //按键扫描
		}

	}
}

3、在需要按键功能的代码文件中引用按键结构体变量Key_Str key[4],这里我在main.c中引用

    //省略cube生成代码
#include "key.h"
//#include "xxx"


extern Key_Str key[4];   //引用外部变量

    //省略cube生成代码
void main(void)
{
    //省略cube生成代码
    while(1)
    {
    	if(1 == key[0].one_flag)                    //按键1单击
    	{
    		key[0].one_flag=0;
    		//功能代码		
    	}else if(1 == key[0].long_flag)            //按键1长按
    		{
    			key[0].long_flag=0;
    			//功能代码
    			
    		}else if(1 == key[0].twice_flag)       //按键1双击
    			{
    				key[0].twice_flag = 0;
    				//功能代码
    			}
    		
    		
    		
    	if(1 == key[1].one_flag)
    	{
    		key[1].one_flag=0;
    		//功能代码
    	}else if(1 == key[1].long_flag)
    		{
    			key[1].long_flag=0;
                   //功能代码
    		}else if(1 == key[1].twice_flag)
    			{
    				key[1].twice_flag = 0;	
    				//功能代码
    			}
    		
    		
    	if(1 == key[2].one_flag)
    	{
	    	key[2].one_flag=0;
	    	//功能代码
	    }else if(1 == key[2].long_flag)
		    {
			    key[2].long_flag=0;
    			//功能代码
	    	}else if(1 == key[2].twice_flag)
		    	{
			    	key[2].twice_flag = 0;
				    //功能代码
    			}
	    
	    
	    
    	if(1  == key[3].one_flag)
    	{
    		key[3].one_flag=0;
    		//功能代码
    	}else if(1 == key[3].long_flag)
    		{
    			key[3].long_flag=0;
                //功能代码
    		}else if(1 == key[3].twice_flag)
    			{
    				key[3].twice_flag = 0;
    				//功能代码
    			}
        
     }

    
}

    //省略cube生成代码

演示效果(在功能代码中,我添加了led开关和lcd显示,方便观察是否读取按键成功)

双击按键程序可以通过以下步骤实现: 1. 初始化IO:将按键对应的IO口配置为上拉输入,使用GPIO_Init函数进行初始化。 2. 设置计时器:使用定时器进行计时,可以通过TIM_TimeBaseInit函数初始化一个定时器,并设置定时周期和分频系数。 3. 监测按键状态:在循环中,通过读取按键状态的函数(如KEY0)来检测按键是否被按下。 4. 检测双击:当检测到按键按下时,开始计时器并等待一段时间(比如50ms),然后再次检测按键状态。如果在这段时间内再次检测到按键按下,则判断为双击。 以下是一个简单的示例代码: ``` #include "key.h" #include "stm32f10x.h" #define TIME_DELAY 50 // 双击间隔时间,单位为毫秒 // 按键状态枚举 typedef enum { KEY_IDLE, // 按键空闲状态 KEY_DOWN, // 按键按下状态 KEY_UP, // 按键松开状态 KEY_DOUBLE_CLICK, // 双击状态 } KeyState; // 按键按下时间 static uint32_t key_down_time = 0; void KEY_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; GPIO_Init(GPIOE, &GPIO_InitStructure); } KeyState GetKeyState(void) { if (GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_4) == Bit_RESET) { return KEY_DOWN; } else { return KEY_UP; } } void Delay(uint32_t ms) { uint32_t i, j; for (i = 0; i < ms; i++) { for (j = 0; j < 5000; j++) { // 延时函数 } } } int main(void) { KeyState key_state = KEY_IDLE; KEY_Init(); while (1) { switch (key_state) { case KEY_IDLE: if (GetKeyState() == KEY_DOWN) { key_state = KEY_DOWN; key_down_time = HAL_GetTick(); } break; case KEY_DOWN: if (GetKeyState() == KEY_UP) { key_state = KEY_UP; } break; case KEY_UP: if (GetKeyState() == KEY_DOWN) { if ((HAL_GetTick() - key_down_time) <= TIME_DELAY) { key_state = KEY_DOUBLE_CLICK; } else { key_state = KEY_IDLE; } } else { key_state = KEY_IDLE; } break; case KEY_DOUBLE_CLICK: // 双击按键的处理逻辑 // TODO: 实现双击按键的功能 key_state = KEY_IDLE; break; } } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值