Продолжаем организовывать псевдомногозадачность на платформе Arduino. Прошлая статье где псевдомногозадачность была организована в основном цикле с помощью встроенной в среду Arduino IDE функции millis() вызвала ожидаемую волну комментариев про то, что так делать не надо, для этого обязательно нужно использовать аппаратный таймер - все другие методы "не комильфо" априори.
Особо одарённые личности даже посоветовали устроиться в Макдональдс
ВНИМАНИЕ:
- если вы плохо представляете что такое аппаратный таймер, рекомендую сначала ознакомиться со статьей про реализацию самодельного осциллографа на Arduino;
 - все что написано в данной будет относиться только к Arduino подобным платформам на "камнях" atmega328p (Nano, UNO) с тремя таймерами на борту. Один из вариантов обхода ограничении поискать платформу с другим микроконтроллером где аппаратных таймеров больше.
 
Если мы попробуем перенастроить любой из имеющихся таймеров на борту Arduino - можем толкнуться со следующими проблемами;
Tаймер 0 (Системное время, ШИМ 5 и 6)
Используется для хранения счетчика времени работы программы. Функция millis() возвращает число миллисекунд с момента запуска программы, используя ISR глобального приращения таймера 0. Таймер 0 также используется для реализации ШИМ на выводах 5 и 6.
(прощайте millis() и delay() )
Tаймер 1 (ШИМ 9 и 10)
Используется для реализации ШИМ для цифровых выводов 9 и 10.
(библиотека Servo говорит - "давай до свидания")
Tаймер 2 (ШИМ 3 и 11)
Используется для управления выходами ШИМ для цифровых выводов 3 и 11. (прощай функция tone() )
Не... конечно если ваша цель грамотно мигать светодиодом - то можете дальше не читать. В моих поделках на Arduino где как правило присутствуют и сервоприводы и электромоторы и терять функционал предоставленный средой Arduino IDE лично у меня нет ни какого желания.
Схема такая же как в предыдущей статье:
Задача:
- каждую секунду отправляем в COM порт символ и переключаем встроенный светодиод
 - при нажатии (отпускании) на кнопки загорается (гаснет) светодиод L1
 
Для решения задачи будем использовать прерывание по совпадению Timer 0 - которое в среде Arduino IDE не используется!!! Сам Timer 0 работает в режиме генерации миллисекундных импульсов и менять его настройки мы не будем.
Как это работает?
В цикле loop() осталась только обработка события "нажатие на кнопку". Там вообще можно код писать как угодно, использовать delay() и любые другие ардуиновские библиотеки.
Встроенный в среде Arduino IDE счетчик millis() увеличивается с использованием прерывания по переполнению - но мы его не задействуем - так, что delay() и millis() будут работать как обычно.
Переключение светодиода и отправка символа в монитор порта происходит когда Timer 0 досчитает до 160. (это значение равно примерно половине максимального значения счетного регистра таймера и оно будет активировать прерывание по совпадению 
SIGNAL (TIMER0_COMPA_vect)  { }
но со сдвигом во времени от основного прерывания.
Осталось посчитать эти миллисекундные импульсы, и переключить светодиод.
Предыдущая статья, посвященная псевдомногозадачности с использованием mills() доступна по этой ссылке
Что такое аппаратный таймер микроконтроллера и с чем его едят читайте в статье "Рисуем Ардуиной синус из розетки"
Полный список статей канала доступен тут.