Как стать автором
Обновить

Блокирующая обработка тактовой кнопки для Arduino. Настолько полный гайд, что ты устанешь его читать

Время на прочтение31 мин
Количество просмотров22K
Всего голосов 35: ↑31 и ↓4+38
Комментарии55

Комментарии 55

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

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

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

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

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

Это удобней и в разы универсальней.

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

И самое главное — опрос не мешает выполнятся остальным частям кода.

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

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

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

Раньше C++ не использовался ввиду того, что давал оверхэд при компиляции для микропррцессоров, сейчас компиляторы его практически не дают - разница может составлять совсем не много около - 0.2-0.5%

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

Но понятно, что при использовании C++, надо и метрологию его использовать, а не процедуры + вкоряченный класс)

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

Деформация и зашоренность сознания сильно ограничивают. К примру, я сам не могу себя заставить на ООП писать под МК. У меня выработались практически "стандартные" для меня подходы, которые позволяют достаточно просто писать код. Я и сам уже анализировал результаты. К примеру, смотрел, как оптимизатор распределяет память для структур, очень не дурно, потери памяти действительно минимальные.

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

Ну, надо начать просто - чем больше, тем лучше будет.

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

Использовать объекты, когда есть что-то повторяющиеся в системе (кнопки, например).

Использовать ссылки, вместо передачи по значению.

Ну и возможность выносить класс или набор классов, как отдельный файл с отдельным функционалом - это прямо киллерфича - глобал не загрязняется, а порождая тип, ну например OLED display( PORTA, PIN1) сразу понятно что куда почему + инициализация, если нужна.

Это вот прямо самое базовое.

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

Когда-то давно вышел пакет WinAVR для микроконтроллеров на основе GCC. Я, на радостях, в мейк файле ткнул ключ G++, и оно тогда заработало. Но вот в отладке оно мне совсем не понравилось. С тех пор как-то скептически относился к классам для микроконтроллеров.

Хотя понимаю, что для ARM-микроконтроллеров уже самое то, перейти на ООП и использовать RTOS. Но пока задач подходящего масштаба не возникало.

О "правильности"... а что считать правильно или не правильно? Есть задача, и есть подходящее для этой задачи решение. Зачем все подряд упаковывать в конечные автоматы?!

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

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

Зачем все подряд упаковывать в конечные автоматы?!

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

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

Но если проект за деньги, то пиши на эти деньги

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

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

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

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

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

Вас это удивляет, или это затруднительно для понимания?

Обратитесь к ГОСТ 19.701-90(ИСО 5807-85), там еще и не такое можно найти. В частности "3.2.2.6. Граница цикла". Мы просто привыкли из курса школьной информатики использовать крайне ограниченное количество символов для изображения алгоритмов.

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

Хороший вопрос, я ждал его! Наберитесь терпения. Я уже готовлю статью, где будет такое решение. Можно будет сделать выводы.

Можно, но везде есть своя цена. Проще всего решать дребезг программно.

Обычный антидребезг, схем много - простейшая кондер 100nf параллельно кнопке.

Можно ещё поставить небольшой резистор от кнопки к кондеру, дабы увеличить её срок службы

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

«чудес» в работе схемы

Хф1
Всегда, читая (или ВИДЯ), что-то о работе схемы, вспоинаю 1 (первый(и главный))
закон OHM(A):
«Не включённая схема не работает»

А вот и не всегда — простые КМОП логические схемы вполне работают без подачи напряжения питания получая паразитное питание из входных сигналов. В эпоху перехода от ТТЛ к КМОП это многих ставило в полнейший ступор.

 получая паразитное питание

2 (второй) закон OHM(a): Все токи дотекают до земли...

С конденсатором - это уже будет простейший RC-фильтр, временная постоянная которого равна R*C. Зная интервал опроса кнопки t, можно вычислить примерную требуемую ёмкость конденсатора: C = t / R.

Есть даже специализированная микросхема MC14490

Я в комментарии к предыдущей статье пример неблокирующей обработки дребезга приводил.
Когда кнопок больше 2 и есть лишний аналоговый вход, то проще посадить кнопки все кнопки на одну линию через резисторы, особенно если кнопки вынесены от платы с МК.

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

Очень и очень хорошо!

Вы не могли бы подробнее озвучить Ваше мнение?

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

А Вы точно читали мою статью? Она разве о дребезге контактов?

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

Конденсатор все могут поставить... А без конденсатора слабо?

