Pull to refresh

Comments 97

Можно добавить к программному методу еще один:

Скрытый текст
  1. создаем программный счетчик, который инкриминируется, если кнопка нажата и декриментируется если кнопка отжата (без переполнения);

  2. создаем флаг, который принимает свое значение в зависимости от пересечения верхнего или нижнего установленного порога (обеспечит гистерезис положения нажато/отжато);

  3. добавляем опрос кнопки в бесконечном цикле.

Это позволит сэкономить аппаратный счетчик и деньги на рассыпухе и площади платы

Ну ну..
Ключевая концептуальная ошибка: "опрос"+"счетчик".

который инкриминируется, если кнопка нажата и декриментируется если кнопка отжата

Получите неопределенное значение. Потому что, момент опроса и момент переходных процессов при нажатии (дребезг) вообще могут не совпасть.

Очень типичная ошибка "опросом" + "счетчик".
Например, так же нельзя опросом энкодер считывать. Если конечно интересует не "качественные" показатели (типа крутилка громкости), а накомительное значение (ось станка и пр.).

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

тогда зачем фраза

программный счетчик, который инкриминируется, если кнопка нажата и декриментируется если кнопка отжата

Количество "0" и "1" на опросе кнопки могут встречатся вообще в любых сочетания при попадании на диапазон "дребезга". И период опроса вообще на это не влияет. Поскольку "дребез" это в общем случае "случайный" период (упрощенно изображают на графиках меандром. но это не меандр).

Я так, понимаю, что автор рассматирвал вопрос сложности определения момента "нажатия" на кнопку как фиксации события "нажата". А не просто определения факта "нажата/не нажата"

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

первая строка значение на входе контроллера (*)
вторая строка - считываение в период опроса.
третья строка - типа счетчик
000001010101010101010100111101000101111111111100111011111111111..0100100001110000000
0____1____0____1____1____1____0____1____0____0____1..0____1____1____0____
__ -1____0____-1____0____1____2____1____2___-1____-2___-1__.. 345__346__347__346___

Ну и что конретно даст значение счетчика для анализа по предложенному...

программный счетчик, который инкриминируется, если кнопка нажата и декриментируется если кнопка отжата.

Только давайте на ходу не придумывать доп условия :) Преврашая алгоритм в лапшу и набор заплат :)
Не надо изобретать велосипед. програмные алгоритмы (как через прерывания, так и через опрос под разные цели) подавления дребезга - это динозавры, которым много десятилетий. Все придумано до нас.
Честно говоря, смысла этой статьи не понимаю.

(*) - условно. значени если бы опрашивали порт в данный момент. Тема для превращения в 0/1, фактически аналогового сигнала дребезка, не проста. Но можно не вдаваться и считать 0/1.

А не могли бы пояснить, где на вашей картинке переходный процесс ?

По-моему, там просто случайные 0 и 1, приходящие на порт МК.

Если снять осцилограмму с кнопки в момент ее нажатия/отпускания (хорошим осцилографом с малой емкостю и большим входным сопростивлением). То будет видна характерная картина. Собствено это и есть "дребез контакта".
наберите "дребез контакта осциллограмма" в поиске. Там будут картинки примера дребезка на осцилографе.

В зависимости от физического исполнения "кнопки", емкости на входa, входного сопротивления входа и пр. может быть разной формы.. разной длитетельности (может до 5-10ms).

По-моему, там просто случайные 0 и 1, приходящие на порт МК.

Не приходящие, а считываемые с порта (я специально это отметил). А приходит напряжение/ток сложной формы.
"Цировой сигнал" это абстракция. Почему то "писатели под ардуино" про это забывают/не знают. А потому удивляются "а почему не так работает как я думал".

4-й курс (времена динозавов правда).. схемотехника.. лаба посвященная этому. "Что бы запомнили".
Для меня электроника и схемотехника - хоби. Но запомнил жжж..

Вы не поняли вопрос.

Я попросил отметить, ГДЕ именно на вашей картинке переходный процесс, который должен с течением времени закончится. Он потому и называется переходный.

