создать кнопку с 3 состояниями в arduino с таймером

Из-за нехватки контактов на esp8266 в arduino мне нужен способ определить, где находится кнопка;

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

преконфигурация;

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

Все это позволяет основному циклу функционировать.

Если я перехватываю прерывание, оно прекращает цикл цикла (), задержки в несколько миллисекунд в порядке, но задержки в секундах слишком много.

Функции уже написаны, я просто не могу придумать, как отслеживать и подтверждать длину удержания, чтобы вызывать правильную функцию на основе правильного времени, не останавливая другой процесс, который должен продолжать цикл.


person user1213320    schedule 06.01.2017    source источник
comment
Спасибо вам всем. вы вытащили меня из своего рода блока писателей и дали мне отличный отзыв. -- далее я добавлю немного времени в примеры кода, чтобы увидеть, на каких скоростях они работают. -- Еще раз спасибо   -  person user1213320    schedule 08.01.2017
comment
Видя, что этот вопрос решен, но до сих пор нет принятого ответа, я добавлю его сюда. и выберите принятый ответ, который решит вашу проблему. Просто говорю ;)   -  person Patrick Trentin    schedule 08.01.2017
comment
Нет ничего необычного в том, чтобы дать правильный ответ и все равно проголосовать против   -  person Luiz Menezes    schedule 08.01.2017


Ответы (4)


Использование прерывания, ИМХО, излишне. Прерывания создаются, когда вам нужно быстро ответить на стимул, а нажатие кнопки — это что-то медленное. Если ваш цикл не блокируется, что я очень не одобряю.

ДОПОЛНЕНИЕ: как заметил Патрик в комментариях, на самом деле есть еще одна причина для использования прерываний: спящий режим. На самом деле, если вы хотите перейти в спящий режим и проснуться с помощью кнопки, вам придется использовать прерывания, чтобы проснуться позже. Однако обычно вам приходится постоянно что-то делать, а не только отвечать на нажатия кнопок. Если вы не можете перейти в спящий режим, использование прерывания для обнаружения кнопок, на мой взгляд, по-прежнему излишне.

Итак, если вы правильно спроектировали свой цикл, чтобы он не блокировался, вот краткая часть кода, выполняющая то, что, по моему мнению, вам следует реализовать:

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 секунд в предыдущем коде, это активирует функцию повтора).

person frarugi87    schedule 07.01.2017
comment
почему вы не рекомендуете блокировать цикл? не позволят ли прерывания переводить Arduino в спящий режим, эффективно блокируя цикл и пробуждая по требованию? - person Patrick Trentin; 07.01.2017
comment
@PatrickTrentin На самом деле я никогда не использовал спящий режим, так как обычно я не могу заснуть, потому что мне нужно постоянно выполнять действия (мониторинг датчиков, отображение вывода, подсчет времени ...), поэтому я даже не рассматривал Это. Но вы правы: прерывания можно использовать для обнаружения кнопок, когда вы можете перейти в спящий режим. Поэтому я добавил примечание в ответ. Благодарю вас! В любом случае, я не думаю, что ОП на самом деле нужен спящий режим (из названий, возможно, он делает будильник, и поэтому ему нужно постоянно выполнять действия) - person frarugi87; 08.01.2017
comment
и, кстати, под блокирующим циклом я подразумеваю цикл, который блокирует выполнение на некоторое время не из-за сна, а из-за длительных операций. Например, в примере с мерцанием есть то, что я называю блокирующим циклом, поскольку он блокирует выполнение цикла (и всей программы) на долгое время (2 с). Мгновение без задержки, с другой стороны, гораздо лучшая программа, так как она достигает того же результата, но функция цикла очень быстрая, и поэтому вы можете делать много других вещей параллельно. - person frarugi87; 08.01.2017
comment
Я понимаю, и спасибо за объяснение, я думаю, что оно очень хорошо объединяет ваш ответ. - person Patrick Trentin; 08.01.2017

я бы использовал простую структуру конечного автомата с двумя глобальными переменными, чтобы избежать сложной вложенной логики:

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
}
person dandavis    schedule 07.01.2017

Подпрограммы обслуживания прерываний должны быть как можно короче. Вам не нужно ждать внутри ISR и приостанавливать основной цикл на несколько секунд.

Просто используйте два разных ISR для нарастающего и спадающего фронта. Когда кнопка нажата, ISR1 запускает таймер, когда его отпускают, ISR2 останавливает его и запускает все необходимое в зависимости от прошедшего времени.

Убедитесь, что ваша кнопка отключена.

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

person Piglet    schedule 06.01.2017

Другой способ сделать это — использовать конечный автомат на основе указателя на функцию. Преимущество этого заключается в том, что вы можете легко добавить больше функций к своей кнопке (скажем, другую функцию, вызываемую через 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();
}
person Luiz Menezes    schedule 07.01.2017
comment
Мне было любопытно, что вы получили против, поэтому я прочитал ваш ответ и теперь думаю, что его можно улучшить. Я предлагаю вам прочитать этот документ по устранению дребезга кнопок. Кроме того, тесты lastPressed > N неверны, поскольку lastPressed равно millis(), а последний возвращает абсолютное время с момента загрузки, а не дельта-время с момента, когда кнопка начала нажиматься. Незначительные отрицания — это синтаксические ошибки и непоследовательное использование квадратных скобок. Кроме того, не могли бы вы поспорить с выбором использования указателей на функции? - person Patrick Trentin; 08.01.2017
comment
@PatrickTrentin последовал твоему совету. Поскольку это был код, он даже не компилировался... Я забыл, что arduino не принимает рекурсивный указатель на объявление функции, в отличие от компилятора, который я использую на POS-машинах, которые просто возвращают значение void*... Это должно работать. - person Luiz Menezes; 08.01.2017
comment
в нем все еще отсутствует дебаунс, но, похоже, он лучше +1 - person Patrick Trentin; 08.01.2017