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);

同时允许主循环运行。

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

函数已经编写好了,我似乎无法想出如何跟踪和确认保持长度以根据正确的时间调用正确的函数,而无需停止其他必须保持循环的进程。

4

4 回答 4

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

于 2017-01-07T08:59:03.590 回答
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
}
于 2017-01-07T09:17:08.340 回答
2

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

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

确保您的按钮已消除抖动。

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

于 2017-01-06T22:52:39.263 回答
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();
}
于 2017-01-07T20:03:20.607 回答