РАБОТАЕМ ПО ТАЙМЕРУ В ARDUINO
Я думаю все знают классический алгоритм создания таймера на millis() – счётчике аптайма:
Классический таймер на millis()
Несколько таймеров
Данный алгоритм позволяет спокойно переходить через переполнение millis() без потери периода, но имеет один большой минус – время считается с момента последнего вызова таймера, и при наличии задержек в коде таймер будет накапливать погрешность, отклоняясь от “ритма”. Недавно я придумал более точный алгоритм таймера на миллис, который соблюдает свой период даже после пропуска хода!
Улучшенный таймер
Данный таймер имеет механику классического таймера с хранением переменной таймера, а его период всегда кратен PERIOD и не сбивается. Эту конструкцию можно упростить до
В этом случае алгоритм получается короче, кратность периодов сохраняется, но теряется защита от пропуска вызова и переполнения millis().
В этой библиотеке реализован полноценный таймер на счётчике аптайма, позволяющий даже “приостановить” счёт (не приостанавливая сам аптайм).
Примечание: таких таймеров можно создать сколько душе угодно (пока хватит памяти), что позволяет создавать сложные программы с кучей подзадач, но для функционирования данного таймера нужен “чистый” loop с минимальным количеством задержек, или вообще без них. Всё таки таймер “опрашивается” в ручном режиме. Таймер, который не боится задержек, делается на прерывании таймера, смотрите вот эту библиотеку.
БИБЛИОТЕКА GYVERTIMER
GyverTimer v3.2
GTimer – полноценный таймер на базе системных millis() / micros(), обеспечивающий удобную мультизадачность и работу с временем, используя всего одно родное прерывание таймера (Timer 0)
- Миллисекундный и микросекундный таймер
- Два режима работы:
- Режим интервала: таймер “срабатывает” каждый заданный интервал времени
- Режим таймаута: таймер “срабатывает” один раз по истечении времени (до следующего перезапуска)
- Служебные функции:
- Старт
- Стоп
- Сброс
- Продолжить
Поддерживаемые платформы: все Arduino (используются стандартные Wiring-функции)
ДОКУМЕНТАЦИЯ
Документация
Конструктор
Класс GTimer позволяет работать как с миллисекундным, так и с микросекундным таймером. В общем виде пример выглядит так:
Где type это MS (мс, миллисекундный таймер) или US (мкс, микросекундный), period – период в мс или мкс соответственно.
Настройки по умолчанию
- При создании таймера можно ничего не указывать : GTimer myTimer; , тогда таймер будет сконфигурирован как миллисекундный и не запустится
- Если указать только тип таймера (MS/US) GTimer myTimer(MS); , таймер настроится на выбранный режим (мс/мкс) и не запустится
- Если указать тип таймера и интервал GTimer myTimer(US, 5000); , таймер настроится на выбранный режим (мс/мкс) и запустится в режиме интервала
Режимы работы
Таймер может работать в режиме интервалов и в режиме таймаута:
- Интервалы. Запуск – метод setInterval(время) с указанием времени. В режиме интервалов таймер срабатывает (метод isReady() возвращает true ) каждый раз при достижении указанного периода и автоматически перезапускается. Удобно для периодических действий
- Таймаут. Запуск – метод setTimeout(время) с указанием времени. В режиме таймаута таймер срабатывает (метод isReady() возвращает true ) только один раз при достижении указанного периода и автоматически отключается. Для повторного запуска нужно вызвать .setTimeout() с указанием периода, или просто .start() – запустит таймер на новый круг с прежним периодом
Важный момент: сравнение времени происходит в методе isReady() , без его вызова таймер ничего не считает! Нужно вызывать isReady() всегда, когда требуется счёт времени.
Управление таймером
Для управления состоянием таймера есть следующие методы:
- start() – запускает (перезапускает) таймер с последним установленным временем
- stop() – останавливает таймер
- resume() – продолжает отсчёт таймера с момента остановки
- reset() – сбрасывает таймер (отсчёт периода/таймаута начинается заново)
- isEnabled() – возвращает true , если таймер работает (если он не stop() или не вышел таймаут)
Источник
Руководство по таймерам Arduino для начинающих
Платформа Arduino была первоначально спроектирована в 2005 году и первоначально предназначалась для того, чтобы люди, мало знакомые с электроникой и программированием, могли конструировать разнообразные электронные устройства. Но со временем она получила широкое распространение не только в кругах начинающих знакомиться с электроникой, но и среди профессионалов в сфере электроники.
В отличие от языков программирования для микроконтроллеров AVR, ARM, PIC, STM, в которых нужно хорошо представлять структуру этих микроконтроллеров, язык программирования для платформы Arduino исключительно простой и понятный. Достаточно легко понять, к примеру, как работают функции digitalWrite(), AnalogWrite(), Delay() и др. не вникая в суть машинного языка, который спрятан внутри них. Также не нужно вникать в суть различных регистров микроконтроллера, которые используются для управления этими процессами.
Но тем не менее, для лучшего понимания всех этих процессов, желательно все таки немного погрузиться внутрь этих процессов. К примеру, функция delay() используется для установки таймеров и битов регистров счета микроконтроллера AVR ATmega, являющегося основой платы Arduino.
В этой статье мы рассмотрим как без использования функции delay() управлять задержками в программе, непосредственно имея дело с регистрами микроконтроллера. Для этого мы будем использовать программную среду Arduino IDE. Мы будем устанавливать соответствующие биты регистра таймера и использовать прерывание переполнения таймера (Timer Overflow Interrupt) чтобы переключать (включать/выключать) состояние светодиода каждый раз когда происходит прерывание. Для контроля длительности задержки в схеме будут использоваться кнопки, с помощью которых можно будет изменять заранее загружаемое значение в биты таймера.
Что такое таймеры
Что же представляют собой таймеры в современной электронике? Фактически это определенный вид прерываний. Это простые часы, которые могут измерять длительность какого-нибудь события. Каждый микроконтроллер имеет встроенные часы (осциллятор), в плате Arduino Uno его роль выполняет кварцевый генератор, расположенный на плате, который работает на частоте 16 МГц. Частота влияет на скорость работы микроконтроллера. Чем выше частота, тем выше скорость работы. Таймер использует счетчик, который считает с определенной скоростью, зависящей от частоты осциллятора (кварцевого генератора). В плате Arduino Uno состояние счетчика увеличивается на 1 каждые 62 нано секунды (1/16000000 секунды). Фактически, это время за которое плата Arduino Uno переходит от одной инструкции к другой (то есть выполняет одну инструкцию).
Таймеры в Arduino Uno
В плате Arduino Uno используется три таймера:
Timer0: 8-битный таймер, используемый в таких функциях как delay(), millis().
Timer1: 16-битный таймер, используемый в библиотеке для управления серводвигателями.
Timer2: 8-битный таймер, используемый в функции tone().
Регистры таймеров в Arduino Uno
Для изменения конфигурации таймеров в плате Arduino Uno используются следующие регистры:
1. Timer/Counter Control Registers (TCCRnA/B) – управляющие регистры таймера/счетчика
Эти регистры содержат основные управляющие биты таймера и используются для управления предварительными делителями частоты (предделителями) таймера. Они также позволяют управлять режимом работы таймера с помощью битов WGM.
Формат этих регистров:
Предделитель (Prescaler)
Биты CS12, CS11, CS10 в регистре TCCR1B устанавливают коэффициент деления предделителя, то есть скорость часов таймера. В плате Arduino Uno можно установить коэффициент деления предделителя равный 1, 8, 64, 256, 1024.
2. Timer/Counter Register (TCNTn) – регистры таймера/счетчика
Эти регистры используются для управления счетчиками и для установки заранее загружаемого значения.
Формула для расчета заранее загружаемого значения (preloader value) для необходимого интервала времени (Time) в секундах выглядит следующим образом:
TCNTn = 65535 – (16×10 6 xTime in sec / Prescaler Value)
Откуда берется величина 16х10 6 ? Здесь все просто — это переведенная в Герцы частота кварцевого генератора 16 МГц.
Чтобы для таймера 1 (timer1) задать время равное 2 секундам, при коэффициент деления предделителя (Prescaler Value) равном 1024, получим:
TCNT1 = 65535 – (16×10 6 x2 / 1024) = 34285
Прерывания таймеров в Arduino
Прерывания таймеров являются видом программных прерываний. В Arduino присутствуют следующие виды прерываний таймеров.
Прерывания переполнения таймера (Timer Overflow Interrupt)
Это прерывание происходит всегда, когда значение счетчика достигает его максимального значения, например, для 16-битного счетчика это 65535. Соответственно, процедура обработки (обслуживания) прерывания (ISR) вызывается когда бит прерывания переполнения таймера установлен (enabled) в TOIEx присутствующем в регистре масок прерываний TIMSKx.
ISR Format:
Output Compare Register (OCRnA/B) – регистр сравнения выхода
Процедура обработки прерывания сравнения выхода (Output Compare Match Interrupt) вызывается при вызове функции TIMERx_COMPy_vect если установлен бит/флаг OCFxy в регистре TIFRx. Эта процедура обработки прерывания (ISR) становится доступной при помощи установки бита OCIExy, присутствующем в регистре маски прерываний TIMSKx.
Захват входа таймера (Timer Input Capture)
Процедура обработки этого прерывания вызывается если установлен бит/флаг ICFx в регистре флагов прерываний таймера (TIFRx — Timer Interrupt Flag Register). Эта процедура обработки прерываний становится доступной при установке бита ICIEx в регистре маски прерываний TIMSKx.
Необходимые компоненты
- Плата Arduino Uno (купить на AliExpress).
- ЖК дисплей 16х2 (купить на AliExpress).
- Резисторы 10 кОм (2 шт.) и 2,2 кОм (купить на AliExpress).
- Светодиод (любого цвета) (купить на AliExpress).
- Кнопка (2 шт.).
- Источник питания с напряжением 5 В.
Работа схемы
Схема устройства представлена на следующем рисунке.
Необходимые соединения между платой Arduino Uno и ЖК дисплеем 16х2 представлены в следующей таблице:
ЖК дисплей 16х2 | Arduino UNO |
VSS | GND |
VDD | +5V |
V0 | к среднему контакту потенциометра для контроля контрастности ЖК дисплея |
RS | 8 |
RW | GND |
E | 9 |
D4 | 10 |
D5 | 11 |
D6 | 12 |
D7 | 13 |
A | +5V |
K | GND |
Две кнопки через подтягивающие резисторы 10 кОм подключены к контактам 2 и 4 платы Arduino Uno, а светодиод подключен к контакту 7 Arduino через резистор 2,2 кОм.
Собранная схема устройства будет выглядеть примерно следующим образом:
Программирование таймеров в плате Arduino UNO
В этом проекте мы будем использовать прерывание переполнения таймера (Timer Overflow Interrupt) и использовать его для включения и выключения светодиода на определенные интервалы времени при помощи установки заранее определяемого значения (preloader value) регистра TCNT1 с помощью кнопок. Полный код программы будет приведен в конце статьи, здесь же рассмотрим его основные части.
Для отображения заранее определяемого значения используется ЖК дисплей, поэтому необходимо подключить библиотеку для работы с ним.
Анод светодиода подключен к контакту 7 платы Arduino, поэтому определим (инициализируем) его как ledPin.
#define ledPin 7
Затем сообщим плате Arduino к каким ее контактам подключен ЖК дисплей.
Установим заранее определенное значение (preloader value) равное 3035 – это будет соответствовать интервалу времени в 4 секунды. Формула для расчета этого значения приведена выше в статье.
float value = 3035;
Затем в функции void setup() установим режим работы ЖК дисплея 16х2 и высветим приветственное сообщение на нем на несколько секунд.
lcd.begin(16,2);
lcd.setCursor(0,0);
lcd.print(«ARDUINO TIMERS»);
delay(2000);
lcd.clear();
Затем контакт, к которому подключен светодиод, установим в режим вывода данных, а контакты, к которым подключены кнопки – в режим ввода данных.
pinMode(ledPin, OUTPUT);
pinMode(2,INPUT);
pinMode(4,INPUT);
После этого отключим все прерывания.
Далее инициализируем Timer1.
TCCR1A = 0;
TCCR1B = 0;
Загрузим заранее определенное значение (3035) в TCNT1.
Затем установим коэффициент деления предделителя равный 1024 при помощи конфигурирования битов CS в регистре TCCR1B.
Разрешим вызов процедуры обработки прерывания переполнения счетчика с помощью установки соответствующего бита в регистре маски прерываний.
Теперь разрешим все прерывания.
Теперь процедура обработки прерывания переполнения счетчика будет отвечать за включение и выключение светодиода с помощью функции digitalWrite . Состояние светодиода будет меняться каждый раз когда будет происходить прерывание переполнения счетчика.
ISR(TIMER1_OVF_vect)
<
TCNT1 = value;
digitalWrite(ledPin, digitalRead(ledPin) ^ 1);
>
В функции void loop() предварительно загружаемое значение увеличивается и уменьшается на 10 (инкрементируется и декрементируется) при помощи кнопок в схеме. Также это значение отображается на экране ЖК дисплея 16х2.
if(digitalRead(2) == HIGH)
<
value = value+10; //увеличиваем preload value
>
if(digitalRead(4)== HIGH)
<
value = value-10; //уменьшаем preload value
>
lcd.setCursor(0,0);
lcd.print(value);
>
Исходный код программы
Далее приведен полный текст программы. Работа нашего проекта продемонстрирована на видео, приведенном в конце статьи.
Источник