STM32江科大学习笔记,全功能按键非阻塞式实现,按键点击,双击,长按

发布于:2025-08-07 ⋅ 阅读:(16) ⋅ 点赞:(0)

目录

一、前言

二、关于实现非阻塞的办法

2.1 中断类型的选择

2.2 定时器中断

二、程序流程图

2.1 状态S=0空闲状态

2.2 状态S=1按键判断长按还是其他的事件

2.3 状态S=2按键判断双击或者单击

2.4 状态S=3按键已双击状态

2.5 状态S=4长按状态

三、编写代码

3.1 按键初始化

3.2 获取按键当前的状态

3.3 按键状态机处理函数

四、完整代码

4.1 定时器中断初始化

4.2 按键初始化


一、前言

由于普通Delay延时函数会出现阻塞主函数的运行,会发生主函数变慢或者停止的情况,在没有使用RTOS的情况应该如何实现非阻塞呢,以下为个人学习经验分享,学习来自江科大。

二、关于实现非阻塞的办法

2.1 中断类型的选择

肯定是中断的办法来实现非阻塞的作用,但是我们使用的不是普通的IO口接收到什么信号就进入中断,这个办法进入的中断难免会有一些按键抖动,因此,我们使用定时器中断

2.2 定时器中断

那么定时器中断为什么能够实现非阻塞呢?我们是用这个办法来解决的,如下图:

我们设置为每20ms进入定时中断读取一次引脚的值,然后读取本次的引脚值和保存上一次的引脚值,然后进行两次的值的分析:


两次都为1,则为松开按键状态


上次是1,本次是0,则为刚按下按键状态


上次是0,本次是1,则为刚松开按键状态


由于我们使用了定时器隔开读数的原因,所以我们避免了使用Delay延迟函数,所以我们实现了不阻塞主函数!

二、程序流程图

2.1 状态S=0空闲状态

状态:空闲状态,也就是我们按键未按下的状态

功能:检测按键是否按下(跳出当前状态的判断)

状态转移:按键按下,然后转去状态1,并设定长按的时间

2.2 状态S=1按键判断长按还是其他的事件

状态:按键已按下状态,也就是我们按键刚按下去的状态

功能:检测按键是否松开(跳出当前状态的判断)

功能:等待上面设计的长按时间是否结束(跳出当前状态的判断)

状态转移:按键松开,然后转去状态2,并设定双击等待的时间

状态转移:长按时间到,然后转去状态4,并设定重复的时间(多久重复一次),设置长按标志位LONG

2.3 状态S=2按键判断双击或者单击

状态:按键松开状态,也就是我们按键刚松开的状态

功能:检测按键是否按下(跳出当前状态的判断)

功能:等待上面设计的双击时间是否结束(跳出当前状态的判断)

状态转移:按键按下,然后转去状态3,并设置双击标志位DOUBLE

状态转移:双击时间到,然后重置状态,回到状态0并设置单击标志位SINGLE

2.4 状态S=3按键已双击状态

状态:按键已双击状态,也就是已经判断完双击事件

功能:检测按键是否松开(跳出当前状态的判断)

状态转移:按键松开,然后重置状态,返回初始状态

2.5 状态S=4长按状态

状态:按键长按状态,一直按着就一直循环,然后置重复事件REPEAT

功能:检测按键是否松开(跳出当前状态的判断)

功能:等待重复时间(当时间到后重复执行当前状态的功能)

状态转移:重复时间到了,然后再设置重复时间,将当前状态再执行一次

状态转移:按键松开,然后重置状态,返回初始状态

三、编写代码

3.1 按键初始化

/**
 * @brief 按键GPIO初始化函数
 * @param 无
 * @retval 无
 * @description 配置按键对应的GPIO引脚
 *              KEY_1(PB1)、KEY_2(PB11): 上拉输入,按下时读取为0
 *              KEY_3(PB13)、KEY_4(PB15): 下拉输入,按下时读取为1
 */
void Key_Init(void)
{
	// 使能GPIOB时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	
	// 配置KEY_1(PB1)和KEY_2(PB11)为上拉输入
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;		// 上拉输入模式
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_11;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	// 配置KEY_3(PB13)和KEY_4(PB15)为下拉输入
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;		// 下拉输入模式
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_15;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
}

初始化就不多说啦,正常输入模式就可以。

3.2 获取按键当前的状态

/* 按键编号定义 */
#define KEY_1					0			// 按键1编号 (对应PB1,上拉输入)
#define KEY_2					1			// 按键2编号 (对应PB11,上拉输入)
#define KEY_3					2			// 按键3编号 (对应PB13,下拉输入)
#define KEY_4					3			// 按键4编号 (对应PB15,下拉输入)

/**
 * @brief 获取按键物理状态
 * @param n 按键编号 (KEY_1, KEY_2, KEY_3, KEY_4)
 * @retval KEY_PRESSED: 按键被按下, KEY_UNPRESSED: 按键未被按下
 * @description 读取指定按键的当前物理状态
 *              KEY_1(PB1)、KEY_2(PB11): 低电平有效
 *              KEY_3(PB13)、KEY_4(PB15): 高电平有效
 */
uint8_t Key_GetState(uint8_t n)
{
	if (n == KEY_1)
	{
		// KEY_1上拉输入,按下时为低电平
		if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)
		{
			return KEY_PRESSED;
		}
	}
	else if (n == KEY_2)
	{
		// KEY_2上拉输入,按下时为低电平
		if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0)
		{
			return KEY_PRESSED;
		}
	}
	else if (n == KEY_3)
	{
		// KEY_3下拉输入,按下时为高电平
		if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_13) == 1)
		{
			return KEY_PRESSED;
		}
	}
	else if (n == KEY_4)
	{
		// KEY_4下拉输入,按下时为高电平
		if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_15) == 1)
		{
			return KEY_PRESSED;
		}
	}
	return KEY_UNPRESSED;
}

获取指定按键的物理状态(也就是高低电平),然后来识别是否按下之类的,用来变成之后代码的合成材料

3.3 按键状态机处理函数

/* 按键数量配置 */
#define KEY_COUNT				4			// 系统支持的按键总数

/* 按键状态标志位定义 */
#define KEY_HOLD				0x01		// 按键保持按下状态
#define KEY_DOWN				0x02		// 按键刚按下瞬间
#define KEY_UP					0x04		// 按键刚松开瞬间
#define KEY_SINGLE				0x08		// 按键单击
#define KEY_DOUBLE				0x10		// 按键双击
#define KEY_LONG				0x20		// 按键长按
#define KEY_REPEAT				0x40		// 按键连按(长按后的重复触发)

/**
 * @brief 按键状态机处理函数
 * @param 无
 * @retval 无
 * @description 按键扫描和状态处理的核心函数,需要定时调用(建议1ms调用一次)
 *              实现了完整的按键状态机,支持以下功能:
 *              - KEY_HOLD: 按键保持按下状态
 *              - KEY_DOWN: 按键刚按下瞬间
 *              - KEY_UP: 按键刚松开瞬间
 *              - KEY_SINGLE: 按键单击(在双击检测时间内只按一次)
 *              - KEY_DOUBLE: 按键双击(在指定时间内按两次)
 *              - KEY_LONG: 按键长按(超过长按时间阈值)
 *              - KEY_REPEAT: 按键连按(长按后的重复触发)
 * 
 * @note 状态机说明:
 *       S[i] = 0: 空闲状态,等待按键按下
 *       S[i] = 1: 第一次按下状态,等待松开或长按超时
 *       S[i] = 2: 第一次松开状态,等待第二次按下或双击超时
 *       S[i] = 3: 双击确认状态,等待第二次松开
 *       S[i] = 4: 长按状态,等待松开或重复触发
 */