Триггер — Шмитта.

Для идеалистов - RS-триггер!
Когда-то начинал в школьные годы с TTL - навесной монтаж, выносная клавиатура на жгуте из МГТФ - работало нормально только с таким решением на микриках :)

Я на 555 одновибратор использовал

К слову, входы Arduino/ATmega имеют встроенные триггеры Шмитта.

Аппаратное подавление хорошо, но как показал опыт разработки промэлектроники работающей в тяжелых условиях, оно не спасает полностью. Работает пока постоянная времени RC цепочки (элементарного НЧ фильтра, интегратора) больше чем период дребезга у кнопки. Но потом со временем какой-то один из контактов либо повышает сопротивление, либо замерзает, либо загрязняется, либо окисляется, либо намагничивается, либо теряет упругость (даже если контакт - геркон с пятой приёмкой), либо на крыше включается радиостанция и начинает просачивается помеха, либо нажимающий подносит к контактам 50Hz сети. В итоге на вход при том что есть подобранная RC цепочка и ТШ начинает просачиваться сигнал быстрых срабатываний. Контакт вещь такая, не предугадаешь его дребезг, сюрпризы он выкинет обязательно. А уж нажимающего его человека - тем более. Поэтому там где возможно, желательно предусмотреть еще и программную обработку дребезга.

Хорошее замечание. Еще, как более частый пример, могу привести энкодеры в автомагнитолах. Там обычно используют RС-цепочки от дребезга. И сколько таких магнитол, у которых со временем энкодер начинает подглючивать?

интересно, не знал!

А теперь рассмотрим экономику массового производства.

Допустим, эти дополнительные элементы обойдутся в 100 рублей при закупке.
Изделие выпускается партией 10 000 штук. Итого предприятие теряет на партии миллион рублей, или вынуждено поднять отпускную цену изделия (в принципе, достаточно на те же 100 р, то есть вроде не намного, но если у конкурентов будет на 100 р дешевле - оптовый покупатель уйдет к нему).

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

Так что знание электроники не всегда выдерживает столкновения с реальной капиталистической жизнью.

Проектировщик должен владеть всеми способами, а считать стоимость уже по месту нужно.

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

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

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

Итого предприятие теряет на партии миллион рублей, или вынуждено поднять отпускную цену изделия (в принципе, достаточно на те же 100 р, то есть вроде не намного

Или недоложить блок зарядки найти на чем сэкономить ещё). На таких больших партиях всегда найдется на чем сэкономить.

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

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

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

На таких больших партиях всегда найдется на чем сэкономить.


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


Да что там - номенклатуру компонентов резали по принципу "зачем у нас несколько типоразмеров резисторов с одинаковым номиналом сопротивления, поставим везде один с максимальными предельными параметрами". То есть вот были резисторы одного номинала, но 1206 10% в силовой части схемы, 0805 10% в логической, и 0805 1% в делителях АЦП. Теперь везде 1206 1%. Казалось бы, решение сомнительное, 1% стоят дороже чем 10%, но зачастую получается, что закупить 30 тыщ 1% оказывается существенно дешевле чем по 10 тыщ трёх разных типономиналов. Хотя все равно экономия копеечная получается. Хотя я не считал, не владея информацией о каналах отдела закупок.

У отдела закупок свой показатели эффективности.

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

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

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

Спасибо за комментарий! Следующая статья к концу недели будет.

Отлично. Буду ждать!

Картинку поправьте, у вас там SB5 из воздуха появился :)

Автор конечно сказочник. Особенно для проверки кнопки Протеусом. У которого нет дребезга.
Ну и делаи конечно же забавляют. Зато КАКУЮ оперу написал! ППЦ!( Видать платят за объём.

Статья очень затянута. Я не просто читать, я листать до конца устал. Для чего вообще обрабатываются кнопки? Кнопки должны порождать какое-то действие. Например, переключение светофора. То есть, есть программа работы светофора по времени и есть нажатие кнопок на переходах. И не смотря на огромный объём материала, я слабо представляю как он поможет решить такую задачу. Я видел заголовок следующей статьи, которую ещё посмотрю, и там должно быть уже решение, но тут решение с блокировками просто доведено до абсолютного абсурда.

P. S. пример со светофором -- это из проекта, что я делал со своим ребёнком пару лет назад (ему было 9), поэтому давать советов как это делать мне не надо, просто пример привёл.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий