Дребезг контактов и способы подавления дребезга.

Что такое дребезг контактов

Дребезг контактов на экране осциллографа

Дребезг контактов на экране осциллографа

Дребезг контактов — явление, возникающее в электрических и электронных переключателях, при котором они вместо некоторого стабильного переключения производят случайные многократные неконтролируемые замыкания и размыкания контактов (происходит в момент переключения, приблизительно в течение 40—100 мс). Иными словами — это явление, вызванное неизбежным несовершенством технологии изготовления переключателей.

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

Для понимания сути, нам нужно рассмотреть простой код, который зажигает встроенный в плату Arduino светодиод на пине 13 при нажатии кнопки и гасит светодиод при следующем нажатии. Кнопка подключена ко второму пину (о подключении кнопки читайте статью « Как подключить кнопку к Arduino »):


byte led=0; //Состояние светодиода
byte oldled=1; //Последнее состояние светодиода, для исключения ложных переключений

void setup()
{
 pinMode(13, OUTPUT);
 pinMode(2, INPUT);
}

void loop()
{
if(digitalRead(2)==HIGH) { //Когда нажата кнопка
   if (led==oldled) { //Проверка, что состояние кнопки изменилось
     led=!led;
   }
} else {
 oldled=led;
}
 digitalWrite(13,led); //Переключаем светодиод

}

Это рабочий код, но из-за дребезжания контактов кнопки он поймает много лишних срабатываний. Причём всё происходит так быстро, что программа успеет поймать ложные срабатывания несколько раз и несколько раз включить-выключить светодиод. В итоге, ваше устройство будет работать случайным образом. Если повезёт — светодиод будет гореть после нажатия. Не повезёт — цикл проскочит на состояние, когда светодиод окажется выключенным.

Программный способ избавления от дребезга

Суть программного способа в том, чтобы внутри программы отличить случайные срабатывания из-за дребезга от настоящего нажатия. Самый простой способ: добавить задержку сразу после первого срабатывания. Тогда при срабатывании кнопки мы останавливаем выполнение программы и дожидаемся, пока контакты кнопки перестанут дребезжать. Ложных срабатываний не будет.


byte led=0; //Состояние светодиода
byte oldled=1; //Последнее состояние светодиода, для исключения ложных переключений

void setup()
{
 pinMode(13, OUTPUT);
 pinMode(2, INPUT);
}

void loop()
{
if(digitalRead(2)==HIGH) { //Когда нажата кнопка
   delay(100); //Защита от дребезга
   if (led==oldled) { //Проверка, что состояние кнопки изменилось
     led=!led;
   }
} else {
 oldled=led;
}
 digitalWrite(13,led); //Переключаем светодиод

}

Этот способ прост и понятен, но имеет недостаток: выполнение программы останавливается при нажатии кнопки, что может быть недопустимо в некоторых случаях, а при использовании прерывания невозможно в принципе. Следующий вариант избавления от дребезга, засекать время, прошедшее от первого срабатывания кнопки с помощью функции millis:

byte led=0; //Состояние светодиода
byte oldled=1; //Последнее состояние светодиода, для исключения ложных переключений
long previousMillis = 0; //Время последнего нажатия кнопки

void setup()
{
 pinMode(13, OUTPUT);
 pinMode(2, INPUT);
}

void loop()
{
if(digitalRead(2)==HIGH) { //Когда нажата кнопка
   //Если от предыдущего нажатия прошло больше 100 миллисекунд
   if (millis() - previousMillis > 100) {
     //Запоминаем время первого срабатывания
     previousMillis = millis();
     if (led==oldled) { //Проверка, что состояние кнопки изменилось
       led=!led;
     }
   }
} else {
 oldled=led;
}
digitalWrite(13,led); //Переключаем светодиод

}

Этот код обеспечивает избавление от дребезга и при этом не блокирует выполнение программы, как это происходит при использовании функции delay.

Еще одним способом избавлением от дребезга является использование библиотеки Bounce. В ней уже все предусмотрено, и для чтения значения на входе используются специальные функции:

// подключение библиотеки Bounce
#include "Bounce.h";

// Создание объекта Bounce.
// В качестве параметров конструктору передаются номер пина кнопки
// и время дребезга в миллисекундах.
Bounce bouncer = Bounce(2,5);

byte led=0; //Состояние светодиода
byte oldled=1; //Последнее состояние светодиода, для исключения ложных переключений

void setup() {
  pinMode(13,OUTPUT);
  pinMode(2,INPUT);
}

void loop() {
  // Опрос изменения состояния кнопки
  if ( bouncer.update() ) {
    if ( bouncer.read() == HIGH) { //Когда нажата кнопка
     if (led==oldled) { //Проверка, что состояние кнопки изменилось
       led=!led;
     }
    } else { //Когда не нажата
       oldled=led;
    }
  }
digitalWrite(13,led); //Переключаем светодиод
}

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

Аппаратный способ подавления дребезга

Аппаратные решения всегда ценились за их надёжность и  возможность облегчить написание программы.

Вот как выглядела схема подключения кнопки без аппаратной защиты от дребезга:

Схема подключения кнопки к Arduino

Схема подключения кнопки к Arduino

Добавив пару дополнительных элементов в схему мы сможем полностью избавиться от дребезга, не усложняя при этом код. А добавим мы инвертирующий триггер Шмитта и конденсатор. Мы построим RC-цепь с инвертирующим триггером.
Вот так это выглядит на схеме:

Подавление дребезга с помощью инвертирующего триггера Шмитта

Резистор 10 кОм у нас теперь подтягивает вывод не к земле, а к +5В. Причём подтягивается не вход Ардуино, а вход инвертирующего триггера и заодно, конденсатор. Кнопка же, наоборот, подключена к земле. Сделано это в связи с тем, что триггер у нас инвертирующий. Суть такова: входящий аналоговый сигнал может быть восходящим, либо нисходящим. Внутри триггера определены пороговые значения: ~1,6 В — для нисходящего сигнала и ~0,9 В — для восходящего. Выход триггера становится логической единицей (5 В), при прохождении нисходящим сигналом через свой порог в 0,9 В, при этом нисхождение восходящим сигналом верхнего порога в 1,6 В будет проигнорировано. Аналогично, выход триггера становится логическим нулём (0 В), только когда восходящий сигнал проходит свой порог в 1,6 В, при этом проигнорируется прохождение нисходящим сигналом нижнего порога в 0,9 В. Зона неопределённости между порогами называется гистерезисом. То есть триггер переворачивает сигнал. Конденсатор с резистором образует RC-цепь. RC — от английских названий резистора и конденсатора (resistor и capacitor). RC-цепь замедляет затухание сигнала. То есть при снятии напряжения, оно пропадёт не сразу, а будет плавно снижаться. При нажатии кнопки мы прижмём RC-цепь к земле, конденсатор начнёт разряжаться и напряжение начнёт плавно падать. После отпускания кнопки, напряжение начнёт так же плавно расти:

График изменения напряжения RC-цепи

График изменения напряжения RC-цепи

Инвертирующий триггер выполняет 2 функции:

1) он инвертирует (переворачивает) логические уровни: единица становится нулём, а ноль становится единицей.

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

График изменения напряжения на выходе инвертирующего триггера Шмитта

График изменения напряжения на выходе инвертирующего триггера Шмитта