Найти в Дзене
Электроника, ESP32, Arduino

Arduino - прерывайся по таймеру правильно

Продолжаем организовывать псевдомногозадачность на платформе 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 работает в режиме генерации миллисекундных импульсов и менять его настройки мы не будем.

Код примера использования Timer 0 в Arduino IDE
Код примера использования Timer 0 в Arduino IDE

Как это работает?

В цикле loop() осталась только обработка события "нажатие на кнопку". Там вообще можно код писать как угодно, использовать delay() и любые другие ардуиновские библиотеки.

Встроенный в среде Arduino IDE счетчик
millis() увеличивается с использованием прерывания по переполнению - но мы его не задействуем - так, что delay() и millis() будут работать как обычно.

Переключение светодиода и отправка символа в монитор порта происходит когда Timer 0 досчитает до 160. (это значение равно примерно половине максимального значения счетного регистра таймера и оно будет активировать прерывание по совпадению
SIGNAL (TIMER0_COMPA_vect) { }
но со сдвигом во времени от основного прерывания.

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

Прерываемся правильно
Прерываемся правильно

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

Что такое аппаратный таймер микроконтроллера и с чем его едят читайте в статье "Рисуем Ардуиной синус из розетки"

Полный список статей канала доступен тут.