То есть, картинка должны быть типа такой: 0110101010101011111111111.

И вот этих вот 111111, на вашей картинке не видно, то есть ваша условная кнопка выдает дребезг ВСЕГДА. Что не является правдой, если кнопка исправна.

там где "..." - там закончивается.

Я диаграммой показывал именно переходной процесс и то, что семплирование с фикисрованной частотой с непрерывным интегрированием результата какого либо смысла не имеет. Поскольку результаты плавают и ловить какое то определенное место в промежутке переходного процесса все одно не выдет таким способом.
Если требуется ловить именно отсутиве дребезга в течении какого то времени. То так и надо писать.
Но тогда лучше (IMHO):

  1. "счетичк нажатия" += 1, если семп == "1"

  2. "счетичк нажатия" = 0, если семп == "0"

  3. счетичк "нажатия" > порога (устоявшийся процесс) -> сигнал о нажатой кнопке + "счетичк нажатия" = 0.

И пороговое значение понятнее и проще. А если в счетчике учитыватеся и переходной процесс и его окончание считается как пороговое значение счетчика, то вермя "нажата кнопка" будет считаться от начала переходного процесса. И будет случаныйм образом плавать, поскольку в порог включить придется и семплы после переходного и семплы внутри переходного. А они случайны (количестов 0 != количеству 1).

Аналогично можно ловить отжатие кнопки.
А если требуется просто ловить "кнопка нажата или нет", то проще просто брать сэмплы (период скажем 500ms) и считать семп за резутьтат "нежата/не нажата" в типичной задаче типа "зажатая кнопка увеличивает громкость".

Ровно как описан алгоритм создаем программный счетчик, который инкриминируется, если кнопка нажата и декриментируется если кнопка отжата
счетчик будет возврастать при каждом опросе нажатой кнопки (за 1 секунду семплирования с периодом скажем 20ms это будет +1000/20).
И как это неперывно увеличивающееся значние используется в "флаг, который принимает свое значение в зависимости от пересечения верхнего или нижнего установленного порога" = ?.

Ровно как описано в исходном посте. А если автор чего то "подразумевал" (типа порог.. обнуляем.. учитываем и т.д.) так я не ясновидец и додумывать не буду что он подразумевал.

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

там где "..." - там закончивается.

А после ... 01001 это что, переходный процесс при отпускании кнопки ? или дрожание контактов после переходного процесса ?

Но тогда лучше (IMHO):

с этим полностью согласен. К этому вопросов нет совершенно. Но разве не на то же самое вы отвечали:

То есть в моменты дребезга счетчик будет болтаться, но после стабилизации уровня счетчик достигнет какого-то максимума при нажатии и нуля при отпускании

 не ясновидец и додумывать не буду что он подразумевал.

так если что-то непонятно, принято спрашивать. Я вот спросил, нахватал минусов, хотя вопрос то был по существу.

Добавил картинку специально для вас, как и просили.

Переходный процесс занимает 2.6 мс. Далее, значение 1. И сравните с тем, что привели вы выше в своем примере (первая ваша строка).

Для иллюстрации.. синие и красное - это ну скажем на 0.01% отличающееся друг от друга частоты семплирования. 100 - типа порговое значение. Просто для иллюстрации/примера. За счет алиасинга пороговое значение будет достигаться в алгоритме "+1/-1 от семпла" фактически в непредскзуемое время.

На синем - больше было не учтенных выбросов 1. А на красном больще не учтенных выбросов 0.
Сделать оверсеплинг для дребезга, где фактическая частота может быть десятки МГц.. это не возможно да и не нужно.

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

Для интерактивной кнопки вообще практически все будет работать.

Но сам подход с ингорирование проблем алисаинга..
как бы для кнопки не важно (хотя и избыточно в большинстве случаев). Но это же будут пихать и для других целей!

Но это же будут пихать и для других целей!

