• Уважаемые посетители сайта esp8266.ru!
    Мы отказались от размещения рекламы на страницах форума для большего комфорта пользователей.
    Вы можете оказать посильную поддержку администрации форума. Данные средства пойдут на оплату услуг облачных провайдеров для сайта esp8266.ru
  • Система автоматизации с открытым исходным кодом на базе esp8266/esp32 микроконтроллеров и приложения IoT Manager. Наша группа в Telegram

Фильтр дребезга контактов в pin change interrupt

grafalex

New member
Всем привет!

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

Есть кнопка, подключенная между пином ESP и землей. Задача - считать количество нажатий кнопки (на самом деле литры воды, но тестирую на обычной кнопке). Тупым опросом пина все работает, но народ в комментариях негодует - говорят нужно pin change interrupt.

Для начала попробовал ловить только falling edge, но все равно при отпускании кнопки из-за дребезга контактов происходят ложные срабатывания. Пробовал также ловить и falling и rising, и даже написал простой фильтр - все равно не работает.

Вот код:
Код:
import machine
import utime

pin = machine.Pin(14, machine.Pin.IN, machine.Pin.PULL_UP)
value = 0
last_value_change_ms = 0

def callback(p):
    global value
    global last_value_change_ms

    pin_state = p.value()
    cur_time = utime.ticks_ms()
    diff = cur_time - last_value_change_ms

    if diff > 500:
        print("")
   
    #print("CurTime={}, LastChange={}, Diff={}, State={}".format(cur_time, last_value_change_ms, diff, p.value()))
   
    if diff > 50:
        if pin_state == 0:
            value += 1
            print('pin change', value)
        else:
            print("Button up")       
    else: 
        print("Debouncing...", pin_state)

    last_value_change_ms = cur_time
   

pin.irq(trigger=machine.Pin.IRQ_FALLING | machine.Pin.IRQ_RISING, handler=callback)

prev_value = value
while True:
    new_value = value
    if new_value != prev_value:
        print("Value Changed:", value)
        prev_value = value
Порой встречается вот такая ситуация
Код:
pin change 8
Value Changed: 8
pin change 9
Debouncing... 1
Debouncing... 0
Debouncing... 1
Debouncing... 0
Value Changed: 9
Т.е. при отпускани кнопки прерывание вызывается (по идее при переходе от 0 к 1), но если прочитать из пина значение, то читается 0, что приводит к ложному срабатыванию (код думает, что кнопка опять замкнулась)

ЧЯДНТ? Можно ли как-то узнать почему тригнулось прерывание?
 

nikolz

Well-known member
Всем привет!

Помогите, пожалуйста, разобраться с проблемой дребезга контактов.
Т.е. при отпускани кнопки прерывание вызывается (по идее при переходе от 0 к 1), но если прочитать из пина значение, то читается 0, что приводит к ложному срабатыванию (код думает, что кнопка опять замкнулась)
ЧЯДНТ? Можно ли как-то узнать почему тригнулось прерывание?
попробуйте простой аппаратный способ берете сопротивление 20..100 ком параллельно емкость 1..10 мкф.
Эту конструкцию в разрыв цепи кнопка- пин.
У пина должна быть подтяжка к питанию примерно 10 ком
----------------------------------
Если не поможет расскажу как сделать программно, но написать ее лучше на CИ.
 

grafalex

New member
Спасибо за совет с аппаратным фиксом - обязательно предусмотрю, когда буду вторую версию делать. А пока, чтобы не колхозить (плата спаяна) хотелось бы сначала программный фикс замутить.
Собственно не в языке дело (на сях и я могу). Вопрос в другом: доступна ли вообще (на уровне регистров) информация о том что вообще вызвало прерывание? Т.е. я даже на осциллографе не вижу никакого дребезга, а прерывания порой вызываются раз по 5. Ну ладно, пускай себе вызываются, так значение то в прерывание не передается, а когда читаешь его из пина, то получается не совсем то....
 

=AK=

New member
Тупым опросом пина все работает, но народ в комментариях негодует - говорят нужно pin change interrupt.
Зря негодуют. Обрабатывать кнопки в прерываниях по пину - это маразм:
- ненужная трата процессорного времени на обработку ложных прерываний при помехах и дребезге контактов
- как следствие - бессмысленное подтормаживание обработки других прерываний, что есть сущее безобразие

