首先说明一下我这个按键扫描函数的作用,我要实现的是长按按键就关闭MCU所有外设,即进入睡眠模式,睡眠之后可以使用这个按键进行唤醒MCU,那么这里就要考虑一个误唤醒的问题,所以思路是长按之后进行一个假睡眠(我这里的表现就是关闭小灯),松手之后才会真正意义上的执行睡眠指令,开启中断,进入睡眠模式。
对于准确的应用按键,那么首先要了解按键的几种状态:
①刚开始都是松开的;
②使用时由松开到按下;
③一直按下不松开;这里还涉及到是否要连续有效还是单次有效;
④由按下到松开;
⑤一直松开;
首先说明几个变量的作用:
1、Release_cnt:松手之后或者没有按下按键进行累加,用以按下按键松手之后的消抖;
2、Presse_cnt:按键按下进行累加,消抖作用;
3、Key1_Flag:按键按下标志,设置按键状态,防止重复触发的作用;
4、Key1_Pressed:标志位,为1表示按键确认按下了;
5、Key1_Function:也是一个标志位,为1去执行相应的操作;
6、Key1_Released:标志位:为1表示按键按下有效并且确认松手了;
下面是一个大概的流程图;
下面是具体的按键扫描函数,长按关机,松手有效,单次触发;
void KeyScan(void)
{
//单纯短按按键
if(!KEY1)
{
Release_cnt = 0;
if(!Key1_Flag)
{
Presse_cnt++;
if(Presse_cnt >= 1000) //1s长按防抖
{
Key1_Flag = 1; //设置按键状态,防止重复触发
Key1_Pressed = 1;
Key1_Function = 1;
}
}
}
else
{
if(Key1_Pressed)
{
Release_cnt++;
if(Release_cnt >= 50) //50ms释放防抖
{
Key1_Pressed = 0;
Key1_Released = 1;
}
}
Presse_cnt = 0;
Key1_Flag = 0;
}
}
下面是主函数里的while循环,Key1_Function这个标志长按不松手就置1,现象就是P20引脚电平拉高,熄灭小灯;松手之后Key1_Released置1,就开启中断进入睡眠;
while (1)
{
delay_ms(1);
KeyScan();
if(Key1_Function)
{
Key1_Function = 0;
printf("Sleep Ready.\r\n");
P20 = 1; //LED OFF(关闭外设,准备进入休眠)
}
if(Key1_Released)
{
Key1_Released = 0;
printf("MCU Sleep.\r\n");
IE0 = 0; //外中断0标志位
EX0 = 1; //INT0 Enable
IT0 = 1; //INT0 下降沿中断
// IT0 = 0; //INT0 上升,下降沿中断
_nop_();
_nop_();
PCON |= 0x02; //Sleep
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
printf("MCU Wakeup.\r\n");
P20 = 0; //LED ON
}
}
最后注意嗷,相应的判断标志在进入之后一定要清0。
-----------------------------------------------------------分割线-------------------------------------------------------------
下面是一个新的按键扫描函数,实际主体和上面思路是一样的,只是多了一个两个按键同时按下的操作,这里所说的定时其实可以使用变量累加来替换;
我猜测这个应该会有一个问题,也就是假如你同时按下的操作很慢然后松手,会执行单个按键短按的操作;
#include <stdbool.h>
#include <stdint.h>
#define SHORT_PRESS_TIME 100 // 短按阈值(单位:毫秒)
#define LONG_PRESS_TIME 1000 // 长按阈值(单位:毫秒)
typedef enum {
BUTTON_IDLE,
BUTTON_PRESSED,
BUTTON_SHORT_PRESS,
BUTTON_LONG_PRESS
} ButtonState;
typedef struct {
bool isPressed; // 当前按键是否被按下
uint32_t pressTime; // 按键按下时的时间
ButtonState state; // 按键的状态
} Button;
Button button1 = {false, 0, BUTTON_IDLE};
Button button2 = {false, 0, BUTTON_IDLE};
uint32_t getCurrentTime(); // 获取当前时间的函数,单位:毫秒
bool isButton1Pressed(); // 检测BUTTON1是否被按下
bool isButton2Pressed(); // 检测BUTTON2是否被按下
void handleShortPress(Button* btn);
void handleLongPress(Button* btn);
void handleSimultaneousPress();
void scanButtons()
{
uint32_t currentTime = getCurrentTime();
// 更新BUTTON1状态
if (isButton1Pressed())
{
if (!button1.isPressed)
{
button1.isPressed = true;
button1.pressTime = currentTime;
button1.state = BUTTON_PRESSED;
}
else if ((currentTime - button1.pressTime) >= LONG_PRESS_TIME)
{
button1.state = BUTTON_LONG_PRESS;
handleLongPress(&button1);
}
}
else
{
if (button1.isPressed)
{
if (button1.state == BUTTON_PRESSED && (currentTime - button1.pressTime) < LONG_PRESS_TIME)
{
button1.state = BUTTON_SHORT_PRESS;
handleShortPress(&button1);
}
button1.isPressed = false;
button1.state = BUTTON_IDLE;
}
}
// 更新BUTTON2状态
if (isButton2Pressed())
{
if (!button2.isPressed)
{
button2.isPressed = true;
button2.pressTime = currentTime;
button2.state = BUTTON_PRESSED;
}
else if ((currentTime - button2.pressTime) >= LONG_PRESS_TIME)
{
button2.state = BUTTON_LONG_PRESS;
handleLongPress(&button2);
}
}
else
{
if (button2.isPressed)
{
if (button2.state == BUTTON_PRESSED && (currentTime - button2.pressTime) < LONG_PRESS_TIME)
{
button2.state = BUTTON_SHORT_PRESS;
handleShortPress(&button2);
}
button2.isPressed = false;
button2.state = BUTTON_IDLE;
}
}
// 检查两个按键同时按下
if (button1.isPressed && button2.isPressed)
{
handleSimultaneousPress();
}
}
void handleShortPress(Button* btn) {
if (btn == &button1) {
// 执行BUTTON1短按操作
} else if (btn == &button2) {
// 执行BUTTON2短按操作
}
}
void handleLongPress(Button* btn) {
if (btn == &button1) {
// 执行BUTTON1长按操作
} else if (btn == &button2) {
// 执行BUTTON2长按操作
}
}
void handleSimultaneousPress() {
// 执行两个按键同时按下的操作
}
|