【stm32】实现按键的单击和长按功能

  • Post author:
  • Post category:其他





前言

在项目中,按键是非常常用的外设,通过按键的单击、长按等,可以实现不同的互动操作。

这里,就通过按键单击、长按功能,分别实现LED点亮,LED闪烁,LED关闭三种状态。




一、原理图

在这里插入图片描述

在这里插入图片描述

根据原理图上的连线,可以得知,一共有6个按键,当按键未按下的时候,GPIO口处于高电平状态,当按键按下,GPIO则处于低电平状态。

在这里插入图片描述

在这里插入图片描述

值得注意的是,KEY1连接的GPIO口,复用状态下是JDIO,可以用重映射功能将其关闭,让PB3变为可用的GPIO口。



二、程序

按键驱动函数

#include "hal_key.h"
#include "stm32F10x.h"

static void hal_keyConfig(void);
static unsigned char hal_getKey1Sta(void);
static unsigned char hal_getKey2Sta(void);
static unsigned char hal_getKey3Sta(void);
static unsigned char hal_getKey4Sta(void);
static unsigned char hal_getKey5Sta(void);
static unsigned char hal_getKey6Sta(void);

KeyEvent_CallBack_t KeyScanCBS;

//函数指针数组,hal_getKeySta,分别是6个按键
unsigned char  (*getKeysState[KEYNUM])(void)={ hal_getKey1Sta,hal_getKey2Sta,hal_getKey3Sta,hal_getKey4Sta,hal_getKey5Sta,hal_getKey6Sta};

unsigned char KeyStep[KEYNUM];            //按键检测
unsigned char KeyScanTime[KEYNUM];			//去抖时间
unsigned char KeyPressLongTimer[KEYNUM];	//长按延时
unsigned char KeyContPressTimer[KEYNUM];	//连续长按延时



//初始化
void hal_KeyInit(void)
{
	unsigned char i;
	hal_keyConfig();
	KeyScanCBS=0;
	for(i=0;i<KEYNUM;i++)
	{
		KeyStep[i]=KEY_STEP_WAIT;
		KeyScanTime[i]=KEY_SCANTIME;
		KeyPressLongTimer[i]=KEY_PRESS_LONG_TIME;
		KeyContPressTimer[i]=KEY_PRESS_CONTINUE_TIME;
	}
}

void hal_KeyScanCBSRegister(KeyEvent_CallBack_t pCBS)   //传入回调函数
{
	if(KeyScanCBS == 0)
	{
			KeyScanCBS = pCBS;  //函数指针指向pCBS函数
	}
}	


void hal_KeyProc(void)
{
	unsigned char i,KeyState[KEYNUM];
	for(i=0;i<KEYNUM;i++)
	{
		unsigned char keys=0;
		KeyState[i]=getKeysState[i]();//读取每个按键的高低电平
		switch(KeyStep[i])//等待状态
		{
			case KEY_STEP_WAIT:
				if(KeyState[i])
				{
					KeyStep[i]=KEY_STEP_CLICK;
				}
				break;
			case KEY_STEP_CLICK://单击状态
				if(KeyState[i])
				{
					if(!(--KeyScanTime[i]))
					{
						KeyScanTime[i]=KEY_SCANTIME;
						KeyStep[i]=KEY_STEP_LONG_PRESS;
						keys=(i*5)+1;//单击
					}
				
				}
				else
				{
					KeyStep[i]=KEY_STEP_WAIT;
					KeyScanTime[i]=KEY_SCANTIME;
				}	
				break;
			case KEY_STEP_LONG_PRESS://长按状态
				if(KeyState[i])
				{
					if(!(--KeyPressLongTimer[i]))
					{
						KeyPressLongTimer[i]=KEY_PRESS_LONG_TIME;
						KeyStep[i]=KEY_STEP_CONTINUOUS_PRESS;
						keys=(i*5)+3;//长按
					}
				
				}
				else
				{
					KeyStep[i]=KEY_STEP_WAIT;
					KeyPressLongTimer[i]=KEY_PRESS_LONG_TIME;
					keys=(i*5)+2;// 单击释放
				}	
				break;
			case KEY_STEP_CONTINUOUS_PRESS://持续长按状态
				if(KeyState[i])
				{
					if(!(--KeyContPressTimer[i]))
					{
						KeyContPressTimer[i]=KEY_PRESS_CONTINUE_TIME;
						keys=(i*5)+4;//持续长按
					}
					
				}
				else
				{
					KeyStep[i]=KEY_STEP_WAIT;
					KeyContPressTimer[i]=KEY_PRESS_CONTINUE_TIME;
					keys=(i*5)+5;// 长按释放
				}
		break;
		}
		if(keys)
		{
			if(KeyScanCBS)
			{
				KeyScanCBS((KEY_VALUE_TYPEDEF)keys);//实现功能
			}
		}
	}
}


static void hal_keyConfig(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_AFIO, ENABLE);//复用功能时钟打开
	GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);  //禁用
	
	GPIO_InitStructure.GPIO_Pin = K1_PIN;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU; //上拉输入
	GPIO_Init(K1_PORT, &GPIO_InitStructure);
	
	 
	GPIO_InitStructure.GPIO_Pin = K2_PIN;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; 
	GPIO_Init(K2_PORT, &GPIO_InitStructure);
	
	 
	GPIO_InitStructure.GPIO_Pin = K3_PIN;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; 
	GPIO_Init(K3_PORT, &GPIO_InitStructure);
	 
	GPIO_InitStructure.GPIO_Pin = K4_PIN;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; 
	GPIO_Init(K4_PORT, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Pin = K5_PIN;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; 
	GPIO_Init(K5_PORT, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Pin = K6_PIN;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; 
	GPIO_Init(K6_PORT, &GPIO_InitStructure);
}



//读取按键状态,按下返回1,未按下返回0

static unsigned char hal_getKey1Sta(void)
{
	return (!GPIO_ReadInputDataBit(K1_PORT, K1_PIN));
}

static unsigned char hal_getKey2Sta(void)
{
	return (!GPIO_ReadInputDataBit(K2_PORT, K2_PIN));		
}

 
static unsigned char hal_getKey3Sta(void)
{
	return (!GPIO_ReadInputDataBit(K3_PORT, K3_PIN));		
}

static unsigned char hal_getKey4Sta(void)
{
	return (!GPIO_ReadInputDataBit(K4_PORT, K4_PIN));		
}

static unsigned char hal_getKey5Sta(void)
{
	return (!GPIO_ReadInputDataBit(K5_PORT, K5_PIN));		
}

static unsigned char hal_getKey6Sta(void)
{
	return (!GPIO_ReadInputDataBit(K6_PORT, K6_PIN));		
}

应用层调用函数

#include "app.h"
#include "hal_led.h"
#include "hal_key.h"

static void KeyEventHandle(KEY_VALUE_TYPEDEF keys);
void AppInit(void)
{
    hal_KeyScanCBSRegister(KeyEventHandle );
}
	
	
void AppProc(void)
{}

static void KeyEventHandle(KEY_VALUE_TYPEDEF keys)
{
	if((keys==KEY1_CLICK)
	|| (keys==KEY2_CLICK)
	|| (keys==KEY3_CLICK)
	|| (keys==KEY4_CLICK)
	|| (keys==KEY5_CLICK)
	|| (keys==KEY6_CLICK))
	{
		LedMsgInput(LED1,LED_LIGHT,1);//调用LED长亮接口
	}else if((keys==KEY1_CLICK_RELEASE)
	|| (keys==KEY2_CLICK_RELEASE)
	|| (keys==KEY3_CLICK_RELEASE)
	|| (keys==KEY4_CLICK_RELEASE)
	|| (keys==KEY5_CLICK_RELEASE)
	|| (keys==KEY6_CLICK_RELEASE))
	{
		LedMsgInput(LED1,LED_BLINK4,1);//LED闪烁
	}else if((keys==KEY1_LONG_PRESS)
	|| (keys==KEY2_LONG_PRESS)
	|| (keys==KEY3_LONG_PRESS)
	|| (keys==KEY4_LONG_PRESS)
	|| (keys==KEY5_LONG_PRESS)
	|| (keys==KEY6_LONG_PRESS))
	{
		LedMsgInput(LED1,LED_DARK,1);//LED熄灭
	}
}

这段代码中,为了实现按键的三种状态,实际上采用了一种类似状态机的编程方法。

在这里插入图片描述

用switch选择的方式,创建了四种状态,当条件成立后,会转入相应的状态中,执行相应代码。



版权声明:本文为jiangfengfeng222原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。