Обрабатывать кнопки проще в прерываниях по таймеру, скажем, раз в 10 мс. А правильнее всего - в основном цикле через равные промежутки времени, те же 10 мс.

Зеведите глобальную переменную и используйте ее как сдвиговый регистр. Каждые 10 мс сдвигайте его влево, а в младший бит прописывайте значение на пине, 0 или 1. Потом проверяйте N младших разрядов, если там все 1 - кнопка отпущена, если все 0 - кнопка нажата. При N=16 время подавления дребезга получится 160 мс, это более-менее оптимально для большинства обычных кнопок.
 

nikolz

Well-known member
Спасибо за совет с аппаратным фиксом - обязательно предусмотрю, когда буду вторую версию делать. А пока, чтобы не колхозить (плата спаяна) хотелось бы сначала программный фикс замутить.
Собственно не в языке дело (на сях и я могу). Вопрос в другом: доступна ли вообще (на уровне регистров) информация о том что вообще вызвало прерывание? Т.е. я даже на осциллографе не вижу никакого дребезга, а прерывания порой вызываются раз по 5. Ну ладно, пускай себе вызываются, так значение то в прерывание не передается, а когда читаешь его из пина, то получается не совсем то....
uint32 gpio_status= GPIO_REG_READ(GPIO_STATUS_ADDRESS);
gpio_status - установлены биты пинов которые вызвали прерывание
=============================
при входе в колбек надо запретить прерывания
ETS_GPIO_INTR_DISABLE();
далее распознать и обработать прерывание от нужного пина
...
потом очистить gpio_status
GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, gpio_status);
при выходе разрешить прерывания
ETS_GPIO_INTR_ENABLE();
 

pvvx

Активный участник сообщества
Есть кнопка, подключенная между пином ESP и землей. Задача - считать количество нажатий кнопки (на самом деле литры воды, но тестирую на обычной кнопке).
Дык кнопки или счет импульсов от механического датчика расхода воды?
На некоторых стоят герконы, а не механические контакты. В большинстве установлены элементы на основе датчика Холла (Hall Elements).
У механических "дребезг контакта" может достигать и 80% времени замкнутого состояния. Время счета у них до пары "импульсов" в секунду.
У кнопки, которые используются для управления человеком, время "дребазга" не может превышать времени необходимой реакции на нажатие. Обычно, за максимальное время реакции от кнопки принимают до 0.2 сек. Если оно более, и не сработало исполнительное устройство, тогда, чтобы пользователь не разбил устройство с кнопкой об стенку или молотком, применяют всяческие индикаторы. О времени не комфортной задержки можно судить по времени отклика для геймеров в компьютерных игрушках. Т.е. оно должно составлять где-то до десятка мс, с момента нажатия кнопки. На клавиатуре кол-во нажатий кнопок обученным наборщиком в секунду тоже известно...
Всё остальное, о чем тут пишут - не о чем. Если кнопка кривая, то её меняют.
Для датчиков - всё равно, с ними можно применять любые программные фильтры. Там главное границы времен изменения параметров, которые задаются характеристиками датчика. А для кнопки в качестве параметров фильтров "датчика" берутся характеристики человека. :p (Временные характеристики опроса кнопки для человекОв у =AK= не вписываются в комфортные. Такие времена задают для кнопок с опасными случайными срабатываниями, которые могут повлечь что-то нехорошее. Ну и у устройств, которые пользователи меняют на другие, вешая ярлык на выброшенное тормозное устройство.)

В простых MCU опрос клавиатурных датчиков обычно организуют по прерываниям, а не Polling (computer science) - Wikipedia.
В Aduino время цикла Loop неопределенное. Прерывания ещё более менее - их WiFi колбасит меньше.

Для механического контактного счетчика воды оптимально применение комбинации аппаратного и программного фильтра. Аппаратный фильтр в данном случае - RC цепь и Триггер Шмитта — Википедия. Запрограммировать эту фигню вы не сможете в Arduino полингом, т.к. время анализа превышает время необходимое для отдачи управления драйверу WiFi. Частичный опрос, организованный когда не попадя ни к чему хорошему не приведет. Но если вам не требуется точность и плевать на пропуски и ложный счет - то пойдет что угодно.
https://esp8266.ru/forum/threads/umenshaem-ehnergopotreblenie-esp8266.3010/#post-49839 -> dontsovcmc/ImpCounter
 
