Защита от Дребезга Контактов Кнопки Схема Устранение эффекта


Определение и суть проблемы в электронике

Дребезг контактов возникает при нажатии на кнопку и переключатель, он возникает из-за реальных вибраций контактной пластины при её перемещении. Любой переключатель устроен так, что у него есть подвижный и неподвижный контакт. Как видно из названия, подвижным называется тот, что соединен с толкателем или рычагом, на который уже нажимает человек или механизм при работе устройства.

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

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

Пример осциллограммы дребезга контактов изображен на рисунке ниже:

Скетч (код) к уроку 8. Arduino: Дребезг — программное устранение.ino1 Kb59Вы можете скачать файл.

Устранение дребезга контактов в счетчике воды | Жизнь, бизнес и IT

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

Мнение эксперта

It-Technology, Cпециалист по электроэнергетике и электронике

Задавайте вопросы «Специалисту по модернизации систем энергогенерации»

Мучаем 5576ХС4Т — часть 5 — подавление дребезга кнопки: nabbla1 — ЖЖ Аппаратное устранение дребезга кнопки представляет собой изменение схемы подключения путем установки элементов, которые способны устранить дребезг. Спрашивайте, я на связи!

Есть ли способ справиться с этим всем

Для эффективной борьбы с данной проблемой мы можем применить аппаратное или программное решение. Первый вариант включает в себя следующее:

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

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

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

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

Схему программного угнетения дребезга в Arduino IDE можно скачать.

Ошибки дребезга кнопки

Как отразится дребезг на нашем проекте? Да самым прямым образом – мы будем получать на входе совершенно случайный набор значений. Ведь если мы считываем значение с кнопки непрерывно, в каждом новом рабочем цикле функции loop, то будем замечать все “всплески” и “падения” сигнала. Потому что пауза между двумя вызовами loop составляет микросекунды и мы измерим все мелкие изменения.

Если мы хотим отследить ситуацию, когда кнопка была отпущена после нажатия, то получим множество ложных сигналов – она будет “нажата-отпущена” десятки раз, хотя мы выполнили лишь однократное нажатие.

Вот пример скетча, в котором непременно обнаружится ошибка дребезга. Мы сможем увидеть в мониторе порта в первые мгновения после нажатия целый набор нулей и единиц в случайной последовательности (не важно, что означает 1 – нажатие или отпускание кнопки, важен сам факт появления хаоса).

Естественно, такое поведение ни к чему хорошему не приведет и нам нужно придумать способ борьбы с дребезгом. В нашем арсенале есть два способа: программный и аппаратный. Первый довольно простой, но не всегда его можно использовать в реальных проектах. Второй – более надежный, но требует существенных изменений в схеме. Давайте рассмотрим оба способа подробнее.

Программирование

Код реализующий подобный принцип может быть разным, вот один из возможных, он «прокачан» за счет сравнения текущего значения с предыдущим (так отслеживается нажатие и отпускание кнопки) [взят из: Software debouncing of buttons snigelen February 5, 2015, и чутка переделан под STM32].

Используется целых три глобальных переменных для каждой кнопки:

/********** PV **********/ /* PA0 */ uint8_t pres_pa0; uint8_t prev_pa0; uint8_t cnt_pa0; /* PA7 */ uint8_t pres_pa7; uint8_t prev_pa7; uint8_t cnt_pa7; /* Other */ uint8_t counter; /************************/

А вот и сама функция с кучей аргументов (перегружена, зато удобная):

/** * @brief Debounce function for button * @note Perform it every 10-20ms (e.g. in timer’s interrupt) * In main loop check first variable ((*pres_px), if (it > 0) NULLify it and execute required code * @param Three global variables, IDR register, Bit for check (e.g. &pres_pa0, &cnt_pa0, &prev_pa0, GPIOA->IDR, GPIO_IDR_ID0) * @retval no */ void Debounce(uint8_t *pres_px, uint8_t *cnt_px, uint8_t *prev_px, uint32_t GPIOx_IDR, uint32_t GPIO_IDR_IDx) { /* Read current state */ uint8_t cur_px = (~GPIOx_IDR & GPIO_IDR_IDx) != 0; /* If level has changed */ if (cur_px != *prev_px) { /* Increase counter */ (*cnt_px)++; /* If consecutive 4*10 = 40ms approved */ if (*cnt_px >= 4) { /* The button have not bounced for four checks, change state */ *prev_px = cur_px; /* If the button was pressed (not released), tell main so */ if (cur_px != 0) { *pres_px = 1; } (*cnt_px) = 0; } } else { /* Reset counter */ *cnt_px = 0; } }