void Key_Tick(void)
{
	static uint8_t Count, i;						// Count: 扫描计数器, i: 循环变量
	static uint8_t CurrState[KEY_COUNT], PrevState[KEY_COUNT];	// 当前状态和前一次状态
	static uint8_t S[KEY_COUNT];					// 状态机状态数组
	static uint16_t Time[KEY_COUNT];				// 定时器数组
	
	// 所有按键的定时器递减
	for (i = 0; i < KEY_COUNT; i ++)
	{
		if (Time[i] > 0)
		{
			Time[i] --;
		}
	}
	
	// 每20ms扫描一次按键状态(假设本函数每1ms调用一次)
	Count ++;
	if (Count >= 20)
	{
		Count = 0;
		
		// 遍历所有按键
		for (i = 0; i < KEY_COUNT; i ++)
		{
			// 更新按键状态
			PrevState[i] = CurrState[i];
			CurrState[i] = Key_GetState(i);
			
			// 处理KEY_HOLD状态
			if (CurrState[i] == KEY_PRESSED)
			{
				Key_Flag[i] |= KEY_HOLD;		// 按键按下时置位HOLD标志
			}
			else
			{
				Key_Flag[i] &= ~KEY_HOLD;		// 按键松开时清除HOLD标志
			}
			
			// 处理KEY_DOWN状态(上升沿检测)
			if (CurrState[i] == KEY_PRESSED && PrevState[i] == KEY_UNPRESSED)
			{
				Key_Flag[i] |= KEY_DOWN;
			}
			
			// 处理KEY_UP状态(下降沿检测)
			if (CurrState[i] == KEY_UNPRESSED && PrevState[i] == KEY_PRESSED)
			{
				Key_Flag[i] |= KEY_UP;
			}
			
			// 按键状态机处理
			if (S[i] == 0)		// 状态0: 空闲状态
			{
				if (CurrState[i] == KEY_PRESSED)
				{
					Time[i] = KEY_TIME_LONG;	// 设置长按计时器
					S[i] = 1;					// 转到状态1
				}
			}
			else if (S[i] == 1)	// 状态1: 第一次按下状态
			{
				if (CurrState[i] == KEY_UNPRESSED)
				{
					Time[i] = KEY_TIME_DOUBLE;	// 设置双击检测计时器
					S[i] = 2;					// 转到状态2
				}
				else if (Time[i] == 0)		// 长按时间到
				{
					Time[i] = KEY_TIME_REPEAT;	// 设置重复触发计时器
					Key_Flag[i] |= KEY_LONG;	// 置位长按标志
					S[i] = 4;					// 转到状态4
				}
			}
			else if (S[i] == 2)	// 状态2: 第一次松开状态
			{
				if (CurrState[i] == KEY_PRESSED)
				{
					Key_Flag[i] |= KEY_DOUBLE;	// 置位双击标志
					S[i] = 3;					// 转到状态3
				}
				else if (Time[i] == 0)		// 双击检测时间到
				{
					Key_Flag[i] |= KEY_SINGLE;	// 置位单击标志
					S[i] = 0;					// 回到状态0
				}
			}
			else if (S[i] == 3)	// 状态3: 双击确认状态
			{
				if (CurrState[i] == KEY_UNPRESSED)
				{
					S[i] = 0;					// 回到状态0
				}
			}
			else if (S[i] == 4)	// 状态4: 长按状态
			{
				if (CurrState[i] == KEY_UNPRESSED)
				{
					S[i] = 0;					// 回到状态0
				}
				else if (Time[i] == 0)		// 重复触发时间到
				{
					Time[i] = KEY_TIME_REPEAT;	// 重新设置重复触发计时器
					Key_Flag[i] |= KEY_REPEAT;	// 置位重复触发标志
					S[i] = 4;					// 保持状态4
				}
			}
		}
	}
}

该代码是放在定时器中断函数里面的!!这个记住了,定时器是每隔1ms调用一次这个函数;

由于Count ++,每1ms加一次,记到20次则为20ms,所以每隔20ms处理一次状态机;

然后和上述的一样,进行当前和之前电平状态的对比,就可以知道当前的按键的物理状态;

