Как написать таймер на ардуино

Содержание
  1. РАБОТАЕМ ПО ТАЙМЕРУ В ARDUINO
  2. Классический таймер на millis()
  3. Несколько таймеров
  4. Улучшенный таймер
  5. БИБЛИОТЕКА GYVERTIMER
  6. ДОКУМЕНТАЦИЯ
  7. Документация
  8. Конструктор
  9. Режимы работы
  10. Управление таймером
  11. Таймер на Ардуино с обратным отсчётом
  12. Простой таймер Ардуино millis()
  13. Пояснения к коду:
  14. Таймер на Arduino Nano с LCD 1602
  15. Схема сборки таймера на Ардуино с кнопками
  16. Скетч для таймера на Ардуино Нано с кнопками
  17. Пояснения к коду:
  18. Кухонный таймер Ардуино с энкодером
  19. Схема таймера на Ардуино Уно / Нано
  20. Скетч таймера обратного отсчета времени
  21. Пояснения к коду:
  22. Функции времени в Ардуино
  23. Откуда берётся время?
  24. Задержки
  25. Функции счёта времени
  26. Таймер на millis()
  27. Проверка переполнения
  28. Ещё варианты
  29. Таймер на остатке от деления
  30. Макрос таймера
  31. Руководство по таймерам Arduino для начинающих
  32. Что такое таймеры
  33. Таймеры в Arduino Uno
  34. Регистры таймеров в Arduino Uno
  35. 1. Timer/Counter Control Registers (TCCRnA/B) – управляющие регистры таймера/счетчика
  36. 2. Timer/Counter Register (TCNTn) – регистры таймера/счетчика
  37. Прерывания таймеров в Arduino
  38. Прерывания переполнения таймера (Timer Overflow Interrupt)
  39. Output Compare Register (OCRnA/B) – регистр сравнения выхода
  40. Захват входа таймера (Timer Input Capture)
  41. Необходимые компоненты
  42. Работа схемы
  43. Программирование таймеров в плате Arduino UNO
  44. Исходный код программы
  45. Arduino и прерывания таймера
  46. Предисловие
  47. Что такое таймер?
  48. Как работает таймер?
  49. Типы таймеров
  50. Конфигурация регистров
  51. Как часто будет мигать светодиод?
  52. Делитель таймера и режим CTC
  53. Послесловие переводчика

РАБОТАЕМ ПО ТАЙМЕРУ В ARDUINO

Я думаю все знают классический алгоритм создания таймера на millis() – счётчике аптайма:

Классический таймер на millis()

Несколько таймеров

Данный алгоритм позволяет спокойно переходить через переполнение millis() без потери периода, но имеет один большой минус – время считается с момента последнего вызова таймера, и при наличии задержек в коде таймер будет накапливать погрешность, отклоняясь от “ритма”. Недавно я придумал более точный алгоритм таймера на миллис, который соблюдает свой период даже после пропуска хода!

Улучшенный таймер

Данный таймер имеет механику классического таймера с хранением переменной таймера, а его период всегда кратен PERIOD и не сбивается. Эту конструкцию можно упростить до

В этом случае алгоритм получается короче, кратность периодов сохраняется, но теряется защита от пропуска вызова и переполнения millis().

В этой библиотеке реализован полноценный таймер на счётчике аптайма, позволяющий даже “приостановить” счёт (не приостанавливая сам аптайм).

Примечание: таких таймеров можно создать сколько душе угодно (пока хватит памяти), что позволяет создавать сложные программы с кучей подзадач, но для функционирования данного таймера нужен “чистый” loop с минимальным количеством задержек, или вообще без них. Всё таки таймер “опрашивается” в ручном режиме. Таймер, который не боится задержек, делается на прерывании таймера, смотрите вот эту библиотеку.

БИБЛИОТЕКА GYVERTIMER

GyverTimer v3.2

GTimer – полноценный таймер на базе системных millis() / micros(), обеспечивающий удобную мультизадачность и работу с временем, используя всего одно родное прерывание таймера (Timer 0)

Поддерживаемые платформы: все Arduino (используются стандартные Wiring-функции)

ДОКУМЕНТАЦИЯ

Документация

Конструктор

Класс GTimer позволяет работать как с миллисекундным, так и с микросекундным таймером. В общем виде пример выглядит так:

