Урок 6. Обработка дребезга контактов кнопки. Интерфейс связи между программными блоками.

Arduino UNO R3

В этом уроке научимся обрабатывать сигнал кнопки для устранения дребезга контактов.

Предыдущий урок     Список уроков     Следующий урок

В прошлом уроке мы написали простую программу для управления светодиодом с помощью кнопки. Нажата – светодиод светится, отжата - не светится. Наверное, вы решили, что кнопка очень простой компонент и работать с ней несложно. Считали состояние кнопки – что-то сделали. Но давайте напишем другую программу управления светодиодом.

 

Программа управления светодиодом.

Программа должна при каждом нажатии кнопки менять состояние светодиода, т.е. включать и выключать его. В этой программе надо:

  • считать состояние кнопки;
  • сравнить его с предыдущим состоянием;
  • если предыдущее состояние было отжата, а текущее состояние нажата – инвертировать состояние светодиода.

Т.е. программа должна отлавливать фронт кнопки или перепад состояния сигнала. Есть два фронта сигнала:

  • с высокого состояния в низкое ( --_ );
  • с низкого состояния в высокое (_--).

Нажатие кнопки будет соответствовать перепаду сигнала из высокого в низкое. Это событие мы и должны выделить.

/*  Программа sketch_6_1 урока 6
 *  На каждое нажатие кнопки инвертирует состояние светодиода
 *  Работает неправильно из-за дребезга контактов */
  
#define LED_PIN 13     // номер вывода светодиода равен 13
#define BUTTON_PIN 12  // номер вывода кнопки равен 12

boolean buttonState;      // состояние кнопки
boolean buttonPrevState;  // предыдущее состояние кнопки
boolean ledState;         // состояние светодиода

void setup() {
  pinMode(LED_PIN, OUTPUT);        // определяем вывод 13 (светодиод) как выход
  pinMode(BUTTON_PIN, INPUT_PULLUP);  // определяем вывод 12 (кнопка) как вход
}

void loop() {

  buttonState= digitalRead(BUTTON_PIN); // записываем состояние кнопки в переменную buttonState
 
  if ( (buttonPrevState == HIGH) && (buttonState == LOW) ) {
   
    // предыдущее состояние кнопки - отжата, а текущее - нажата
    ledState= ! ledState;             // инверсия состояния светодиода
    digitalWrite(LED_PIN, ledState);  // запись состояния светодиода из переменной нв выход   
  }
 
  buttonPrevState= buttonState;         // предыдущее состояние кнопки = текущему
}

Выделение события происходит в конструкции

if ( (buttonPrevState == HIGH) && (buttonState == LOW) )

&& это логическое И, производимое над условными выражениями. В результате его применения будет выработано условие истинно, если оба выражения истинны. В данном случае блок оператора if будет выполнен, если одновременно предыдущее состояние кнопки = HIGH и текущее состояние кнопки = LOW. Т.е. если кнопка была отжата в предыдущей проверке, а  сейчас нажата.

По этому условию инвертируется состояние светодиода.

В принципе, несложная программа. Но загрузите ее в плату Ардуино и проверьте, как она работает.

Неправильно она работает. Иногда, на нажатие кнопки, она инвертирует состояние светодиода, иногда нет. Часто светодиод мерцает в момент нажатия. Происходит это потому, что сигнал с кнопки совсем не такой, как мы его представляем.

 

Дребезг контактов кнопки.

Контакты кнопки это механические предметы. При замыкании они отскакивают друг от друга, касаются друг друга неровностями, поверхностями покрытыми окислами, грязью и т.п. Все это приводит к переходному процессу, называемому дребезгом контактов. Диаграмма сигнала кнопки при замыкании и размыкании выглядит примерно так.

Дребезг контактов

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

Очевидно, что надо программно обрабатывать сигнал кнопки для устранения явления дребезга контактов. Есть еще проблема - это электромагнитные помехи. Если подключить кнопку длинными проводами, то в сигнале могут появиться короткие импульсы электромагнитных помех, что приведет к дополнительным  ложным срабатываниям.

Надежная программа должна обязательно обрабатывать сигналы с  кнопок и механических датчиков.

Как бороться с дребезгом контактов?

Существует несколько способов обработки дребезга контактов. Описанный ниже – один из самых надежных. Еще один способ я опишу в следующих уроках.

Так вот. Решение достаточно очевидно. Надо не реагировать на частое переключение сигнала. Не может кнопка быть нажата на время, например,  0,0001 сек. Значит это дребезг. Надо дождаться, когда в течение определенного времени состояние кнопки будет стабильным, и только тогда принимать решение. А на частые переключения сигнала не реагировать.

 

Программа устранения дребезга контактов кнопки.

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

Существуют готовые функции обработки сигналов с кнопок. Вызываешь функцию с номером вывода в качестве аргумента, и она возвращает состояние вывода, “очищенное” от дребезга. Но операция устранения дребезга занимает значительное время, не меньше длительности переходного процесса, как правило, 10 мс. И все это время программа будет ждать результата работы функции. А кнопок может быть несколько. Да и программе, кроме чтения состояния кнопок, еще что-то надо делать, и не все процессы можно надолго прерывать.

Поэтому давайте стараться обрабатывать состояние кнопок параллельным процессом. Сделаем первый шаг к многозадачности. Пока к условной многозадачности.

Выделим обработку сигнала кнопки в отдельный программный блок. Поставим условие, что этот блок должен регулярно вызываться с периодом, например, 2 мс. Для связи с другими программными модулями создадим глобальные переменные, определяющие состояние кнопки. Таким образом, сигнал кнопки постоянно будет обрабатываться в параллельном процессе, а в любой части программы можно узнать о состоянии кнопки, проверив эти переменные.

Программа состоит из двух основных блоков в бесконечном цикле loop():

  • блок обработки сигнала кнопки;
  • блок управления светодиодом.

Что нам может потребоваться в результате обработки кнопки. Какие переменные создавать. Как правило, необходимы только два признака:

  • Один показывает  текущее состояние кнопки (нажата или отжата).
  • Второй сообщает о том, что кнопка была нажата (был перепад сигнала с высокого уровня в низкий).

Заводим глобальные переменные. Глобальные потому что, неизвестно в какой части программы мы захотим контролировать состояние кнопки.

boolean flagPress= false;    // признак кнопка в нажатом состоянии
boolean flagClick= false;    // признак нажатия кнопки (фронт)

Блок обработки сигнала кнопки мы вызываем с периодом 2 мс. Для отсчета времени устойчивого состояния сигнала необходим счетчик состояния кнопки и константа, задающее это время.

byte  buttonCount= 0;        // счетчик подтверждений состояния кнопки  
#define TIME_BUTTON 12       // время устойчивого состояния кнопки (* 2 мс)

Функции программного блока заключаются в том, что он вырабатывает признаки:

  • flagPress= true, если кнопка нажата;
  • flagPress= false, если кнопка отжата;
  • flagClick= true, если было событие – кнопка была нажата. Этот признак должен сбрасываться после обработки события. В блоке обработки он может только устанавливаться.

Если вызывать такой блок регулярно с периодом 2 мс, то признаки будут соответствовать текущему состоянию кнопки. Их могут использовать другие блоки программы в любом месте. Получается параллельное выполнение задач.

Вот скетч программы, построенной по такому принципу.

/*  Программа sketch_6_2 урока 6
 *  На каждое нажатие кнопки инвертирует состояние светодиода */

#define LED_PIN 13     // номер вывода светодиода равен 13
#define BUTTON_PIN 12  // номер вывода кнопки равен 12

// переменные и константы для обработки сигнала кнопки
boolean flagPress= false;    // признак кнопка в нажатом состоянии
boolean flagClick= false;    // признак нажатия кнопки (фронт)
byte  buttonCount= 0;        // счетчик подтверждений состояния кнопки  
#define TIME_BUTTON 12       // время устойчивого состояния кнопки (* 2 мс) 

boolean ledState;            // переменная состояния светодиода
  
void setup() {
  pinMode(LED_PIN, OUTPUT);        // определяем вывод 13 (светодиод) как выход
  pinMode(BUTTON_PIN, INPUT_PULLUP);  // определяем вывод 12 (кнопка) как вход
}

// бесконечный цикл с периодом 2 мс
void loop() {

/* блок обработки сигнала кнопки
 * при нажатой кнопке flagPress= true
 * при отжатой кнопке flagPress= false
 * при нажатии на кнопку flagClick= true */

  if ( flagPress == (! digitalRead(BUTTON_PIN)) ) {
     // признак flagPress = текущему состоянию кнопки
     // (инверсия т.к. активное состояние кнопки LOW)
     // т.е. состояние кнопки осталось прежним
     buttonCount= 0;  // сброс счетчика подтверждений состояния кнопки
  }
  else {
     // признак flagPress не = текущему состоянию кнопки
     // состояние кнопки изменилось
     buttonCount++;   // +1 к счетчику состояния кнопки

     if ( buttonCount >= TIME_BUTTON ) {
      // состояние кнопки не мянялось в течение заданного времени
      // состояние кнопки стало устойчивым
      flagPress= ! flagPress; // инверсия признака состояния
buttonCount= 0;  // сброс счетчика подтверждений состояния кнопки

      if ( flagPress == true ) flagClick= true; // признак фронта кнопки на нажатие     
     }   
  }

 
  // блок управления светодиодом
  if ( flagClick == true ) {
    // было нажатие кнопки
    flagClick= false;       // сброс признака фронта кнопки
    ledState= ! ledState;   // инверсия состояние светодиода
    digitalWrite(LED_PIN, ledState);  // вывод состояния светодиода   
  }

  delay(2);  // задержка на 2 мс
}

В блоке обработки состояния кнопки происходит следующее:

  • Если текущее состояние кнопки соответствует признаку flagPress, то ничего не делается. Только сбрасывается счетчик подтверждения состояния.
  • Если состояния кнопки и признака различны, то счетчик начинает считать время подтверждения. Любой возврат к равенству состояния кнопки и признака сбрасывает счетчик.
  • Если состояние сигнала устойчивое в течение количества циклов, определяемого константой TIME_BUTTON, то признак состояния кнопки flagPress инвертируется.
  • При этом формируется признак фронта сигнала flagClick, если кнопка стала нажатой. На отжатие кнопки этот признак не вырабатывается.

Блок управления светодиодом, думаю, пояснять не надо.

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

Загрузите программу в контроллер Ардуино, проверьте, что она работает устойчиво. Ложных срабатываний не возникает.

В константе TIME_BUTTON задано время устойчивого состояния сигнала кнопки – 24 мс. Я рекомендую выбирать это время в пределах 20-30 мс.

Для проверки алгоритма обработки сигнала увеличьте значение константы TIME_BUTTON до 250 (время 500 мс). Увидите, что при быстром нажатии (менее 0,5 сек) светодиод не меняет своего состояния. Т.е. алгоритм подтверждения устойчивого состояния работает правильно.

 

