Дребезг контактов, и как с ним бороться

Добавлено 23 октября 2017 в 06:16

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

Теория

Что такое дребезг контактов? Когда вы нажимаете на кнопку или на микропереключатель или изменяете положение тумблера, два металлических контакта замыкаются. Для пользователя может показаться, что контакт наступил мгновенно. Это не совсем правильно. Внутри коммутатора есть движущиеся части. Когда вы нажимаете на коммутатор, он вначале создает контакт между металлическими частями, но только в кратком разрезе микросекунды. Затем он делает контакт немного дольше, а затем еще немного дольше. В конце коммутатор полностью замыкается. Коммутатор скачет (дребезжит) между состояниями наличия и отсутствия контакта. «Когда коммутатор замыкается, два контакта фактически разъединяются и снова соединяются обычно от 10 до 100 раз за время, примерно равное 1 мс» («Искусство схемотехники», Хоровиц и Хилл, второе издание). Обычно оборудование работает быстрее, чем дребезг, что приводит к тому, что оборудование думает, что вы нажали на кнопку несколько раз. Оборудование часто является интегральной микросхемой. Следующие скриншоты иллюстрируют типовой дребезг контактов без какой-либо обработки:

Осциллограмма дребезга контактов
Осциллограмма дребезга контактов

Каждый коммутатор обладает своими собственными характеристиками относительно дребезга. Если вы сравните два одинаковых коммутатора, есть большая вероятность того, что они будут «дребезжать» по-разному.

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

Исследуемые коммутаторы
Исследуемые коммутаторы

Настройка оборудования

Все коммутаторы будут подключены одинаково (это важно, если мы собираемся сравнивать результаты). Сначала мы увидим, как коммутаторы ведут себя без обработки. Основой нашей схемы будет HCF4017BE. Это десятичный счетчик/делитель, производимый STMicroelectronics. Они больше не производят эту микросхему, так как этот тип устарел. Тем не менее, есть много других производителей, которые всё еще выпускают эту маленькую микросхему, и они часто совместимы по контактам.

Микросхема получает тактовый импульс на вывод 14, после чего загорается светодиод, подключенный к Q1. Когда принимается следующий тактовый импульс, микросхема отключает Q1 и зажигает Q2, и так далее. Когда счетчик достигает Q8 (вывод 9), он подает импульс на вывод 15, который является выводом сброса. Это означает запуск отсчета, начиная с Q0.

Наша основная схема:

Схема тестового макета (описание выше)
Схема тестового макета (описание выше)

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

Тактовый вывод удерживается на уровне лог. 0, импульс лог. 1
Тактовый вывод удерживается на уровне лог. 0,
импульс – лог. 1
Тактовый вывод удерживается на уровне лог. 1, импульс – лог. 0
Тактовый вывод удерживается на уровне лог. 1,
импульс – лог. 0

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

Видео:

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

Для коммутатора A:

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

Для коммутатора B:

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

Для коммутатора C:

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

Для коммутатора D:

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

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

Дребезг контактов коммутатора C (импульс соответствует логическому нулю)
Дребезг контактов коммутатора C (импульс соответствует логическому нулю)

Как вы можете видеть, микросхеме кажется, что было несколько нажатий на коммутатор. Хотя это и не так, поскольку на коммутатор было выполнено только одно нажатие.

Добавим керамический конденсатор:

Тактовый вывод удерживается на уровне лог. 0, импульс лог. 1
Тактовый вывод удерживается на уровне лог. 0,
импульс – лог. 1
Тактовый вывод удерживается на уровне лог. 1, импульс – лог. 0
Тактовый вывод удерживается на уровне лог. 1,
импульс – лог. 0

При добавлении конденсатора мы создаем RC-цепь. RC-цепи здесь не обсуждаются.

Новые скриншоты осциллографа сильно отличаются от полученных ранее. Это показывает, что RC-цепь отфильтровывает дребезг.

Данное видео показывает, как работает схема с керамическим конденсатором 0,1 мкФ:

Для коммутатора A:

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

Для коммутатора B:

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

Для коммутатора C:

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

Для коммутатора D:

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

Для коммутатора C (импульс соответствует логическому нулю):

