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

С давних времен, любой специалист в области электроники, знает о таком коварном явлении как дребезг контактов (contact bounce). Человека, не посвященного в эту тайну, проблемы возникающие из-за дребезга контактов могут довести до исступления. Рассмотрим причины этого явления и методы его устранения на простом примере.

Представьте себе примитивную схему, состоящую из микроконтроллера, ЖК дисплея и одной единственной кнопки. Суть работы устройства самая что ни на есть примитивная -  жмешь кнопку, и число на дисплее увеличивается на единичку. Соответствующая программа для платформы Arduino представлена ниже.

Счетчик с ЖК дисплеем

#include <LiquidCrystal.h>

LiquidCrystal lcd(3, 4, 5, 6, 7, 8);
const byte btn_pin = 2;
const int lcd_timeout = 100;

boolean btn = 0;
boolean btn_raw;
unsigned short num = 0;
unsigned int lcd_time;

void setup() {
    lcd.begin(16, 2);
    pinMode(btn_pin, INPUT); 
}

void loop() {
    btn_raw = digitalRead(btn_pin);

    if( btn and !btn_raw )
        num += 1;
    btn = btn_raw;

    if( millis()-lcd_time > lcd_timeout ){
        lcd_time = millis();
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print(num);
    }
}

Несмотря на простую схему и такую же несложную программу, созданное устройство работает неправильно. Нажимая кнопку всего один раз, счетчик увеличивается сразу на 10-15 единиц!

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

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

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

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

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

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

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

Устранение дребезга контактов

#include <LiquidCrystal.h>

LiquidCrystal lcd(3, 4, 5, 6, 7, 8);
const byte btn_pin = 2;
const int lcd_timeout = 100;
const int deb_timeout = 100;

boolean btn = 0;
boolean btn_old = 0;
boolean btn_raw, btn_chk;
unsigned short num = 0;
unsigned int lcd_time, deb_time;

void setup() {
    lcd.begin(16, 2);
    pinMode(btn_pin, INPUT); 
}

void loop() {
    btn_raw = digitalRead(btn_pin);

    if( btn != btn_old ){
        if( btn and !btn_old )
            num += 1;
        btn_old = btn;
    }

    // проверка на устойчивость
    if( btn_raw != btn_chk )
        deb_time = millis();
    btn_chk = btn_raw;

    // завершение проверки
    if( millis()-deb_time > deb_timeout )
        btn = btn_raw;

    if( millis()-lcd_time > lcd_timeout ){
        lcd_time = millis();
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print(num);
    }
}



Comments