在arduino中创建一个定时的3状态按钮



由于arduino中的esp8266上缺少引脚,我需要一种方法来检测其中的按钮;

momentary press runs snooze() 
15 sec press runs conf_Desk() 
30 sec press runs calibration()

预配置;

int buttonPin = D7;
pinMode( buttonPin , INPUT_PULLUP);

同时允许主循环发挥作用。

如果我捕获了一个中断,它会停止循环loop(),几毫秒的延迟是可以的,但秒的延迟太多了。

函数已经写好了,我似乎无法想出如何在不停止其他必须循环的过程的情况下跟踪和确认保持长度,以便在正确的时间调用正确的函数。

使用中断是过度使用。当你需要快速回复刺激时,就会出现中断,而按下按钮是很慢的。除非你的循环被阻塞,否则我强烈反对。

补充:正如Patrick在评论中指出的,使用中断实际上还有另一个原因:睡眠模式。事实上,如果你想进入睡眠模式并用按钮唤醒,你必须使用中断来稍后唤醒。然而,通常你必须不断地做一些事情,而不仅仅是回复按钮输入。如果你不能进入睡眠模式,在我看来,使用中断进行按钮检测仍然是过分的。

因此,如果你正确地设计了你的循环而不是阻塞,这里有一个简单的代码部分,我认为你应该实现:

uint8_t buttonState;
unsigned long lastPressTime;
void setup()
{
...
buttonState = digitalRead(buttonPin);
lastPressTime = 0;
}
void loop()
{
uint8_t currRead = digitalRead(buttonPin);
if (buttonState != currRead)
{ // Button transition
buttonState = currRead;
if (buttonState == LOW)
{ // Button pressed, start tracking
lastPressTime = millis();
}
else
{ // Button released, check which function to launch
if (lastPressTime < 100)
{} // Discard (it is just a bounce)
else if (lastPressTime < 15000)
snooze();
else if (lastPressTime < 30000)
conf_Desk();
else
calibration();
}
}
...
}

由于你做了三个非常遥远的间隔,我认为这部分更适合你的需求:

if ((lastPressTime > 100) && (lastPressTime < 7000))
snooze();
else if ((lastPressTime > 12000) && (lastPressTime < 20000))
conf_Desk();
else if ((lastPressTime > 26000) && (lastPressTime < 40000))
calibration();

所以你定义了有效性范围,所以如果有人按下按钮10秒,什么都不会发生(这很有用,因为如果有人在前一段代码中按下按钮14.9秒,就会触发打盹功能)。

我会使用一个带有两个全局变量的简单状态机结构来避免复杂的嵌套逻辑:

int buttonDown = 0;
unsigned long buttonStart;
void loop(){
int snapshot = digitalRead(buttonPin);
if(!buttonDown && snapshot ){ //pressed, reset time
buttonDown = 1; // no longer unpressed
buttonStart = millis(); // when it was pressed
}
if(buttonDown && !snapshot ){ //released, count time
buttonDown = 0; // no longer pressed
int duration = millis() - buttonStart; // how long since pressed?
// now the "event part"
if(duration>30000) return calibration();
if(duration>15000) return conf_Desk();
snooze();
}
sleep(1); // or whatever
}

中断服务例程应尽可能短。您不必在ISR内等待并暂停主循环数秒。

只需对上升沿和下降沿使用两个不同的ISR。当按下按钮时,ISR1启动计时器,当释放时,ISR2停止计时器,并根据所经过的时间触发任何必要的操作。

确保你的纽扣没有松动。

https://www.arduino.cc/en/Reference/attachInterrupt

另一种方法是使用指向基于函数的状态机的指针。这样做的好处是,你可以很容易地为你的按钮引入更多的功能(比如,在45秒时调用的另一个功能)。

试试这个:

typedef void(*state)();
#define pressed (millis() - lastPressed)
void waitPress();
void momentPress();
void shortPress();
void longPress();
state State = waitPress;
unsigned long lastPressed;
int buttonState;
int buttonPin = 7;// or whathever pin you use
void snooze(){} // stubs for your functions
void conf_Desk(){}
void callibration(){}
void waitPress()
{
if (buttonState == HIGH)
{
lastPressed = millis();
State = momentPress;
return;
}
else
return;
}
void momentPress()
{
if (buttonState == LOW)
{
snooze();
State = waitPress;
return;
}
if (pressed > 15000)
State = shortPress;
return;
return;
}
void shortPress()
{
if (buttonState == LOW)
{
conf_Desk();
return;
}
if (pressed > 30000)
State = longPress;
return;
return;
}
void longPress()
{
if (buttonState == LOW)
{
callibration();
return;
}
return;    
}
void loop() 
{
buttonState = digitalRead(buttonPin);
State();
}

相关内容

最新更新