• Система автоматизации с открытым исходным кодом на базе 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 триггер Шмитта никакой и фильтрация установкой внешней емкости (НЧ фильтра первого порядка) дело не спасет. Вот цитата из статьи: "В железе это делается с помощью ФНЧ на входе триггера Шмидта."
 
Последнее редактирование:
Сверху Снизу