Изначально вы начали отвечать на сообщение, где написано про кнопку. И только про кнопку. Причем, еще в таком тоне, что все дураки, "ардуинщики" делают неправильно и т.д. Откуда столько агрессии ?

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

Мне фраза одного из комментаторов
"по-хорошему, надо энкодер соответствующий использовать, а не лепить из того есть под рукой." напомнила просьбу помочь с "почему у меня ошибка с энкодера накапливается" (что то там про балансирующего робота, где это было очень важно).

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

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

Так что.. да. Агрессия и клеймо "ардуинщик". Может у меня шаблон в голове на "ардуинщик". Но асоциация такая:

  1. Хочет найти готовую либу.

  2. Не хочет вникать как работает "работает и ладно".

  3. От електронники далек как только можно и не хочет в это вникать.

Мне фраза одного из комментаторов

Но, согласитесь, к текущей ветке она не имеет никакого отношения. Человек привел в пример вполне рабочий алгоритм. На что вас тригернуло.

На все попытки объяснить что такое джиттер 

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

Типа у меня экодеры видимо дерьмо и попробую другие

Так многие люди и рассуждают, и дело тут не в Ардуино совсем, а в целом в нежелании разобраться в причинно-следственных связях. Я также слышал от программистов, у которых код не работал, что дело в компиляторе. Через 5 мин просмотра кода, находилась проблема, исправлялась и о чудо, дело не в компиляторе совсем было.

в этой ветку был коммент про "по-хорошему, надо энкодер соответствующий использовать". На него и тригернуло.

Похоже подумали, что я на Ваш счет.. Не.. даже не думал.
Извините именно Вам не хотел.
Да и даже не к исходному комменту.
вообще "про ардуинщиков" было "лирическое отступление" и в не том комменте что нужно.

в этой ветку был коммент про

Похоже, что не в этой, так как эта ветка начинается с первого комментария и все начало ветки идет про кнопку.

Похоже подумали, что я на Ваш счет

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

Перепутал ветки. Читал одновременно.. (в коротком переыве в работе)
бывает.
еще раз извинините.

Да ничего страшного, бывает! )

Не приходящие, а считываемые с порта (я специально это отметил). А приходит напряжение/ток сложной формы

первая строка значение на входе контроллера (*)

Вы сами себе противоречите.

Меня интересовала ТОЛЬКО первая ваша строка, которую вы называете "значение на входе контроллера". Чтобы вы там на ней показали переходный процесс и его окончание.

не корректно написал. НЕ на входе. А считываемое с входа в программе.

А считываемое с входа в программе.

вторая строка - считываение в период опроса

Тогда вторая строчка, это что, если первая, это уже то, что считали в программе ?

1-я строка это то, что потенциально можно считать с входа в программе (ну, скажем, если считывать на максимальной частоте проца/входа).
ну не нарисую я же текстом аналоговый сигнал :)
А вторая строчка - то что считали. А "___" во второй строке - это типа dt между считываниями.

Корректной картинкой. Но долго и неохота рисовать.

В общем, если подвести итог, первая строка, это аналоговый сигнал, который виртуально переведен в цифровой.

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

Все верно ?

Если да, то тогда что означают .... 01001 вот эти 0 и 1 уже после переходного процесса в первой строке, если "..." это окончание переходного процесса ?

новый переходной процесс (отпускания).
Для демонстрации поведения счетчика "+1/-1 от сэпла". Что счетчик по формулировке алгоритма продолжает щелкать при нажатой кнопке.

новый переходной процесс (отпускания).

Ага, именно поэтому я и задал изначально свой вопрос, так как у вас не совсем корректный пример. Если после ... это отпускание кнопки, то ваш счетчик, перед ... не должен иметь значение -1 (3-я строка).

А нормальный пример, будет выглядеть как-то так (нажатие кнопки):

11000001111100111111111111111111111 - будем считать это реальным сигналом

1__0__0__1__0__1__1__1__1__1 - это то, что считали в цикле с какой-то частотой

1, 0, 0, 1, 0, 1, 2, 3, 4, 5 - значение нашего программного счетчика.

Условно, досчитали до 5, выставили флаг, что кнопка нажата.

У вас же, в вашем примере, перед тем, как кнопку отпустили, счетчик имеет значение -1, хотя переходный процесс уже давно закончился. И еще одно замечание, в нормальной реализации счетчик не должен быть отрицательным, так как нас интересуют только фронты, а не спады.

Условно, досчитали до 5, выставили флаг, что кнопка нажата

Да. флаг выставили. Но нигде не сказано, что счетчик в 0 сбросили или перестали +1 при нажатой кнопке. Фактом выставления флага же все не заканчивается. Т.е. можно продолжить ... 4,5,6,7,8,.. (кнопка же нажата)

Занудничаю..
Но тут и по работе в постановке задачи "а мы это подразумевали" наложилось.

Т.е. можно продолжить ... 4,5,6,7,8,.. (кнопка же нажата)

А далее переполнение и начинаем считать с нуля.. )))

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

Зачем его сбрасывать в 0, ведь его потом можно использовать на отпускание кнопки, чтобы не заводить на отпускание еще один счетчик. Ограничили сверху да и все. При отпускании декремент до 0.

Ограничили сверху да и все.

Ну так это и есть доп. типа "а мы это подразумевали".
Только что на работе с этим столкнулся (конечно не по столь простому случаю). И жду ответа "а что вы подразумевали?". .
вот поэтому и фигней сейчас страдаю на хабре :)

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

Именно поэтому в вашем примере счетчик должен был быть ограничен как сверху, так и снизу, то есть -1, -2, -3 тоже значений у него быть не должно, по моему мнению, ниже 0 мы считать не должны точно также, как и выше порогового значения.

Эхх.
Никогда не нужно в ТЗ/описании додумывать за кого то.
"это логично", додуманное за кого то (если четко не описано), ведет в ад.

Это применительно ко всему.

Я не оспариваю ваш вариант.
Классический вариант плавующего окна (счетчик с ограниченим и тригерное значение). Но только в исходном описание детали пропущены, которые уже "додумываются" :)

Позволю себе процитировать сообщение, на которое вы ответили в самом начале ветки (выделения мои):

  1. создаем программный счетчик, который инкриминируется, если кнопка нажата и декриментируется если кнопка отжата (без переполнения);

  2. создаем флаг, который принимает свое значение в зависимости от пересечения верхнего или нижнего установленного порога (обеспечит гистерезис положения нажато/отжато);

  3. добавляем опрос кнопки в бесконечном цикле.

Что я лично вижу в этом описании:

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

То есть все уже задано в ТЗ. )

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

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

А я не вижу в этом описание условия ограничения самого счетчика.
Есть только условия на флаг.
А фраза "без переполнения" != "ограничен сверху значеним порога а снизу 0".

Да я нудный. Но слишком часто приходилось тратить на переделку и общение с заказчиками из за "подразумевалось".

Что мне не понравилось в вашем конкретном примере, это то, что нижнего порога у вас нет.

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

И прекрасно осознаю, что если у автора по факту работало, то это просто алгоритм не до конца описан.

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

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

Ух какой шустрый мух
Ух какой шустрый мух

Но этот риск можно счесть приемлемым.

А можно иначе рассудить - если кнопка будет до ~100 мс дребезжать, то зачем вообще её повторно опрашивать в это время? Чтобы собирать статистику по длительности дребезга и реализовывать антидребезг, адаптирующийся к старению кнопок? Ну-ну. В одной статье упоминают возможный "short glitch" в нажатом состоянии - это, видимо, про полунажатое состояние в случае с мягкими мембранами как в пультах или компьютерных клавиатурах.

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

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

Чья-то реализация: https://www.eevblog.com/forum/beginners/keyboard-switch-matrix-hw-debounce-multiplexer/msg3926876/#msg3926876

Если мгновенная реакция не нужна, то решение сводится к редкому опросу с периодом, совпадающим с выбранным dead time (как в комментах ниже про 0,5 секунды и 200 мс).

