Код проверки продолжительности нажатия кнопки

Я написал код, который отслеживает продолжительность нажатия кнопки. Имеет защиту от отскока и частые щелчки. (Заморозить)

Мой код:

bool freeze_time, btn_read, debounce;
unsigned int freeze_timer, btn_timer;

#define BTN 6

void setup() {
  Serial.begin(9600);
  pinMode(BTN, INPUT);
}

void loop() {
  /* Freeze Timer Protection */
  if(freeze_time) {
    if((millis() - freeze_timer) >= 2500) {
      freeze_time = false;
      btn_read = false;
    }
  }
  /* Read Button State */
  if(digitalRead(BTN) == HIGH) {
    if(!btn_read) {
      btn_timer = millis();
      debounce = true;
      btn_read = true;
    }
  }
  /* BTN Read Process */  
  if(btn_read) {
    /* Debounce Protection */
    if(debounce) {
      if((millis() - btn_timer) >= 1000) {
        if(digitalRead(BTN) == HIGH) {
          Serial.println("Debounce ok.");
          btn_timer = millis();
          debounce = false;
        } else {
          Serial.println("Debounce fail.");
          freeze_time = true;
          debounce = false;
        }
      }
    }
    /* BTN Read Process */ 
    else if(!freeze_time) {
      if((millis() - btn_timer) >= 1500) {
        if(digitalRead(BTN) == HIGH) {
          Serial.println("Ok. Button Pressed > 1500ms");
          freeze_timer = millis();
          freeze_time = true;
        } else {
          Serial.println("Ok. Button Pressed < 1500ms");
          freeze_timer = millis();
          freeze_time = true;
        }
      }
    }
  }  
}

Я проверил это на реальном симуляторе Arduino UNO и Tinkercad. Изначально все работает хорошо, но если я буду нажимать кнопку бесконечно, система выйдет из строя (каждый раз при нажатии на последовательный порт появляются сообщения, как будто все флаги потеряны и таймеры не сработали. Я заснял поведение:

1) Первоначально: http://recordit.co/lVlR1z1CUq 2) Система неисправна: http://recordit.co/lhvzM100DI

В чем может быть проблема и как сделать ее отказоустойчивой?

, 👍3

Обсуждение

(millis() - btn_timer) >= 1000 Отскок переключателя не длится целой секунды. Однако вы можете нажать кнопку и отпустить ее в течение одной секунды. Попробуйте изменить его на более [разумное значение](http://www.ganssle.com/debouncing.htm), например «10». Остальная часть кода для меня не имеет смысла. Не могли бы вы объяснить, чего вы хотите достичь, поскольку похоже, что код слишком сложен, чтобы просто измерить, была ли кнопка нажата более или менее 1500 мс., @Gerben

голосование за публикацию правильно отформатированного кода, @jsotola

вы можете уменьшить количество строк кода, поместив повторяющиеся командные строки за пределы блоков if-else..... например, в последнем блоке if-else оставьте строки serial.println() внутри блок, но переместите freeze_timer = millis(); и freeze_time = true; за пределы блока, до или после блока (в данном случае это не имеет логического значения) ........ ... то же самое с debounce = false; в предыдущем блоке if-else, @jsotola

Используйте более краткую и надежную модель устранения дребезга (https://arduinoprosto.ru/q/33577/42061)., @Kelly S. French


1 ответ


Лучший ответ:

4

Почему таймеры перестали работать?

Основная проблема заключается в том, что freeze_timer и btn_timer имеют тип unsigned int, а millis, как указано в документации, возвращает unsigned long .

Как вы, возможно, знаете, unsigned int обычно может содержать гораздо меньший диапазон значений, чем unsigned long. Я проверил, какой бы компилятор Tinkercad ни использовал для Arduino Uno, unsigned int может хранить значения от 0 до 65535, а unsigned long может содержать значения от 0 до 4294967295. Каждый раз, когда вы сохраняете unsigned long в unsigned int, оно уменьшается по модулю 65536.

Это означает, что после того, как вы пройдете 65,536 секунд, такие проверки, как (millis() - Freeze_timer) >= 2500, всегда будут истинными (по крайней мере, до тех пор, пока millis не обернется обратно через 4294967,296 секунды — около 50 дней).

Если вы хотите узнать больше о целочисленных преобразованиях, я думаю, этот вопрос о переполнении стека покрывает это.

Самое простое решение — это, конечно, объявить freeze_timer и btn_timer как имеющие соответствующий тип, unsigned long.

>

Используйте более простой код

С учетом всего вышесказанного, как и сказал Гербен, код кажется слишком сложным.

Похоже, есть и другие проблемы. Например, я ожидаю, что поведение по устранению дребезга не должно игнорировать нажатия, которые длятся менее 1 секунды. Как сказал Гербен, 10 мс должно быть достаточно, чтобы исключить любые отскоки.

Более того, я думаю, было бы целесообразнее даже не отбрасывать нажатия длительностью менее 10 мс. Я бы сказал, что дебаунсер должен вызывать «нажатие» независимо от того, длилось ли нажатие менее 10 мс. Его роль должна заключаться в том, чтобы игнорировать множественные нажатия в течение этого 10-миллисекундного окна (хотя, честно говоря, игнорирование коротких нажатий не является такой уж большой проблемой).

Хотя можно сделать некоторые предположения, но чтобы действительно помочь вам в этом, нам понадобится описание вашего предполагаемого поведения. Какова цель заморозки? Предназначено ли оно для того, чтобы разрешить многократное нажатие, если вы никогда не отпускаете кнопку (или, может быть, вам следует подождать, пока она будет отпущена, прежде чем снова нажать кнопку)?

,

Работает! Спасибо!!!, @Delta