Библиотеки TimerOne и TimerThree для Arduino

Добавлено 2 апреля 2016 в 18:00

Timer1

Данная библиотека представляет собой набор функций для настройки аппаратного 16-битного таймера Timer1 в ATMega168/328. В микроконтроллере доступно 3 аппаратных таймера, которые могут быть настроены различными способами для получения различных функциональных возможностей. Начало разработки данной библиотеки было вызвано необходимостью быстро и легко установить период или частоту ШИМ сигнала, но позже она разраслась, включив в себя обработку прерываний по переполнению таймера и другие функции. Она может быть легко расширена или портирована для работы с другими таймерами.

Точность таймера зависит от тактовой частоты процессора. Тактовая частота таймера Timer1 определяется установкой предварительного делителя частоты. Этот делитель может быть установлен в значения 1, 8, 64, 256 или 1024.

для тактовой частоты 16 МГц
ДелительДлительность одного отсчета, мксМаксимальный период, мс
10,06258,192
80,565,536
644524,288
256162097,152
1024648388,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.13, 425, 32
Teensy 3.03, 4 
Teensy 2.04, 14, 159
Teensy++ 2.025, 26, 2714, 15, 16
Arduino Uno9, 10 
Arduino Leonardo9, 10, 115
Arduino Mega11, 12, 132, 3, 5
Wiring-S4, 5 
Sanguino12, 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, если обработчик прерывания изменит многобайтную переменную между последовательностью команд, эта переменная может быть прочитана неправильно. Если данные состоят из нескольких переменных, например, массив и счетчик, прерывания, как правило, должны быть отключены на протяжении всего кода, который получает доступ к данным.

Теги

ArduinoПрерываниеТаймер

На сайте работает сервис комментирования DISQUS, который позволяет вам оставлять комментарии на множестве сайтов, имея лишь один аккаунт на Disqus.com.

В случае комментирования в качестве гостя (без регистрации на disqus.com) для публикации комментария требуется время на премодерацию.


  • 2017-12-16Алексей Иванов

    "Точность таймера зависит от тактовой частоты процессора. Тактовая частота таймера Timer1 определяется установкой предварительного делителя частоты. Этот делитель может быть установлен в значения 1, 8, 64, 256 или 1024."
    А как этот делитель установить? Или это автоматически происходит? Я вывожу частоту от 50 до 1000 гц для управления step/dir; начиная с 200 гц погрешность частоты увеличивается с 1-2 гц до 4-5 гц при 1000 гц, что не очень то... я от прерываний ждал большего! Но, скорее, это я просто чего то недопонял. Пробовал и стандартным Timer1.pwm(9, 512) частоту выводить и обработчиком прерываний, в обработчике применял запись через порты напрямую -- все равно погрешность не улучшилась.