然后根据流程图进行代码的编写,比如状态0通过按下按键跳到状态1,然后设置长按定时,也就是这样

if (S[i] == 0)       // 状态0: 空闲状态

            {

                if (CurrState[i] == KEY_PRESSED)

                {

                    Time[i] = KEY_TIME_LONG;    // 设置长按计时器

                    S[i] = 1;                   // 转到状态1

                }

            }

以此类推即可!!

四、完整代码

4.1 定时器中断初始化

#include "stm32f10x.h"                  // Device header

void Timer_Init(void)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
	
	TIM_InternalClockConfig(TIM2);
	
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInitStructure.TIM_Period = 1000 - 1;
	TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1;
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
	
	TIM_ClearFlag(TIM2, TIM_FLAG_Update);
	TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_Init(&NVIC_InitStructure);
	
	TIM_Cmd(TIM2, ENABLE);
}

/*
void TIM2_IRQHandler(void)
{
	if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
	{
		
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
	}
}
*/

4.2 按键初始化

/**
 * @file Key.c
 * @brief 全功能按键非阻塞式实现源文件
 * @author STM32江科大
 * @date 2025年8月6日
 * @description 实现多种按键检测功能,包括按下、松开、单击、双击、长按、连按等
 *              采用状态机方式实现,支持非阻塞式检测
 */

#include "stm32f10x.h"                  // Device header
#include "Key.h"

/* 按键物理状态定义 */
#define KEY_PRESSED				1		// 按键被按下的物理状态
#define KEY_UNPRESSED			0		// 按键未被按下的物理状态

/* 按键时间参数定义 (单位: ms) */
#define KEY_TIME_DOUBLE			200		// 双击间隔时间阈值
#define KEY_TIME_LONG			2000	// 长按时间阈值
#define KEY_TIME_REPEAT			100		// 连按重复间隔时间

/* 按键状态标志数组 */
uint8_t Key_Flag[KEY_COUNT];			// 存储各按键的状态标志位

/**
 * @brief 按键GPIO初始化函数
 * @param 无
 * @retval 无
 * @description 配置按键对应的GPIO引脚
 *              KEY_1(PB1)、KEY_2(PB11): 上拉输入,按下时读取为0
 *              KEY_3(PB13)、KEY_4(PB15): 下拉输入,按下时读取为1
 */
void Key_Init(void)
{
	// 使能GPIOB时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	
	// 配置KEY_1(PB1)和KEY_2(PB11)为上拉输入
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;		// 上拉输入模式
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_11;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	// 配置KEY_3(PB13)和KEY_4(PB15)为下拉输入
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;		// 下拉输入模式
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_15;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
}

/**
 * @brief 获取按键物理状态
 * @param n 按键编号 (KEY_1, KEY_2, KEY_3, KEY_4)
 * @retval KEY_PRESSED: 按键被按下, KEY_UNPRESSED: 按键未被按下
 * @description 读取指定按键的当前物理状态
 *              KEY_1(PB1)、KEY_2(PB11): 低电平有效
 *              KEY_3(PB13)、KEY_4(PB15): 高电平有效
 */
uint8_t Key_GetState(uint8_t n)
{
	if (n == KEY_1)
	{
		// KEY_1上拉输入,按下时为低电平
		if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)
		{
			return KEY_PRESSED;
		}
	}
	else if (n == KEY_2)
	{
		// KEY_2上拉输入,按下时为低电平
		if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0)
		{
			return KEY_PRESSED;
		}
	}
	else if (n == KEY_3)
	{
		// KEY_3下拉输入,按下时为高电平
		if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_13) == 1)
		{
			return KEY_PRESSED;
		}
	}
	else if (n == KEY_4)
	{
		// KEY_4下拉输入,按下时为高电平
		if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_15) == 1)
		{
			return KEY_PRESSED;
		}
	}
	return KEY_UNPRESSED;
}

