Библиотеки TimerOne и TimerThree для Arduino
Timer1
Данная библиотека представляет собой набор функций для настройки аппаратного 16-битного таймера Timer1 в ATMega168/328. В микроконтроллере доступно 3 аппаратных таймера, которые могут быть настроены различными способами для получения различных функциональных возможностей. Начало разработки данной библиотеки было вызвано необходимостью быстро и легко установить период или частоту ШИМ сигнала, но позже она разраслась, включив в себя обработку прерываний по переполнению таймера и другие функции. Она может быть легко расширена или портирована для работы с другими таймерами.
Точность таймера зависит от тактовой частоты процессора. Тактовая частота таймера Timer1 определяется установкой предварительного делителя частоты. Этот делитель может быть установлен в значения 1, 8, 64, 256 или 1024.
Делитель | Длительность одного отсчета, мкс | Максимальный период, мс |
---|---|---|
1 | 0,0625 | 8,192 |
8 | 0,5 | 65,536 |
64 | 4 | 524,288 |
256 | 16 | 2097,152 |
1024 | 64 | 8388,608 |
В общем:
- Максимальный период = (Делитель / Частота) × 217
- Длительность одного отсчета = (Делитель / Частота)
Скачать можно здесь (TimerOne-r11.zip) или с Google Code.
Для установки просто распакуйте и поместите файлы в каталог Arduino/hardware/libraries/Timer1/.
Timer3
Обратите внимание, что библиотека Timer1 может использоваться на Arduino Mega, но она не поддерживает все три выходных вывода OCR1A
, OCR1B
и OCR1C
. Поддерживаются только A
и B
. OCR1A
подключен к выводу 11 на Mega, а OCR1B
– к выводу 12. С помощью одного из трех вызовов, которые задают вывод, значение 1 задаст вывод 11 на Mega, а 2 – задаст вывод 12. Библиотека Timer3 была протестирована только на Mega.
Библиотеку для таймера Timer3 можно здесь (TimerThree.zip)
Для установки просто распакуйте и поместите файлы в каталог Arduino/hardware/libraries/Timer3/.
Методы библиотек TimerOne и TimerThree
Настройка
void initialize(long microseconds=1000000);
- Вы должны вызвать этот метод первым, перед использованием любых других методов библиотеки. При желании можно задать период таймера (в микросекундах), по умолчанию период устанавливается равным 1 секунде. Обратите внимание, что это нарушает работу
analogWrite()
на цифровых выводах 9 и 10 на Arduino. void setPeriod(long microseconds);
- Устанавливает период в микросекундах. Минимальный период и максимальная частота, поддерживаемые данной библиотекой, равны 1 микросекунде и 1 МГц, соответственно. Максимальный период равен 8388480 микросекунд, или примерно 8,3 секунды. Обратите внимание, что установка периода изменит частоту срабатывания прикрепленного прерывания и частоту, и коэффициент заполнения на обоих ШИМ выходах.
Управление запуском
void start();
- Запускает таймер, начиная новый период.
void stop();
- Останавливает таймер.
void restart();
- Перезапускает таймер, обнуляя счетчик и начиная новый период.
Управление выходным ШИМ сигналом
void pwm(char pin, int duty, long microseconds=-1);
- Генерирует ШИМ сигнал на заданном выводе
pin
. Выходными выводами таймера Timer1 являются выводыPORTB
1 и 2, поэтому вы должны выбрать один из них, всё остальное игнорируется. На Arduino это цифровые выводы 9 и 10, эти псевдонимы также работают. Выходными выводами таймера Timer3 являются выводыPORTE
, соответствующие выводам 2, 3 и 5 на Arduino Mega. Коэффициент заполненияduty
задается, как 10-битное значение в диапазоне от 0 до 1023 (0 соответствует постоянному логическому нулю на выходе, а 1023 – постоянной логической единице). Обратите внимание, что при необходимости в этой функции можно установить и период, добавив значение в микросекундах в качестве последнего аргумента. void setPwmDuty(char pin, int duty);
- Быстрый способ для настройки коэффициента заполнения ШИМ сигнала, если вы уже настроили его, вызвав ранее метод
pwm()
. Этот метод позволяет избежать лишних действий по включению режима ШИМ для вывода, изменению состояния регистра, управляющего направлением движения данных, проверки необязательного значения периода и прочих действий, которые являются обязательными при вызовеpwm()
. void disablePwm(char pin);
- Выключает ШИМ на заданном выводе, после чего вы можете использовать этот вывод для чего-либо другого.
Прерывания
void attachInterrupt(void (*isr)(), long microseconds=-1);
- Вызывает функцию через заданный в микросекундах интервал. Будьте осторожны при попытке выполнить слишком сложный обработчик прерывания при слишком большой тактовой частоте, так как CPU может никогда не вернуться в основной цикл программы, и ваша программа будет «заперта». Обратите внимание, что при необходимости в этой функции можно установить и период, добавив значение в микросекундах в качестве последнего аргумента.
void detachInterrupt();
- Отключает прикрепленное прерывание.
Остальные
unsigned long read();
- Считывает время с момента последнего переполнения в микросекундах.
Пример 1
В примере ШИМ сигнал с коэффициентом заполнения 50% подается на вывод 9, а прикрепленный обработчик прерывания переключает состояние цифрового вывода 10 каждые полсекунды.
#include "TimerOne.h"
void setup()
{
pinMode(10, OUTPUT);
Timer1.initialize(500000); // инициализировать timer1, и установить период 1/2 сек.
Timer1.pwm(9, 512); // задать шим сигнал на выводе 9, коэффициент заполнения 50%
Timer1.attachInterrupt(callback); // прикрепить callback(), как обработчик прерывания по переполнению таймера
}
void callback()
{
digitalWrite(10, digitalRead(10) ^ 1);
}
void loop()
{
// ваша программа...
}
Модифицированные библиотеки от Paul Stoffregen
Также доступны отдельно поддерживаемые и обновляемые копии TimerOne и TimerThree, которые отличается поддержкой большего количества оборудования и оптимизацией для получения более эффективного кода.
Плата | ШИМ выводы TimerOne | ШИМ выводы TimerThree |
---|---|---|
Teensy 3.1 | 3, 4 | 25, 32 |
Teensy 3.0 | 3, 4 | |
Teensy 2.0 | 4, 14, 15 | 9 |
Teensy++ 2.0 | 25, 26, 27 | 14, 15, 16 |
Arduino Uno | 9, 10 | |
Arduino Leonardo | 9, 10, 11 | 5 |
Arduino Mega | 11, 12, 13 | 2, 3, 5 |
Wiring-S | 4, 5 | |
Sanguino | 12, 13 |
Методы модифицированных библиотек аналогичны описанным выше, но добавлен еще один метод управления запуском таймера:
void resume();
- Возобновляет работу остановленного таймера. Новый период не начинается.
Пример 2
#include <TimerOne.h>
// Данный пример использует прерывание таймера, чтобы
// помигать светодиодом, а также продемонстрировать, как
// делить переменную между обработчиком прерывания и
// основной программой.
const int led = LED_BUILTIN; // вывод со светодиодом
void setup(void)
{
pinMode(led, OUTPUT);
Timer1.initialize(150000);
Timer1.attachInterrupt(blinkLED); // вызывать blinkLED каждые 0.15 сек.
Serial.begin(9600);
}
// Обработчик прерывания будет мигать светодиодом и
// сохранять данные о том, сколько раз мигнул.
int ledState = LOW;
volatile unsigned long blinkCount = 0; // используйте volatile для общих переменных
void blinkLED(void)
{
if (ledState == LOW) {
ledState = HIGH;
blinkCount = blinkCount + 1; // увеличить значение при включении светодиода
} else {
ledState = LOW;
}
digitalWrite(led, ledState);
}
// Основная программа будет печатать счетчик миганий
// в Arduino Serial Monitor
void loop(void)
{
unsigned long blinkCopy; // хранит копию blinkCount
// чтобы прочитать переменную, которая записана в обработчике
// прерывания, мы должны временно отключить прерывания, чтобы
// быть уверенными, что она не изменится, пока мы считываем ее.
// Чтобы минимизировать время, когда прерывания отключены,
// просто быстро копируем, а затем используем копию, позволяя
// прерываниям продолжать работу.
noInterrupts();
blinkCopy = blinkCount;
interrupts();
Serial.print("blinkCount = ");
Serial.println(blinkCopy);
delay(100);
}
Проблемы с контекстом прерываний
Для обмена данными между кодом обработчика прерывания и остальной частью вашей программы необходимо принять дополнительные меры.
Переменные обычно должны быть типа volatile
. Volatile
говорит компилятору о необходимости избегать оптимизаций, которые предполагают, что переменная не может спонтанно измениться. Поскольку ваша функция может изменять переменные, пока ваша программа использует их, компилятор нуждается в этой подсказке. Но одного volatile
часто не хватает.
При доступе к общим переменным прерывания, как правило, должны быть отключены. Даже с volatile
, если обработчик прерывания изменит многобайтную переменную между последовательностью команд, эта переменная может быть прочитана неправильно. Если данные состоят из нескольких переменных, например, массив и счетчик, прерывания, как правило, должны быть отключены на протяжении всего кода, который получает доступ к данным.