Где type это MS (мс, миллисекундный таймер) или US (мкс, микросекундный), period – период в мс или мкс соответственно.

Настройки по умолчанию

Режимы работы

Таймер может работать в режиме интервалов и в режиме таймаута:

Управление таймером

Для управления состоянием таймера есть следующие методы:

Источник

Таймер на Ардуино с обратным отсчётом

Таймер на Ардуино с обратным отсчетом с кнопками или энкодером и дисплеем LCD 1602 I2C — интересный и полезный проект. Рассмотрим несколько вариантов таймера с отсчетом времени на Arduino Nano или Uno, которые можно использовать на кухне или в аквариуме для включения световой и звуковой индикации или реле от Ардуино. А вы можете выбрать для себя наиболее подходящий вариант данного проекта.

Простой таймер Ардуино millis()

Чтобы понять принцип работы функции millis() Arduino продемонстрируем пример программы счетчика с выводом времени на монитор порта. Команда millis позволяет осуществлять задержу в выполнении программы без delay и осуществлять при выполнении программы многозадачность. Отсчет времени начинается сразу после загрузки программы в микроконтроллер и открытия монитора порта Arduino IDE.

Пояснения к коду:

Таймер на Arduino Nano с LCD 1602

Для этого занятия нам потребуется:

Первый вариант проекта — таймер на Ардуино с управлением от кнопки. При нажатии на первую и вторую кнопку можно увеличивать и уменьшать временной интервал в минутах. При клике на третью кнопку, подключенную к пину 6 Ардуино Нано, запускает обратный отсчет. По окончании отсчета зажигается светодиод и включается звуковой сигнал. Четвертая кнопка, подключенная к пину 8, служит для сброса таймера.

Схема сборки таймера на Ардуино с кнопками

Также таймер можно в любой момент остановить нажатием на третью кнопку. После сборки электрической схемы, загрузите следующий пример программы таймера на Ардуино. Обратите внимание, что для работы часов на дисплее с I2C модулем, потребуется установить библиотеку LiquidCrystal_I2C.h. Эту и другие популярные библиотеки можно скачать на нашем сайте на странице — Библиотеки для Ардуино.

Скетч для таймера на Ардуино Нано с кнопками

Пояснения к коду:

Кухонный таймер Ардуино с энкодером

Сейчас рассмотрим, как сделать таймер на Ардуино своими руками с энкодером и LCD. Принцип управления, подобен предыдущему варианту. Поворотом ручки энкодера можно задать необходимый временной интервал, а нажатием на ручку можно запускать и останавливать обратный отсчет времени. Далее размещена схема сборки проекта на Arduino Nano, этот проект можно собрать и на плате Arduino Uno.

Схема таймера на Ардуино Уно / Нано

Кроме сборки готовой схемы, предлагаем вам скачать чертеж корпуса для проекта из фанеры, который можно изготовить на лазерном ЧПУ станке. Готовую программу и макет корпуса для часов таймера на Ардуино Нано можно скачать по ссылке здесь. После сборки схемы загрузите пример скетча в микроконтроллер. В коде добавлены подробные комментарии для понимания работы программы и даны пояснения.

Скетч таймера обратного отсчета времени

Пояснения к коду:

Заключение. В этом обзоре представлено лишь два варианта изготовления часов с таймером для аквариума или кухни своими руками. В комментариях вы можете оставить вопросы по данному проекту (если они есть) или предложить свой вариант проекта с обратным отсчетом времени, который можно добавить к этому обзору.

Источник

Функции времени в Ардуино

Откуда берётся время?

Начнём с того, откуда вообще микроконтроллер знает, сколько проходит времени. Ведь у него нет часов! Для работы микроконтроллера жизненно важен так называемый тактовый генератор, или кварцевый генератор, или он же кварц. Он же oscillator, он же clock. Clock по-английски это часы. Да, но не всё так просто =) Кварц расположен рядом с МК на плате (также во многих МК есть встроенный тактовый генератор), на Ардуинах обычно стоит генератор на 16 МГц, также встречаются модели на 8 МГц. Тактовый генератор выполняет очень простую вещь: он пинает микроконтроллер со своей тактовой частотой, то есть 16 МГц кварц пинает МК 16 миллионов раз в секунду. Микроконтроллер, в свою очередь зная частоту кварца, может прикинуть время между пинками (16 МГц = 0.0625 микросекунды), и таким образом ориентироваться во времени. Но на деле не всё так просто, потому что принимают пинки таймера так называемые таймеры-счётчики (Timer-counter). Это физически расположенные внутри МК устройства, которые занимаются подсчётом пинков тактового генератора. И вот микроконтроллер уже может обратиться к счётчику и спросить, а сколько там натикало? И счётчик ему расскажет.