/**
 * @brief 检测按键状态
 * @param n 按键编号 (KEY_1, KEY_2, KEY_3, KEY_4)
 * @param Flag 要检测的状态标志位 (KEY_HOLD, KEY_DOWN, KEY_UP, KEY_SINGLE, KEY_DOUBLE, KEY_LONG, KEY_REPEAT)
 * @retval 1: 指定状态发生, 0: 指定状态未发生
 * @description 检测指定按键的指定状态是否发生
 *              除了KEY_HOLD状态会持续保持外,其他状态在检测后会自动清除
 */
uint8_t Key_Check(uint8_t n, uint8_t Flag)
{
	// 检查指定按键的指定标志位是否置位
	if (Key_Flag[n] & Flag)
	{
		// 除了KEY_HOLD外,其他标志位检测后自动清除
		if (Flag != KEY_HOLD)
		{
			Key_Flag[n] &= ~Flag;	// 清除对应标志位
		}
		return 1;
	}
	return 0;
}

/**
 * @brief 按键状态机处理函数
 * @param 无
 * @retval 无
 * @description 按键扫描和状态处理的核心函数,需要定时调用(建议1ms调用一次)
 *              实现了完整的按键状态机,支持以下功能:
 *              - KEY_HOLD: 按键保持按下状态
 *              - KEY_DOWN: 按键刚按下瞬间
 *              - KEY_UP: 按键刚松开瞬间
 *              - KEY_SINGLE: 按键单击(在双击检测时间内只按一次)
 *              - KEY_DOUBLE: 按键双击(在指定时间内按两次)
 *              - KEY_LONG: 按键长按(超过长按时间阈值)
 *              - KEY_REPEAT: 按键连按(长按后的重复触发)
 * 
 * @note 状态机说明:
 *       S[i] = 0: 空闲状态,等待按键按下
 *       S[i] = 1: 第一次按下状态,等待松开或长按超时
 *       S[i] = 2: 第一次松开状态,等待第二次按下或双击超时
 *       S[i] = 3: 双击确认状态,等待第二次松开
 *       S[i] = 4: 长按状态,等待松开或重复触发
 */
