stm32高效长短按检测--基于状态机实现
·
文章目录
- 前言
- 一、电路图和控制逻辑:
- 二、状态机思路:
-
- 1、状态机代码实现:Key.c
- 2.按键状态机头文件 Key.h
- 3.滴答定时器配置
- 4、主函数main
前言
最近在做关于裸机开发的一个stm32按键检测小项目,总结了一种可以快速检测多个按键长短按的思路:使用状态机,消抖使用滴答定时器中断,可以实现非阻塞式消抖。
一、电路图和控制逻辑:

KEY1短按LED1切换状态,长按LED1和LED2均点亮
KEY2短按LED2切换状态,长按LED1和LED2均熄灭
二、状态机思路:
定义几个变量:
①开关引脚电平记为 KEY_IN ,高电平松开,低电平按下。
②时间戳:定义于SysTick.c变量tick ,滴答定时器每1ms进中断,tick自增1。
每次进入按键扫描函数KEY_Scan()都获取当前时间戳tick(current_tick),在状态转换中仅需要判断按键的输入电平、当前状态时间戳current_tick与上一状态时间戳last_tick的差值Δtick,即可进行下一状态的状态的判断。
1、状态机代码实现:Key.c
两个开关的GPIO都配置为上拉输入,按键松开(默认状态)下为高电平。
#include "stm32f10x.h" // Device header
#include "Key.h"
#include "SysTick.h"
#include "stdio.h"
//按键状态
typedef enum
{
KEY_STATE_RELEASED,//按键松开
KEY_STATE_DEBOUNCE,//按键消抖
KEY_STATE_PRESSED,//按键确认按下
KEY_STATE_LONGPRESS//按键长按
}KEY_State;
//按键结构体
typedef struct
{
GPIO_TypeDef* GPIOx;//按键所在GPIO
uint16_t GPIO_Pin;//按键所在引脚
KEY_State state;//按键当前状态
uint16_t last_tick;//上次状态变化的时间戳
}Key;
#define DEJITTED_TIME 30 //消抖时间30ms
#define KEY_LONG_TIME 500//长按时间500ms
Key key1 = {GPIOB,GPIO_Pin_13,KEY_STATE_RELEASED,0};
Key key2 = {GPIOB,GPIO_Pin_15,KEY_STATE_RELEASED,0};
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 = key1.GPIO_Pin | key2.GPIO_Pin;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
//按键状态机 2:按键长按 1:按键确认按下 0:按键松开
uint8_t KEY_Scan(Key *key)
{
uint8_t KEY_IN = GPIO_ReadInputDataBit(key->GPIOx,key->GPIO_Pin);
uint32_t current_tick = Get_tick();
switch(key->state)
{
case KEY_STATE_RELEASED://按键松开
if(KEY_IN == 0)//检测到低电平
{
key->state = KEY_STATE_DEBOUNCE;//进入按键消抖
key->last_tick = current_tick;
}
break;
case KEY_STATE_DEBOUNCE://消抖检测
if((current_tick - key->last_tick) >=DEJITTED_TIME)//如果消抖超过30ms,再次读取端口电平
{
if(KEY_IN == 0)//低电平(默认上拉输入),有电平按下
{
key->state = KEY_STATE_PRESSED;
}
else
key->state = KEY_STATE_RELEASED;//扔进入按键消抖
}
break;
case KEY_STATE_PRESSED://按键稳定按下
if(KEY_IN == 1) //端口高电平,按键松开
{
key->state = KEY_STATE_RELEASED;
return 1;
}
else if((current_tick-key->last_tick >= KEY_LONG_TIME)&&KEY_IN == 0)//长按
{
key->state = KEY_STATE_LONGPRESS;
return 2;
}
break;
case KEY_STATE_LONGPRESS://长按已触发
if(KEY_IN == 1) //端口高电平,按键松开
{
key->state = KEY_STATE_RELEASED;
}
break;
}
return 0;
}
uint8_t KEY1_State(void)
{
return KEY_Scan(&key1);
}
uint8_t KEY2_State(void)
{
return KEY_Scan(&key2);
}
2.按键状态机头文件 Key.h
#ifndef __KEY_H
#define __KEY_H
#include "stm32f10x.h" // Device header
void Key_Init(void);
uint8_t KEY1_State(void);
uint8_t KEY2_State(void);
#endif
3.滴答定时器配置
- SysTick.c
#include "stm32f10x.h" // Device header
volatile uint32_t tick;//时间戳定义
void SysTick_Init(void)
{
if(SysTick_Config(SystemCoreClock/1000))//1ms中断
{
while(1);
}
}
void SysTick_Handler(void)
{
tick++;
if(tick>=5000) tick = 0;//每5秒自动清零,防止定时器溢出
}
uint32_t Get_tick(void)
{
return tick;
}
- SysTick.h
#include "stm32f10x.h" // Device header
uint32_t Get_tick(void);
void SysTick_Init(void);
4、主函数main
KEY1短按LED1切换状态,长按LED1和LED2均点亮
KEY2短按LED2切换状态,长按LED1和LED2均熄灭
#include "stm32f10x.h" // Device header
#include "OLED.h"
#include "Key.h"
#include "SysTick.h"
#include "LED.h"
uint32_t count;
uint8_t KEY1,KEY2;
int main(void)
{
OLED_Init();
SysTick_Init();
Key_Init();
LED_Init();
OLED_ShowString(1, 1, "HelloWord:");
while (1)
{
KEY1 = KEY1_State();
KEY2 = KEY2_State();
if(KEY1 == 1) LED1_Turn();
if(KEY2 == 1) LED2_Turn();
if(KEY1 == 2)
{
LED1_ON();
LED2_ON();
}
if(KEY2 == 2)
{
LED1_OFF();
LED2_OFF();
}
}
}
演示
更多推荐
所有评论(0)