А давайте не будем натягивать сову на глобус и всё-таки примем 1 условие: размер программного счётчика и частота опроса GPIO должны быть подобраны так, чтобы при максимальной частоте полезного сигнала счётчик успевал пройти весь цикл. В таком случае: 1-я строка - данные от GPIO, 2-я значение счётчика, 3 - отфильтрованное состояние кнопки, которое предоставляется остальным программным модулям вместо данных напрямую из GPIO.
0000010101101111111111010110010000000
0000010101212345555555454554343210000
0000000000000001111111111111111110000
Такой алгоритм работает у меня и моих коллег в тысячах устройств по всему миру, и доказал свою состоятельность. И с энкодерами тоже всё хорошо работает, просто частоту опроса GPIO надо приподнять, и обработчику энкодера фильтрованные данные давать, а не с GPIO напрямую. Если надо нервно на один из фронтов реагировать - тоже пожалуйста, с минимальными правками всё делается:
0000010101101111111111010110010000000
0000054545545555555555454554343210000
0000011111111111111111111111111110000
А если речь идёт о "точная координата оси станка после часа его работы", то, по-хорошему, надо энкодер соответствующий использовать, а не лепить из того есть под рукой. Хотя, если с умом подойти, то и из подножного корма можно конфетку собрать, но тут уже изощряться надо, решение "в лоб" обычно порождает много "но".

У вас в примере магическим образом частотад дребезка это константа, совпадающая с
частотой опроса.
А проблемы алиасинга так же магически "частоту опроса GPIO надо приподнять".

Мне больше нечего сказать на это..

В моём примере нет частоты дребезга, только выборки через +- равные промежутки времени. Что творится между выборками, меня особо не волнует, если выполняется условие, которое я упомянул. И если это условие выполнить невозможно, тогда нужно использовать другой алгоритм, соглашусь. Или использовать подходящую кнопку или энкодер, которая бОльшую часть времени всё-таки выдаёт правильный сигнал, а не дребезг.
По поводу "частоту опроса GPIO надо приподнять" - энкодер в нормальном режиме может выдавать бОльшую частоту, чем кнопка, подъём частоты нужен исключительно чтобы опять-таки выполнилось упомянутое условие.
В другом треде Вы писали, что Вас триггернула фраза, что нужно правильный энкодер выбирать, чтобы получить "точная координата оси станка после часа его работы" . Позвольте узнать, с чем Вы не согласны? Почему для такого ответственного применения Вы хотите использовать квадратурный, а не абсолютный энкодер? Или хотя бы датчик оборота не хотите добавить для синхронизации, как это делается в автомобилях для датчика коленвала?

Очень типичная ошибка "опросом" + "счетчик".

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

Например, так же нельзя опросом энкодер считывать.

Квадратурный энкодер обрабатывается как две кнопки.

Если конечно интересует не "качественные" показатели (типа крутилка громкости), а накомительное значение (ось станка и пр.).

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

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

просто отмечу (на правах рассуждения)
Процессорное время в обработке события от нажатия на кнопку (человеком) не самый определяющий критерий.
И я критиковал очень конкретный алгоритм со "счетчиком" чего то и измением этого счетчика по опросу.
А не способ "опроса" вообще.

Квадратурный энкодер обрабатывается как две кнопки.

К слову, квадратурный энкодер программный то же работать не будет, если задача не "крутилка громкости", а "точная координата оси станка после часа его работы". Для таких целей есть аппартаный режим счетчиков (в STM32 например).

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

Для "крутилки громкости", зачастую достаточно в фоновом процессе (прерывание по таймера раз в 50ms) тупо считывать состояние и риск возникновения дребезга в момент считывания не учитывать.. кнопки/энкодеры типа "крутилки громкости" - интерактивны. Щелчком больше/меньше значения не имеет вообще.

>Можно добавить к программному методу еще один:

В дополнение имеем возможность определять длинное нажатие.

И для этого метода есть "вертикальные" счетчики.

А ещё можно специализированные микросхемы использовать, например HT16K33 со встроенным подавлением дребезга (не идеальным, но лучше, чем ничего). Есть ещё TM1638, но не знаю, есть ли у неё встроенное подавление дребезна.

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

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

Да, кстати, я уверен, что автор сгенерил в LLM эту и все предыдущие свои статьи. Но учитывая сколько танцующих с волками говорящих с копипастой читателей набежало в его первую статью, я бы сказал, что это не так уж и плохо. Надеюсь, такие статьи (я имею ввиду конкретно "как избавиться от дребезга контактов") через год-другой будут выходить на Хабре каждый день или даже каждый час. Самому автору я желаю поскорей написать статью "как подключить лампочку накаливания к батарейке", пока это не стало мейнстримом.

У вас, неактуальная или нерелевантная информация. Как раз наоборот, микроконтроллеры (а тут упомянут stm32) обычно итак имеют на входе триггер Шмитта.

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

И эту статью автор скорее всего сам писал, ибо чат-гпт выдал бы что-то на порядок лучше.

У вас, неактуальная или нерелевантная информация. Как раз наоборот, микроконтроллеры (а тут упомянут stm32) обычно итак имеют на входе триггер Шмитта.

У триггера Шмитта та же базовая проблема. Да, он не переключится в лог.1, если вы не доведёте уровень напряжения от лог.0 до напряжения переключения. Но у него также в большинстве случаев есть такое входное напряжение, при котором приоткрывается и верх и низ.

И эту статью автор скорее всего сам писал, ибо чат-гпт выдал бы что-то на порядок лучше.

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

У триггера Шмитта та же базовая проблема. Да, он не переключится в лог.1, если вы не доведёте уровень напряжения от лог.0 до напряжения переключения. Но у него также в большинстве случаев есть такое входное напряжение, при котором приоткрывается и верх и низ.

Не должно быть такого эффекта.

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

Чёёёрт! ))))) Реально по промту "Напиши статью характерным для Хабра языком о том как я справился с дребезгом контактов от кнопки." DeepSeek написал заметно более интересную и живую статью!

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

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

Касательно ёмкости конденсатора могу сказать что путём эксперимента были проверены разные значения, в том числе и 100нф и в конце было выявлено что 10 мкФ оптимальнее всего, а что же касается прерывания бывают моменты когда их использование корректнее всего

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

Десяточка мкФ конденсатора фильтра может вытечь через защитные диоды на входе триггера Шмитта на шину питания, это может привести к выходу из строя мсх триггеров Шмитта. Я бы воткнул или резистор в килоом последовательно со входом ТШ, или диод мелкий анодом к конденсатору и катодом к шине питания.

Но в принципе, если вы реализуете динамическую индикацию, оч легко организовать цифровую фильтрацию кнопки. Каждый раз во время регенерации просто сдвигаете состояние пина кнопки в переменную, например uint8_t, то если она будет 0xff, то нужно перебросить бит кнопки в одно состояние, а если 0, то в другое. Не хватит восьмого инта, есть 16 или 32)))

Десяточка мкФ конденсатора фильтра может вытечь через защитные диоды на входе триггера Шмитта на шину питания, это может привести к выходу из строя мсх триггеров Шмитта.

Ток через защитные диоды потечет при отключении питания. Если по линии питания установлена маленькая емкость, то ток через диоды будет максимальным, и будет зависеть от тока потребления устройства.
У защитных диодов максимальный допустимый ток 20мА. В схеме 4 цепочки. Значит при потреблении до 80 мА должно обойтись без последствий. В грубом приближении.

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

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

Даже подключение кнопки к пину с прерыванием не избавляет от проблемы

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

Согласен с уже высказавшимися. Есть давно устоявшиеся алгоритмы обработки дребезга контактов. Можно обойтись и без дополнительного таймера, и без прерываний вообще. Вы молодец. Сделали красиво. Но дорого. И чем больше кнопок, тем дороже. А программная обработка сколько-то будет стоить только в тех редких случаях, когда ресурсы чипа уже забиты под завязку , что бывает крайне редко: обычно стараются брать с хотя бы небольшим запасом. И всё равно переход на следующий в каталоге микроконтроллер окажется дешевле дополнительных деталей, дополнительного места, дополнительных операций по монтажу.

