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

时间:2017-01-06 22:09:59

标签: button arduino esp8266 timed arduino-esp8266

由于arduino中esp8266的引脚不足,我需要一种方法来检测按钮在哪里;

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

preconfig;

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

同时允许主循环运行。

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

这些功能已经写好我似乎无法提出如何跟踪和确认保持长度以根据正确的时间调用正确的功能而不停止其他必须保持循环的过程。

4 个答案:

答案 0 :(得分:5)

使用中断是,恕我直言,过度杀伤。当您需要快速回复刺激时按下中断,按下按钮的速度很慢。除非你的循环阻塞,否则我强烈反对。

附加:正如帕特里克在评论中指出的那样,实际上还有另一个使用中断的理由:睡眠模式。实际上,如果您想进入睡眠模式并使用按钮唤醒,则必须使用中断稍后唤醒。但是通常你必须连续做一些事情而不仅仅是回复按钮输入。如果你不能进入睡眠模式,我认为使用中断进行按钮检测仍然有点过头了。

所以,如果你正确设计你的循环不要阻止,这里是我认为你应该实现的代码的简短部分:

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秒,它将触发贪睡功能)。

答案 1 :(得分:3)

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

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
}

答案 2 :(得分:2)

中断服务程序应该尽可能短。您无需在ISR内等待并暂停主循环几秒钟。

只需使用两个不同的ISR即可获得上升沿和下降沿。 当按下按钮时,ISR1启动一个计时器,当它被释放时,ISR2将其停止并根据所经过的时间触发任何必要的计时器。

确保您的按钮被去抖动。

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

答案 3 :(得分:1)

另一种方法是使用基于指针到函数的状态机。 这样做的好处是你可以轻松地为你的按钮引入更多功能(比如,另一个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();
}