Когда программируешь для Arduino, иногда бывает нужно периодически выполнять какие-либо действия через определённые интервалы времени — скажем, 100 раз в секунду; или хочется иметь ШИМ с большим разрешением или другой частотой, чем позволяет стандартная функция analogWrite(), или вдруг хочется выполнять какую-то работу параллельно, а вездесущий delay() всё портит. Вот тут-то может пригодиться библиотека TimerOne, которую я вам в двух словах опишу.
Эта библиотека позволяет использовать в своих целях один из таймеров в МК AVR, установленных на платах Arduino. Аппаратных таймеров в ATmega168 и ATmega328 имеется 3 штуки (в ATmega1280 и ATmega2560 их 6), и называются они Timer0, Timer1 и Timer2. Все они используются для ШИМ в функции analogWrite(), но разрядность такой ШИМ ограничена наименьшей разрядностью таймеров, а Timer0 и Timer2 — 8-разрядные, поэтому максимальное значение, которое воспринимает analogWrite() — 255. Но Timer1-то 16-разрядный! Чтобы задействовать мощь этого таймера, один добрый человек и написал библиотеку TimerOne. Скачаем библиотеку отсюда, установим и посмотрим, какие методы доступны в библиотеке:
void initialize(long period = 1000000)
Этот метод нужно вызвать для инициализации таймера, прежде чем вызывать какие-либо другие. По умолчанию задаётся интервал срабатывания в 1 секунду, но можно указать свой в микросекундах. Минимальный доступный интервал — 1 микросекунда.
После инициализации пины 9 и 10 на Arduino (11 и 12 на Arduino Mega) смогут использоваться библиотекой для ШИМ, но остановится analogWrite()’овская ШИМ на этих пинах, если таковая была задействована до вызова initialize(). Пока работаете с библиотекой TimerOne, для перечисленных выше пинов analogWrite() не вызывайте, и наоборот.
void setPeriod(long period)
Устанавливает интервал срабатывания таймера в микросекундах. Минимальный интервал равен 1 мкс (соответственно, максимальная частота — 1 МГц), максимальный — 8388480 мкс (около 8.4 с).
void pwm(char pin, int duty, long period = -1)
Включает ШИМ на указанном пине. Параметр duty указывает заполнение ШИМ, которое может меняться от 0 до 1023. Да-да, не вся «16-битность» таймера задействована, увы. Параметр period позволяет задать интервал срабатывания, но учтите, что период задаётся не для отдельного пина, а вообще для таймера. То есть, если вы укажете период для 9го пина, то повлияет и на 10й. Неочевидно, да.
Ещё один момент — можно указывать в качестве номера пина 1 и 2 вместо 9 и 10, но здесь тоже нужно быть внимательным: если указать 1 или 2, то это сработает и на Arduino Mega (пины 11 и 12), и на простой Arduino (пины 9 и 10), но если указать 9 или 10, то сработает только на Arduino… Брр, и что только автор библиотеки курил?
void setPwmDuty(char pin, int duty)
Устанавливает заполнение ШИМ для указанного пина, если ШИМ на нём уже включена.
void disablePwm(char pin)
Выключает ШИМ на выбранном пине.
void attachInterrupt(void (*isr)(), long period = -1)
Устанавливает функцию (обработчик прерывания), которая будет вызываться по завершении установленного интервала таймера. Такая функция должна иметь вид:
void xxxxx() { ... }
Параметр period, как обычно, задаёт интервал срабатывания (и, как всегда, для всего таймера). Нужно быть осторожным, и не выполнять слишком много кода в обработчике, иначе главный цикл (loop) может вообще никогда не получить управления. Если задать период в 1 мкс, то такой исход практически гарантирован, т.к. за это время AVR с кварцем в 16 МГц (как на Arduino) успеет выполнить инструкций где-то на десяток тактов — то есть, сложить пару-тройку чисел, и даже на один digitalWrite() этого времени не хватит. В общем, не советую устанавливать обработчик для периодов меньше 100 мкс (0.1 мс), если вы не рассчитали по тактам время выполнения обработчика и не уверены на 100%, что времени хватит (:
void detachInterrupt()
Отключает обработчик прерывания.
void start()
Запускает таймер, если он был остановлен.
void stop()
Останавливает таймер.
Применение всему этому придумать несложно: первое, что пришло в голову — мигалка двумя светодиодами не то что без delay(), а вообще без нашего участия. Светодиоды будут на пинах 9 и 10:
#include <TimerOne.h> void setup() { Timer1.initialize(); // 1 секунда по умолчанию - то, что надо Timer1.pwm(9, 512); // полсекунды светимся (512 / 1024), на полсекунды гасим pinMode(10, OUTPUT); Timer1.attachInterrupt(Timer1_action); } void Timer1_action() { // LED секунду горит, секунду - нет digitalWrite(10, !digitalRead(10)); } void loop() { // пока мы тут что-нибудь делаем, светодиоды мигают сами по себе }
Ну, а что ещё можно сделать, используя TimerOne, зависит уже от вашей фантазии.
Ссылки
http://www.arduino.cc/playground/Code/Timer1
Timer One Library for Arduino
По теме
Программирование Arduino — аналоговый ввод/вывод
ШИМ — Широтно-Импульсная Модуляция
Программирование Arduino — прерывания
Программирование Arduino/CraftDuino — Blink без delay
6 комментариев на «“Библиотека TimerOne”»
Спасибо за статью. Библиотека пригодится.
еще одно спасибо
О! То, что нужно! Спасибо!
Вот этоттаймер на Arduino будет гораздо функциональнее. По крайней мере, мне понравился больше.
Это не таймер, а фигня. Эта библиотека __совсем__ не использует аппаратные таймеры.
У меня stop() работает странно… если в лупе просто поставить стоп, то светодиод всёравно
будет моргать, только время увеличивается в несколько раз +- (>10)
т.е. если период 0,1с, то со стоп получится 1,28с
если период 1с, то со стоп получится 14,2с
если период 5с, то со стоп получится 78,4c
если период 10с, то со стоп получится 132с
Примерно так… почему не знаю…