Но технически - да - работает здорово.

P.S. У микроконтроллера, кстати, вход точно так же определяет логическую единицу по "больше порогового напряжения/меньше порогового напряжения", так что дополнительный триггер можно было и не ставить.

В аппаратном методе у вас получилась схема одновибратора. Правда несколько усложненная.
Обычно одновибратор использует более простую схему:

Аппаратно у автора интегратор + компаратор с гистерезисом. Функционально это ФНЧ и линия задержки. ФНЧ подавляет дребезг. А линия задержки это негативный побочный фактор, который уменьшает максимальную частоту нажатий.

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

Ну если так говорить, то у меня на схеме двухэлементный повторитель с положительной обратной связью и ёмкостной развязкой :)))))

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

Установка одновибратора для устранения дребезга контактов - это стандартный прием применяющийся с 70-х годов (во всяком случае меня в институте так учили).

и ёмкостной развязкой

Это не емкостная развязка, а дифференцирующая цепь.

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

У автора не одновибратор.

  • Собственно, из аппаратного можно использовать одновибратор. Для себя такое реализовывал на копеешной ne555, предусматривающей соответствующую схему включения. Но у меня задача была достаточно тривиальная - дополнительная кнопка к магнитоле, там частота нажатий пальцем не более 2 раз в секунду, задержка на одновибраторе выставлялась достаточно большая.

Плата, которую Вы привели на фото - Blue Pill - может выдавать дребезг даже при работе с хорошим датчиком. Схемы этих плат различаются как количеством элементов, так и схемой получения 3.3 V.

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

Для них нужно применять фильтрацию как по питанию, так и по цепям датчиков.

А зачем механика. Как минимум есть ТТР223.

Защищаться от дребезга контактов с помощью RC-цепочки... Это не решение, а маскировка проблемы. Пока кнопка новая оно сработает, а после нескольких тысяч нажатий (в лучшем случае) начнутся проблемы...

В целом, есть аппаратные методы. Специализированные 3-5-и выводные супервизоры. Они очень хорошо работают. Но реально, софтовый метод (задержка чтения после срабатывания) при всем при этом самый надежный.

RC цепочка все равно нужна для подавления наводок и выбросов при замыкании кнопки.

Резистор ставится максимально близко к кнопке последовательно кнопке, конденсатор около МК. Подтяжка и софтовые методы про вкусу.

Ну, раз говорим про самый надёжный, то тогда надо триггер на каждую кнопку лепить, и кнопки с переключающим контактом использовать. А что за схема с супервизором? Хотелось бы расширить кругозор...

Например MAX6817 или ей подобные. Не сказать что их много, но и совсем не экзотика.

Более точно это называется switch debounce ic. Давно уже не экзотика. На рынке их достаточно, даже китайского. Внутрянка их уже все описанное содержит и даже больше. Необходимости изобретать эти схемы заново нет смысла. Собирать это все на рассыпухе выйдет дороже и менее надежным, смысла нет. Микрухи эти работают четко, программно минимум действий требуется.

Замыкать 10uF кнопкой, даже через 100 Ом, такое себе.

50мА в пике, в чём проблема? Другое дело, что постоянная времени очень большая, быстрые нажатия будут игнорироваться. Я обычно использую 10к, 100нФ - проблем не знаю.

А я знаю эти проблемы)) Например через год использования нестабильное нажимание на кнопку ввиду подгорания контактов. Хотя конечно если эта кнопка нажимается раз в год или любительская поделка, то это не важно.