Взял крутую заряженную отладочную плату MiniF4 (МК: STM32F411CEU6). Ну, а теперь нужно настроить вывод(ы) на вход (к нему подключена кнопка) и один на выход (к нему подключен светодиод):

/* GPIOC 13 OUT PP */ /* Clock GPIOC */ RCC->AHB1ENR |= RCC_AHB1ENR_GPIOCEN; /* 01: General-purpose output */ GPIOC->MODER |= GPIO_MODER_MODE13_0; GPIOC->MODER &= ~GPIO_MODER_MODE13_1; /* 0: Output push-pull */ GPIOC->OTYPER &= ~GPIO_OTYPER_OT13; /* 00: Low speed */ GPIOC->OSPEEDR &= ~GPIO_OSPEEDER_OSPEEDR13; /* 00: Np pull-pull up, pull-down */ GPIOC->PUPDR &= ~GPIO_PUPDR_PUPD13; /**********************/ /* GPIOA 0 IN PU */ /* Clock GPIOA */ RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN; /* 00: Input (reset state) */ GPIOA->MODER &= ~GPIO_MODER_MODE0_0; GPIOA->MODER &= ~GPIO_MODER_MODE0_1; /* 01: Pull-up */ GPIOA->PUPDR |= GPIO_PUPDR_PUPD0_0; GPIOA->PUPDR &= ~GPIO_PUPDR_PUPD0_1;

Также прерывание от Таймера 11, частота 100 Гц (при APB2 = 16000000 Гц):

/* Clock Tim11 */ RCC->APB2ENR |= RCC_APB2ENR_TIM11EN; /* T=10ms */ TIM11->PSC = 160-1; TIM11->ARR = 1000-1; /* Update Interrupt Enable */ TIM11->DIER |= TIM_DIER_UIE; /* Counter enable */ TIM11->CR1 |= TIM_CR1_CEN; /* Tim11 Interrupt enable */ NVIC_EnableIRQ(TIM1_TRG_COM_TIM11_IRQn);

В прерывании выполняем функцию:

void TIM1_TRG_COM_TIM11_IRQHandler(void){ if(TIM11->SR & TIM_SR_UIF){ /* GPIOA-0 KEY */ Debounce(&pres_pa0, &cnt_pa0, &prev_pa0, GPIOA->IDR, GPIO_IDR_ID0); Debounce(&pres_pa7, &cnt_pa7, &prev_pa7, GPIOA->IDR, GPIO_IDR_ID7); TIM11->SR &= ~TIM_SR_UIF; } }

И в главном цикле при каждом нажатии будет изменятся состояние свечения СИД и инкрементироваться значение переменной (чисто для примера):

while(1){ if(pres_pa0){ pres_pa0 = 0; GPIOC->ODR ^= GPIO_ODR_OD13; counter++; } }

Полный код:

#include «main.h» /****** Prototypes ******/ void Cnf_GPIO(void); void Cnf_TIM11(void); void Debounce(uint8_t *pres_px, uint8_t *cnt_px, uint8_t *but_px, uint32_t GPIOx_IDR, uint32_t GPIO_IDR_IDx); /************************/ /********** PV **********/ /* PA0 */ uint8_t pres_pa0; uint8_t prev_pa0; uint8_t cnt_pa0; /* PA7 */ uint8_t pres_pa7; uint8_t prev_pa7; uint8_t cnt_pa7; /* Other */ uint32_t counter; /************************/ int main(void) { /* Configuration */ Cnf_GPIO(); Cnf_TIM11(); while(1){ if(pres_pa0){ pres_pa0 = 0; GPIOC->BSRR |= GPIO_BSRR_BR13; counter++; } if(pres_pa7){ pres_pa7 = 0; GPIOC->BSRR |= GPIO_BSRR_BS13; counter—; } } } /*****************************************************/ void Cnf_GPIO(void){ /* GPIOC 13 OUT PP */ /* Clock GPIOC */ RCC->AHB1ENR |= RCC_AHB1ENR_GPIOCEN; /* 01: General-purpose output */ GPIOC->MODER |= GPIO_MODER_MODE13_0; GPIOC->MODER &= ~GPIO_MODER_MODE13_1; /* 0: Output push-pull */ GPIOC->OTYPER &= ~GPIO_OTYPER_OT13; /* 00: Low speed */ GPIOC->OSPEEDR &= ~GPIO_OSPEEDER_OSPEEDR13; /* 00: Np pull-pull up, pull-down */ GPIOC->PUPDR &= ~GPIO_PUPDR_PUPD13; /**********************/ /* GPIOA 0 IN PU */ /* Clock GPIOA */ RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN; /* 00: Input (reset state) */ GPIOA->MODER &= ~GPIO_MODER_MODE0_0; GPIOA->MODER &= ~GPIO_MODER_MODE0_1; /* 01: Pull-up */ GPIOA->PUPDR |= GPIO_PUPDR_PUPD0_0; GPIOA->PUPDR &= ~GPIO_PUPDR_PUPD0_1; /* GPIOA 7 IN PU */ /* Clock GPIOA */ RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN; /* 00: Input (reset state) */ GPIOA->MODER &= ~GPIO_MODER_MODE7_0; GPIOA->MODER &= ~GPIO_MODER_MODE7_1; /* 01: Pull-up */ GPIOA->PUPDR |= GPIO_PUPDR_PUPD7_0; GPIOA->PUPDR &= ~GPIO_PUPDR_PUPD7_1; } void Cnf_TIM11(void){ /* Clock TIM11 */ RCC->APB2ENR |= RCC_APB2ENR_TIM11EN; /* T=10ms f=100Hz */ TIM11->PSC = 160-1; TIM11->ARR = 1000-1; /* Update Interrupt Enable */ TIM11->DIER |= TIM_DIER_UIE; /* Counter enable */ TIM11->CR1 |= TIM_CR1_CEN; /* TIM11 Interrupt enable */ NVIC_EnableIRQ(TIM1_TRG_COM_TIM11_IRQn); } void TIM1_TRG_COM_TIM11_IRQHandler(void){ if(TIM11->SR & TIM_SR_UIF){ /* GPIOA-0 KEY */ Debounce(&pres_pa0, &cnt_pa0, &prev_pa0, GPIOA->IDR, GPIO_IDR_ID0); Debounce(&pres_pa7, &cnt_pa7, &prev_pa7, GPIOA->IDR, GPIO_IDR_ID7); TIM11->SR &= ~TIM_SR_UIF; } } /** * @brief Debounce function for button * @note Perform it every 10-20ms (e.g. in timer’s interrupt) * In main loop check first variable ((*pres_px), if (it > 0) NULLify it and execute required code * @param Three global variables, IDR register, Bit for check (e.g. &pres_pa0, &cnt_pa0, &prev_pa0, GPIOA->IDR, GPIO_IDR_ID0) * @retval no */ void Debounce(uint8_t *pres_px, uint8_t *cnt_px, uint8_t *prev_px, uint32_t GPIOx_IDR, uint32_t GPIO_IDR_IDx) { /* Read current state */ uint8_t cur_px = (~GPIOx_IDR & GPIO_IDR_IDx) != 0; /* If level has changed */ if (cur_px != *prev_px) { /* Increase counter */ (*cnt_px)++; /* If consecutive 4*10 = 40ms approved */ if (*cnt_px >= 4) { /* The button have not bounced for four checks, change state */ *prev_px = cur_px; /* If the button was pressed (not released), tell main so */ if (cur_px != 0) { *pres_px = 1; } (*cnt_px) = 0; } } else { /* Reset counter */ *cnt_px = 0; } }

Аппаратное устранение дребезга

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

Подавление дребезга контактов для водосчетчика (Arduino/ESP8266/EPS32)

Поиграться с работой схемы можно здесь. При разомкнутой кнопке емкость C1 заряжается через сопротивление R1. На входе инвертирущего триггера Шмитта +VCC. На выходе, соответственно 0.

Подавление дребезга контактов для водосчетчика (Arduino/ESP8266/EPS32)

