Не отступай и не сдавайся !!!

+7(495)233-9079
arduino4home@gmail.com

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

Предыдущий урок Ардуино - главная Следуюший урок

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

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

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

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

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

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

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

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

/*  Программа 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 (кнопка) как вход
}

void loop() { // бесконечный цикл с периодом 2 мс 
/* блок обработки сигнала кнопки
 * при нажатой кнопке 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 сек) светодиод не меняет своего состояния. Т.е. алгоритм подтверждения устойчивого состояния работает правильно.

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


Хостинг нашего сайта осуществляется узлом www.cherepovets-city.ru
© 2000-2018 23/8/18 21:46->