Эммм... У Вас между конденсатором и кнопкой резистор, который ток ограничивает, есть... У меня есть 2 опыта подобных проблем: 100нф напрямую замыкалось кнопкой (проблемы появились через примерно год), и вообще без конденсатора, чисто программная обработка - проблемы полезли через месяц, контакты подкисли. Кнопки одинаковые, то же самое устройство, просто в нём конденсаторы выпаял и прошивку обновил. Так что мой опыт подсказывает, что пинать кнопку зарядом нужно, но так, чтобы окислы, появляющиеся естесственным путём, успевали выгорать, а металл контактов - нет. Для себя нашёл золотую середину - 10к, 100нф, и 100Ом между кнопкой и конденсатором.

По основной работе очень много логических параметров, которые имеют дребезг в переходном процессе (это состояния реальных объектов, например, пересечение траекторий летательных аппаратов). В хоббийных развлечениях как только столкнулся с дребезгом кнопки рефлекторно воспользовался программным алгоритмом. Как-то все на уровне подсознания. Так что я за программную реализацию: таймаут, состояние (никаких программных прерываний, таймеров).

Самый простой софтовый вариант имхо:

Заводим счетчик и переменную, куда сохраняем текущее состояние кнопки.

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

Если значение счетчика превысило некий порог, фиксируем новое состояние кнопки, уже отфильтрованное.

Самый простой софтовый вариант, это таймер блокирующий опрос. Зафиксировали нажатие, установили счетчик. По прерыванию уменьшаем счетчик. Пока счетчик не обнулился, кнопку игнорируем. Таймер на 0,5 секунды разрешит два нажатия в секунду. Легко сделать автоповтор при нажатой кнопке.
Дубовый метод. Не подходит для игровых мышей.

У этого способа есть один минус. Если идут помехи, или просто возможно кратковременное случайное нажатие, то можем его (или ее - помеху) зарегистрировать как нажатие на кнопку и пока блокирующий таймер активен, мы считаем, что кнопка нажата.

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

А автоповтор, срабатывание на нажатие/отпускание можно уже делать, опираясь на вот это отфильтрованное от дребезга значение.

Но с точки зрения простоты, согласен, ваш способ проще.

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

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

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

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

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

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

Опрашиваем кнопки раз в 200мсек, выставляем флаги нажатия, если нажатия нет флаги снимаем. Все. Я использую одну char переменную для хранения кода кнопки вместо флагов. Если функция ждет например нажатия enter то при появлении этого кода она выполняет необходимые действия и сбрасывает переменную. Если кнопка все еще нажата через 200 мсек снова появится код кнопки и очень удобно делать инкремент при вводе с клавиатуры.

Делал так: в программе на любое количество кнопок 1 счетчик. Если он 0, то прилетевшее прерывание добавляет 1 к счетчику и выполняет основной код, если нет - происходит сброс таймера и выход из прерывания. Дальше таймер сбрасывает счетчик по факту прерывания на переполнении. То есть какое-бы не было время дребезга и сколько бы фронтов не прилетело, валидным будет только первый фронт.

А пробовал ли кто-нибудь извлекать из этого переходного процесса полезную информацию - например, о скорости или силе нажатия на кнопку?

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

Единственное не очень себе представляю как на обычной механической кнопке отслеживать силу нажатия

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

Давным давно придумали софтовые таймеры, их может быть огромное количество и всеми ими управляет один аппаратный таймер с одним каналом сравнения. Далее все как в статье с аппаратным таймером. Всё.

Вечная проблема

Это проблема для цифровых схем на рассыпухе. Если кнопка подключена ко входу процессора то никакой проблемы нет.

Если просто перечислить все способы, которыми эта проблема успешно решалась - будет статья с таким же объемом, а то и больше.

По сигналу с пина кнопки запоминаем текущий счетчик тиков и через 50 мсек проверяем состояние пина. Если то же, то обрабатываем нажатие кнопки. Или второй способ -- сглаживание: state = state * 0.99 + new_state * 0.01. if state < 0.1 pressed(); if state > 0.9 released().

через 50 мсек

Время срабатывания есть в даташите на кнопку. Можно умножить на 2.

А можно просто после первого сигнала игнорировать следующие в течение этого времени. Если это датчик на валу, например.

Sign up to leave a comment.

Articles