Определение того, была ли нажата и отпущена кнопка
Я хочу определить, была ли нажата и отпущена кнопка. Поэтому я подумал, что правильным подходом будет сначала подождать, пока пин не выдаст LOW
, а затем подождать, пока пин не выдаст HIGH
:
void push(int pin) {
// ждем, пока кнопка не будет нажата...
while (digitalRead(pin) == LOW);
// ... и снова отпущен
while (digitalRead(pin) == HIGH);
}
Эту функцию можно легко использовать повторно. Код работает отлично, но мне просто интересно, считается ли это решение чистым и хорошей практикой.
@Patrick Bucher, 👍4
Обсуждение3 ответа
Лучший ответ:
Нет, я бы не считал это хорошей практикой — по одной веской причине: это блокирует.
Ваша система не может делать ничего другого, пока вы ждете кнопку. Это может быть хорошо для некоторых ситуаций, но далеко не идеально для 99% других.
Лучше запомнить состояние кнопки и обнаружить это изменение состояния.
Лично я бы, чтобы сделать его должным образом переносимым, реализовал класс, содержащий всю информацию о состоянии. Что-то вроде:
class Button {
private:
bool _state;
uint8_t _pin;
public:
Button(uint8_t pin) : _pin(pin) {}
void begin() {
pinMode(_pin, INPUT_PULLUP);
_state = digitalRead(_pin);
}
bool isReleased() {
bool v = digitalRead(_pin);
if (v != _state) {
_state = v;
if (_state) {
return true;
}
}
return false;
}
};
Тогда вы можете сделать что-то вроде:
Button myButton(3);
void setup() {
myButton.begin();
Serial.begin(115200);
}
void loop() {
if (myButton.isReleased()) {
Serial.println(F("Released"));
}
}
Каждый раз, когда вызывается myButton.isReleased()
, текущее состояние кнопки сравнивается с состоянием, в котором она была в последний раз, когда она была вызвана. Если оно изменилось, то кнопка должна была быть либо нажата, либо отпущена. Таким образом, вы запоминаете это изменение. Если она была отпущена, то вы возвращаете true
, в противном случае возвращаете false
.
Теперь вы можете легко заставить его работать со многими кнопками:
Button myButton(3);
Button myOtherButton(4);
void setup() {
myButton.begin();
myOtherButton.begin();
Serial.begin(115200);
}
void loop() {
if (myButton.isReleased()) {
Serial.println(F("Released 1"));
}
if (myOtherButton.isReleased()) {
Serial.println(F("Released 2"));
}
}
Я согласен с Majenko, хотя ваш код будет работать, код блокирующий, то есть ваш код останавливается в этой точке, пока кнопка не будет отпущена снова. Оставшийся код в вашем цикле не выполняется, пока кнопка не будет разрешена.
Гораздо лучший метод — проверять состояние кнопки при каждом прохождении цикла. Если состояние меняется (т. е. становится низким), вы можете установить флаг. Затем, когда вы считываете, что кнопка снова находится в высоком состоянии и флаг был установлен, вы выполняете соответствующий код. Например:
const int buttonPin = 2; // номер контакта кнопки
boolean buttonWasLow = false; // переменный флаг для случая, когда кнопка переходит в состояние низкого уровня
void setup() {
pinMode(buttonPin, INPUT); // инициализируем вывод кнопки как вход:
}
void loop() {
// считываем состояние кнопки и устанавливаем флаг, если оно низкое:
if (digitalRead(buttonPin) == LOW) {
buttonWasLow = true;
}
// Этот оператор if сработает только по переднему фронту сигнала нажатия кнопки
if (digitalRead(buttonPin) == HIGH && buttonWasLow) {
// сбросить флаг низкого уровня кнопки
buttonWasLow = false;
// Событие кнопки здесь
}
}
Мой любимый способ обнаружения нарастающего фронта входного сигнала на самом деле гораздо проще:
void loop() {
button = digitalRead(buttonPin);
// Если кнопка нажата
if (button && !buttonLast)
{
// Событие кнопки здесь
}
// Обновить флаг кнопки
buttonLast = button;
}
user8886193 поднимает очень хорошую тему о устранении дребезга кнопок.
Дрожание — это тенденция любых двух металлических контактов в электронном устройстве генерировать несколько сигналов при замыкании или размыкании контактов; устранение дребезга — это любой вид аппаратного устройства или программного обеспечения, который гарантирует, что только один сигнал будет обработан при одном размыкании или замыкании контакта.
Я предпочитаю устранять дребезг входов кнопок аппаратно, добавляя 100 нФ (или больше) от входного контакта к земле. Обратите внимание, что для этого требуется сопротивление 10 кОм (или больше) последовательно с цепью кнопки, чтобы конденсатор заряжался/разряжался.
В качестве альтернативы, в программном обеспечении наиболее распространенным способом устранения дребезга для новичков является добавление задержки, например delay(100);
, которая будет выглядеть примерно так:
void loop() {
button = digitalRead(buttonPin);
// Если кнопка нажата
if (button && !buttonLast)
{
// Событие кнопки здесь
delay(100); // <---- Задержка вставлена после события кнопки
}
// Обновить флаг кнопки
buttonLast = button;
}
Недостатком этого является то, что он блокируется на 100 мс, пока выполняется задержка. Лучший метод — устранить дребезг без задержки. Один из встроенных примеров демонстрирует устранение дребезга кнопки. Основной цикл показан ниже:
void loop() {
// считываем состояние переключателя в локальную переменную:
int reading = digitalRead(buttonPin);
// проверьте, нажали ли вы только что кнопку
// (т.е. входной сигнал перешел из LOW в HIGH), и вы ждали достаточно долго
// с момента последнего нажатия, чтобы игнорировать любой шум:
// Если переключатель изменился из-за шума или нажатия:
if (reading != lastButtonState) {
// сбросить таймер устранения дребезга
lastDebounceTime = millis();
}
if ((millis() - lastDebounceTime) > debounceDelay) {
// независимо от показаний, они существуют дольше, чем дребезг
// задержка, поэтому принимаем ее как фактическое текущее состояние:
// если состояние кнопки изменилось:
if (reading != buttonState) {
buttonState = reading;
// переключать светодиод только если новое состояние кнопки HIGH
if (buttonState == HIGH) {
ledState = !ledState;
}
}
}
// устанавливаем светодиод:
digitalWrite(ledPin, ledState);
// сохранить показания. В следующий раз в цикле это будет lastButtonState:
lastButtonState = reading;
}
«Я обычно ставлю конденсатор рядом с кнопкой»
Конденсатор емкостью 1 мкФ на кнопке — это просто, дешево и не нужно возиться с кодами устранения дребезга. См. отличное руководство по переключателям Ника Гэммона, которое охватывает практически любую мыслимую ситуацию при использовании переключателей с Arduino и клонами (высокий или низкий уровень, GND или +5 В, внутренняя подтяжка вверх или вниз, устранение дребезга конденсатора или программное устранение дребезга и т. д.). В любом случае, у Ника Гэммона есть много замечательных пояснительных примеров на его сайте и в других местах, где он комментирует в сети.
+1 за ссылку на урок Ника Гэммона, он действительно отличный!, @Kelly S. French
- Хорошая кнопка debouncing/Библиотека StateChange
- Остановить мигание светодиодов
- Интеграция 2 кнопок для включения и выключения светодиода.
- Код Arduino для управления 4 светодиодами с 4 кнопок
- Нужен ли подтягивающий/понижающий резистор для цепи светодиода кнопки?
- Как прервать функцию цикла и перезапустить ее?
- Как перезапустить счетчик в программе с помощью кнопки в настройке LDR Tripwire
- Кнопочная матрица / схемы подключения
Вы можете спросить Google, как они решают эту проблему на языке C с помощью микроконтроллеров. Для поиска вы можете использовать: микроконтроллер с дребезгом кнопок или микроконтроллер с изменением состояния кнопок. Я думаю, что решение выше хорошо только тогда, когда вы запускаете эту функцию только один раз. В этом случае изменение состояния (дребезг) ничего не даст. Например, если вы хотите подсчитать нажатия, найдите другое решение., @user8886193
он поддерживает только одну кнопку, так что это довольно слабо., @dandavis