Скрыть объявление
На нашем форуме недоступен просмотр изображений для неавторизованных пользователей. Если Вы уже зарегистрированы на нашем форуме, то можете войти. Если у Вас еще нет аккаунта, мы будем рады, если Вы к нам присоединитесь. Зарегистрироваться Вы можете здесь.

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

Тема в разделе "MicroPython", создана пользователем grafalex, 8 апр 2018.

  1. grafalex

    grafalex Новичок

    Сообщения:
    17
    Симпатии:
    0
    Всем привет!

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

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

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

    Вот код:
    Код (Text):
    1.  
    2. import machine
    3. import utime
    4.  
    5. pin = machine.Pin(14, machine.Pin.IN, machine.Pin.PULL_UP)
    6. value = 0
    7. last_value_change_ms = 0
    8.  
    9. def callback(p):
    10.     global value
    11.     global last_value_change_ms
    12.  
    13.     pin_state = p.value()
    14.     cur_time = utime.ticks_ms()
    15.     diff = cur_time - last_value_change_ms
    16.  
    17.     if diff > 500:
    18.         print("")
    19.    
    20.     #print("CurTime={}, LastChange={}, Diff={}, State={}".format(cur_time, last_value_change_ms, diff, p.value()))
    21.    
    22.     if diff > 50:
    23.         if pin_state == 0:
    24.             value += 1
    25.             print('pin change', value)
    26.         else:
    27.             print("Button up")      
    28.     else:
    29.         print("Debouncing...", pin_state)
    30.  
    31.     last_value_change_ms = cur_time
    32.    
    33.  
    34. pin.irq(trigger=machine.Pin.IRQ_FALLING | machine.Pin.IRQ_RISING, handler=callback)
    35.  
    36. prev_value = value
    37. while True:
    38.     new_value = value
    39.     if new_value != prev_value:
    40.         print("Value Changed:", value)
    41.         prev_value = value
    42.  
    Порой встречается вот такая ситуация
    Код (Text):
    1. pin change 8
    2. Value Changed: 8
    3. pin change 9
    4. Debouncing... 1
    5. Debouncing... 0
    6. Debouncing... 1
    7. Debouncing... 0
    8. Value Changed: 9
    Т.е. при отпускани кнопки прерывание вызывается (по идее при переходе от 0 к 1), но если прочитать из пина значение, то читается 0, что приводит к ложному срабатыванию (код думает, что кнопка опять замкнулась)

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

    nikolz Гуру

    Сообщения:
    4.753
    Симпатии:
    453
    попробуйте простой аппаратный способ берете сопротивление 20..100 ком параллельно емкость 1..10 мкф.
    Эту конструкцию в разрыв цепи кнопка- пин.
    У пина должна быть подтяжка к питанию примерно 10 ком
    ----------------------------------
    Если не поможет расскажу как сделать программно, но написать ее лучше на CИ.
     
  3. grafalex

    grafalex Новичок

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

    =AK= Гуру

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

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

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

    nikolz Гуру

    Сообщения:
    4.753
    Симпатии:
    453
    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();
     
  6. pvvx

    pvvx Активный участник сообщества

    Сообщения:
    8.762
    Симпатии:
    1.284
    Дык кнопки или счет импульсов от механического датчика расхода воды?
    На некоторых стоят герконы, а не механические контакты. В большинстве установлены элементы на основе датчика Холла (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
     
    Последнее редактирование: 9 апр 2018
  7. pvvx

    pvvx Активный участник сообщества

    Сообщения:
    8.762
    Симпатии:
    1.284
    Резистор в 10 кОм 0.1 мкФ и механический контакт на датчике воды дадут увеличение ко-ва импульсов, а постоянная времени такой RC-цепи даст вам пропуски при опросе полигом датчика без дребезга в Arduino.
    С помощью 2-х резисторов задаются оптимальные времена заряда/разряда емкости. Но какие они нужны от ТС не дано, т.к. не указан тип счетчика воды и границы расхода (пиковый и/или максимальный расход, определяемый входным давлением, сечениями трубопровода, кранов и прочих устройств у потребителя).
     
    Последнее редактирование: 9 апр 2018
  8. grafalex

    grafalex Новичок

    Сообщения:
    17
    Симпатии:
    0
    Понятно, только список пинов.... эх, а хотелось бы еще и какой именно триггер сработал.
    Кстати, точно такой же код в обработке прерываний по пинам в micropython, что наводит на мысль, что это ограничение самого контроллера.

    В общем сделал с задержками, получилось весьма и весьма надежно
    Код (Text):
    1. import machine
    2. import utime
    3.  
    4. pin = machine.Pin(14, machine.Pin.IN, machine.Pin.PULL_UP)
    5. new_click_detected = False
    6.  
    7. def callback(p):
    8.     global new_click_detected
    9.     new_click_detected = True
    10.  
    11.  
    12. #pin.irq(trigger=machine.Pin.IRQ_FALLING | machine.Pin.IRQ_RISING, handler=callback, hard=True)
    13. pin.irq(trigger=machine.Pin.IRQ_FALLING, handler=callback, hard=True)
    14.  
    15. value = 0
    16. while True:
    17.     while not new_click_detected:
    18.         pass
    19.  
    20.     utime.sleep_ms(20)
    21.     if pin.value() == 0:
    22.         value += 1
    23.         print("New value: ", value)
    24.  
    25.     new_click_detected = False

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

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

    nikolz Гуру

    Сообщения:
    4.753
    Симпатии:
    453
    контроллер не виноват.
    Специально написал сегодня прогу для работы с кнопками.
    Несколько кнопок на один пин.
    Работает без дребезгов и помех как и ожидалось.
    прилеплю реле чтобы мигало по-разному от кнопок и выложу прошивку для тестирования желающим.
     
  10. Slacky

    Slacky Читатель

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

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

    pvvx Активный участник сообщества

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

Поделиться этой страницей