ОБНОВЛЕНИЯ
- v1.7 — поправлена документация
- v1.8 — исправлен баг с макс периодом
- v1.9 — исправлен баг с возвращаемым 2х периодом
ТЕОРИЯ
На сайте есть отдельный более подробный урок по прерываниям таймера.
Прерывание позволяет «отвлекаться» от выполнения программы, выполнить необходимый блок кода, а затем вернуться обратно в программу. Может быть использовано для опроса датчиков с высокой частотой, генерации сигналов, точного тайминга для переключений и проч. У микроконтроллера есть пара десятков прерываний по различным событиям, одно из них — прерывание по таймеру. Таймер является аппаратной единицей микроконтроллера, и его счёт идёт параллельно выполнению программы, т.е. не тратит процессорное время. Также таймер имеет аппаратные выходы, состоянием которых он может управлять самостоятельно без участия вычислительного ядра (именно так и генерируется ШИМ).
Таймер 0 используется для работы системных функций времени
delay()
, delayMicroseconds()
и millis()
и micros()
, поэтому перенастройка таймера 0 под свои нужды приведёт к некорректной работе функций времени, но при желании можно их скорректировать следующим образом:
#define delay(x * div) #define delayMicroseconds(x * div) #define millis() (millis() / div) #define micros() (micros() / div) // где div - делитель (или множитель), подбирать из степеней двойкиТаймер 1 используется в некоторых библиотеках, например Servo. При перенастройке таймера 1 под свои нужды Servo перестанет работать. Таймер 2 используется для генерации звука стандартной функцией
tone()
, причём перенастройка таймера не «сломает» tone — вызов функции перенастроит таймер под нужды функции.
Различные библиотеки могут использовать таймеры для своих целей, если вам нужны прерывания по таймерам совместно с работой других библиотек — смотрите, не используют ли они эти же таймеры!
Прерывание по таймеру настраивается при помощи обращения к регистрам таймера. Мы сделали удобную, очень лёгкую и быструю библиотеку, которая позволит контролировать прерывания по таймерам при помощи простых команд.
БИБЛИОТЕКА
Настройка и контроль прерываний по аппаратным таймерам AVR:
- Поддерживаются все три таймера на ATmega328 и шесть таймеров на ATmega2560;
- Настройка периода (мкс) и частоты (Гц) прерываний:
- 8 бит таймеры: 61 Гц – 1 МГц (16 384 мкс – 1 мкс);
- 16 бит таймеры: 0.24 Гц – 1 МГц (4 200 000 мкс – 1 мкс);
- Автоматическая корректировка настройки периода от частоты тактирования (F_CPU);
- Функция возвращает точный установившийся период/частоту для отладки (частота ограничена разрешением таймера);
- Поддержка многоканального режима работы: один таймер вызывает 2 (ATmega328) или 3 (ATmega2560, таймеры 1, 3, 4, 5) прерывания с настраиваемым сдвигом по фазе 0-360 градусов;
- Настраиваемое действие аппаратного вывода таймера по прерыванию: высокий сигнал, низкий сигнал, переключение. Позволяет генерировать меандр (одно- и двухтактный);
- Контроль работы таймера: старт/стоп/пауза/продолжить/инициализация;
- Разработано by Egor ‘Nich1con’ Zaharov специально для AlexGyver
Поддерживаемые платформы: платы на ATmega328p (Arduino Nano/UNO/Pro Mini), ATmega2560 (Arduino Mega).
УСТАНОВКА
- Библиотеку можно найти и установить через менеджер библиотек по названию GyverTimers в:
- Arduino IDE (Инструменты/Управлять библиотеками)
- Arduino IDE v2 (вкладка «Library Manager»)
- PlatformIO (PIO Home, вкладка «Libraries»)
- Про ручную установку читай здесь
ДОКУМЕНТАЦИЯ
Документация
Супер важный момент
При использовании
.enableISR()
обязательно нужно объявить обработчик прерывания ISR(TIMERn_A) {}
соответствующего таймера и канала, иначе другие прерывания не будут работать!
Ещё немного теории
Таймер-счётчик является аппаратной единицей МК и работает параллельно вычислительному ядру, т.е. никакие задержки и длинные вычисления не влияют на его работу. На ATmega328p (Arduino Nano/UNO/Pro Mini) у нас есть три таймера, а на ATmega2560 (Arduino Mega) - целых шесть. У таймера есть несколько каналов прерываний и аппаратные выводы, позволяющие генерировать ШИМ сигнал. Библиотека GyverTimers позволяет генерировать прерывания по таймеру с заданной частотой на выбранном канале таймера или на нескольких каналах сразу со сдвигом по фазе: прерывания будут происходить с одинаковой частотой, но "сдвинуты" друг относительно друга. Также можно задать действие для вывода таймера: включить/выключить/переключить: таймер будет управлять выбранным пином независимо от вычислительного ядра МК, таким образом можно генерировать меандр (квадратный сигнал), причём как однотактный, так и двух- и трёхтактный с настраиваемым сдвигом по фазе. Для Arduino Nano/UNO/Pro Mini доступно три таймера: Timer0, Timer1, Timer2. Для Arduino MEGA - шесть: Timer0, Timer1, Timer2, Timer3, Timer4, Timer5. В библиотеке таймеры описаны как объекты, обращение происходит, как обычно, через точку. Например
Timer1.stop();
Таблица таймеров ATmega328p
Таймер | Разрядность | Частоты | Периоды | Выходы | Пин Arduino | Пин МК |
Timer0 | 8 бит | 61 Гц.. 1 МГц | 16 384.. 1 мкс | CHANNEL_A | D6 | PD6 |
CHANNEL_B | D5 | PD5 | ||||
Timer1 | 16 бит | 0.24 Гц.. 1 МГц | 4 200 000.. 1 мкс | CHANNEL_A | D9 | PB1 |
CHANNEL_B | D10 | PB2 | ||||
Timer2 | 8 бит | 61 Гц.. 1 МГц | 16 384.. 1 мкс | CHANNEL_A | D11 | PB3 |
CHANNEL_B | D3 | PD3 |
Таблица таймеров ATmega2560
Таймер | Разрядность | Частоты | Периоды | Выходы | Пин Arduino | Пин МК |
Timer0 | 8 бит | 61 Гц.. 1 МГц | 16 384.. 1 мкс | CHANNEL_A | 13 | PB7 |
CHANNEL_B | 4 | PG5 | ||||
Timer1 | 16 бит | 0.24 Гц.. 1 МГц | 4 200 000.. 1 мкс | CHANNEL_A | 11 | PB5 |
CHANNEL_B | 12 | PB6 | ||||
CHANNEL_C | 13 | PB7 | ||||
Timer2 | 8 бит | 61 Гц.. 1 МГц | 16 384.. 1 мкс | CHANNEL_A | 10 | PB4 |
CHANNEL_B | 9 | PH6 | ||||
Timer3 | 16 бит | 0.24 Гц.. 1 МГц | 4 200 000.. 1 мкс | CHANNEL_A | 5 | PE3 |
CHANNEL_B | 2 | PE4 | ||||
CHANNEL_C | 3 | PE5 | ||||
Timer4 | 16 бит | 0.24 Гц.. 1 МГц | 4 200 000.. 1 мкс | CHANNEL_A | 6 | PH3 |
CHANNEL_B | 7 | PH4 | ||||
CHANNEL_C | 8 | PH5 | ||||
Timer5 | 16 бит | 0.24 Гц.. 1 МГц | 4 200 000.. 1 мкс | CHANNEL_A | 46 | PL3 |
CHANNEL_B | 45 | PL4 | ||||
CHANNEL_C | 44 | PL5 |
Максимальный период
В таблице выше приведены диапазоны для 16 МГц тактирования. Для другого системного клока максимальный период считается по формуле, где F_CPU - системная частота в Гц:
- 8 бит таймеры: (1000000UL / F_CPU) * (1024 * 256)
- 16 бит таймеры: (1000000UL / F_CPU) * (1024 * 65536)
Настройка частоты/периода
setPeriod(период);
- установка периода в микросекундах и запуск таймера. Возвращает реальный период в мкс (точность ограничена разрешением таймера).setFrequency(частота);
- установка частоты в Герцах и запуск таймера. Возвращает реальную частоту в Гц (точность ограничена разрешением таймера).setFrequencyFloat(частота float);
- установка частоты в Герцах и запуск таймера, разрешены десятичные дроби. Возвращает реальную частоту (точность ограничена разрешением таймера).
Контроль работы таймера
pause();
- приостановить счёт таймера, не сбрасывая счётчикresume();
- продолжить счёт после паузыstop();
- остановить счёт и сбросить счётчикrestart();
- перезапустить таймер (сбросить счётчик)
Прерывания
enableISR(канал);
- запустить прерывания на выбранном канале- Канал -
CHANNEL_A
,CHANNEL_B
илиCHANNEL_С
(см. таблицу выше!)
- Канал -
disableISR(канал);
- отключить прерывания на выбранном канале. Если ничего не указывать, будет выбран канал A
ISR(канал) {}
, пример:
ISR(TIMER1_A) { // ваш код } ISR(TIMER1_B) { // ваш код } ISR(TIMER2_B) { // ваш код } ISR(TIMER0_A) { // ваш код }Внимание! Если не объявить ISR(), все прерывания будут выключены и перестанут работать все завязанные на них функции (системный миллис, Сериал...).
Аппаратные выходы
outputEnable(канал, режим);
- включить управление аппаратным выходом таймера- Канал:
CHANNEL_A
илиCHANNEL_B
(+CHANNEL_C
у ATmega2560, см. таблицу таймеров). - Режим:
TOGGLE_PIN
,CLEAR_PIN
,SET_PIN
(переключить/выключить/включить пин по срабатыванию таймера)
- Канал:
outputDisable(канал);
- отключить выход таймера- Канал:
CHANNEL_A
илиCHANNEL_B
(+CHANNEL_C
у Mega2560, см. таблицу таймеров)
- Канал:
outputState(канал, состояние);
- вручную сменить состояние канала. Например для установки каналов в разное состояние для запуска генерации двухтактного меандра.- Канал:
CHANNEL_A
илиCHANNEL_B
(+CHANNEL_C
у ATmega2560, см. таблицу таймеров). - Состояние:
HIGH
илиLOW
- Канал:
Сдвиг фазы (с 1.6)
При помощи
phaseShift(source, angle)
можно сдвинуть прерывания или переключения пинов на выбранном канале source
по фазе angle
- угол сдвига в градусах от 0 до 360.
- У 8-битных таймеров можно задать сдвиг только у второго канала (
CHANNEL_B
) - У 16-битных можно двигать все три канала
Настройка по умолчанию
При помощи метода
setDefault()
можно сбросить настройки таймера на "ардуиновские" умолчания: частоту и режим работы.
Список функций
Таймер описан как объект: Timer0, Timer1... Timer5. Обращение через точку, например Timer1.stop() . Методы:
setPeriod(период) - установка периода в микросекундах и запуск таймера. Возвращает реальный период (точность ограничена разрешением таймера). setFrequency(частота) - установка частоты в Герцах и запуск таймера. Возвращает реальную частоту (точность ограничена разрешением таймера). setFrequencyFloat(частота float) - установка частоты в Герцах и запуск таймера, разрешены десятичные дроби. Возвращает реальную частоту (точность ограничена разрешением таймера). enableISR(источник) - включить прерывания, канал прерываний CHANNEL_A или CHANNEL_B (+ CHANNEL_C у Mega2560) disableISR(источник) - выключить прерывания, канал CHANNEL_A или CHANNEL_B. Счёт таймера не останавливается (без указания параметров будет выключен канал А). pause() - приостановить счёт таймера, не сбрасывая счётчик resume() - продолжить счёт после паузы stop() - остановить счёт и сбросить счётчик restart() - перезапустить таймер (сбросить счётчик) setDefault() - установить параметры таймера по умолчанию ("Ардуино-умолчания") outputEnable(канал, режим) - канал: включить выход таймера CHANNEL_A или CHANNEL_B (+ CHANNEL_C у Mega2560). Режим: TOGGLE_PIN, CLEAR_PIN, SET_PIN (переключить/выключить/включить пин по прерыванию) outputDisable(канал) - отключить выход таймера CHANNEL_A или CHANNEL_B (+ CHANNEL_C у Mega2560, см. такблицу таймеров) outputState(канал, состояние) - сменить состояние канала: HIGH / LOW phaseShift(источник, фаза) - сдвинуть фазу канала на 0-360 градусов (у 8 бит таймеров двигается только канал B)
ПРИМЕРЫ
Остальные примеры смотри в папке examples библиотеки, также примеры можно открыть из Arduino IDE/Файл/Примеры
Демо - все возможности библиотеки с комментариями
// Демонстрация всех функций библиотеки #include "GyverTimers.h" void setup() { // Перенастроить таймер и задать ему период или частоту // Все функции возвращают реальный период / частоту, которые могут отличаться от введенных Timer2.setPeriod(1000); // Задать конкретный период 1000 мкс (~ 1000 гц), вернет реальный период в мкс Timer0.setFrequency(250); // Задать частоту прерываний таймера в Гц, вернет реальную частоту в герцах Timer1.setFrequencyFloat(50.20); // Задать частоту более точно, в дробных числах, актуально для низких частот и таймера 1 // С этого момента таймер уже перенастроен и гоняет с выьранной частотой / периодом // Подключить прерывание таймера, с этого момента прерывания начнут вызываться Timer0.enableISR(); // Подключить стандартное прерывание, канал А, без сдига фаз Timer2.enableISR(CHANNEL_B, 180); // Подключить прерывание таймера 2, канал B, начальная фаза - 180 градусов Timer1.enableISR(CHANNEL_A, 60); // Подключить прерывание канала А, задать фазу для канала А доступно только для таймера 1! Timer1.enableISR(CHANNEL_B, 120); // Подключить второе прерывание таймера 1, и задать фазовый сдвиг для этого потока // Прерывание уже начнет вызываться // Если вдруг прерывание нужно отключить, не останавливая таймер Timer1.disableISR(CHANNEL_B); // С этого момента прерывание B больше не будет вызываться // Если нужно приостановить таймер ПОЛНОСТЬЮ, аппаратно Timer2.pause(); // С этого момента таймер стоит на месте, содержимое счетчика остается нетронутым // Теперь таймер можно вернуть в строй Timer2.resume(); // Таймер продолжил считать с того же места // Если нужно полностью остановить таймер и сбросить содержимое счетчика Timer1.stop(); // Таймер стоит, счетчик сброшен // Возвращаем таймер в строй Timer1.restart(); // Таймер перезапущен, начал считать с начала // Если нужно вернуть стандартные Arduino - настройки таймера Timer0.setDefault(); // Теперь таймер работает в станлартном режиме } // векторы прерываний ISR(TIMER1_A) { } ISR(TIMER1_B) { } ISR(TIMER2_B) { } ISR(TIMER0_A) { } void loop() { }
Простой пример с прерываниями
// Пример простой генерации прерываний аппаратным таймером #include "GyverTimers.h" void setup() { Serial.begin(9600); Timer1.setFrequency(3); // Высокоточный таймер 1 для первого прерывания, частота - 3 Герца //Timer1.setPeriod(333333); // то же самое! Частота 3 Гц это период 333 333 микросекунд //Timer1.setFrequencyFloat(4.22); // Если нужна дробная частота в Гц Timer1.enableISR(); // Запускаем прерывание (по умолч. канал А) // запустим второй таймер Timer2.setPeriod(20000); // Устанавливаем период таймера 20000 мкс -> 50 гц Timer2.enableISR(CHANNEL_A); // Или просто .enableISR(), запускаем прерывание на канале А таймера 2 pinMode(13, OUTPUT); // будем мигать } void loop() {} // Прерывание А таймера 1 ISR(TIMER1_A) { // пишем в сериал Serial.println("timer1"); } // Прерывание А таймера 2 ISR(TIMER2_A) { // генерируем меандр 25 гц, мигаем digitalWrite(13, !digitalRead(13)); //Serial.println("timer2"); }
Прерывания на двух каналах
// Пример генерации двухканальных прерываний на таймере с РАВНЫМ периодом, но сдвинутых по фазе // два потока прерываний с сдвигом 180 градусов (полная инверсия) #include "GyverTimers.h" void setup() { Serial.begin(9600); Serial.print("Real timer frequency is : "); // Выведем реальную частоту, реальная может отличаться от заданой (ограничено разрешением таймера) Serial.println(Timer1.setFrequencyFloat(2.50)); // Частота прерываний - 2.5 гц , используй .setFrequency(...) для целых чисел delay(1000); Timer1.enableISR(CHANNEL_A, 0); // Первый канал - А, начальная фаза - 0 градусов Timer1.enableISR(CHANNEL_B, 180); // Второй канал - B, начальная фаза - 180 градусов } void loop() {} // два прерывания на одном таймере ISR(TIMER1_A) { Serial.println(" Channel A interrupt !"); // Прерывание А } ISR(TIMER1_B) { Serial.println(" Channel B interrupt !"); // Прерывание B }
Генерация меандра
//Пример генерации меандра на таймере 2 , канале B (D3 на Arduino UNO) #include "GyverTimers.h" void setup() { pinMode(3, OUTPUT); // настроить пин как выход // из-за особенности генерации меандра таймером // частоту нужно указывать в два раза больше нужной! Timer2.setFrequency(500 * 2); // настроить частоту таймера в Гц Timer2.outputEnable(CHANNEL_B, TOGGLE_PIN); // в момент срабатывания таймера пин будет переключаться } void loop() { }
Генерация 2х тактного меандра
// Пример генерации двухтактного меандра на таймере 2 (пины D3 и D11) #include "GyverTimers.h" void setup() { pinMode(3, OUTPUT); // настроить пин как выход pinMode(11, OUTPUT); // настроить пин как выход // из-за особенности генерации меандра таймером // частоту нужно указывать в два раза больше нужной! Timer2.setFrequency(15000 * 2); // настроить частоту в Гц и запустить таймер. Меандр на 15 кГц Timer2.outputEnable(CHANNEL_A, TOGGLE_PIN); // в момент срабатывания таймера пин будет переключаться Timer2.outputEnable(CHANNEL_B, TOGGLE_PIN); // в момент срабатывания таймера пин будет переключаться Timer2.outputState(CHANNEL_A, HIGH); // задаём начальное состояние пина 11 Timer2.outputState(CHANNEL_B, LOW); // задаём начальное состояние пина 3 } void loop() { }
Генерируем сигнал энкодера
// Пример генерации энкодерного сигнала на таймере 2 (пины D3 и D11) #include "GyverTimers.h" void setup() { pinMode(3, OUTPUT); // настроить пин как выход pinMode(11, OUTPUT); // настроить пин как выход // из-за особенности генерации меандра таймером // частоту нужно указывать в два раза больше нужной! Timer2.setFrequency(15000 * 2); // настроить частоту в Гц и запустить таймер. Меандр на 15 кГц Timer2.outputEnable(CHANNEL_A, TOGGLE_PIN); // в момент срабатывания таймера пин будет переключаться Timer2.outputEnable(CHANNEL_B, TOGGLE_PIN); // в момент срабатывания таймера пин будет переключаться // задаём фазовый сдвиг 0-360 (для всех каналов кроме A на 8 бит таймерах) Timer2.phaseShift(CHANNEL_B, 180); } void loop() { }
ВИДЕО
ПОДДЕРЖАТЬ
Вы можете поддержать меня за создание доступных проектов с открытым исходным кодом, полный список реквизитов есть вот здесь.
5
1
голос
Рейтинг статьи