在一般情况下,我们处理按键时,用延时函数实现按键的消抖,用while死循环实现按键的松手检测,但是这样会使程序卡死,浪费CPU资源,在延时函数里只进行+1+1+1+1的操作,对程序运行的实时性很不友好。
以下使我们正常处理按键的代码
uint8_t KeyNum = 0;
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0)
{
Delay_ms(20);
while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0);
Delay_ms(20);
KeyNum = 1;
}
那么如何不利用delay函数和while死循环实现按键的消抖和松手检测呢
利用定时器中断,5ms执行按键检测程序,设立标志位,每执行一次标志位+1,当标志位为4是,表明程序被执行了4次,5ms执行一次程序,即20ms开始执行剩下按键按下的程序,即实现了按键的消抖,松手检测也是同理
话不多说,先看代码
首先tim2定时器初始化
void Timer2_Init()
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
TIM_InternalClockConfig(TIM2);
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
TIM_TimeBaseInitStruct.TIM_ClockDivision =TIM_CKD_DIV1;
TIM_TimeBaseInitStruct.TIM_CounterMode =TIM_CounterMode_Up;
TIM_TimeBaseInitStruct.TIM_Period = 10-1;
TIM_TimeBaseInitStruct.TIM_Prescaler =7200-1;
TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStruct);
TIM_ClearFlag(TIM2,TIM_FLAG_Update);
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
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);
}
72Mhz的主频,经过计数和分频后得到1000hz的频率,即1ms进入一次中断,中断函数内代码如下;
void TIM2_IRQHandler()
{
if (TIM_GetITStatus(TIM2,TIM_IT_Update) == SET)
{
static uint16_t count = 0;
//**********************************************************//
if(count%5 == 0 )
{
if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0)==0)
{
myKeyHandler.status = KEY_DOWN;
}
key_handler(&myKeyHandler);
if (myKeyHandler.event == SHORT_PRESS)
LED1_Turn();
else if (myKeyHandler.event == LONG_PRESS)
LED2_Turn();
count=0;
}
count++;
//*************************************************************//
TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
}
}
中间一大坨等下再说,再在main函数上面声明一个东西
KeyHandler myKeyHandler;
这个也等下再说,以下是key.h中的代码
/*定义按键状态枚举这个枚举定义了按键可能的两种状态:KEY_DOWN 表示按键按下,KEY_UP 表示按键抬起。*/ typedef enum { KEY_DOWN, KEY_UP, } KeyStatus; /*定义按键事件枚举,这个枚举定义了按键可能的三种事件:NULL_PRESS 表示没有按键事件,SHORT_PRESS 表示短按,LONG_PRESS 表示长按。*/ typedef enum { NULL_PRESS, SHORT_PRESS, LONG_PRESS } KeyEvent; /*定义按键配置结构体,这个结构体包含了按键的状态 (status)、事件类型 (event) 和长按计数 (longPressCount)。它用于存储按键的相关信息。*/ typedef struct { KeyStatus status; KeyEvent event; uint16_t longPressCount; } KeyHandler; void Key_Init(void); void initKeyHandler(KeyHandler *handler); void key_handler(KeyHandler *handler); /* Key_Init 函数可能用于初始化按键硬件,但在提供的代码中并没有具体的实现。 initKeyHandler 函数用于初始化 KeyHandler 结构体的实例,将按键的状态、事件和计数等设置为初始值。 key_handler 函数用于处理按键的状态和事件。它接受一个 KeyHandler 结构体的实例作为参数,并根据实际情况更新结构体的成员。 这些代码提供了一个基本的框架,可以用于处理按键的状态和事件。你需要根据具体的硬件和需求实现 Key_Init 函数以初始化按键硬件,并根据实际情况实现 key_handler 函数。*/
以下为key.c中的内容
#include "stm32f10x.h" // Device header #include "Delay.h" #include "key.h" void Key_Init(void) { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1|GPIO_Pin_4|GPIO_Pin_5; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); } void initKeyHandler(KeyHandler *handler) { handler->status = KEY_UP; handler->event = NULL_PRESS; handler->longPressCount = 0; } void key_handler(KeyHandler *handler) { unsigned char keyStatus=1; if (handler->longPressCount == 0 && handler->status != NULL_PRESS) { handler->event = NULL_PRESS; // 重置按键状态标志位 } if (handler->status == KEY_DOWN) { handler->status = KEY_UP; keyStatus= GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0); } if (keyStatus == 0) { handler->longPressCount++; } else { if (handler->longPressCount >= 4 && handler->longPressCount < 200) { handler->event = SHORT_PRESS; } else if (handler->longPressCount >= 200) { handler->event = LONG_PRESS; } else { handler->event = NULL_PRESS; } handler->longPressCount = 0; } }
初始化按键状态:
unsigned char keyStatus = 1;
这里初始化了一个名为
keyStatus
的变量,表示按键的当前状态,默认设置为1(假设高电平,具体值取决于你的硬件电平定义)。按键状态和事件重置:
if (handler->longPressCount == 0 && handler->status != NULL_PRESS) { handler->event = NULL_PRESS; // 重置按键状态标志位 }
如果长按计数为0且按键状态不为
NULL_PRESS
,则将按键事件重置为NULL_PRESS
。判断按键状态变化:
if (handler->status == KEY_DOWN) { handler->status = KEY_UP; keyStatus = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0); }
如果按键状态为
KEY_DOWN
,将状态切换为KEY_UP
,并读取实际的按键状态到keyStatus
变量中。按键计数及事件判断:
if (keyStatus == 0) { handler->longPressCount++; } else { if (handler->longPressCount >= 4 && handler->longPressCount < 200) { handler->event = SHORT_PRESS; } else if (handler->longPressCount >= 200) { handler->event = LONG_PRESS; } else { handler->event = NULL_PRESS; } handler->longPressCount = 0; }
如果
keyStatus
为0(按键按下),递增longPressCount
计数。在按键抬起时,根据longPressCount
判断按键事件类型,并更新handler->event
。如果longPressCount
在4到199之间,事件为短按;如果longPressCount
大于等于200,事件为长按;否则,事件为NULL_PRESS
,即没有事件。然后,重置longPressCount
为0。这个函数的目的是根据按键状态的变化来判断按键事件,并更新
KeyHandler
结构体中的状态和事件成员。这样,你可以根据KeyHandler
结构体的成员来获取按键的当前状态和事件类型。