Понимание кода Debounce

Я не понимаю следующий код Debounce. (Полный код приведен внизу.) Насколько я понимаю, когда пин что-то считывает, мы ждем не менее 50 миллисекунд, прежде чем что-либо выполнить. Но разве переменная чтения не сбрасывается при каждой итерации цикла, поэтому чтение равно нулю во внутреннем условии if на каждой итерации.

Я думаю, это последовательность событий: loop(), loop(), loop(), "loop(), нажатие кнопки, чтение=1, установка lastDebounceTime, сначала, если условие не выполняется, потому что нам нужно ждать, set lastButtonState=1", loop(), loop(),loop(), "loop()" прошло достаточно времени, но поскольку ни одна кнопка не нажата в этой и последних итерациях, чтение и lastButtonState равны нулю", цикл (), ... .

Однако этот код работает.

Почему мы сохраняем чтение в локальной переменной? Тогда не будет ли код практически всегда таким:

if (reading != lastButtonState) {
    // сброс таймера устранения дребезга
    lastDebounceTime = millis();
  }

  if ((millis() - lastDebounceTime) > debounceDelay) {
    // каким бы ни было чтение, оно было там дольше, чем устранение дребезга
    // задержка, так что примите это как фактическое текущее состояние:

    // если состояние кнопки изменилось:
    if 0 != buttonState) {
      buttonState = 0;

...

Весь код:

/*
  Debounce

  Each time the input pin goes from LOW to HIGH (e.g. because of a push-button
  press), the output pin is toggled from LOW to HIGH or HIGH to LOW. There's a
  minimum delay between toggles to debounce the circuit (i.e. to ignore noise).

  The circuit:
  - LED attached from pin 13 to ground
  - pushbutton attached from pin 2 to +5V
  - 10 kilohm resistor attached from pin 2 to ground

  - Note: On most Arduino boards, there is already an LED on the board connected
    to pin 13, so you don't need any extra components for this example.

  created 21 Nov 2006
  by David A. Mellis
  modified 30 Aug 2011
  by Limor Fried
  modified 28 Dec 2012
  by Mike Walters
  modified 30 Aug 2016
  by Arturo Guadalupi

  This example code is in the public domain.

  http://www.arduino.cc/en/Tutorial/Debounce
*/

// константы не изменятся. Они используются здесь для установки номеров контактов:
const int buttonPin = 2;    // номер вывода кнопки
const int ledPin = 13;      // номер вывода светодиода

// Переменные изменятся:
int ledState = HIGH;         // текущее состояние выходного вывода
int buttonState;             // текущее чтение с входного вывода
int lastButtonState = LOW;   // предыдущее чтение с входного вывода

// следующие переменные являются беззнаковыми длинными, потому что время, измеренное в
// миллисекунды, быстро станет большим числом, чем может быть сохранено в int.
unsigned long lastDebounceTime = 0;  // последний раз, когда выходной пин был переключен
unsigned long debounceDelay = 50;    // время устранения дребезга; увеличить, если выход мерцает

void setup() {
  pinMode(buttonPin, INPUT);
  pinMode(ledPin, OUTPUT);

  // устанавливаем начальное состояние светодиода
  digitalWrite(ledPin, ledState);
}

void loop() {
  // прочитать состояние переключателя в локальную переменную:
  int reading = digitalRead(buttonPin);

  // проверяем, не нажали ли вы кнопку только что
  // (т.е. входной сигнал изменился с НИЗКОГО на ВЫСОКИЙ), и вы ждали достаточно долго
  // с момента последнего нажатия, чтобы игнорировать любой шум:

  // Если переключатель изменился из-за шума или нажатия:
  if (reading != lastButtonState) {
    // сброс таймера устранения дребезга
    lastDebounceTime = millis();
  }

  if ((millis() - lastDebounceTime) > debounceDelay) {
    // каким бы ни было чтение, оно было там дольше, чем устранение дребезга
    // задержка, так что примите это как фактическое текущее состояние:

    // если состояние кнопки изменилось:
    if (reading != buttonState) {
      buttonState = reading;

      // переключать светодиод только в том случае, если состояние новой кнопки ВЫСОКОЕ
      if (buttonState == HIGH) {
        ledState = !ledState;
      }
    }
  }

  // устанавливаем светодиод:
  digitalWrite(ledPin, ledState);

  // сохранить чтение. В следующий раз в цикле это будет lastButtonState:
  lastButtonState = reading;
}

(Код устранения отказов взят из: https://www.arduino.cc/en/tutorial/debounce.<!-- а-->)

, 👍1

Обсуждение

Что непонятного в комментариях? Кстати: последняя строка кода является частью алгоритма устранения дребезга., @DataFiddler

Ах, извини @jsotola. Кажется, я неправильно копирую/вставляю-d, @thegoodhunter-9115

@DataFiddler, я отредактировал вопрос. Я не понимаю часть алгоритма. Прости за это., @thegoodhunter-9115

подумайте об этом .... представьте, что у вас есть секундомер ... перед вами световой индикатор и кнопка ... свет включается и выключается через случайные промежутки времени ... ваша задача - нажимать кнопку, если состояние лампы не меняется в течение 10 секунд... как бы вы выполнили свою работу? ... алгоритм делает то же самое, @jsotola

Я снова отредактировал вопрос. Почему «чтение» является локальным?, @thegoodhunter-9115

потому что чтение должно существовать только в течение одной итерации loop(), @jsotola

@jsotola, разве он нам не нужен для итерации, когда завершается lastDebounceTime?, @thegoodhunter-9115

нет завершения lastDebounceTime, @jsotola

Давайте [продолжим это обсуждение в чате](https://chat.stackexchange.com/rooms/113017/discussion-between-jsotola-and-thegoodhunter-9115)., @jsotola


1 ответ


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

2

loop() не требует времени и постоянно повторяется.

lastButtonState читает из предыдущего цикла.

Пока кнопка отскакивает, lastDebounceTime обновляется.

Только когда кнопка зафиксирована (нажата или отпущена), условие

 ((millis() - lastDebounceTime) > debounceDelay)

становится true и код выполняется -> если buttonState (предыдущее состояние debounced) и reading показывают изменение на HIGH, состояние индикатора переключается.

Не уверен, что это объяснение делает что-то более ясным, так как оно и так ясно, ИМО.

,

Почему мы не проверяем, вместо этого lastButtonState показывает изменение на высокое? Чтение сбрасывается в каждом цикле () и практически всегда будет равно нулю (в моем ограниченном понимании)., @thegoodhunter-9115

Почему всегда 0? Это будет то, что написано на контакте. Каждый раз, когда цикл повторяется, он снова считывает вывод, и вы получаете новое чтение. Мы могли бы проверить lastButtonState или чтение, они будут одинаковыми в этом месте кода. Если они не совпадают, то lastDebounceTime устанавливается равным текущему времени, и мы не вводим этот второй оператор if., @Delta_G

Если цикл занимает микросекунду, а отскок занимает две миллисекунды, «чтение» будет оцениваться 2000 раз за эти 2 мс и изменяться пару раз в течение этого периода. Только если он стабилен в течение 50 мс (вы получили почти бесконечное количество одних и тех же значений чтения), вы принимаете это как новое состояние кнопки с отказом от дребезга., @DataFiddler

Я думаю, что это последовательность событий: loop(), loop(), loop(), "loop(), нажатие кнопки, чтение = 1, установка lastDebounceTime, сначала, если условие не выполняется, потому что мы должны ждать, установить lastButtonState = 1 ", loop(), loop(),loop(),"loop() прошло достаточно времени, но так как в этой и последних итерациях ни одна кнопка не нажата, чтение и lastButtonState равны нулю ", loop(), . .., @thegoodhunter-9115

@thegoodhunter-9115 серьезно подумай о том, что я сказал о свете и секундомере .... что бы ты сделал с секундомером, если бы свет включился или выключился?, @jsotola

@jsotola, кажется, я понял. Я (думаю) не мог этого понять из-за моего незнания того, насколько «длинным» является нажатие кнопки и сколько раз loop() будет выполняться во время одного нажатия. Просто чтобы убедиться: в течение 50 мс (debounceDelay) кнопка будет оставаться в нажатом состоянии пользователем (даже если это был просто щелчок), и в течение этого времени loop() будет выполняться несколько раз ( таким образом, он будет «запоминать», что такое чтение). Я думал, что 50 мс пройдут за один вызов loop(). Я все еще могу ошибаться в своем понимании, но большое спасибо за вашу помощь и извините за раздражение., @thegoodhunter-9115

@DataFiddler Большое спасибо за вашу помощь и терпение. Я объяснил, почему (я думаю) у меня возникло это недоразумение, в моем комментарии выше к jsotola (в этой теме). Еще раз спасибо., @thegoodhunter-9115

@ thegoodhunter-9115 спасибо за объяснение вашего недопонимания ... сегодня я кое-что узнал от вас ... теперь я знаю, что время выполнения цикла () может быть источником путаницы, @jsotola

@ thegoodhunter-9115, вы можете выполнить простой тест ... написать скетч, который в setup () печатает значение millis () и устанавливает счетчик на ноль .... в цикле () увеличивает счетчик ... когда счетчик достигает 1000, снова печатает millis() .... это даст вам представление о том, сколько примерно времени занимает 1000 итераций loop(), @jsotola

Хорошо. Еще раз спасибо, @jsotola., @thegoodhunter-9115