stm32定时器中断实现按键检测,消抖

在一般情况下,我们处理按键时,用延时函数实现按键的消抖,用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;
    }
}
  1. 初始化按键状态:

    unsigned char keyStatus = 1;

    这里初始化了一个名为 keyStatus 的变量,表示按键的当前状态,默认设置为1(假设高电平,具体值取决于你的硬件电平定义)。

  2. 按键状态和事件重置:

    if (handler->longPressCount == 0 && handler->status != NULL_PRESS) { handler->event = NULL_PRESS; // 重置按键状态标志位 }

    如果长按计数为0且按键状态不为NULL_PRESS,则将按键事件重置为NULL_PRESS

  3. 判断按键状态变化:

    if (handler->status == KEY_DOWN) { handler->status = KEY_UP; keyStatus = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0); }

    如果按键状态为KEY_DOWN,将状态切换为KEY_UP,并读取实际的按键状态到keyStatus变量中。

  4. 按键计数及事件判断:

    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 结构体的成员来获取按键的当前状态和事件类型。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值