Читайте также:  Музыкальная подложка для поздравления

И вот этим мы уже можем пользоваться, для этого у Ардуино есть готовые функции времени. В Ардуино на ATmega328 имеются три счётчика, и подсчётом времени занимается таймер под номером 0. Этим может заниматься любой другой счётчик, но работая в Arduino IDE вы сразу получаете такую настройку, т.к. создавая скетч в Arduino IDE вы автоматически работаете с библиотекой Arduino.h, где и реализованы все удобные функции.

Задержки

Простейшей с точки зрения использования функцией времени является задержка, их у нас две:

Задержки использовать очень просто:

И вот мы можем делать какое-то действие два раза в секунду.

delayMicroseconds() иногда не совсем корректно работает с переменными, нужно стараться использовать константы ( const или просто число). Для создания микросекундных задержек с переменным периодом и корректной работы в циклах лучше использовать следующую конструкцию:

Функции счёта времени

Данные функции возвращают время, прошедшее с момента запуска микроконтроллера, так называемый аптайм (англ. uptime). Таких функций у нас две:

70 минут), имеет разрешение в 4 микросекунды, после переполнения сбрасывается в 0. Работает на системном таймере Timer 0

Таймер на millis()

Вы спросите, а как время со старта МК поможет нам организовать действия по времени? Очень просто, схема вот такая:

Реализация такого “таймера на millis()” выглядит вот так:

Второй вариант сброса таймера будет записан вот так:

В чём преимущества и недостатки? Первый способ ( timer = millis(); ) “уходит”, если в коде появляются задержки и прочие блокирующие участки, во время выполнения которых millis() успевает увеличиться на большее чем период время, и в перспективе период будет “уходить”! Но в то же время если заблокировать выполнение кода на время, большее чем один период – таймер скорректирует эту разницу, так как мы его сбрасываем актуальным миллисом.

Второй способ ( timer += period; ) жёстко отрабатывает период, то есть не “уходит” со временем, если в коде присутствует малая задержка. Минусом здесь является то, что если таймер пропустит период – он “сработает” несколько раз при следующей проверке.

Проверка переполнения

Почему эта конструкция работает и не ломается? Потому что мы используем беззнаковый тип данных, который при переполнении начинает считать с нуля. Подробнее об этом читайте в уроке про вычисления. Таким образом когда миллис становится равен нулю и растёт, а мы вычитаем из него огромное число – получаем не отрицательное, а вполне корректное значение, которое является временем с предыдущего сброса таймера. Поэтому конструкция не то что продолжает работать через

50 суток, но и проходит момент “переполнения” без потери периода!

Вернёмся к вопросу многозадачности: хотим выполнять одно действие два раза в секунду, второе – три, и третье – 10. Нам понадобится 3 переменные таймера и 3 конструкции с условием:

И вот так мы можем например 10 раз в секунду опросить датчик, фильтровать значения, и два раза в секунду выводить показания на дисплей. И три раза в секунду мигать лампочкой. Почему нет?

Рассмотрим ещё несколько алгоритмов.

Ещё варианты

Таймер на остатке от деления

Часто можно встретить вот такую конструкцию: условие выполняется, когда остаток от деления миллис на период равен нулю. Казалось бы, очень крутой и простой алгоритм! Но у него есть один серьёзный недостаток: если условие проверяется чаще одного раза в миллисекунду – оно успеет выполниться несколько раз! То есть для корректной работы такого таймера должна быть задержка, либо естественная (какие-то блокирующие функции), либо самостоятельно созданная. Например так:

Иначе это всё будет работать некорректно, вот наглядный пример:

Также напомню, что операция остатка от деления выполняется гораздо дольше вычитания, и, вызывая её в лупе много раз, мы отдаём под это кучу процессорного времени.

Можно сделать более хитро: один “правильный” таймер на миллис, который инкрементирует счётчик, и по этому счётчику работают остальные таймеры:

Такой подход хорош тем, что у нас всего одна тяжёлая переменная для таймера, а также мы выполняем “остаток от деления” не постоянно, а по своему таймеру. Период своего таймера можно поставить отличным от 1 мс, чтобы снизить нагрузку на процессор. Но не забывать, что счётчик будет отсчитывать уже новый период!

Макрос таймера

Либо вот такой вариант, он не уходит в отличие от предыдущего (разбирали разницу в главе “Таймер на millis()”). Разница напомню в способе сброса таймера.

Данный макрос заменяет “таймер на миллис” одной строчкой, без использования библиотек и создания классов! Пользоваться очень просто: добавьте указанный выше макрос в самое начало кода и вызывайте его как функцию

Единственное ограничение: нельзя вызывать макрос больше одного раза в одном и том же блоке кода, это приведёт к ошибке =) То есть вот так нельзя:

Если очень нужна такая конструкция – помещаем каждый вызов в свой блок кода:

Либо используем блоки кода по условиям или как отдельную функцию, которая “оборачивает” макрос:

Источник

Руководство по таймерам 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)

Чтобы для таймера 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)

Необходимые компоненты

Работа схемы

Схема устройства представлена на следующем рисунке.

Необходимые соединения между платой 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.

Затем сообщим плате Arduino к каким ее контактам подключен ЖК дисплей.

Установим заранее определенное значение (preloader value) равное 3035 – это будет соответствовать интервалу времени в 4 секунды. Формула для расчета этого значения приведена выше в статье.

Затем в функции 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.

Разрешим вызов процедуры обработки прерывания переполнения счетчика с помощью установки соответствующего бита в регистре маски прерываний.

Теперь разрешим все прерывания.

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);
>

Исходный код программы

Далее приведен полный текст программы. Работа нашего проекта продемонстрирована на видео, приведенном в конце статьи.

Источник

Arduino и прерывания таймера

Привет, Хабр! Представляю вашему вниманию перевод статьи «Timer interrupts» автора E.

Предисловие

Плата Arduino позволяет быстро и минимальными средствами решить самые разные задачи. Но там где нужны произвольные интервалы времени (периодический опрос датчиков, высокоточные ШИМ сигналы, импульсы большой длительности) стандартные библиотечные функции задержки не удобны. На время их действия скетч приостанавливается и управлять им становится невозможно.

В подобной ситуации лучше использовать встроенные AVR таймеры. Как это сделать и не заблудиться в технических дебрях даташитов, рассказывает удачная статья, перевод которой и предлагается вашему вниманию.

В этой статье обсуждаются таймеры AVR и Arduino и то, как их использовать в Arduino проектах и схемах пользователя.

Что такое таймер?

Как и в повседневной жизни в микроконтроллерах таймер это некоторая вещь, которая может подать сигнал в будущем, в тот момент который вы установите. Когда этот момент наступает, вызывается прерывание микроконтроллера, напоминая ему что-нибудь сделать, например выполнить определенный фрагмент кода.

Таймеры, как и внешние прерывания, работают независимо от основной программы. Вместо выполнения циклов или повторяющегося вызова задержки millis() вы можете назначить таймеру делать свою работу, в то время как ваш код делает другие вещи.

Итак, предположим, что имеется устройство, которое должно что-то делать, например мигать светодиодом каждые 5 секунд. Если не использовать таймеры, а писать обычный код, то надо установить переменную в момент зажигания светодиода и постоянно проверять не наступил ли момент ее переключения. С прерыванием по таймеру вам достаточно настроить прерывание, и затем запустить таймер. Светодиод будет мигать точно вовремя, независимо от действий основной программы.

Как работает таймер?

Он действует путем увеличения переменной, называемой счетным регистром. Счетный регистр может считать до определенной величины, зависящей от его размера. Таймер увеличивает свой счетчик раз за разом пока не достигнет максимальной величины, в этой точке счетчик переполнится и сбросится обратно в ноль. Таймер обычно устанавливает бит флага, чтобы дать вам знать, что переполнение произошло.

Вы можете проверять этот флаг вручную или можете сделать таймерный переключатель — вызывать прерывание автоматически в момент установки флага. Подобно всяким другим прерываниям вы можете назначить служебную подпрограмму прерывания (Interrupt Service Routine или ISR), чтобы выполнить заданный код, когда таймер переполнится. ISR сама сбросит флаг переполнения, поэтому использование прерываний обычно лучший выбор из-за простоты и скорости.