Сигнал с коммутатора C после добавления конденсатора (импульс соответствует логическому нулю)
Сигнал с коммутатора C после добавления конденсатора (импульс соответствует логическому нулю)

Эти скриншоты говорят нам о том, что дребезг устранен, и что микросхема «видит» только одно нажатие или переключение. Это то, чего мы и хотели.

Программное подавление дребезга

При работе с микроконтроллерами мы можем справиться с дребезгом контактов по-другому, что позволит сэкономить и место под детали, и деньги. Некоторые программисты не задумываются о дребезжащих коммутаторах и просто добавляют 50 мс задержки после первого «отскока». Это заставляет микроконтроллер ждать остановку дребезга 50 мс, а затем продолжить работу программы. На самом деле это не очень хорошая практика, так как она удерживает микроконтроллер в ожидании окончания задержки.

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

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

/* Программное подавление дребезга
 * 
 * При каждом переходе от LOW к HIGH или от HIGH к LOW
 * выполняется подавление дребезга во входном сигнале
 * путем выборки по нескольким считываниям в течение
 * нескольких миллисекунд. Входной сигнал не считается
 * устоявшимся на уровне LOW или HIGH до тех пор, пока
 * не будет считан по крайней мере в течение
 * "debounce_count" (10) миллисекунд в новом состоянии.
 * 
 * Примечания:
 *   Отрегулируйте значение debounce_count, чтобы
 *   отобразить время, в течение которого входной
 *   сигнал может дребезжать, прежде чем перейдет
 *   в устойчивое состояние.
 *
 */

int inPin = 7;         // номер входного вывода
int outPin = 13;       // номер выходного вывода

int counter = 0;       // сколько раз мы должны получить новое значение
int reading;           // текущее значение, прочитанное с входного вывода
int current_state = LOW;    // входное значение, полученное после подавления дребезга

// следующая переменная long, потому что время, измеренное в миллисекундах,
// быстро станет числом, большим, чем может храниться в int.
long time = 0;         // время последней выборки входного вывода
int debounce_count = 10; // количество миллисекунд/выборок для решения, что сигнал на входе принял устойчивое состояние

void setup()
{
  pinMode(inPin, INPUT);
  pinMode(outPin, OUTPUT);
  digitalWrite(outPin, current_state); // установить выход светодиода в начальное состояние
}


void loop()
{
  // Если мы перешли к следующей миллисекунде
  if(millis() != time)
  {
    reading = digitalRead(inPin);

    if(reading == current_state && counter > 0)
    {
      counter--;
    }
    if(reading != current_state)
    {
       counter++; 
    }
    // Если вход показывает одно значение достаточно долго, давайте переключим его
    if(counter >= debounce_count)
    {
      counter = 0;
      current_state = reading;
      digitalWrite(outPin, current_state);
    }
    time = millis();
  }
}

Код выше был написан в Arduino IDE.

Следующая программа мигает двумя светодиодами, подключенными к PIC микроконтроллеру. Код может быть похожим на этот:

// включения
#include <stdio.h>
#include <stdlib.h>
#include <xc.h>

// конфигурация
#pragma config FOSC = INTOSCIO  // Биты выбора генератора (INTOSC генератор: I/O функция на выводе RA6/OSC2/CLKOUT, I/O функция на выводе RA7/OSC1/CLKIN)
#pragma config WDTE = OFF       // Бит включения сторожевого таймера (WDT выключен)
#pragma config PWRTE = OFF      // Бит включения таймера включения (PWRT выключен)
#pragma config MCLRE = ON       // Бит выбора функции вывода RA5/MCLR/VPP (функция вывода RA5/MCLR/VPP - это is MCLR)
#pragma config BOREN = ON       // Бит включения обнаружения просадки питания (BOD включен)
#pragma config LVP = ON         // Бит включения низковольтного программирования (функция вывода RB4/PGM - это PGM, низковольтное программирование включено)
#pragma config CPD = OFF        // Бит защиты данных памяти eeprom (защита данных памяти eeprom выключена)
#pragma config CP = OFF         // Бит защиты кода Flash памяти программ (защита кода выключена)

// Определения
#define _XTAL_FREQ 4000000
#define LED1 PORTBbits.RB3
#define LED2 PORTBbits.RB2
#define BTN PORTBbits.RB5

// Переменные
char BTN_pressed = 0;
unsigned int BTN_press = 0;
unsigned int BTN_release = 0;
unsigned int Bouncevalue = 500;

// Основная программа
int main(int argc, char** argv) {
    // Компараторы выключены
    CMCON = 0x07;
    
    // Направления портов, RB5 вход, остальные - выходы
    TRISA = 0b00000000;
    TRISB = 0b00100000;
    
    // Состояние портов, на всех лог. 0
    PORTA = 0b00000000;
    PORTB = 0b00000000;
    
    // Начинаем с включенным (лог. 1) LED1 и выключенным (лог. 0) LED2
    LED1 = 1;
    LED2 = 0;
    
    while (1)
    {
        // Если кнопка BTN нажата 
        if (BTN == 1)
        {
            // Дребезг началсь, поэтому увеличим BTN_press на 1 при каждом отскоке к высокому логическому уровню
            BTN_press++;
            // "сбросить" BTN_release
            BTN_release = 0;
            // Если отскоков столько, что BTN_press больше Bouncevalue,
            // кнопка должно быть нажата
            if (BTN_press > Bouncevalue)
            {
                // Это первоначальное значение BTN_pressed. 
                // Если программа дошла до этого места, кнопка должно быть нажата
                if (BTN_pressed == 0)
                {
                    // Поменять состояния светодиодов
                    LED1 ^= 1;
                    LED2 ^= 1;
                    // Устанавливаем BTN_pressed в 1, убеждаясь что мы 
                    // не попадем снова в этот блок кода
                    BTN_pressed = 1;
                }
                // Светодиоды переключены, установить BTN_press в 0, чтобы мы могли снова войти 
                // в блок переключающего кода
                BTN_press = 0;
            }
        }
        else
        {
            // Увеличить "низкий уровень" на 1 для подавления дребезга
            BTN_release++;
            BTN_press = 0;
            // Если BTN_release больше Bouncevalue, то кнопка у нас не нажата
            if (BTN_release > Bouncevalue)
            {
                BTN_pressed = 0;
                BTN_release = 0;
            }
        }
        
    }
    return (EXIT_SUCCESS);
}

Этот пример написан MPLAB X с компилятором XC8. Микроконтроллер – это PIC 16F628A, и я использовал внутренний генератор на 4 МГц. Вам необходимо поэкспериментировать с Bouncevalue. У меня лучше всего программа работала со значением 500.

Микроконтроллер без какого-либо подавления дребезга контактов:

Это пример того, как коммутатор может «запутать» микроконтроллер. Нормального переключения светодиодов не получилось. Похоже, что при нажатии кнопки они живут своей жизнью.

Микроконтроллер с управлением подавлением дребезга контактов:

Как видите, светодиоды хорошо включаются и выключаются по нажатию кнопки.

Заключение

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

Теги

MCUДребезг контактовКлючКнопкаМикроконтроллер

На сайте работает сервис комментирования DISQUS, который позволяет вам оставлять комментарии на множестве сайтов, имея лишь один аккаунт на Disqus.com.

В случае комментирования в качестве гостя (без регистрации на disqus.com) для публикации комментария требуется время на премодерацию.


  • 2022-10-06disqus_mgntyjXh5J

    Можно еще запоминать в защелку, но это скорее на ПЛИСах наверное

  • 2022-07-17Vladimir Kotov

    плавное нарастание фронта некоторыми микросхемами тоже воспринимается как дребезг т.е. в какой то момент наступает ситуация неопределенного состояния. Для быстродействующих М/С фронт должен быть крутым.

  • 2020-07-11Александр

    А если применить RS-ТРИГГЕР на 155 серии для подавления дребезга, всегда ли получится?

  • 2020-05-05radioprog

    Спасибо. При переводе статьи пропустил эту ошибку.
    Теперь тип переменных, задействованных в подавлении дребезга, – unsigned int.

  • 2020-05-04Борис Васильев

    Тип "char" - однобайтный, значение 500 в него не поместится.