Схема почти такая-же, но при замыкании кнопки емкость С2 разряжается на землю через сопротивление R4, поэтому ток протекающий через контакты уже безопасно небольшой для контактов: I = U/R = 5/1000 = 5 мА.

t — время разряда с уровня Vdc до Vc. Vc — напряжение при котором контроллер идентифицирует уровень напряжения как логический ноль. Я взял Vc = 0,5 V. В статье подробно описаны уровни для ESP8266. Я взял несколько более низкое напряжение, вместо 0,8 V для напряжения, ниже которого ESP8266 считает уровень логическим нулем.

Если емкость увеличить с 0,01 мкФ до 0,1 мкФ, то время разрядки конденсатора C1 до уровня V0 будет уже 180 мс, что довольно много. Однако, в данном случае процессы настолько медленные, что можно оставить и такую емкость. Её увеличение, а, соответственно, инерционности, лучше сглаживает короткие всплески, уменьшая вероятность ложных срабатываний.

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

Мнение эксперта

It-Technology, Cпециалист по электроэнергетике и электронике

Задавайте вопросы «Специалисту по модернизации систем энергогенерации»

Как избавиться от дребезга контактов при подключении кнопки к Arduino На рисунке 2 наглядно изображена осциллограмма напряжения в результате коммутации электрического тока вследствие нажатия на кнопку. Спрашивайте, я на связи!

Какое напряжение нужно для замены реле?

Большинство из них имеют рейтинг 12v

, обычно диапазон 12-15в. Фактическое напряжение, необходимое для работы реле, обычно немного ниже 12 В. Вот почему ваши аксессуары могут работать при включенном ключе и выключенном двигателе, обычно при включенном свете фактическое напряжение немного ниже 12 В.

Интересные материалы:

Срок годности моющих средств истекает? Срок годности слитков Клондайка истекает? Срок службы Adobe AIR подошел к концу? Срок службы стиральных машин LG? SSAO — это хорошо или плохо? SSAO лучше HBAO? SSD диски изнашиваются быстрее? SSD RAM или ROM? США — 110 или 120? ССК МТС проводится каждый год?

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

  • Вместо программного опроса состояния входов в цикле loop использовать более красивый вариант с прерываниями. При использовании схемы устранения дребезга этот способ будет работать корректно. Пример кода с использованием прерываний описан в статье.
  • Загонять ESP8266/ESP32 в deep-sleep с указанием времени через которое он должен просыпаться для обработки состояния входов. В данном случае использовать аппаратное устранение дребезга не нужно. При выходе из спячки ESP8266 мог уже пропустить срабатывание геркона, поэтому нужно опросить текущее состояние входов, после чего опять уйти в спячку. Время спячки должно быть меньше минимальной продолжительности срабатывания геркона. Нужно убедится, что после срабатывания геркона, если сразу выключить воду, он останется в замкнутом состоянии. Если отключится, то нужно выбирать время deep sleep с учетом такого «форс-мажора».
  • Загонять ESP8266/ESP32 в deep-sleep и выводить его из этого состояния сняв данные с герконов через схему устранения дребезга контактов и подав импульс на вход EXT_RSTB. Для этого можно использовать свободные элементы триггера Шмитта. Схемотехнику для такого варианта опишу в другой статье.
  • Учитывая, что ESP8266/ESP32 стартуют мгновенно, можно вообще завязать цепь питания на геркон. Т.е. включать схему, при замыкании одного из герконов.

Схема водосчетчика на ESP8266 (Wemos D1 mini)

Схема и пример печатной платы в проекте EasyEDA. Оставил I2C входы незанятыми для подключения индикатора.

Схему сделал на Wemos breadboard.

Монтажная плата shield водосчетчика на Wemos D1 mini (ESP8266) с аппаратным устранением дребезга контактов на триггере Шмитта

Пример печатной платы сформированный автотрассировкой в EasyEDA.

Печатная плата shield водосчетчика на Wemos D1 mini (ESP8266) с аппаратным устранением дребезга контактов на триггере Шмитта

Рейтинг
( 2 оценки, среднее 4.5 из 5 )
Понравилась статья? Поделиться с друзьями:
Для любых предложений по сайту: [email protected]