基于状态机的按键扫描实验

基于状态机的按键扫描实验

基于正点原子精英板(stm32zet6),实验一共实现三个按键的短按和长按,其中一个按键

配置为下拉模式,另外两个按键为上拉模式,原理图见下

视频可以去B站搜郭天祥老师

一、原理图

按键原理图

二、状态机逻辑

状态机逻辑图

三、代码实现

systick.c用于获取系统时钟

#ifndef __SYSTICK_H
#define __SYSTICK_H

#include "sys.h"

void SystickInit(void);
void SysTick_Handler(void);
uint64_t GetSysRunTime(void);

#endif

#include "systick.h"
#include <stdint.h>
#include "core_cm3.h"


static uint64_t g_sysRunTime=0;

/**
  * @brief	systick初始化
  * @param  
  * @retval 
  */
void SystickInit(void)
{
	
	/*系统嘀嗒校准值固定为 9000,当系统嘀嗒时钟设定为 9MHz ( HCLK/8 的最大值),产生 1ms 时间基准。*/
	/*1ms产生一次中断*/
	if(SysTick_Config(SystemCoreClock/8/1000))// 系统时钟8分频产生1秒的时基,在除以1000产生1毫秒时基
	{
		while(1);
	}
}	


/**
  * @brief	定时中断服务函数,1ms产生一次中断
  * @param  
  * @retval 
  */
void SysTick_Handler(void)
{
	g_sysRunTime++;
}


/**
  * @brief	获取系统运行时间
  * @param  
  * @retval 已1ms为单位
  */
uint64_t GetSysRunTime(void)
{
	return g_sysRunTime;
}


key.c实现按键扫描

#ifndef __KEY_H
#define __KEY_H

#include "sys.h"

#define KEY0	0
#define KEY1	1
#define KEY_UP	2

//长按短按的码值
#define KEY0_SHORT_PRESS		0x01
#define KEY0_LONG_PRESS			0x81

#define KEY1_SHORT_PRESS		0x02
#define KEY1_LONG_PRESS			0x82

#define KEY_UP_SHORT_PRESS		0x03
#define KEY_UP_LONG_PRESS		0x83


void KeyInit(void);
uint8_t GetKeyVal(void);

#endif

#include "key.h"
#include "systick.h"

/**
  * @brief	按键硬件初始化
  * @param  
  * @retval 
  */
void KeyInit(void)
{
	GPIO_InitTypeDef GPIO_InitStruct;
	 
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOE,ENABLE);
	
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPU;
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_3|GPIO_Pin_4;
	GPIO_Init(GPIOE,&GPIO_InitStruct);
	 
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPD;
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_0;
	GPIO_Init(GPIOA,&GPIO_InitStruct);

} 


#define CONFIRM_TIME 10 	//按键消抖时间窗10ms
#define LONGPRESS_TIME 1000	//长按时间窗1S

typedef enum
{
	KEY_RELEASE=0,	//释放松开
	KEY_CONFIRM,	//消抖确认
	KEY_SHORTPRESS,	//短按
	KEY_LONGPRESS	//长按
}KEY_STATE;

typedef struct
{
	KEY_STATE keyState;
	uint64_t prvSysTime;
}Key_Info_t;

static Key_Info_t g_keyInfo[3];

/**
  * @brief	按键扫描函数
  * @param  
  * @retval 返回码值
  */
static uint8_t KeyScan(uint8_t keyIndex)
{
	uint8_t keypress;
	uint64_t curSysTime;
	
	if(keyIndex==KEY0)
		keypress=GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4);
	else
		if(keyIndex==KEY1)
			keypress=GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3);
		else
			keypress=GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0);
	
	switch(g_keyInfo[keyIndex].keyState)
	{
		case KEY_RELEASE ://按键释放状态
			if(keyIndex==KEY_UP)
			{
				if(keypress)
				{
					g_keyInfo[keyIndex].keyState=KEY_CONFIRM;
					g_keyInfo[keyIndex].prvSysTime=GetSysRunTime();
				}
			}
			else
			{
				if(!keypress)
				{
					g_keyInfo[keyIndex].keyState=KEY_CONFIRM;
					g_keyInfo[keyIndex].prvSysTime=GetSysRunTime();
				}
			}
			break;
		case KEY_CONFIRM ://按键按下确认状态
			if(keyIndex==KEY_UP)
			{
				if(keypress)
				{
					curSysTime=GetSysRunTime();
					if(curSysTime-g_keyInfo[keyIndex].prvSysTime > CONFIRM_TIME)
					{
						g_keyInfo[keyIndex].keyState=KEY_SHORTPRESS;
					}
				}
				else
				{
					g_keyInfo[keyIndex].keyState=KEY_RELEASE;
				}
			}
			else
			{
				if(!keypress)
				{
					curSysTime=GetSysRunTime();
					if(curSysTime-g_keyInfo[keyIndex].prvSysTime > CONFIRM_TIME)
					{
						g_keyInfo[keyIndex].keyState=KEY_SHORTPRESS;
					}
				}
				else
				{
					g_keyInfo[keyIndex].keyState=KEY_RELEASE;
				}
			}
			break;
		case KEY_SHORTPRESS ://按键短按状态
			if(keyIndex==KEY_UP)
			{
				if(!keypress)
				{
					g_keyInfo[keyIndex].keyState=KEY_RELEASE;
					return (keyIndex+1); //返回按键码值
				}
				else
				{
					curSysTime=GetSysRunTime();
					if(curSysTime-g_keyInfo[keyIndex].prvSysTime > LONGPRESS_TIME)
					{
						g_keyInfo[keyIndex].keyState=KEY_LONGPRESS;
					}
				}
			}
			else
			{
				if(keypress)
				{
					g_keyInfo[keyIndex].keyState=KEY_RELEASE;
					return (keyIndex+1); //返回短按按键码值
				}
				else
				{
					curSysTime=GetSysRunTime();
					if(curSysTime-g_keyInfo[keyIndex].prvSysTime > LONGPRESS_TIME)
					{
						g_keyInfo[keyIndex].keyState=KEY_LONGPRESS;
					}
				}
			}
			break;
		case KEY_LONGPRESS ://按键长按状态
			if(keyIndex==KEY_UP)
			{
				if(!keypress)
				{
					g_keyInfo[keyIndex].keyState=KEY_RELEASE;
					return (0x83); //返回长按按键码值
				}
			}
			else
			{
				if(keypress)
				{
					g_keyInfo[keyIndex].keyState=KEY_RELEASE;
					return (keyIndex+0x81); //返回长按按键码值
				}
			}
			break;
		default :
			g_keyInfo[keyIndex].keyState=KEY_RELEASE;break;
	}
	return 0;
}

/**
  * @brief	获取按键码值
  * @param  
  * @retval 三个按键码值,短按0x01,0x02,0x03,长按0x81,0x82,0x83,没有按下为0
  */
uint8_t GetKeyVal(void)
{
	uint8_t res=0,i;
	
	for(i=0;i<3;i++)
	{
		res=KeyScan(i);
		if(res != 0)
		{
			break;
		}
	}
	return res;
}

main.c验证实验现象

别忘了滴答时钟初始化SystickInit()

#include "sys.h"
#include "led.h"
#include "key.h"
#include "systick.h"

int main(void)
{	
	uint8_t keyval;
	
	SystickInit(); 		//别忘了滴答时钟初始化  
	LED_Init();		  	//初始化与LED连接的硬件接口
	KeyInit();
	
	while(1)
	{
		keyval=GetKeyVal();
		switch(keyval)
		{
			case KEY_UP_SHORT_PRESS : LED0=0;break; //led亮
			case KEY_UP_LONG_PRESS : LED0=1; break;
			
			case KEY1_SHORT_PRESS : LED1=0;break;
			case KEY1_LONG_PRESS : LED1=1; break;
		}
	}
}
 
  • 6
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值