Код проверки продолжительности нажатия кнопки
Я написал код, который отслеживает продолжительность нажатия кнопки. Имеет защиту от отскока и частые щелчки. (Заморозить)
Мой код:
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
В чем может быть проблема и как сделать ее отказоустойчивой?
@Delta, 👍3
Обсуждение1 ответ
Лучший ответ:
Почему таймеры перестали работать?
Основная проблема заключается в том, что 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
- Устранение дребезга кнопки с помощью прерывания
- Хорошая кнопка debouncing/Библиотека StateChange
- Кнопка с таймером переключения и функцией сброса времени + светодиод обратной связи
- Прерывание при нажатии кнопки + устранение дребезга
- Таймер Arduino с кнопкой увеличения времени
- Кнопка переключения переключает между операторами обращения с разблокированием кнопки
- Как сделать, чтобы светодиоды загорались один за другим в зависимости от того, как долго кнопка была нажата и удерживалась
- Как прочитать значение таймера после остановки?
(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