В этом уроке написан практический модуль для устранения дребезга кнопки. Вы можете использовать его в своих программах. В следующем уроке я расскажу о классах в языке  программирования Ардуино, мы создадим кнопку как объект.

 

Предыдущий урок     Список уроков     Следующий урок

6

Автор публикации

не в сети 3 дня

Эдуард

279
Комментарии: 1936Публикации: 197Регистрация: 13-12-2015

213 комментариев на «Урок 6. Обработка дребезга контактов кнопки. Интерфейс связи между программными блоками.»

  1. начинаю изучать программирование контроллеров, этот материал мне подходит, а вопросы для уточнения можно задавать?

    1
  2. Здравствуйте,а как изменить программу,чтобы светодиод в состоянии HIGH мигал?

    0
  3. в первом примере buttonPrevState не инициализирован явно, но уже используется. Поправьте 🙂

    0
    • По второму примеру.
      Все-таки использование функции millis() из встроенного в IDE примера debounce, выглядит привлекательнее «тупой» задержки delay().

      0
      • В принципе Вы правы, но при объявлении переменные инициализируются нулями.
        Что касается «тупой задержки delay()» из второго примера. Задержка функцией delay используется для простой, понятной реализации периодического вызова программного блока. Другие способы еще не были представлены в уроках. Начиная с урока 10 подобные блоки вызываются в обработчике прерывания по таймеру.

        0
        • А разве при таком коде не происходит просто задержка выполнения всей программы? Нам же нужно задерживать определенный программный блок, а так мы задерживаем просто все выполнение. Если, допустим, вы измените программу, чтобы блок управления светодиодом заставлял светодиод быстро-быстро моргать (быстрее, чем вызывается блок обработки сигнала), то при таком использовании задержки и у вас он должен мигать не чаще, чем позволяет эта задержка. То есть, если вы меняете каждый цикл, то меняться будет раз в 2мс, а не в 1, скажем. А вашей целью было разделить время выполнения блоков. Поправьте, если я ошибаюсь.

          0
          • Но это учебный вариант программы. В уроке написано, что необходим циклический вызов функции обработки сигнала кнопки, а как это реализуется пока не важно. Начиная с урока 10 кнопки обслуживаются в обработчике прерывания по таймеру. Там программа не зависает.

            0
  4. Эдуард,
    Большое вам спасибо за то, что вы делаете! Мало где информация подается под таким правильным «методическим» углом: не только изучить железо и как копировать решения на нем, но и выработать общие подходы к программированию МК, научиться самому выбирать и рассчитывать компоненты. Надеюсь у вас еще много есть чем поделиться! Можно бы потом из этого и книгу собрать было.

    3
  5. Только вникаю в азы программирования, поэтому вопросы могут показаться смешными))..
    Разве в строке программы
    #define TIME_BUTTON 12
    не происходит присвоение константе TIME_BUTTON вывода под номером 12, ведь строка
    #define BUTTON_PIN 12
    та же самая, только имя константы другое

    0
    • Строка #define TIME_BUTTON 12 означает, что везде где в тексте программы встретится имя TIME_BUTTON компилятор тупо заменит его на символы 12. А применять его к номеру вывода или к времени это решаете Вы. Просто так совпало что и номер вывода 12 и константа времени 12. В предыдущем уроке #define описывается подробно.

      0
      • да да, я понял, перечитал 3 раза и врубился, был невнимателен, TIME_BUTTON это счетчик количества времени включенного состояния, спасибо

        0
      • #define TIME_BUTTON 12 // время устойчивого состояния кнопки (* 2 мс)
        Как у вас получается, что 12 равно 2 мс?

        0
          • Все равно не понял. Объясните, пожалуйста. В описательной части везде указано время ожидания 2 мс., а #define TIME_BUTTON 12

            0
        • #define TIME_BUTTON 12 это… задаём ограничение счётчику. Каждый раз программа в цикле loop выполняется сверху вниз, по кругу, бесконечно. один такой проход называется итерация. У нас там есть такая строчка- buttonCount++; // +1 к счетчику состояния кнопки. Эта строчка за 1 проход (одну итерацию) добавляет к счётчику одну единичку. а в конце программы другая строчка- delay(2); Которая замораживает деятельность процессора на 2 мс. вот и получается проходит первая итерация, счетчик добавляет единичку, замораживаемся на 2мс. вторая итерация проходит, на счетчике уже 2 отсчета, снова замораживаемся на 2 мс. итд, по итогу 1 итерация в 2 мс, 24мс до срабатывания счётчика.

          0
        • вот комментарий :
          #define TIME_BUTTON 12 // время устойчивого состояния кнопки (* 2 мс)
          TIME — на английском означает не только время но и события и соответственно циклы . здесь у нас TIME_BUTTON 12 — количество событий ( проверок , циклов ) . а раз проверки идут через каждые :
          delay(2); // задержка на 2 мс
          то просто умножаем количество проверок состояния счётчика :
          if ( buttonCount >= TIME_BUTTON )
          равное TIME_BUTTON 12 ,12 умножаем на 2 и получаем 24 мс время проверки счетчика не учитывая других шагов программы . давно хотел об этом написать , это то место которое в программе неправильно названо и из за этого все об него спотыкаются .

          0
    • название #define TIME_BUTTON 12 надо заметить неправильное и путает многих . так как это не время а количество проверок состояния . вместо #define TIME_BUTTON 12 лучше написать #define CHECKS 12 . это точнее отражает суть происходящего . а в комментарии вместо ( #define TIME_BUTTON 12 // время устойчивого состояния кнопки (* 2 мс) «) я б написал
      ( #define CHECKS 12 // количество проверок состояния кнопки )

      0
  6. И еще вопрос, а в блоке управления светодиодом разве не нужно вместе с flagClick= false, также обнулить переменную flagPress?
    flagPress = false
    Иначе выходить должно, что при отжатии кнопки светодиод снова переключится ?

    0
    • Нет. Почитайте внимательно выдержку из урока:
      Функции программного блока заключаются в том, что он вырабатывает признаки:
      •flagPress= true, если кнопка нажата;
      •flagPress= false, если кнопка отжата;
      •flagClick= true, если было событие – кнопка была нажата. Этот признак должен сбрасываться после обработки события. В блоке обработки он может только устанавливаться.

      flagPress показывает текущее состояние, а flagClick — признак того, что было событие. Он запоминает его.

      0
      • Спасибо, Эдуард, я проверил еще раз и увидел строку
        if ( flagPress == true ) flagClick= true

        0
      • Будет ли правильным вот такой код для избежания дребезжания ?

        // мигаем диодом от кнопки.
        //Включение и выключение по отдельному нажатию

        #define PIN_DIOD 13
        #define PIN_KNOPKA 2
        boolean last_button = LOW;// предыдущее состояние
        boolean Now = LOW; // значение для светодиода
        boolean now_button = LOW; // текущее состояние

        // настройка, предустановка
        void setup()
        {
        pinMode (PIN_DIOD, OUTPUT);
        pinMode (PIN_KNOPKA, INPUT);
        }

        // бесконечный цикл
        void loop()

        {
        now_button = digitalRead(PIN_KNOPKA); // читаем состояние кнопки

        if (last_button == LOW && now_button == HIGH) // если кнопка была в лоу и стала в хай
        {
        Now = ! Now; // инвертируем значение на светодиоде

        }
        last_button=digitalRead(PIN_KNOPKA); // читаем стостояние кнопки
        delay(2);
        digitalWrite(PIN_DIOD, Now);//записываем значение в порт диода
        }

        0
      • sketch_6_2 урока 6
        Никак не могу понять: после того как состояние кнопки стало устойчивым происходит следующее:
        flagPress= ! flagPress; // инверсия признака состояния.
        А как flagPress возвращается в исходное состояние?
        Клик мы сбрасываем: flagClick= false; // сброс признака фронта кнопки,
        а как ардуино «понимает» что flagPress онять false, после того как мы отпустили кнопку.

        0
        • Здравствуйте!
          Он опять инвертируется
          flagPress= ! flagPress; // инверсия признака состояния
          и его состояние становится соответствующим входному сигналу.

          0
          • Здравствуйте!
            Получаетсяся flagPress обратно инвертируется при следующей прокрутке loop когда кнопка уже не нажата? Извиняюсь за беспокойство)

            0
          • Но он же инвертируется при условии, что состояние кнопки изменилось (текущее не равно flagPress ) и оно стало устойчивым ( buttonCount >= TIME_BUTTON ).

            0
  7. Здравствуйте! Не понятно откуда берется значение переменной ledState в блоке упр. светодиодом «ledState= ! ledState; // инверсия состояние светодиода». Ведь она только объявлена как глобальная переменная и нигде в программе не установлено ее начальное значение.

    0
    • Здравствуйте!
      Во первых, при инициализации переменных их значение равно 0.
      Во вторых, в данной программе нам не важно начальное состояние светодиода. При каждом нажатии кнопки он должен менять свое состояние.

      0
      • 1)Все равно не понял, почему каким-то переменным задаётся нулевое значение, а каким -то нет? И тем более не понятно почему если не задаем никакого значения то по умолчанию переменным присваивается нулевое значение, зачем тогда им задавать «принудительно» нулевое значение (false)? Или есть какие-то правила?

        1
        • Формально Вы правы. Но, как правило, все компиляторы инициализируют не заданные переменные 0. Некоторые переменные не надо инициализировать по алгоритму. Задавайте при инициализации. Это дисциплинирует, хуже не будет.

          0
  8. 2)Если я правильно понял то 2-й пример работает сл.образом: после включения LED не горит; при нажатии на кнопку через 24 мс LED загорается; при отжатии LED продолжает гореть; при повторном нажатии через 24 мс LED гаснет и т.д. Это так?

    0
    • В принципе все правильно. Светодиод меняет свое состояние на каждое нажатие кнопки. Именно нажатие, не отжатие. А то, что состояние меняется через 24 мс, не столь важно. Это время на обработку сигнала кнопки. Не думаю, что его в реальной программе надо учитывать.

      0
  9. 3)Почему в выражении нет фигурных скобок: if ( flagPress == true ) flagClick= true; или при построении простого выражения они не нужны?

    0
  10. Здравствуйте!
    Если в вашем цикле loop мы его слегка изменим, станет ли программа работать не правильно?

    void loop() {
    if ( flagPress == (! digitalRead(BUTTON_PIN)) ) {
    buttonCount= 0;
    }

    else {
    buttonCount++;

    if ( buttonCount >= TIME_BUTTON ) {
    buttonCount= 0;
    flagClick= true;
    }
    }

    if ( flagClick == true ) {
    flagClick= false;
    ledState= ! ledState;
    digitalWrite(LED_PIN, ledState);
    }

    delay(2);
    }

    а именно избавимся от строчки flagPress= ! flagPress; и не станем выполнять условие if ( flagPress == true ) нужное для присвения flagClick= true;. Выполним присвоение flagClick= true после того как выполнится условие buttonCount >= TIME_BUTTON.

    0
      • Извиняюсь, поторопился и ошибся. Да программа будет работать не правильно, но мне кажется, что ошибка будет выглядеть так: после того как кнопка нажата, а дребезг устранен сработает flagClick= true и произойдет инвертация значения светодиода. Однако в связи с тем что пока палец находится на кнопке условие flagPress == (! digitalRead(BUTTON_PIN) будет продолжать отправлять нас на ветку else, тем самым каждую последующую итерацию цикла loop, будет увеличиваться счетчик buttonCount. И пока палец на кнопке, каждые 12 итераций цикла loop с задержкой по 2 мс каждый (т е каждые 24 мс), будет также происходить инвертация значения светодиода. Таким образом пока кнопка нажата светодиод будет моргать, до тех пор пока палец не отпустит кнопку и светодиод не сохранит своего текущего значения, которое может быть как включенным, так и выключенным. Большое спасибо, за интересную статью и ответ.

        0
  11. Встречал много реализаций гашения дребезга, но Ваш код самый краткий и элегантный !
    Спасибо за примеры!

    0
  12. Здравствуйте!
    Как я понимаю, в программе предыдущего урока тоже будет
    наблюдаться явление дребезга контактов?

    0
  13. Здравствуйте!
    А в программе из предыдущего урока тоже будет проявляться дребезг контактов?

    0
    • В программе из предыдущего устройства дребезг мы не замечаем. В момент коммутации конечно будет дребезг, но у нас отображается состояние кнопки, а не инверсия светодиода на каждое нажатие.

      0
  14. Далеко не сразу понял работу скетча, я начинающий…
    Я правильно понимаю, что если тупо задать время на отработку дребезга, скажем 20 мс (не используя алгоритмы устранения дребезга) и проверку состояния, то 20 мс программа будет висеть? Я хочу подключить шаговый двигатель по протоколу STEP/DIR. В схеме должны быть двигатель, кнопки, ЖК экран для установки скорости и т.п.. Я правильно понимаю, что если программа будет висеть при каждой обработке кнопки, а их предполагается несколько, то и импульсы STEP не будут поступать на шаговый двигатель и прочие функции выполняться не будут во время?

    0
    • Анатолий, это один из начальных уроков. В нем демонстрируется принцип устранения дребезга. Я пишу, что обработка кнопки должна вызываться примерно каждые 2 мс. Задержка 2 мс реализуется функцией delay(), в которой действительно программа зависает.
      Дойдите до 10 урока. В нем совсем другая реализация обработки кнопок параллельным процессом. Там программа не зависает. В рабочей программе надо использовать обработку кнопок в прерывании по таймеру. Это совсем не сложно. Код программы будет даже меньше, чем в этом уроке.

      0
  15. Спасибо, Эдуард, я обязательно прочту все уроки. Однако много вопросов. Один из них я озвучил. На 2 ли миллисекунды или на 20, не важно. Протокол STEP/DIR не выполняется в это время?
    Или я не правильно понимаю?

    0
    • При реализации задач параллельными процессами (начиная с урока 10) все выполняется одновременно. В контроллере элемента Пельтье (урок 36) столько всего выполняется вместе с обработкой кнопок. Кстати, есть уроки и по STEP/DIR драйверам.

      0
  16. В программе sketch_6_2 я никак не пойму переменные boolean, поэтому не могу двигаться дальше. Условие if ( flagPress == (! digitalRead(BUTTON_PIN)) ) мне почему-то кажется не выполнимым. Ведь переменные boolean могут принимать ТОЛЬКО значения true или false. Я пытаюсь «думать» как машина:
    объявлена некоторая переменная boolean flagPress= false;
    Далее имеем условие if ( flagPress == (! digitalRead(BUTTON_PIN)) )
    которое означает, что если ранее объявленной переменной flagPress соответствует значение функции ! digitalRead(BUTTON_PIN)), то и тд..
    Смотрим что за функция: digitalRead — функция чтения состояния контакта 12 (а к нему подключена кнопка). В момент подачи питания на кнопку скорее всего никто не нажимал, стало быть на контакте высокий уровень HIGHT, по условию мы его инвертируем на LOW. Значит нашей переменной flagPress соответствует значение LOW. Мало того, что кнопку никто не нажимал, а получается, что нажали (именно это же происходит при нажатии на кнопку, высокий уровень меняется на низкий), так ещё и значение LOW никаким боком не относится к двум возможным значениям переменной boolean, так как она может принимать ТОЛЬКО значения true или false. Как это понять?

    0
    • Переменные типа boolean занимают в памяти один байт. Состояние false отображается нулевым значением байта, любое другое значение — true. Поэтому HIGH и LOW могут использоваться как значения типов boolean.

      А что касается if ( flagPress == (! digitalRead(BUTTON_PIN)) ).
      Это проверка — признак состояния кнопки определенный в программе ранее равен или не равен текущему состоянию кнопки. Знак ! инвертирует результат digitalRead(BUTTON_PIN), потому что нажатая кнопка у признака отображается true, а реальный сигнал нажатой кнопки низкого уровня.

      0
  17. В дополнение к вышесказанному:

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

    int LEDpin = 5; // Светодиод на входе 5
    int switchPin = 13; // выключатель на порту 13, замыкает на землю

    boolean running = false;

    void setup()
    {
    pinMode(LEDpin, OUTPUT);
    pinMode(switchPin, INPUT);
    digitalWrite(switchPin, HIGH); // включаем подтягивающий резистор
    }

    void loop()
    {
    if (digitalRead(switchPin) == LOW)
    { // выключатель нажат, т.к. подтягивающий резистор будет давайть HIGH на входе, если не замкнут напрямую на землю
    delay(100); // ждем 0.1сек
    running = !running; // меняем значение булевой переменной
    digitalWrite(LEDpin, running) // включаем или выключаем светодиод.
    }
    }

    Хотя в конце тоже не понятно, что записывается на контакт 5. Значение true что ли? И чему это значение соответствует в вольтах?

    0
  18. Эдуард, пытаюсь далее понять логику…..что-то не получается.
    Почему условие if ( flagPress == (! digitalRead(BUTTON_PIN)) ) мне не кажется условием. Мы включили прибор (ведь все начинается с начала), на кнопку никто не нажимал (очень трудно представить себе, что на кнопку нажали раньше включения устройства). Оператор IF означает, что если условие выполняется, то далее выполняются операторы расположенные непосредственно за оператором IF, до оператора else, правильно? Но у нас нет условия, у нас идет присвоение переменной flagPress значения функции (! digitalRead(BUTTON_PIN)). С чем сравнивает оператор IF это присвоение, с исходным значением переменной? Но в описании оператора IF везде в справочниках указывается, что условие заключено в скобках оператора. А у нас там нет никакого условия…

    0
    • Я не совсем понял вопрос. Я уже подзабыл эту программу, возможно, ошибусь, но алгоритм примерно такой.
      У нас есть flagPress – признак состояния кнопки, вычисленный программой. Он может не соответствовать текущему реальному состоянию кнопки, т.к. на его определение требуется время.

      У нас есть функция, которой мы определяем реальное состояние кнопки в данный момент digitalRead(BUTTON_PIN). Это состояние с дребезгом, который мы хотим устранить, отфильтровать.

      Мы сравниваем эти значения с учетом того, что реальный сигнал инверсный (когда кнопка нажата = 0).

      Если они равны, то мы ничего не делаем. Зачем что-то делать.

      Если не равны, то мы начинаем отсчитывать время
      buttonCount++; // +1 к счетчику состояния кнопки
      if ( buttonCount >= TIME_BUTTON ) {
      По прошествие определенного времени, если они все это время остаются не равны, то мы меняем flagPress.

      По моему все логично.

      0
  19. А-а, дело в том, что используется оператор не присвоения =, а оператор сравнения == Верно?
    Получается, что переменной было что-то присвоено, а оператор IF проверяет, соответствует ли это присвоение истине или нет. Правильно?

    0
  20. Отлично, все вроде понял, все сходится :)) А подскажите ещё, пожалуйста, в литературе пишется, что код программы выполняется «очень быстро», но это весьма расплывчато. А вот как быстро? Наверное есть какое-то среднее время выполнения оператора в программе. Я, как писал, хочу использовать шаговый двигатель с драйвером TB6600, который работает на частоте 200 кГЦ, соответственно с периодом 5 мкс. Я ранее спрашивал, в каждом ли такте STEP проходит весь цикл loop? Ну, если я хочу в каждом такте Step менять длительность периода (для разгона, например), то остро встает вопрос обработки состояния кнопок, выведения информации на ЖК индикатор и т.п., а это зависит от того, как быстро выполяется код программы…

    0
    • К сожалению код программы не очень быстро выполняется. Быстродействия всегда не хватает.
      У каждой команды микроконтроллера есть определенное время выполнения. Но на языке высокого уровня оператор реализуется несколькими командами низкого уровня. Вычислительные задачи могут требовать несколько сотен команд. Поэтому сказать определенно время выполнения операторов C нельзя. Более того время вычислительных операций зависит еще от конкретных данных.
      Можно просто измерить время выполнения команд, функций, программных блоков. На форуме сайта у меня есть тема об измерении времени выполнения блоков кода. Там есть время выполнения некоторых стандартных функций Ардуино. Например digitalWrite() или digitalReed() выполняются за 4-6 мкс. Можно уменьшить время выпонения операций прямым обращением к регистрам микроконтроллера.

      Что касается шагового двигателя. Вы явно напутали про частоту 200 кГц. Возможно это частота внутреннего тактирования драйвера. Вам важна частота шагов или сигнала STEP. Но она не может быть 5 мкс. Я участвовал в разработке такого двигателя. Но его STEP/DIR драйвером не закрутишь. Посчитайте какая скорость вращения будет.
      Реальное время цикла управления на Aduino UNO, я думаю, не менее 100 мкс.

      И еще. Анатолий, если у Вас какие-то объемные вопросы, то открывайте тему на форуме сайта. Там удобнее давать материал и кто-то еще поделится своим опытом. Чего-то в уроках я не досказал, расскажу на форуме.

      0
  21. Для чего в конце задержка delay(2)?

    Не понятно. Ведь от этого теряется весь смысл.

    Если использовать delay() тогда можно просто поставить его вначале первого скетча.

    buttonState= digitalRead(BUTTON_PIN); // записываем состояние кнопки в переменную buttonState
    delay(3); // и дребезга тоже не будет.
    Проверял.
    Ну а за скетч распознавания фронтов — спасибо.
    Действительно красивый код.

    0
    • Это один из начальных уроков. В нем демонстрируется метод. В последующих уроках кнопки обрабатываются в прерывании по таймеру.

      Цитирую из урока:

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

      0
  22. Здравствуйте, Эдуард! Большое спасибо за Ваши уроки! Поясните пожалуйста, есть ли принципиальное различие между операторами HIGH/LOW и true/falce

    0
    • Здравствуйте!
      Размер false, true, LOW, HIGH один байт.
      false, LOW передается байтом с нулевым значением.
      true, HIGH это байт с любым ненулевым значением.
      Т.е. в принципе одно и тоже false, LOW и 0.
      Также одно и тоже true, HIGH, 1, 2 …, 255
      Бесконечный цикл можно создать
      while(true) или while(1).

      Но хороший стиль программирования предполагает в логических операциях использовать false и true, в функциях ввода вывода LOW и HIGH.

      0
      • Стоило бы ввести термин «именованная константа». Слишком часто в комментариях к урокам вижу вопросы на эту тему.

        0
  23. Здравствуйте, Эдуард.
    Я только начинаю осваивать программирование, при изучении по вашим урокам, возник вопрос, в Программе sketch_6_1 урока 6 ,не могу понять как микроконтроллер понимает, что это предыдущее состояние кнопки и она отжата в этой строчке
    if ( (buttonPrevState == HIGH) && (buttonState == LOW) ) ?

    0
    • Здравствуйте!
      buttonPrevState — переменная, в которой содержится предыдущее состояние кнопки,
      buttonState — переменная текущего состояния кнопки.

      if ( (buttonPrevState == HIGH) && (buttonState == LOW) )
      означает если предыдущее состояние было высоким, а текущее низкое, то выделен момент нажатия кнопки.

      0
  24. Эдуард, я правильно понимаю, в начале программы мы создаем переменные buttonPrevState и buttonState, и им задаются нулевые значения.
    Когда программа начинает обрабатывает оператор if

    if ( (buttonPrevState == HIGH) && (buttonState == LOW) )

    buttonPrevState рано LOW
    buttonState равно HIGH- так как считывается состоянии кнопки.
    И при проверке условия на истину, не проходит,и выполняется далее строчка
    buttonPrevState= buttonState;
    В которой в переменную buttonPrevState записывает HIGH.
    Я правильно понял выполнение программы?

    0
    • Да. Но вы описали только начальный алгоритм выполнения.

      Оператор buttonPrevState=buttonState; выполняется в каждом цикле. Получается, что buttonPrevState это задержанное на один такт значение buttonState. Нарисуйте на бумаге сдвинутые диаграммы состояния нажатой и отжатой кнопки. Вы увидите, что только в момент нажатия buttonPrevState=HIGH и buttonState=LOW. Это состояние и выделяет алгоритм.

      0
  25. Здравствуйте,
    Вопрос по первому коду в статье по переменной
    boolean buttonPrevState; // предыдущее состояние кнопки
    В if мы сразу сравниваем buttonPrevState == HIGH
    Но до этого мы переменной ничего не присвоили.
    При первом проходе цикла переменная buttonPrevState получается вообще не определена? Объясните как это работает

    0
  26. Здравствуйте, Эдуард.
    Попытался применить Ваш метод гашения дребезга к энкодеру. Больше половины импульсов обрабатываются в неверном направлении, либо вообще пропускаются. В чём может заключаться проблема?

    0
    • Здравствуйте. С какой частотой вы опрашиваете сигналы энкодера?
      У меня буквально вчера появилась задача, в которой требуется энкодер. Я реализовал ее сначала библиотекой Button, затем написал библиотеку, которая вычисляет скорость изменния положения энкодера.
      Если потерпите, я напишу урок на следующей неделе.
      А ваша ошибка, наверняка, заключается в том, что энкодер необходимо опрашивать достаточно часто. Сделайте 4 подтверждения и 250 мкс период прерывания функции scanState. Думаю, заработает.

      0
      • Будет интересно ознакомиться с уроком!
        Частота опроса установлена 4 мс. Сам энкодер 24-разрядный. При этом, если вращать ручку медленно, то сигналы обрабатываются как положено.
        Я так понимаю, при такой частоте контроллер видит зажатые выводы в неправильной последовательности, верно?

        0
  27. Добрый день.
    1.Подскажите пожалуйста
    Какая связь между числом 12 в коде:
    #define TIME_BUTTON 12
    и ( * 2 мс) в комментариях к нему:
    // время устойчивого состояния кнопки (* 2 мс)
    А это строка целиком:
    #define TIME_BUTTON 12 // время устойчивого состояния кнопки (* 2 мс)
    2. И какое отношение счетчик имеет к промежутку времени
    в этом коде: if ( buttonCount >= TIME_BUTTON )
    или я чего то не понял, объясните пожалуйста!
    3. И стоит ли применять в этом случае функцию: delay(2);
    Скорей всего автор применяет это для демонстрации программы анти дребезга?
    Зарание благодарен,и проститите ежли спросил глупость
    антидребезга

    0
  28. Добрый день.
    В начале недели отправил вопросы, но куда то все исчезло.
    Попробую еще раз и по одному вопросу.
    Вот часть кода из этого скетча:
    «#define TIME_BUTTON 12 // время устойчивого состояния кнопки (* 2 мс)»
    То есть символическая константа «TIME_BUTTON» получает свое значение «12», и в комментарии к этой части » // время устойчивого состояния кнопки (* 2 мс)». Вопрос если «TIME_BUTTON» — «время устойчивого состояния кнопки», то тогда какое отношение значение «12» имеет к части комментария «(* 2 мс)»?
    Объясните пожалуйста!

    0
    • Здравствуйте!
      Константе TIME_BUTTON вы присваиваете число 12. А сколько это будет в реальном времени?
      В комментариях я указываю, что каждая единица TIME_BUTTON соответствует 2 мс. Т.е. время подтверждения будет 12 * 2 = 24 мс.

      0
  29. Добрый день.
    Да теперь понятно.
    Спасибо.
    Просто читать между строк, к сожалению не научился.
    Как бы там ни говорили, все люди в своем большинстве мыслят одинаково.
    Мыслительный процесс, как бы та программа, которая оперирует некими образами или шаблонами, с биологической точки зрения, логика поведения в своем большинстве однообразна, вот только образы различны. (Это я объясняю, как большинству все понятно, а мне нет.)
    Не сочтите мое обращение к Вам как «придирки», если мои дальнейшие вопросы Вы посчитаете не важными, так тому и быть.
    Вот часть описание этого урока:
    «Программа устранения дребезга контактов кнопки.
    Давайте напишем такую программу. Чтобы быть ближе к практическому программированию, …..»
    Теперь вопрос, пример кода для обработки сигнала кнопки, это практический вариант, или все-таки показательный, учебный пример?
    Дело в том, что Вы предлагаете :
    «Поэтому давайте стараться обрабатывать состояние кнопок параллельным процессом. Сделаем первый шаг к многозадачности.»
    Тогда почему в конце используете функцию «delay(x)», насколько я понимаю, в течение промежутка времени «x», ничего происходить не должно, это пауза, контролер просто ждет. Где тогда параллельный процесс?
    Извините, конечно, может я опять чего то не понял, ведь понять другого человека, иногда не меньший труд, чем донести до него информацию, как то так….

    0
    • Здравствуйте!
      В уроке 6 это учебный пример, поясняющий принцип действия алгоритма.
      Начиная с урока 10, когда я рассказываю о прерывании по таймеру, такой алгоритм используется в библиотеке обработки сигналов кнопок Button.h.

      0
    • Я прочитал ваш так называемый вопрос.
      И хочу вам ответить…
      1) Ваш вопрос называется не «придирки» он называется «цепляться к словам».
      2) Все что здесь написано есть учебные примеры!

      Я бы сказал вам конкретно: вы точно опять чего-то не поняли
      Как то так…

      0
  30. Друзья, вы уж извините, но сломала голову, догоняя вашу логику, и все-равно считаю, что в блоке цикла в первом же операторе ошибка..
    if ( flagPress == (!digitalRead(BUTTON_PIN)) ) — здесь вы должны проверить, что кнопка все еще не нажата. По умолчанию flagPress это false, что соответствует отжатому состоянию кнопки, т.е. LOW на 12м пине. В этот состоянии (не обратном!) вы должны делать сброс счетчика. Т.е. проверка должна быть без инверсии digitalRead(BUTTON_PIN).
    Однако программа успешно работает в обоих вариантах.
    Но Вы вводите в заблуждение..

    0
    • Здравствуйте!
      У признака flagPress активное состояние true. Т.е. если кнопка нажата, то flagPress=true. У вывода при нажатой кнопке сигнал низкого уровня digitalRead(BUTTON_PIN) = LOW.
      Вы читайте комментарии к программным блокам и старайтесь рассуждать относительно их.
      if ( flagPress == (! digitalRead(BUTTON_PIN)) ) {
      // признак flagPress = текущему состоянию кнопки
      // (инверсия т.к. активное состояние кнопки LOW)
      // т.е. состояние кнопки осталось прежним

      Этот программный блок означает:
      Если состояние кнопки осталось прежним, то мы ничего не делаем, только сбрасываем счетчик подтверждений. Где здесь нарушена логика?

      0
  31. Здравствуйте!
    Правильно ли я понимаю, когда я нажал на кнопку, программа занимает 24 мс чтобы определить это дребезг или нажатие?

    0
  32. Здравствуйте!
    Поясните пожалуйста, если я использую две кнопки я нажал на первую кнопку и следом на вторую то может случиться что контроллер не увидит это срабатывание , то о каких 2 мс мы говорим?

    0
    • Здравствуйте!
      Хоть 10 кнопок одновременно. Каждая кнопка обрабатывается своей функцией параллельно. В этом уроке пример больше учебный. Начиная с 10 урока будет обработка состояния кнопки в прерывании по таймеру. Там начнется действительно параллельная обработка сигналов.

      0
  33. Здравствуйте!
    Эдуард, по моему пониманию программа которую выполняет микроконтроллер -это последовательное выполнение операций записанных в программный блок, или я что-то не понимаю?

    0
    • Здравствуйте!
      Да, конечно. Но если каждые, например, 2 мс последовательно обрабатывать 10 кнопок, то для пользователя они будут обрабатываться одновременно.

      0
  34. День добрый!
    Хочу поблагадарить за столь хорошие уроки!
    А мне для собственного развития и подтверждения понимания кода стало интересно вот что:
    В данном коде ожидается уверенный (неизменный) сигнал продолжительностью, примерно, 24 миллисекунды. То есть включение светодиода произойдет через время равное времени дребезга ПЛЮС 24мс. Но на рисунке показан дребезг контактов продолжитеньностью примерно 10 мс, к тому же в нем несколько раз меняется значение (пусть, например, 10 раз). То есть можно утверждать, что если сигнал неизменен на протяжении чуть больше, 10/10=1мс, то у нас уже нет дребезга?
    Или что бы не было конфликта в понимании с delay (2), то предположим, что у нас плохая кнопка и она дребезжит аж 100 мс. При этом происходит 20 переключений (ну, например, осцилографом померили). Тогда мы можем утверждать, что если у нас на протяжении 6 мс (чуть больше, чем 100/20, и при условии одинаковости импульсов при дребезге) не меняется значение- то дребезга уже нет. Тогда и светодиод чуть быстрее загорится.
    Повторюсь, мне интересно для собственного понимания кода. Мне ясно, что на самом деле разницы я не почувствую.

    0
    • Здравствуйте!
      Все правильно: «То есть включение светодиода произойдет через время равное времени дребезга ПЛЮС 24мс.»
      Вы ведете речь о выборе времени подтверждения состояния сигнала. Да, его можно выбирать по разным критериям. В том числе и по быстродействию. Но никто не гарантирует длительность дребезга. Лучше выбирать время подтверждения с запасом. Контакт кнопки разболтаются, и дребезг станет дольше.

      0
  35. Доброго времени суток!
    в программе мы определяем TIME_BUTTON=12. Почему мы считаем, что время дребезга контактов 24 мс, а не 12 мс?
    Спасибо.

    0
  36. Спасибо не поздно сказать?
    ЭДУАРД, СПАСИБО!
    Мало того, что материал хороший, да еще столько терпения всем все разжевать.

    0
  37. Здравствуйте!
    Вопрос не по теме, Это реализация Т-триггера? Т.е. деление на «2»?

    0
  38. Здесь нет ошибки ?
    «»»»»»»»»»»»»boolean flagPress= false; // признак кнопка в нажатом состоянии
    boolean flagClick= false; // признак нажатия кнопки (фронт)

    Блок обработки сигнала кнопки мы вызываем с периодом 2 мс. Для отсчета времени устойчивого состояния сигнала необходим счетчик состояния кнопки и константа, задающее это время.

    byte buttonCount= 0; // счетчик подтверждений состояния кнопки
    #define TIME_BUTTON 12 // время устойчивого состояния кнопки (* 2 мс)

    Функции программного блока заключаются в том, что он вырабатывает признаки:

    flagPress= true, если кнопка нажата;»»»»»»»»»»»»»»»

    boolean flagPress= false; // признак кнопка в нажатом состоянии
    flagPress= true, если кнопка нажата
    тогда как обозначаете когда отжата ?

    0
    • Здравствуйте!
      flagPress это переменная. У нее могут быть два состояния: true — значит кнопка нажата, и false — отжата.
      Хороший стиль называть переменные так, чтобы названия соответствовали активному состоянию true. Т.е. flagPress (флаг нажата) означает, что активное состояние нажата соответствует true. Если бы переменная была названа flagUnPress (флаг не нажата), то состояние true соответствовало отжатой кнопке.

      0
      • Здравствуйте Эдуард!
        Пожалуйста еще раз о #define TIME_BUTTON 12 .

        Если директива #define BUTTON_PIN 12 указывает на номер вывода, а #define TIME_BUTTON 12 , как написано ниже в комментариях, это константа времени.

        То тогда почему в первом случае #define это номер пина, а во втором это таймер?

        0
        • Здравствуйте!
          Директива #define просто заменяет при трансляции одну последовательность символов на другую.
          Строка #define TIME_BUTTON 12 означает, что везде где в тексте программы встретится имя TIME_BUTTON компилятор тупо заменит его на символы 12.
          Если вы напишите #define MY_NAME Andrey, то везде где встретится последовательность символов MY_NAME, компилятор заменит ее на последовательность Andrey.
          И нет никакой разницы, что означает последовательность символов.

          0
          • Да уж, в комментариях выше несколько раз перечитывал это, как все просто! Спасибо
            .
            Тогда по:

            buttonCount= 0; // сброс счетчика подтверждений состояния кнопки

            Мы, что считаем , что программа записывает в эту переменную; время? фронты сигнала?

            и далее else {
            buttonCount++; // +1 к счетчику состояния кнопки

            для чего прибавляем единицу когда ниже сравниваем buttonCount и TIME_BUTTON которому присвоено число 12. Каким образом происходит запись в buttonCount.

            0
          • Здравствуйте!
            В переменную buttonCount записывается число подтверждений стабильного состояния кнопки. Если состояние стабильное, мы прибавляем единицу. Если состояние изменилось, то сбрасываем buttonCount в 0. Так как мы сканируем состояние кнопки через определенные равные промежутки времени, то можно считать, что в этой переменной хранится время стабильного состояния кнопки. Поэтому мы сравниваем его с константой, которую называем временем.

            0
  39. Здравствуйте! Эдуард огромное Вам спасибо за ваш труд. Редко где в сети встретишь такую школу ). За себя могу сказать, что урок освоен на все сто и можно идти дальше.

    0
  40. Эдуард, присоединяюсь ко всем поблагодарившим Ваш труд! Спасибо огромное!
    Не тратьте свое время, не отвечайте на этот комментарий! Просто знайте, Вам благодарны многие.

    0
  41. Эдуард !
    Очень благодарен вам за вашу роботу , терпение и желание отдавать.
    Я самоучусь , и ваши примеры для меня это лучшее из того , что было познано ранее.
    маленькая просьба , в примере Антидребезг поменяйте номер Пина Кнопки , чтоб не путался с цифрой Таймера.

    Как говориться , «горох отдельно , а капуста отдельно» .

    0
  42. Эдуард! Огромное спасибо за такой замечательный курс! Получаю массу удовольствия, вникая в детали. Обработку дребезга контактов кнопки представил в виде дорожки домино, которую мы постоянно толкаем, если одна пластинка пошатнулась но не зацепила другую, мы поднимаем её и ставим обратно, если пошатнулась то смотрим толкнула ли она другие)) Видимо, дело в том, что уже 2 часа ночи ) Очень увлекательно!, Предвосхищаю следующие уроки!

    0
      • возник вопрос, можно ли бороться с дребезгом, установив ёмкость между землёй и ногой кнопки?

        у Вас объективно самый лучший курс по Ардуино, который я встречал в сети! Уже собираю сигнализацию (первую, простую версию) на макетной плате! От души благодарю!

        0
        • Здравствуйте! Спасибо!
          Бороться с дребезгом с помощью емкости не самое лучшее решение. При размыкании кнопки напряжение на входе будет медленно повышаться и в момент перехода уровня срабатывания входа могут проскочить ложные срабатывания. При замыкании кнопки будет происходить разряд емкости через контакты кнопки. При значительной емкости контакты будут подгорать. Такая защита от дребезга будет работать нестабильно и непредсказуемо. Может быстрые нажатия пропускать.

          0
  43. Спасибо большое, после прочтения комментариев прояснил для себя все непонятное!

    0
  44. Доброго дня! Подскажите, чем тип boolean отличается от типа bool? Правильно ли я понял из гугла, что в отличие от других языков, в ардуино не отличается ничём и просто введены оба для удобства?

    0
  45. Эдуард, огромное спасибо за ваш сайт! Очень познавательно.
    В предпоследнем абзаце урока №6 Вы предлагаете:
    «Для проверки алгоритма обработки сигнала увеличьте значение константы TIME_BUTTON до 250 (время 500 мс). Увидите, что при быстром нажатии (менее 0,5 сек) светодиод не меняет своего состояния.»
    Попробовал… да, действительно работает. Решил сделать TIME_BUTTON ещё больше… 500, ради эксперимента )). Светодиод перестал включаться вообще. Опытным путём установил последнее значение на котором срабатывание — это 255 ))))) Вот собственно и вопрос:
    В алгоритме кажется нет никаких ограничений на TIME_BUTTON , почему не загорается светодиод если TIME_BUTTON > 255, даже если кнопку держать нажатой больше нескольких секунд?

    0
    • Всё, кажется сам понял. buttonCount это же тип byte, соответственно возникает переполнение и buttonCount скидывается в 0.

      0
  46. #define LED_PIN 13 // номер вывода светодиода равен 13
    #define BUTTON_PIN 12 // номер вывода кнопки равен 12
    boolean buttonState;
    boolean ledState;

    void setup() {
    pinMode(LED_PIN, OUTPUT); // определяем вывод 13 (светодиод) как выход
    pinMode(BUTTON_PIN, INPUT_PULLUP); // определяем вывод 12 (кнопка) как вход
    }

    void loop() {

    buttonState = digitalRead(BUTTON_PIN);
    delay(20);
    if (buttonState == digitalRead(BUTTON_PIN)){
    ledState = digitalRead(LED_PIN);
    ledState= ! ledState;
    digitalWrite(LED_PIN, ledState);
    }
    }

    0
  47. Можно немного упростить программку.

    void loop()
    {
    if(digitalRead(12)==LOW)
    {
    buttonCount++;
    if(buttonCount == 6)
    {
    digitalWrite(13, !digitalRead(13));
    }
    }
    else buttonCount=0;
    delay(2);
    }

    0
  48. Эдуард, добрый день!

    Ваш сайт, по моему мнению, содержит лучший контент по инженерному (в лучшем смысле этого слова) подходу к разработкам на Ардуино.
    Спасибо, что делитесь своим опытом. Я, после долгого перерыва, пытаюсь вернуться в профессию инженера-электронщика, и ваш материал здорово помогает.
    Хочу поддержать ваш сайт, но в соответствующем разделе плагин не выдает реквизиты вашего кошелька Яндекс и пластиковых карточек. Как бы мне их уточнить. Все-таки просто «спасибо» в карман не положишь )).

    0
  49. Большое спасибо за не стандартный подход. Подскажите, пожалуйста, я никак не пойму. В основной конструкции:

    if ( flagPress == (! digitalRead(BUTTON_PIN)) ) {
    // признак flagPress = текущему состоянию кнопки
    // (инверсия т.к. активное состояние кнопки LOW)
    // т.е. состояние кнопки осталось прежним
    buttonCount= 0; // сброс счетчика подтверждений состояния кнопки
    }
    else {
    Если условие if истинно, то выполняется блок в фигурных скобках, т.е. там один оператор buttonCount= 0;
    Тогда условие else со своим огромным нижеследующим блоком не выполняется. И наоборот. Правильно я понимаю?

    0
  50. Здравствуйте, Эдуард! Спасибо за оперативный ответ. Но тогда, извините, полная эпидерсия получается и программа не зарабоатет, или я такой тупой. Ну смотрите:
    включили устройство и не трогаем его, т.е. кнопка не нажата, стало быть функция digitalRead(BUTTON_PIN) прочитает нам единичку, мы ее инвертируем, т.к. «!» стоит, и получаем «0», т.е. false. Правильно? Значит блок идущий сразу после if не выполняем, а переходим к else. Правильно? В блоке else мы счетчик увеличиваем на единичку, проверяем условие, равна ли единичка двенадцати, не равна, уходим на задержку 2 мс и т.д. Затем, когда счетчик сравнялся с 12-ю, инвертируем flagPress, flagClick и переключаем диод. Но мы ничего же не трогали…..

    0
  51. Ну как же так. Получается если условие if ложно, Вы же тоже мне пишите вверху «flagPress равен false», то выполняться должно условие else…
    Или Вы имеете ввиду, что переменная boolean flagPress= false; у нас объявлена в начале программы, поэтому если условие if ( flagPress == (! digitalRead(BUTTON_PIN)) ) не нарушает это положение, то вроде как оно (условие) истинно, и else не выполняется. Так что ли?

    0
  52. Т.е. я был бы прав, если бы вы в начале программы не объявили переменную boolean flagPress= false; и не присвоили бы ей исходное значение false. Правильно?

    0
  53. Здравствуйте. Присоединяюсь ко всем благодарностям за ваши уроки, Эдуард. У меня вопрос по sketch_6_1. Если кнопка будет нажата в момент времени, исполнение программы будет находиться где то между этими строчками

    if ( (buttonPrevState == HIGH) && (buttonState == LOW) ) {

    buttonPrevState= buttonState;

    то ни чего не произойдет? т.е. состояние светодиода не изменится?

    0
    • Здравствуйте!
      Алгоритм имеет временную задержку. После реального нажатия кнопки признак будет сформирован только через время подтверждения. Это свойство любого электронного фильтра.

      0
      • Понял в чем ошибался, переменная buttonState не может измениться в данном место, она может измениться только в начале бесконечного цикла командой » buttonState= digitalRead(BUTTON_PIN)»

        0
    • Одна команда выполняется за четыре такта кварцевого генератора. При кварце 16МГц ( на arduino uno) за одну секунду выполняется 4 миллиона команд. Вы просто физически не сможете так быстро нажать и отпустить кнопку.

      0
  54. Здравствуйте ! Подскажите , если мы включим устройство с нажатой кнопкой то светодиод загорится ?

    0
    • Здравствуйте!
      Да. Признак flagClick после включения будет равен false. Алгоритм начнет проверять состояние кнопки и выделит фронт. Светодиод также при включении будет погашен, его состояние проинвертируется по признаку.

      0
  55. Здравствуйте Эдуард. Подскажите как сделать чтобы светодиод гас с задержкой в мили секундах, а время высокого уровня выхода, должно задаваться переменным резистором. Время работы выхода общее 300мл сек.
    Алгоритм следующий. поступил сигнал на вход, на переменным резисторе установлено 250мл сек. на выходе высокий уровень.
    На входе низкий уровень, на выходе отрабатывается 250 мл сек. и устанавливается низкий уровень.

    0
  56. Добрый день.
    Начал изучать Ваши уроки, до сие урока нажатие кнопки работало хорошо (правда светодиод горел в несколько раз слабее).
    Начиная с данного урока при включении контроллера, светодиод гас, но при первом нажатии кнопки, загорается ярко и больше ни на что не реагирует. Код писал как сам так и копировал, логика понятна, но контроллер почему то не фурычит.

    0
  57. проверил 3 урока с кнопкой.
    scetch_5_1 урока 5 — работает, светодиод горит слабо.

    sketch_6_2 урока 6 и sketch_7_1 урока 7
    загорается ярко при первом нажатии и больше не реагирует.
    Хочу разобраться.

    0
    • Здравствуйте!
      Ищут обычно в ситуации с самой простой ошибкой. Разберитесь, почему светодиод светится тускло в уроке 5. Возможно у вас аппаратное замыкание с другим сигналом.

      0
  58. Доброго времени суток.
    Каким образом признак flagPress возвращается обратно в false?
    В комментариях уже был этот вопрос, но я никак не могу вникнуть.
    Может он сбрасывается каждый раз при начале цикла?
    flagClick ведь специально сбрасывается командой flagClick= false;

    0
      • После того как мы его инвертировали:
        flagPress= ! flagPress в состояние true, этот признак вызывается только в выражении:
        if ( flagPress == true ) flagClick= true;
        и получается в следующем цикле flagPress имеет значение true а нужно false…

        0
          • Как я понимаю, чтобы flagPress отслеживал текущее значение у него должно быть что то вроде вот этого:

            flagPress = digitalRead(BUTTON_PIN);

            Тогда тут возникает другой вопрос: как мы определяем для flagClick то, что у него есть память?
            как так получается что оба признака имеют одинаковый тип данных boolean, но один сохраняет свое значение, а другой нет?и как тогда flagPress может хранить значение true при инверсии?
            Или это зависит от применяемых к ним операторов «=» или» !» ?

            0
          • Признак flagPress отслеживает состояние вывода с некоторой задержкой. Происходит цифровая фильтрация.
            flagClick метод класса Button только устанавливает. Сбрасываться он должен в основной программе при обработке события. Поэтому он и запоминается.

            0
  59. Проще говоря, нажатие кнопки ставит flagPress в значение true, а отпускание сбрасывает до false?

    0
  60. Огромное спасибо, материал излагается в доступной и очень понятной форме. Не планируются ли уроки по работе связки ардуино+адресные светодиоды (ws2812)?

    0
    • Здравствуйте! Спасибо за отзыв.
      Что касается ws2812 — да собираюсь сделать внеплановый урок. Даже библиотека для создания локальных эффектов есть.
      Планирую написать еще 2-3 урока по STM 32 (надеюсь это неделя). Затем WEB-клиент. А дальше внеплановые уроки по ws2812 и управлению мощной нагрузкой.

      0
  61. Здравствуйте Эдуард! А можно где нибудь подобный урок найти для amtelstudio, интересует именно предыдущее состояние, т.к. все примеры продолжают выполнение программы если кнопка остаётся во включенном состоянии

    0
  62. Дребезг контактов можно отлавливать проще. По умолчанию кнопка отжата =HI, ловим первый LOW, и запускаем задержку в 0,1с. Задержку можно реализовать любыми способами — таймером, а лучше счетчиком цикла. Тогда программа будет крутиться без ожидания. Так же можно реализовать подсчет времени нажатия для различения коротких/длинных нажатий.

    0
  63. Здравствуйте! Подскажите как построить алгоритм программы,если нужно фильтровать перепады сигнала как с высокого в низкий ,так и с низкого в высокий от механического контактного датчика. Нужно ли вводить новый признак для фиксации перепада в высокий фронт? Заранее благодарен.

    0
    • Здравствуйте!
      Сейчас признак перепада с высокого на низкий уровень вырабатывается в строке:
      if ( flagPress == true ) flagClick= true; // признак фронта кнопки на нажатие
      Т.е. если произошло изменение состояния сигнала и оно стало низкого уровня, то формируем признак flagClick.
      Вам необходимо объявить еще один признак (на положительный фронт), например flagClickP, и устанавливать его, когда произошло изменение состояния и оно стало высоким.
      if ( flagPress == false ) flagClickP= true; // признак фронта кнопки на нажатие

      0
  64. Здравствуйте Эдуард. Огромное спасибо за ваш труд!!) Возник вопрос по Программе sketch_6_2, после строки flagPress= ! flagPress; идёт строка «buttonCount= 0; // сброс счетчика подтверждений состояния кнопки». так вот суть вопроса- зачем тут нужен сброс счётчика? По факту и без этой строчки всё правильно работает, так как счётчик обнулится в верхних строках if ( flagPress == (! digitalRead(BUTTON_PIN)) ) { buttonCount= 0;} следующей итерации. Или же есть какие то специфичные редкие ситуации для которых нужна будет эта строчка?

    0
    • Здравствуйте!
      А если в следующем проходе условие flagPress == (! digitalRead(BUTTON_PIN) не выполнится, т.е. кнопку отпустят.
      Формальный подход — счетчик достиг нужного значения, надо его сбросить. Если искать последующие ветки алгоритма в которых произойдет нужное событие, то можно ошибиться, особенно при сложных алгоритмах. А если алгоритм захочется подкорректировать, то при вашем подходе надо будет отслеживать, как это повлияет на локальные состояния алгоритма. Лучше подходить формально.

      0
      • «А если в следующем проходе условие flagPress == (! digitalRead(BUTTON_PIN) не выполнится, т.е. кнопку отпустят.»
        Тогда получится flagPress станет единицей а (! digitalRead(BUTTON_PIN)) станет равным нулю, что запустит buttonCount++; счётчик добавляет еще одну единичку к отсчёту, допустим buttonCount= 13, но след строка if ( buttonCount >= TIME_BUTTON ) опять же выполняет нужное нам условие flagPress= ! flagPress; что в следующей итерации будет выглядеть как flagPress станет нулём и (! digitalRead(BUTTON_PIN)) всё так же будет нулём, что совпадёт с условием обнуления счётчика. Тоесть всё итак будет работать. Очень интересная и элегантная конструкция) Расписываю скорее для себя и тех кто потом может будет читать. Итог: именно тут можно всё оставить как я написал, без строчки buttonCount= 0; после строки flagPress= ! flagPress;. и всё будет абсолютно правильно работать. НО! лучше в последующих работах несколько перестраховываться и «подходить формально». Огромное спасибо ещё раз)

        0
  65. За счёт дребезга счётчик может обнуляться даже при нажатой кнопке- вывод , у невезучих светодиод никогда не переключится:)

    0
  66. Эдуард, здравствуйте!
    Спасибо за интересный урок по Ардуино.
    Подскажите, пожалуйста, как можно в данном скетче реализовать раздельную регулировку времени антидребезга, чтобы при переходе из «0» в «1» было время, например, 0,5 сек, а при переходе из «1» в «0» — 2 сек?

    0
    • Здравствуйте!
      В скетче переходы на оба фронта сигнала объединены в одном блоке:
      if ( flagPress == (! digitalRead(BUTTON_PIN)) )
      Надо разъединить на блоки перехода из состояний 0 в 1 и из 1 в 0.
      if ( flagPress == true && digitalRead(BUTTON_PIN) == HIGH )
      if ( flagPress == false && digitalRead(BUTTON_PIN) == LOW )
      И в каждом блоке использовать разные времена подтверждения.

      0
      • Эдуард, здравствуйте!
        Видимо, я что-то всё равно делаю не так.
        Добавил второе время подтверждения, но программа все равно работает только с одним временем, причем, выбирает всегда время с наименьшим значением.
        Вместо предложенного Вами
        if ( flagPress == true && digitalRead(BUTTON_PIN) == HIGH ) сделал
        if ( flagPress == true && digitalRead(BUTTON_PIN) == LOW ) с учетом того, что у нас кнопка PULLUP (если кнопка нажата, то вывод сидит на земле).
        Разъединить время подтверждения не получается.
        Что я делаю не так?

        #define LED_PIN 13 // номер вывода светодиода равен 13
        #define BUTTON_PIN 6 // номер вывода кнопки равен 6

        // переменные и константы для обработки сигнала кнопки
        boolean flagPress= false; // признак кнопка в нажатом состоянии
        boolean flagClick= false; // признак нажатия кнопки (фронт)
        byte buttonCount= 0; // счетчик подтверждений состояния кнопки
        #define TIME_BUTTON_01 2500 // время устойчивого состояния кнопки (* 2 мс)
        #define TIME_BUTTON_10 250 // время устойчивого состояния кнопки (* 2 мс)

        boolean ledState; // переменная состояния светодиода

        void setup() {
        pinMode(LED_PIN, OUTPUT); // определяем вывод 13 (светодиод) как выход
        pinMode(BUTTON_PIN, INPUT_PULLUP); // определяем вывод 6 (кнопка) как вход
        Serial.begin(9600);
        }

        // бесконечный цикл с периодом 2 мс
        void loop() {

        /* блок обработки сигнала кнопки
        * при нажатой кнопке flagPress= true
        * при отжатой кнопке flagPress= false
        * при нажатии на кнопку flagClick= true */

        if ( flagPress == true && digitalRead(BUTTON_PIN) == LOW ) // переход в состояние «Кнопка нажата»
        {

        buttonCount= 0; // сброс счетчика подтверждений состояния кнопки
        }
        else {
        // признак flagPress не = текущему состоянию кнопки
        // состояние кнопки изменилось
        buttonCount++; // +1 к счетчику состояния кнопки

        if ( buttonCount >= TIME_BUTTON_01 ) {
        // состояние кнопки не мянялось в течение заданного времени
        // состояние кнопки стало устойчивым
        flagPress= ! flagPress; // инверсия признака состояния
        buttonCount= 0; // сброс счетчика подтверждений состояния кнопки

        if ( flagPress == true ) flagClick= true; // признак фронта кнопки на нажатие
        }
        }

        if ( flagPress == false && digitalRead(BUTTON_PIN) == HIGH ) // переход в состояние «Кнопка отпущена»
        {

        buttonCount= 0; // сброс счетчика подтверждений состояния кнопки
        }
        else {
        // признак flagPress не = текущему состоянию кнопки
        // состояние кнопки изменилось
        buttonCount++; // +1 к счетчику состояния кнопки

        if ( buttonCount >= TIME_BUTTON_10 ) {
        // состояние кнопки не мянялось в течение заданного времени
        // состояние кнопки стало устойчивым
        flagPress= ! flagPress; // инверсия признака состояния
        buttonCount= 0; // сброс счетчика подтверждений состояния кнопки

        if ( flagPress == true ) flagClick= true; // признак фронта кнопки на нажатие
        }
        }

        // блок управления светодиодом

        if (flagPress == true) digitalWrite(LED_PIN, LOW); // вывод состояния светодиода
        if (flagPress == false) digitalWrite(LED_PIN, HIGH); // вывод состояния светодиода

        delay(2); // задержка на 2 мс
        }

        0
        • События должны быть такими:
          Кнопка была не нажата, а стала нажатой
          if ( flagPress == false && digitalRead(BUTTON_PIN) == LOW )

          Кнопка была нажатой, а стала не нажатой

          if ( flagPress == true && digitalRead(BUTTON_PIN) == HIGH )

          Только чтение состояния кнопки лучше сделать одно, занести в переменную и сравнивать ее.

          0
          • Эдуард, добрый день.
            С наступившим Вас Новым годом и всего самого наилучшего!
            Пользуясь выходными, снова вернулся к Ардуино.
            Для TIME_BUTTON не удалось задать значение, больше, чем 250. Это всего лишь 0,5 секунды, а мне надо хотя бы 1 сек при переходе «выкл-вкл»и 7 секунд при переходе «вкл-выкл». Причину не понял.
            К сожалению, в остальном ситуация не поменялась, разделения времени антидребезга получить не удалось.

            Подскажите, что я неправильно делаю?

            #define LED_PIN 13 // номер вывода светодиода равен 13
            #define BUTTON_PIN 6 // номер вывода кнопки равен 6

            // переменные и константы для обработки сигнала кнопки
            boolean flagPress = false; // признак кнопка в нажатом состоянии
            boolean flagClick = false; // признак нажатия кнопки (фронт)
            byte buttonCount = 0; // счетчик подтверждений состояния кнопки
            #define TIME_BUTTON_01 250 // время устойчивого состояния кнопки (* 2 мс)
            #define TIME_BUTTON_10 200 // время устойчивого состояния кнопки (* 2 мс)

            //boolean ledState; // переменная состояния светодиода

            void setup() {
            pinMode(LED_PIN, OUTPUT); // определяем вывод 13 (светодиод) как выход
            pinMode(BUTTON_PIN, INPUT_PULLUP); // определяем вывод 6 (кнопка) как вход
            Serial.begin(9600);
            }

            // бесконечный цикл с периодом 2 мс
            void loop() {

            /* блок обработки сигнала кнопки
            * при нажатой кнопке flagPress= true
            * при отжатой кнопке flagPress= false
            * при нажатии на кнопку flagClick= true */

            if ( flagPress == false && digitalRead(BUTTON_PIN) == LOW ) // переход в состояние «Кнопка нажата»
            {

            buttonCount= 0; // сброс счетчика подтверждений состояния кнопки
            }
            else {
            // признак flagPress не = текущему состоянию кнопки
            // состояние кнопки изменилось
            buttonCount++; // +1 к счетчику состояния кнопки

            if ( buttonCount >= TIME_BUTTON_01 ) {
            // состояние кнопки не менялось в течение заданного времени
            // состояние кнопки стало устойчивым
            flagPress= ! flagPress; // инверсия признака состояния
            buttonCount= 0; // сброс счетчика подтверждений состояния кнопки

            if ( flagPress == true ) flagClick = true; // признак фронта кнопки на нажатие
            }
            }

            if ( flagPress == true && digitalRead(BUTTON_PIN) == HIGH ) // переход в состояние «Кнопка отпущена»
            {

            buttonCount= 0; // сброс счетчика подтверждений состояния кнопки
            }
            else {
            // признак flagPress не = текущему состоянию кнопки
            // состояние кнопки изменилось
            buttonCount++; // +1 к счетчику состояния кнопки

            if ( buttonCount >= TIME_BUTTON_10 ) {
            // состояние кнопки не мянялось в течение заданного времени
            // состояние кнопки стало устойчивым
            flagPress= ! flagPress; // инверсия признака состояния
            buttonCount= 0; // сброс счетчика подтверждений состояния кнопки

            if ( flagPress == true ) flagClick = false ; // признак фронта кнопки на отжатие
            }
            }
            // блок управления светодиодом

            if (flagPress == true) digitalWrite(LED_PIN, HIGH); // вывод состояния светодиода
            if (flagPress == false) digitalWrite(LED_PIN, LOW); // вывод состояния светодиода

            delay(2); // задержка на 2 мс
            }

            0
          • Здравствуйте!
            Вот мой вариант программы. Мог ошибиться, не проверял.

            #define LED_PIN 13 // номер вывода светодиода равен 13
            #define BUTTON_PIN 12 // номер вывода кнопки равен 12

            // переменные и константы для обработки сигнала кнопки
            boolean flagPress= false; // признак кнопка в нажатом состоянии
            boolean flagClick= false; // признак нажатия кнопки (фронт)
            byte buttonCount= 0; // счетчик подтверждений состояния кнопки
            #define TIME_BUTTON_P 12 // время устойчивого состояния нажатой кнопки (* 2 мс)
            #define TIME_BUTTON_F 12 // время устойчивого состояния отжатой кнопки (* 2 мс)

            boolean ledState; // переменная состояния светодиода
            boolean sigState; // переменная состояния сигнала

            void setup() {
            pinMode(LED_PIN, OUTPUT); // определяем вывод 13 (светодиод) как выход
            pinMode(BUTTON_PIN, INPUT_PULLUP); // определяем вывод 12 (кнопка) как вход
            }

            // бесконечный цикл с периодом 2 мс
            void loop() {

            sigState= digitalRead(BUTTON_PIN);

            if( flagPress == true && sigState == HIGH ) {
            // кнопка была нажата, а стала отжатой
            buttonCount++; // +1 к счетчику состояния кнопки
            if ( buttonCount >= TIME_BUTTON_F ) {
            flagPress= false;
            }
            }

            else if( flagPress == false && sigState == LOW ) {
            // кнопка была отжата, а стала нажатой
            buttonCount++; // +1 к счетчику состояния кнопки
            if ( buttonCount >= TIME_BUTTON_P ) {
            flagPress= true;
            flagClick= true;
            }
            }

            else {
            // кнопка не изменила состояния
            buttonCount= 0; // сброс счетчика подтверждений состояния кнопки
            }

            // блок управления светодиодом
            if ( flagClick == true ) {
            // было нажатие кнопки
            flagClick= false; // сброс признака фронта кнопки
            ledState= ! ledState; // инверсия состояние светодиода
            digitalWrite(LED_PIN, ledState); // вывод состояния светодиода
            }

            delay(2); // задержка на 2 мс
            }

            0
  67. Здравствуйте, Эдуард.
    Подскажите, пожалуйста, почему в данном примере (sketch_6_2) если установить время устойчивого состояния кнопки нечетным, светодиод горит до следующего нажатия кнопки? Возможно, где-то ошибся при написании аналогичного кода, но найти причину не могу, код прилагаю:

    #define BUTTON1 13
    #define LED1 12
    #define BUTTON1_TIME 15

    bool button1_pressed = false;
    bool button1_clicked = false;
    byte button1_count = 0;
    bool led1_lit;

    void setup()
    {
    pinMode(BUTTON1, INPUT_PULLUP);
    pinMode(LED1, OUTPUT);
    }

    void loop()
    {
    if (button1_pressed == (! digitalRead(BUTTON1))) button1_count = 0;
    else
    {
    button1_count++;
    if (button1_count >= BUTTON1_TIME)
    {
    button1_pressed = (! button1_pressed);
    button1_count = 0;
    }
    if (button1_pressed == true) button1_clicked = true;
    }

    if (button1_clicked == true)
    {
    button1_clicked = false;
    led1_lit = (! led1_lit);
    digitalWrite(LED1, led1_lit);
    }

    delay(2);
    }

    0
    • Здравствуйте!
      Так он и должен гореть до следующего нажатия кнопки. При каждом нажатии состояние светодиода инвертируется.

      0
      • Тогда другой вопрос… Почему при четном времени устойчивого состояния кнопки он горит, пока кнопка нажата?

        0
          • Да, в вашем примере работает правильно. Тогда не понятно, что не так с моим, по структуре, кажется, он полностью аналогичен..

            0
          • Да мне тоже кажется все правильно. Проверьте аккуратно каждую строку.
            А еще, у вас кнопка с вывода на землю подключена? Активный уровень 0?

            0
          • Так, вроде, все верно. Активный режим 0. Пробовал даже разные пины, думал, вдруг конструктивные особенности какие-то, но нет.

            0
          • Вы в качестве вывода для кнопки выбрали вывод 13. Но к нему подключен светодиод, который засаживает сигнал на землю. У светодиода падение напряжения 1,5 В, плюс падение на ограничивающем резисторе. В результате напряжение на входе близко к пороговому.

            0
          • Здравствуйте, Эдуард.
            С момента предыдущего обсуждения появилась возможность проверить: светодиод в схеме подключен к выводу 12, кнопка к 13, активный уровень кнопки 0. Эффект сохраняется. По всем признакам ошибка где-то в коде, но я никак не вижу, где именно несоответствие.

            0
          • Я нашел)
            Строка 27, вынес из блока случайно. Какой ужас)
            Спасибо Вам огромное за Ваш труд и помощь)

            0
  68. Добрый день Эдуард. Только начинаю изучать программирование, скажите пожалуйста, а если мы включим программный блок исключения дребезга контактов в какой-нибудь другой блок, то получается что задержку delay 2 ms надо тоже включать в тот другой блок, иначе задержка будет только 12 ms? Или я не прав?

    0
    • Здравствуйте!
      Это учебный пример. В 10 уроке рассказывается о практическом применении библиотеки. Обработка состояния кнопки происходит в прерывании по таймеру.

      0
  69. Здравствуйте, Эдуард. А насколько эффективен такой вариант? Его работоспособность проверена, но вдруг ошибка в подходе и что-то не учтено?

    void loop() {
    if (digitalRead(5)==LOW) Change13();
    }
    void Change13() {
    long int cnt=0;
    while(digitalRead(5)==0) cnt++;
    if (cnt >100000) flagPress=TRUE;
    return;
    }

    0
    • Здравствуйте!
      В вашем варианте программа зависает в обработчике сигнала кнопки. А если несколько кнопок? В программе, кроме обработки кнопок, еще что-то надо делать.
      Есть еще недостатки. Например, вы измеряете время, которое кнопка находится в нажатом состоянии в каких-то условных единицах. В результате время устранения дребезга будет зависеть от быстродействия микроконтроллера.

      0
  70. Здравствуйте, не совсем понятно зачем использовать низкий уровень сигнала кнопки для условия срабатывания. В случае обрыва провода, неисправности кнопки и т.п. вы получите ложный сигнал на выполнение команды

    0
    • Здравствуйте!
      Сигнал, который находится в высоком уровне значительно меньше подвержен помехам, чем сигнал с нулевым уровнем. Поэтому принято выбирать для сигналов низкий активный уровень.

      0
  71. перечитываю ваши уроки чтобы освежить память . на склоне небосвода ардуино вас можно назвать суперзвездой . и вот мне пришла мысль , может проще написать программу реагирующую только на одно нажатие и затем в течении времени больше времени дребезга в 2 или 3 раза не реагировать на состояние кнопки. то есть с нажатием кнопки сначала сьрасываетсяв ноль и затем запускается счётчик , и реакция отклика ардуино равна логическому умножению условия превышения показания счётчика нами заданной величины на показания кнопки . программа может стать ещё более простой и более понятной а рейтинг ваш поднимется ещё выше .

    0
  72. в этом случае нам нужно будет пока только flagClick и количество циклов счетчика в течении которых ардуино не реагирует на угрозу дребезга . ну а flagPress понадобится только в программах где долгое нажатие скажем на 5 секунд вызывает одно действие а еще скажем на 10 секунд уже другое действие , если мало кнопок .

    0
  73. программа не будет ждать так как не будет delay , а будет приращение счётчика в каждом цикле , то есть условно параллельная многозадачность . а помехи и наводки легко убираются привязкой кнопки через резистор 10Ком к плюсу или минусу или просто командой , INPUT_PULLAP .

    0
  74. пожалуй имеет смысл исправить ошибку в строке
    #define TIME_BUTTON 12 // время устойчивого состояния кнопки (* 2 мс)
    на более правильный комментарий
    #define TIME_BUTTON 12 // TIME_BUTTON это количество проверок состояния кнопки (* 2 мс)

    0
  75. Здравствуйте , Эдуард .
    Начал учиться программированию для контроллера Ардуино. Ваши уроки, это что-то.Так еще ни кто не разжевывал. И за это большое СПАСИБО. Мне ухе за 50, поэтому приходится напрягаться, да и память не как в молодости. Поэтому в скетче для себя делаю пояснения ( /**/ ) действий, да и определения на функции туда же, для лучшего понимания рисую алгоритм отдельно. Читая такой скетч не надо ни куда отвлекаться, что либо вспоминать.
    НО я тольно начал и прошу Вас не удаляйте Ваши уроки (всякое бывает), они очень актуальны.
    Еще раз большое спасибо.

    0
      • Спасибо и Вам.
        Еще есть вопрос.
        Я изменил итоговый скетч урока 5 добавив еще одну кнопку и изменив выход светодиода, дабы небыло ни каких зависимостей схемы выхода внутреннего 13го светодиода.В итоге вот что получилось :

        /* Программа урока 5 (дополнения)
        Зажигает светодиод (вывод 3 на минус с R = 2К ) при нажатии кнопок (вывод 12) и (вывод 11) */

        #define LED_PIN 3 // номер вывода светодиода равен 3
        #define BUTTON1_PIN 12 // номер вывода кнопки равен 12
        #define BUTTON2_PIN 11 // номер вывода кнопки равен 11

        void setup() {
        pinMode(LED_PIN, OUTPUT); // определяем вывод 3 (светодиод) как выход
        pinMode(BUTTON1_PIN, INPUT); // определяем вывод 12 (кнопка1) как вход
        pinMode(BUTTON2_PIN, INPUT); // определяем вывод 11 (кнопка2) как вход
        }

        void loop() {
        digitalWrite(LED_PIN, ! digitalRead(BUTTON1_PIN) );
        digitalWrite(LED_PIN, ! digitalRead(BUTTON2_PIN) );
        }

        //——————- итоги—————————————————————————————
        /* 1 — 2 кнопки ( 12 , 11 ) на минус с внутренним подтягивающим резистором прописанным програмно( INPUT_PULLUP)
        — напряжения на — вых3 — — вх12 — — вх11 — — падение на LED — падение на R(2К) — яркость LED —
        2.44 1 0 1.26 0.93 2 из 5
        4.61 1 1 2.61 1.99 3 из 5
        2.40 0 1 1.26 0.93 2 из 5
        2 — 2 кнопки ( 12 , 11 ) на минус с внешним подтягивающим резистором на плюс(10К) прописанным програмно( INPUT )
        — напряжения на — вых3 — — вх12 — — вх11 — — падение на LED — падение на R(2К) — яркость LED —
        2.44 1 0 1.26 0.93 2 из 5
        4.61 1 1 2.61 1.99 3 из 5
        2.40 0 1 1.26 0.93 2 из 5 */

        не знаю как отобразится скетч в комментариях.

        Но вопрос: почему так влияет количество входов работающих на один выход, ведь они не зависимы. Или я ошибаюсь ?

        0
        • Здравствуйте!
          А что вы хотели добиться такой программой? Какой практический или учебный смысл?
          У вас:
          если обе кнопки отжаты, то на выходе лог. 0;
          если нажата только кнопка 1, то в цикле на короткое время (примерно 5 мкс, от первого до второго digitalWrite) на выходе проскакивает лог. 1;
          если нажата кнопка 2, то импульс на выходе удлиняется (от второго digitalWrite до первого);
          если обе кнопки нажаты, то на выходе лог. 0.
          Вы измеряете вольтметром среднего значения, поэтому напряжение получается пропорциональное скважности сигнала. И еще изменение напряжения на выходе вносит светодиод, т.к. напряжение на выходе микроконтроллера зависит от сопротивления нагрузки.

          0
          • Вникнув в ответ решил возразить, основываясь на проделанной практике. Когда включены одновременно две кнопки, то на выходе лог. 1.с более высоким выходным напряжением (я своими словами).

            0
          • Если вы посмотрите осциллографом, то увидите, что в этом случае на выходе сигнал со стабильным уровнем. А если нажата одна кнопка, то импульсы _-_-_-_. Амплитуду импульсов умноженную на скважность.

            1
        • Прошу прощения, не учел ширины вводимого комментария, а как изменить введенный не знаю.
          Итоговые:
          таблица: вариант 1 (с внутренним подтягивающим резистором)

          вых3—вх12—вх11—-LED——R———яркость
          2.44——1———0——1.26—-0.93——-2 из 5
          4.61——1———1——2.61—-1.99——-3 из 5
          2.44——0———1——1.26—-0.93——-2 из 5

          таблица вариант 2 (с внешним подтягивающим резистором (10К))

          вых3—вх12—вх11—-LED——R———яркость
          2.44——1———0——1.93——1.49——2 из 5
          4.61——1———1——2.61——1.99——3 из 5
          2.44——0———1——1.93——1.49——2 из 5

          Почему количество входов програмно завязаных на один выход так меняют его выходное напряжение ?

          0
  76. Во дворе 2023 год. Не смотря на то, что прошло больше 6 лет с выпуска этих уроков, я до сих пор поражаюсь этими великолепными уроками. Только прочитав повторно доходит с большим пониманием. Спасибо, что когда-то решили стать обучать людей в интернет просторах с невероятно ясным языком и бесплатно, вы прекрасный человек!

    0
  77. и еще такой вопрос — я заменил название типа boolean на bool,
    а значение этой переменной на 0 и 1.Работа программы не изменилась.
    Можно ли это делать в языке WIRING

    0
  78. Если кнопка не нажата, то есть не активна , то на ее ножке функция digitalRead
    читает высокий уровень т.е.1.После инверсии имеем 0.
    Флаг flagPress при не нажатой кнопке равен 0. Таким образом имеем 0==0
    и оператор if работает.Происходит сброс счетчика buttonCount= 0.
    Если нажимаем на кнопку , т.е. она становиться активной и функция digitalRead
    читает на ее ножке 0.После инверсии получаем 1.Почему считаем » // признак flagPress не = текущему состоянию кнопки» если чуть раньше записано
    «* при нажатой кнопке flagPress= true»
    «* при отжатой кнопке flagPress= false»
    т.е. flagPress= 1 (при нажатой кнопке) и состояние кнопки =1 имеем 1==1, т.е. условие для if не изменилось. получается противоречие.Где я ошибаюсь?
    Спасибо.

    0
    • Условие для if изменилось на время, когда предыдущее состояние (flagPress) еще не равно новому текущему состоянию (digitalRead). А предыдущее состояние изменяется после отработки счетчика (flagPress). По окончанию этого времени вырабатывается флаг flagClick и принимается решение о состоянии кнопки (flagPress).

      0
  79. А у меня всё по простому.Нажимаю кнопку выставляю флаг .И при условии выставленного флага в обработчике прерывания по таймеру начинается счёт.И при count==10 произходит какое то действие.При отжатии кнопки флаг=0.count(счёт)=0;у меня другая проблема как избежать дребезга контактов аппаратного энкодера stm32?

    0

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Нажимая кнопку "Отправить" Вы даёте свое согласие на обработку введенной персональной информации в соответствии с Федеральным Законом №152-ФЗ от 27.07.2006 "О персональных данных".