void Key_Tick(void)
{
	static uint8_t Count, i;						// Count: 扫描计数器, i: 循环变量
	static uint8_t CurrState[KEY_COUNT], PrevState[KEY_COUNT];	// 当前状态和前一次状态
	static uint8_t S[KEY_COUNT];					// 状态机状态数组
	static uint16_t Time[KEY_COUNT];				// 定时器数组
	
	// 所有按键的定时器递减
	for (i = 0; i < KEY_COUNT; i ++)
	{
		if (Time[i] > 0)
		{
			Time[i] --;
		}
	}
	
	// 每20ms扫描一次按键状态(假设本函数每1ms调用一次)
	Count ++;
	if (Count >= 20)
	{
		Count = 0;
		
		// 遍历所有按键
		for (i = 0; i < KEY_COUNT; i ++)
		{
			// 更新按键状态
			PrevState[i] = CurrState[i];
			CurrState[i] = Key_GetState(i);
			
			// 处理KEY_HOLD状态
			if (CurrState[i] == KEY_PRESSED)
			{
				Key_Flag[i] |= KEY_HOLD;		// 按键按下时置位HOLD标志
			}
			else
			{
				Key_Flag[i] &= ~KEY_HOLD;		// 按键松开时清除HOLD标志
			}
			
			// 处理KEY_DOWN状态(上升沿检测)
			if (CurrState[i] == KEY_PRESSED && PrevState[i] == KEY_UNPRESSED)
			{
				Key_Flag[i] |= KEY_DOWN;
			}
			
			// 处理KEY_UP状态(下降沿检测)
			if (CurrState[i] == KEY_UNPRESSED && PrevState[i] == KEY_PRESSED)
			{
				Key_Flag[i] |= KEY_UP;
			}
			
			// 按键状态机处理
			if (S[i] == 0)		// 状态0: 空闲状态
			{
				if (CurrState[i] == KEY_PRESSED)
				{
					Time[i] = KEY_TIME_LONG;	// 设置长按计时器
					S[i] = 1;					// 转到状态1
				}
			}
			else if (S[i] == 1)	// 状态1: 第一次按下状态
			{
				if (CurrState[i] == KEY_UNPRESSED)
				{
					Time[i] = KEY_TIME_DOUBLE;	// 设置双击检测计时器
					S[i] = 2;					// 转到状态2
				}
				else if (Time[i] == 0)		// 长按时间到
				{
					Time[i] = KEY_TIME_REPEAT;	// 设置重复触发计时器
					Key_Flag[i] |= KEY_LONG;	// 置位长按标志
					S[i] = 4;					// 转到状态4
				}
			}
			else if (S[i] == 2)	// 状态2: 第一次松开状态
			{
				if (CurrState[i] == KEY_PRESSED)
				{
					Key_Flag[i] |= KEY_DOUBLE;	// 置位双击标志
					S[i] = 3;					// 转到状态3
				}
				else if (Time[i] == 0)		// 双击检测时间到
				{
					Key_Flag[i] |= KEY_SINGLE;	// 置位单击标志
					S[i] = 0;					// 回到状态0
				}
			}
			else if (S[i] == 3)	// 状态3: 双击确认状态
			{
				if (CurrState[i] == KEY_UNPRESSED)
				{
					S[i] = 0;					// 回到状态0
				}
			}
			else if (S[i] == 4)	// 状态4: 长按状态
			{
				if (CurrState[i] == KEY_UNPRESSED)
				{
					S[i] = 0;					// 回到状态0
				}
				else if (Time[i] == 0)		// 重复触发时间到
				{
					Time[i] = KEY_TIME_REPEAT;	// 重新设置重复触发计时器
					Key_Flag[i] |= KEY_REPEAT;	// 置位重复触发标志
					S[i] = 4;					// 保持状态4
				}
			}
		}
	}
}
/**
 * @file Key.h
 * @brief 全功能按键非阻塞式实现头文件
 * @author STM32江科大
 * @date 2025年8月6日
 * @description 实现多种按键检测功能,包括按下、松开、单击、双击、长按、连按等
 */

#ifndef __KEY_H
#define __KEY_H

/* 按键数量配置 */
#define KEY_COUNT				4			// 系统支持的按键总数

/* 按键编号定义 */
#define KEY_1					0			// 按键1编号 (对应PB1,上拉输入)
#define KEY_2					1			// 按键2编号 (对应PB11,上拉输入)
#define KEY_3					2			// 按键3编号 (对应PB13,下拉输入)
#define KEY_4					3			// 按键4编号 (对应PB15,下拉输入)

/* 按键状态标志位定义 */
#define KEY_HOLD				0x01		// 按键保持按下状态
#define KEY_DOWN				0x02		// 按键刚按下瞬间
#define KEY_UP					0x04		// 按键刚松开瞬间
#define KEY_SINGLE				0x08		// 按键单击
#define KEY_DOUBLE				0x10		// 按键双击
#define KEY_LONG				0x20		// 按键长按
#define KEY_REPEAT				0x40		// 按键连按(长按后的重复触发)

/**
 * @brief 按键初始化函数
 * @param 无
 * @retval 无
 * @description 初始化按键对应的GPIO引脚,配置为输入模式
 */
void Key_Init(void);

/**
 * @brief 按键状态检测函数
 * @param n 按键编号 (KEY_1, KEY_2, KEY_3, KEY_4)
 * @param Flag 要检测的按键状态标志位
 * @retval 1: 指定状态发生, 0: 指定状态未发生
 * @description 检测指定按键的指定状态是否发生,除KEY_HOLD外,其他状态检测后会自动清除
 */
uint8_t Key_Check(uint8_t n, uint8_t Flag);

/**
 * @brief 按键扫描处理函数
 * @param 无
 * @retval 无
 * @description 按键状态机处理函数,需要定时调用(建议1ms调用一次)
 *              处理所有按键的状态转换和事件检测
 */
void Key_Tick(void);

#endif

网站公告

今日签到

点亮在社区的每一天
去签到