学习到了一个新的更好的按键代码
代码取自b站up:lil_jx
【STM32开源模块】更好的按键(单击+双击+长按)_哔哩哔哩_bilibili
Key.c:
#include "stm32f10x.h" // Device header
#include "Delay.h"
#define LONG_PRESS_TIME 1000 // 长按时间阈值,单位ms
#define CONTINUOUS_PRESS_INTERVAL 100 // 长按后连续返回按键键码的时间间隔,单位ms
#define DOUBLE_CLICK_TIME 300 // 双击时间阈值,单位ms
/*
函数: 按键初始化,使用引脚PB0,PB1,PB10,配置为上拉输入
参数: 无
返回: 无
注意: 在主循环前调用
*/
void Key_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); // 开启GPIOB时钟
GPIO_InitTypeDef GPIO_InitStructure; // 定义GPIOB初始化结构体
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // GPIOB上拉输入模式
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_10 | GPIO_Pin_11; // 使用GPIOB引脚0、1、10
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure); // 初始化GPIOB
}
/*
函数: 按键扫描
参数: 无
返回: 按键对应的键码值,可在此修改
注意: 主函数无需调用
*/
uint8_t Key_Scan(void)
{
if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)
{
return 1;
}
else if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_10) == 0)
{
return 2;
}
else if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0)
{
return 3;
}
return 0;
}
/*
函数: 扫描按键并获取键码值
参数: 无
返回: 按键对应键码值。双击按键返回值 = 键码值 + 10(例如:双击1返回11,双击2返回12,双击3返回13)
注意: 放在主函数主循环中
*/
uint8_t Key_GetNum(void)
{
static uint32_t PressTime = 0; // 静态变量保持其值
static uint32_t LastReleaseTime = 0; // 上次按键松开时间
static uint8_t LastKeyNum = 0; // 上次按键的键码
uint8_t KeyNum = 0; // 默认键码为0
static uint32_t CurrentTime = 0; // 当前时间,模拟一个系统计时器
KeyNum = Key_Scan();
if (KeyNum != 0)
{
while(Key_Scan() == KeyNum)
{
Delay_ms(10);
PressTime += 10;
CurrentTime += 10;
if (PressTime >= LONG_PRESS_TIME)
{
// 长按1秒后,连续返回按键键码
while (Key_Scan() == KeyNum)
{
Delay_ms(CONTINUOUS_PRESS_INTERVAL);
return KeyNum;
}
}
}
if (PressTime >= 20 && PressTime < LONG_PRESS_TIME)
{
// 短按,去抖动时间为20ms
if ((LastKeyNum == KeyNum) && ((CurrentTime - LastReleaseTime) < DOUBLE_CLICK_TIME))
{
// 检测到双击
LastKeyNum = 0; // 重置LastKeyNum
LastReleaseTime = 0; // 重置LastReleaseTime
PressTime = 0; // 重置PressTime
return KeyNum + 10; // 返回双击键码(例如:双击1返回11,双击2返回12,双击3返回13)
}
else
{
LastKeyNum = KeyNum; // 记录本次按键的键码
LastReleaseTime = CurrentTime; // 记录本次按键松开的时间
PressTime = 0; // 重置PressTime
return KeyNum; // 返回单击键码
}
}
}
else
{
PressTime = 0; // 按键松开时重置PressTime
CurrentTime += 10; // 模拟系统时间的增加
}
return 0;
}
代码对我很新颖,很多地方值得我学习
按键捕获部分代码:
uint8_t Key_Scan(void)
{
if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)
{
return 1;
}
else if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_10) == 0)
{
return 2;
}
else if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0)
{
return 3;
}
return 0;
}c
通过GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_x)来监测按键是否被按下,并返回相应的值,若按键均是释放状态,函数就会返回0
返回键码部分:
uint8_t Key_GetNum(void)
{
static uint32_t PressTime = 0; // 静态变量保持其值
static uint32_t LastReleaseTime = 0; // 上次按键松开时间
static uint8_t LastKeyNum = 0; // 上次按键的键码
uint8_t KeyNum = 0; // 默认键码为0
static uint32_t CurrentTime = 0; // 当前时间,模拟一个系统计时器
KeyNum = Key_Scan();
if (KeyNum != 0)
{
while(Key_Scan() == KeyNum)
{
Delay_ms(10);
PressTime += 10;
CurrentTime += 10;
if (PressTime >= LONG_PRESS_TIME)
{
// 长按1秒后,连续返回按键键码
while (Key_Scan() == KeyNum)
{
Delay_ms(CONTINUOUS_PRESS_INTERVAL);
return KeyNum;
}
}
}
if (PressTime >= 20 && PressTime < LONG_PRESS_TIME)
{
// 短按,去抖动时间为20ms
if ((LastKeyNum == KeyNum) && ((CurrentTime - LastReleaseTime) < DOUBLE_CLICK_TIME))
{
// 检测到双击
LastKeyNum = 0; // 重置LastKeyNum
LastReleaseTime = 0; // 重置LastReleaseTime
PressTime = 0; // 重置PressTime
return KeyNum + 10; // 返回双击键码(例如:双击1返回11,双击2返回12,双击3返回13)
}
else
{
LastKeyNum = KeyNum; // 记录本次按键的键码
LastReleaseTime = CurrentTime; // 记录本次按键松开的时间
PressTime = 0; // 重置PressTime
return KeyNum; // 返回单击键码
}
}
}
else
{
PressTime = 0; // 按键松开时重置PressTime
CurrentTime += 10; // 模拟系统时间的增加
}
return 0;
}
代码首先定义了几个变量,KeyNum = Key_Scan()这里进行第一次赋值,当KeyNum不为0,即有任意按键被按下,继续执行判断内部代码
while(Key_Scan() == KeyNum)
{
Delay_ms(10);
PressTime += 10;
CurrentTime += 10;
if (PressTime >= LONG_PRESS_TIME)
{
// 长按1秒后,连续返回按键键码
while (Key_Scan() == KeyNum)
{
Delay_ms(CONTINUOUS_PRESS_INTERVAL);
return KeyNum;
}
}
}
先Key_Scan() == KeyNum判断按键是否被长按,一旦长按PressTime和CurrentTime就会持续增加,当长按时间大于1000ms(PressTime >= LONG_PRESS_TIME)则每经过0.1s返回一次键码
if (PressTime >= 20 && PressTime < LONG_PRESS_TIME)
{
// 短按,去抖动时间为20ms
if ((LastKeyNum == KeyNum) && ((CurrentTime - LastReleaseTime) < DOUBLE_CLICK_TIME))
{
// 检测到双击
LastKeyNum = 0; // 重置LastKeyNum
LastReleaseTime = 0; // 重置LastReleaseTime
PressTime = 0; // 重置PressTime
return KeyNum + 10; // 返回双击键码(例如:双击1返回11,双击2返回12,双击3返回13)
}
else
{
LastKeyNum = KeyNum; // 记录本次按键的键码
LastReleaseTime = CurrentTime; // 记录本次按键松开的时间
PressTime = 0; // 重置PressTime
return KeyNum; // 返回单击键码
}
}
这段代码用于单击和双击的判断,当按键时间不超过设定的长按时间时,进行短按判断,若两次短按都按同一按钮(LastKeyNum == KeyNum)且两次按的时间间隔小于双击时间间隔((CurrentTime - LastReleaseTime) < DOUBLE_CLICK_TIME),则清除状态并返回双击键码
main.c:
#include "stm32f10x.h" // Device header
#include "OLED.h"
#include "Key.h"
#include "Delay.h"
int main(void)
{
/*初始化*/
OLED_Init();
Key_Init();
/*定义变量*/
uint8_t KeyNum; //键码
uint8_t Count = 0; //单击和长按计数
uint8_t DoubleClickCount = 0; //双击计数
/*主循环*/
while(1)
{
KeyNum = Key_GetNum(); //获取键码
//单击和长按
if(KeyNum == 1)
{
Count++;
}
else if(KeyNum == 3)
{
Count--;
}
else if(KeyNum == 2)
{
Count = 0;
}
//双击
else if(KeyNum == 11)
{
DoubleClickCount++;
}
else if(KeyNum == 13)
{
DoubleClickCount--;
}
else if(KeyNum == 12)
{
DoubleClickCount = 0;
}
//OLED显示
OLED_ShowChinese(0,0,"单击,长按");
OLED_ShowNum(88,0,Count,3,OLED_8X16);
OLED_ShowChinese(0,20,"双击");
OLED_ShowNum(88,20,DoubleClickCount,3,OLED_8X16);
OLED_Update();
}
}
main里没什么好说的,就一个计数器和oled显示