Чтобы увеличивать значения счетчика через точные интервалы времени, таймер надо подключить к тактовому источнику. Тактовый источник генерирует постоянно повторяющийся сигнал. Каждый раз, когда таймер обнаруживает этот сигнал, он увеличивает значение счетчика на единицу. Поскольку таймер работает от тактового источника, наименьшей измеряемой единицей времени является период такта. Если вы подключите тактовый сигнал частотой 1 МГц, то разрешение таймера (или период таймера) будет:

Читайте также:  Как правильно написать слово аппетитно

T = 1 / f (f это тактовая частота)
T = 1 / 1 МГц = 1 / 10^6 Гц
T = (1 ∗ 10^-6) с

Таким образом разрешение таймера одна миллионная доля секунды. Хотя вы можете применить для таймеров внешний тактовый источник, в большинстве случаев используется внутренний источник самого чипа.

Типы таймеров

В стандартных платах Arduino на 8 битном AVR чипе имеется сразу несколько таймеров. У чипов Atmega168 и Atmega328 есть три таймера Timer0, Timer1 и Timer2. Они также имеют сторожевой таймер, который можно использовать для защиты от сбоев или как механизм программного сброса. Вот некоторые особенности каждого таймера.

Timer0:
Timer0 является 8 битным таймером, это означает, что его счетный регистр может хранить числа вплоть до 255 (т. е. байт без знака). Timer0 используется стандартными временными функциями Arduino такими как delay() и millis(), так что лучше не запутывать его если вас заботят последствия.

Timer1:
Timer1 это 16 битный таймер с максимальным значением счета 65535 (целое без знака). Этот таймер использует библиотека Arduino Servo, учитывайте это если применяете его в своих проектах.

Timer2:
Timer2 — 8 битный и очень похож на Timer0. Он используется в Arduino функции tone().

Timer3, Timer4, Timer5:
Чипы ATmega1280 и ATmega2560 (установлены в вариантах Arduino Mega) имеют три добавочных таймера. Все они 16 битные и работают аналогично Timer1.

Конфигурация регистров

Для того чтобы использовать эти таймеры в AVR есть регистры настроек. Таймеры содержат множество таких регистров. Два из них — регистры управления таймера/счетчика содержат установочные переменные и называются TCCRxA и TCCRxB, где x — номер таймера (TCCR1A и TCCR1B, и т. п.). Каждый регистр содержит 8 бит и каждый бит хранит конфигурационную переменную. Вот сведения из даташита Atmega328:

TCCR1A
Бит 7 6 5 4 3 2 1
0x80 COM1A1 COM1A0 COM1B1 COM1B0 WGM11 WGM10
ReadWrite RW RW RW RW R R RW RW
Начальное значение
TCCR1B
Бит 7 6 5 4 3 2 1
0x81 ICNC1 ICES1 WGM13 WGM12 CS12 CS11 CS10
ReadWrite RW RW R RW RW RW RW RW
Начальное значение

Наиболее важными являются три последние бита в TCCR1B: CS12, CS11 и CS10. Они определяют тактовую частоту таймера. Выбирая их в разных комбинациях вы можете приказать таймеру действовать на различных скоростях. Вот таблица из даташита, описывающая действие битов выбора:

CS12 CS11 CS10 Действие
Нет тактового источника (Timer/Counter остановлен)
1 clk_io/1 (нет деления)
1 clk_io/8 (делитель частоты)
1 1 clk_io/64 (делитель частоты)
1 clk_io/256 (делитель частоты)
1 1 clk_io/1024 (делитель частоты)
1 1 Внешний тактовый источник на выводе T1. Тактирование по спаду
1 1 1 Внешний тактовый источник на выводе T1. Тактирование по фронту

По умолчанию все эти биты установлены на ноль.

Допустим вы хотите, чтобы Timer1 работал на тактовой частоте с одним отсчетом на период. Когда он переполнится, вы хотите вызвать подпрограмму прерывания, которая переключает светодиод, подсоединенный к ножке 13, в состояние включено или выключено. Для этого примера запишем Arduino код, но будем использовать процедуры и функции библиотеки avr-libc всегда, когда это не делает вещи слишком сложными. Сторонники чистого AVR могут адаптировать код по своему усмотрению.

Сначала инициализируем таймер:

Регистр TIMSK1 это регистр маски прерываний Таймера/Счетчика1. Он контролирует прерывания, которые таймер может вызвать. Установка бита TOIE1 приказывает таймеру вызвать прерывание когда таймер переполняется. Подробнее об этом позже.

Когда вы устанавливаете бит CS10, таймер начинает считать и, как только возникает прерывание по переполнению, вызывается ISR(TIMER1_OVF_vect). Это происходит всегда когда таймер переполняется.

Дальше определим функцию прерывания ISR:

Сейчас мы можем определить цикл loop() и переключать светодиод независимо от того, что происходит в главной программе. Чтобы выключить таймер, установите TCCR1B=0 в любое время.

Как часто будет мигать светодиод?

Timer1 установлен на прерывание по переполнению и давайте предположим, что вы используете Atmega328 с тактовой частотой 16 МГц. Поскольку таймер 16-битный, он может считать до максимального значения (2^16 – 1), или 65535. При 16 МГц цикл выполняется 1/(16 ∗ 10^6) секунды или 6.25e-8 с. Это означает что 65535 отсчетов произойдут за (65535 ∗ 6.25e-8 с) и ISR будет вызываться примерно через 0,0041 с. И так раз за разом, каждую четырехтысячную секунды. Это слишком быстро, чтобы увидеть мерцание.

Если мы подадим на светодиод очень быстрый ШИМ сигнал с 50% заполнением, то свечение будет казаться непрерывным, но менее ярким чем обычно. Подобный эксперимент показывает удивительную мощь микроконтроллеров — даже недорогой 8-битный чип может обрабатывать информацию намного быстрей чем мы способны обнаружить.

Делитель таймера и режим CTC

Чтобы управлять периодом, вы можете использовать делитель, который позволяет поделить тактовый сигнал на различные степени двойки и увеличить период таймера. Например, вы бы хотели мигания светодиода с интервалом одна секунда. В регистре TCCR1B есть три бита CS устанавливающие наиболее подходящее разрешение. Если установить биты CS10 и CS12 используя:

то частота тактового источника поделится на 1024. Это дает разрешение таймера 1/(16 ∗ 10^6 / 1024) или 6.4e-5 с. Теперь таймер будет переполняться каждые (65535 ∗ 6.4e-5с) или за 4,194с. Это слишком долго.

Но есть и другой режим AVR таймера. Он называется сброс таймера по совпадению или CTC. Вместо счета до переполнения, таймер сравнивает свой счетчик с переменой которая ранее сохранена в регистре. Когда счет совпадет с этой переменной, таймер может либо установить флаг, либо вызвать прерывание, точно так же как и в случае переполнения.

Чтобы использовать режим CTC надо понять, сколько циклов вам нужно, чтобы получить интервал в одну секунду. Предположим, что коэффициент деления по-прежнему равен 1024.

Расчет будет следующий:

Вы должны добавить дополнительную единицу к числу отсчетов потому что в CTC режиме при совпадении счетчика с заданным значением он сбросит сам себя в ноль. Сброс занимает один тактовый период, который надо учесть в расчетах. Во многих случаях ошибка в один период не слишком значима, но в высокоточных задачах она может быть критичной.

Функция настройки setup() будет такая:

Также нужно заменить прерывание по переполнению на прерывание по совпадению:

Сейчас светодиод будет зажигаться и гаснуть ровно на одну секунду. А вы можете делать все что угодно в цикле loop(). Пока вы не измените настройки таймера, программа никак не связана с прерываниями. У вас нет ограничений на использование таймера с разными режимами и настройками делителя.

Вот полный стартовый пример который вы можете использовать как основу для собственных проектов:

Помните, что вы можете использовать встроенные ISR функции для расширения функций таймера. Например вам требуется опрашивать датчик каждые 10 секунд. Но установок таймера, обеспечивающих такой долгий счет без переполнения нет. Однако можно использовать ISR чтобы инкрементировать счетную переменную раз в секунду и затем опрашивать датчик когда переменная достигнет 10. С использованием СТС режима из предыдущего примера прерывание могло бы выглядеть так:

Поскольку переменная будет модифицироваться внутри ISR она должна быть декларирована как volatile. Поэтому, при описании переменных в начале программы вам надо написать:

Послесловие переводчика

В свое время эта статья сэкономила мне немало времени при разработке прототипа измерительного генератора. Надеюсь, что она окажется полезной и другим читателям.

Источник

Простые слова
Adblock
detector