Последнее редактирование:

pvvx

Активный участник сообщества
попробуйте простой аппаратный способ берете сопротивление 20..100 ком параллельно емкость 1..10 мкф.
Эту конструкцию в разрыв цепи кнопка- пин.
У пина должна быть подтяжка к питанию примерно 10 ком
Резистор в 10 кОм 0.1 мкФ и механический контакт на датчике воды дадут увеличение ко-ва импульсов, а постоянная времени такой RC-цепи даст вам пропуски при опросе полигом датчика без дребезга в Arduino.
С помощью 2-х резисторов задаются оптимальные времена заряда/разряда емкости. Но какие они нужны от ТС не дано, т.к. не указан тип счетчика воды и границы расхода (пиковый и/или максимальный расход, определяемый входным давлением, сечениями трубопровода, кранов и прочих устройств у потребителя).
 
Последнее редактирование:

grafalex

New member
uint32 gpio_status= GPIO_REG_READ(GPIO_STATUS_ADDRESS);
gpio_status - установлены биты пинов которые вызвали прерывание
Понятно, только список пинов.... эх, а хотелось бы еще и какой именно триггер сработал.
Кстати, точно такой же код в обработке прерываний по пинам в micropython, что наводит на мысль, что это ограничение самого контроллера.

В общем сделал с задержками, получилось весьма и весьма надежно
Код:
import machine
import utime

pin = machine.Pin(14, machine.Pin.IN, machine.Pin.PULL_UP)
new_click_detected = False

def callback(p):
    global new_click_detected
    new_click_detected = True
  

#pin.irq(trigger=machine.Pin.IRQ_FALLING | machine.Pin.IRQ_RISING, handler=callback, hard=True)
pin.irq(trigger=machine.Pin.IRQ_FALLING, handler=callback, hard=True)

value = 0
while True:
    while not new_click_detected:
        pass

    utime.sleep_ms(20)
    if pin.value() == 0:
        value += 1
        print("New value: ", value)

    new_click_detected = False

Дык кнопки или счет импульсов от механического датчика расхода воды?
Импульсы. На кнопках проще отлаживаться, но работать это будет в итоге с какими то магнитными датчиками (скорее всего геркон).

В целом за комментарий спасибо, но не очень понял при чем тут WiFi?
Если что, то у меня код сделан на корутинах и все (и опрос, и WiFi) выполняется псевдпараллельно. Разумеется если никакая корутина не залипнет
 

nikolz

Well-known member
Понятно, только список пинов.... эх, а хотелось бы еще и какой именно триггер сработал.
Кстати, точно такой же код в обработке прерываний по пинам в micropython, что наводит на мысль, что это ограничение самого контроллера.
контроллер не виноват.
Специально написал сегодня прогу для работы с кнопками.
Несколько кнопок на один пин.
Работает без дребезгов и помех как и ожидалось.
прилеплю реле чтобы мигало по-разному от кнопок и выложу прошивку для тестирования желающим.
 

Slacky

Member
Вот хорошая статья про дребезг (в конце есть с
ссылка на первоисточник) - Устранение дребезга контактов на основе вертикальных счетчиков | Embedders.org

Я тут намедни баловался со вводом времени на stm32f4 с трех кнопок. Приведенный алгоритм в статье полностью решил проблему дребезга ...
 

pvvx

Активный участник сообщества
Вот хорошая статья про дребезг (в конце есть с
ссылка на первоисточник) - Устранение дребезга контактов на основе вертикальных счетчиков | Embedders.org

Я тут намедни баловался со вводом времени на stm32f4 с трех кнопок. Приведенный алгоритм в статье полностью решил проблему дребезга ...
Всё бы хорошо, но тут тема про "Фильтр дребезга контактов в pin change interrupt", а не в таймерном прерывании. :(
Согласно указанным и в данной статье графикам, дребезг создает прерывания изменения пина чаще 5 мкс, а на входе у ESP8266 триггер Шмитта никакой и фильтрация установкой внешней емкости (НЧ фильтра первого порядка) дело не спасет. Вот цитата из статьи: "В железе это делается с помощью ФНЧ на входе триггера Шмидта."
 
Последнее редактирование:
Сверху Снизу