Микроконтроллеры и электроника

Устраняем программно дребезг кнопок и переключателей

Кнопки и прочие переключатели — являются неотъемлемой частью, практически любого устройства. Однако, реализация этих устройств приводит к такому нежелательному явлению для микроконтроллерной техники, как дребезг контактов. Что такое дребезг? Давайте разбираться!

Рассмотрим дребезг на примере обычной кнопки, которая представляет из себя 2 контакта, замыкающиеся между собой при нажатии кнопки, т.е. в нажатом состоянии кнопка проводит электрический ток, а в не нажатом не проводит. Также следует иметь ввиду, что в данной статье мы будем рассматривать дребезг на примере подпружиненной кнопки — которая сама возвращается в исходное положение и размыкает цепь, если её отпустить, но все высказанные мною мысли справедливы и для кнопок с фиксацией положения: нажал — кнопка нажалась и осталась нажатой, нажал ещё раз — кнопка вернулась в исходное положение.

При замыкании кнопки напряжение начинает скакать, что связано с тем, что до полного нажатия происходят многократные замыкания-размыкания кнопки. Таким образом, в момент нажатия кнопки её состояние некоторое время изменяется случайным образом и спустя некоторого время, обычно 10-20 мс, хотя зависит от конкретной кнопки, значение стабилизируется. Если кнопка подключена к контроллеру, то контроллер регистрирует этот дребезг как многократное нажатие. Если бы не было защиты от дребезга, то нажимая на клавиатуре букву — вы бы получали, например, 7 букв на экране, затем при попытке стереть их нажатием кнопки backspace, вы бы стирали случайное число символов, например 5. Работать на такой клавиатуре было бы не возможно. Осциллограмма напряжения на такой кнопке выглядит следующим образом. На фото дребезг длиться ровно 10 мс.

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

По хорошему же осциллограмма должна выглядеть следующим образом. Интересный факт, эта осциллограмма является идеальной: как видно из рисунка, переключение происходит мнгновенно, так что для этой кнопки можно не устранять дребезг. Для впечатления, вспомните сколько длится дребезг (я упомянул об этом ранее), и посмотрите на эту осциллограмму, где одна клетка — это 250 нс (1000 нс = 1 мкс). Фото кликабельна — жмите для увеличения картинки.

Идеальная кнопка

Для получения стабильного значения предлагаю следующий алгоритм: контроллер слушает порт (пин), к которому подключена кнопка. Если пин изменяет своё логическое знчение, по сравнению с прошлым состоянием, которое мы тоже запоминаем, то начинаем процедуру проверки: трижды проверяем через равные промежутки времени текущее значение пина и прошлое. Если значения трижды совпали, то можно считать, что значение стабилизировалось и функция возвращает логическое значение пина. Для реализации этого алгоритма предлагаю следующую программу на C. Время задержки между проверками подбирается экспериметально. В случае ошибок в определении — увеличивайте время задержки. Помните, что 1000 мкс = 1 мс. Это общий код, как переделать его под Arduino и язык processing я объясню ниже в этой статье. Код снабжён комментариями, но если что-либо останется для Вас непонятным — я всегда жду Ваши вопросы в комментариях к этому посту.

#define BUTTON PORTC.B2 // Порт, к которому подключена кнопка: в нашем случае PORTC.B2
#define DEBOUNCE_DELAY 5000 // Время между проверками в микросекундах.

int buttonDebounce(void);

void main() { 
	if(buttonDebounce() == 1) {
		// действие если кнопка нажата
	} else {
		// действие если кнопка не нажата
	}
}

int buttonDebounce(void) {
   int result, i, currState, lastState;
   result = lastState = 0;

   if(BUTTON != lastState) {
		lastState = BUTTON; // Запоминаем последнее состояние кнопки
		Delay_us(DEBOUNCE_DELAY); // Делаем задержку
         for(i = 3; i > 0; --i) { // Трижды сравниваем значение кнопки через равные промежутки времени. 
               currState = BUTTON;
               if (currState != lastState) { // Если текущее состояние не совпало с прошлым - завершаем все циклы.
                  break;
               }
               Delay_us(DEBOUNCE_DELAY);
               lastState = currState;
               if(i == 1) {
                    result = BUTTON; // Если трижды значение было одинаково, то возвращаем его в качестве значения функции
               }
         }

   }

   return result; 
}

Для переделывания этого кода под Arduino замените main() на loop(). Перед loop() добавьте конструкцию setup() {} и настройте требуемый пин (куда подключена кнопка) в качестве входа. В макроопределении BUTTON вместо адреса порта укажите номер пина с кнопкой и в дальнейшем коде замените все BUTTON на digitalRead(BUTTON); А также, замените Delay_us на delayMicroseconds. Всё остальное без изменений.

Также, Вы можете почитать о дребезге в Википедии.

Пробуйте, экспериментируйте! Удачных Вам разработок! Жду Ваших вопросов, замечаний, пожеланий и предложений как по этой статье, так и пожелания о теме будущий статей. До скорых встреч!

  • Антон К

    Спасибо за алгоритм, не пришлось самому изобретать, выглядит вполне надежным, хотя может и не самый оптимальный.

    Но в коде есть логическая ошибка. в условиях » if(i = 1) » нужно == ставить, а не присваивание =, иначе алгоритм не будет правильно работать.

    • Благодарю за внимательность! Исправил.