4927 subscribers

Микроконтроллеры для начинающих. Часть 54. Таймеры AVR

414 full reads
1,3k story viewsUnique page visitors
414 read the story to the endThat's 30% of the total page views
3,5 minutes — average reading time

Мы уже познакомились с таймерами в целом в статье "Микроконтроллеры для начинающих. Часть 52. Таймеры" и с подробностями реализации таймеров в Microchp PIC в статье "Микроконтроллеры для начинающих. Часть 53. Таймеры PIC". Теперь, как я и обещал, познакомимся с таймерами в Atmel AVR (ныне Microchip AVR). Напомню, что популярная Arduino это те самые микроконтроллеры AVR, так что статья может быть полезной и любителям Arduino.

Еще раз отмечу, что мы пока не изучали прерывания. Поэтому сегодня будут рассматриваться только возможности счета времени/импульсов таймерами. Не будет рассматриваться и использование таймеров для работы других модулей, например ШИМ. Это все темы последующих статей.

Базовый 8-битный таймер Timer0

Несмотря на то, что AVR считается "более одинаковой" чем тот же PIC, это далеко не всегда так. И сегодня мы увидим это на примере таймеров. Они так же, как в PIC, прошли свой путь развития. Вот такой простейший базовый таймер можно встретить в ATmega8A

Базовый 8-битный Timer/Counter0 в микроконтроллере ATmega8A. Из документации
Базовый 8-битный Timer/Counter0 в микроконтроллере ATmega8A. Из документации
Базовый 8-битный Timer/Counter0 в микроконтроллере ATmega8A. Из документации

В отличии от Microchip, Atmel не показывает особых подробностей схемотехники, предпочитая ограничиваться регистрами и крупными основными блоками. Именно поэтому я и не назвал схему функциональной. Она скорее ближе к блок-схеме.

Здесь все основные компоненты нам уже знакомы. Собственно сам регистр-счетчик таймера - TCNTn, где n обозначает номер таймера. В данном случае это TCNT0. Счетчик может считать только в одну сторону, в сторону увеличения. Функциональный блок "Control Logic" скрывает в себе все подробности схемы управления. Но для нас это не страшно.

Начинка блока управляющей логики достаточно проста. Это мультиплексор выбора источника счетных импульсов и схема формирования сигнала TOV0, который соответствует флагу переполнения таймера. Сам флаг переполнения TOV0 располагается в регистре TFIR, который не относится к модулю таймера.

На иллюстрации показана схема определения момента переполнения, компаратор "=0xFF". Однако, в этом простейшем таймере компаратор на самом деле отсутствует. Сигналом переполнения является перенос из старшего разряда счетчика. Именно из него и формируется сигнал TOV0.

Источник счетных импульсов выбирается с помощью бит SC00-SC02 регистра TCCR0. Он показан на иллюстрации. Если все три бита равны 0, источник счетных импульсов отсутствует, что соответствует остановке таймера.

Основным внутренним источником счетных импульсов является системный тактовый генератор с предварительным делителем. Причем предделитель в AVR общий для таймеров 0 и 1, но для каждого таймера можно выбирать свой коэффициент деления независимо. Этот предварительный делитесь мы чуть позже рассмотрим подробнее.

Так же счетные импульсы могут поступать от внешнего источника, через вывод T0 (Tn). При этом работа счетчика возможна по нарастающему или по спадающему фронту.

Функциональная схема обработки сигнала с выводов Tn. Из документации
Функциональная схема обработки сигнала с выводов Tn. Из документации
Функциональная схема обработки сигнала с выводов Tn. Из документации

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

И, по традиции, рассмотрим небольшой фрагмент программы использующей данный базовый таймер. Как и в случае PIC я буду рассматривать организацию задержки на 10 мс.

Обратите внимание, адреса регистров я указал для ATmega8A. Все очень похоже на то, как мы делали такую задержку на базовом TIMER0 в PIC. Но теперь у нас нет возможности задать коэффициент деления 1:4, поэтому выбираем 1:8. Соответственно и начальное значение счетчика будет -125.

Так же, вопросы может вызвать и такая вот операция

if(TIFR.TOV0) TIFR.TOV0=1;

Зачем устанавливать бит, если он и так установлен? Но дело в том, что этот бит устанавливается аппаратно, при переполнении. А вот сбрасывается аппаратно только при выполнении обработчика прерывания, которого у нас нет. И я даже не показал регистр маскирования прерываний. Поэтому сбрасывать бит приходится вручную. При этом его сброс выполняется несколько "парадоксальным" образом - повторной установкой. Что мы и делаем.

Предварительный делитель. Один на двоих

В AVR один предварительный делитель на два таймера, 0 и 1. И выбор коэффициентов деления для таймеров довольно ограничен.

Блок выбора источника счетных импульсов таймеров 0 и 1, с предварительным делителем. Из документации
Блок выбора источника счетных импульсов таймеров 0 и 1, с предварительным делителем. Из документации
Блок выбора источника счетных импульсов таймеров 0 и 1, с предварительным делителем. Из документации

Собственно говоря, предварительный делитель это просто цепочка триггеров (счетчик) с выводами сигналов с отдельных триггеров. И коэффициенты деления могут быть лишь 1:1, 1:8, 1:64, 1:256 и 1:1024.

Биты CS просто управляют мультиплексорами, которые выбирают и источник сигнала, и фронт, и коэффициент деления.

Усовершенствованный 8-битный таймер Timer 0

Страсть к усовершенствованиям не обошла стороной и Atmel. При этом они сохранили номер таймера, но весьма существенно изменили его работу. А это далеко не лучшим образом влияет на совместимость микроконтроллеров и лишь усложняет переносимость программ. Вот так выглядит таймер, например, в ATtiny13A

Усовершенствованный 8-битный таймер Timer 0 в ATiny13A. Из документации
Усовершенствованный 8-битный таймер Timer 0 в ATiny13A. Из документации
Усовершенствованный 8-битный таймер Timer 0 в ATiny13A. Из документации

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

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

На месте и регистр счетчик таймера TCNT0. И он по прежнему 8-битный. Правда научился считать в обе стороны. И даже "Control Logic" на месте, только сигналов у него добавилось.

Сразу скажу, что генерацию сигналов (блоки Waveform Generation) мы сегодня рассматривать не будем, как и различные режимы формирования ШИМ с помощью таймера. Я об этом предупреждал в самом начале статьи. Эти вопросы будут рассматриваться позднее, в отдельных статьях.

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

Режимами работы, в основном, управляет регистр TCCR0A. За исключением одного бита, который по неизвестной причине был выселен в TCCR0B, не смотря на то, что свободное место в TCCR0A оставалось.

Нас будут интересовать биты WGM02:WGM00. И пусть вас не смущает, что эта аббревиатура образовалась от Waveform Generation Mode, который мы сегодня не рассматриваем. Именно эти биты управляют работой таймера. А вот биты COM0A1:COM0A0 и COM0B1:COM0B0 управляют использованием выводов OC0A и OC0B генераторами сигналов.

Для нас интересны только два режима из 6 доступных. Режимы ШИМ использовать возможно, но не имеет особого смысла. Поэтому их рассмотрим в соответствующей статье.

Нормальный режим

Если WGM02:WGM00=000, то таймер работает в нормальном режиме, который практически полностью идентичен работе базового таймера. В этом режиме счетчик таймера считает только в сторону увеличения, а при его переполнении устанавливается флаг TOV0, правда в регистре TIFR0. А таймер продолжает счет с начала.

Именно этот факт обозначен на иллюстрации прямоугольником в надписью "Fixed TOP Value". Это фиксированное верхнее значение и равно 0xFF.

Однако, этот режим все таки отличается от работы базового таймера. Так как два регистра, OCR0A и OCR0B, хоть и не оказывают влияния на работу собственно таймер, но позволяют управлять двумя дополнительными флагами. И эти флаги могут вызывать прерывания.

Когда содержимое TCNT0 совпадет с содержимым регистра OCR0A, будет установлен бит OCRF0A в регистре TIFR0. Этот бит будет сброшен автоматически при обработке прерывания, или потребует ручного сброса, если прерывания не используются. Причем для его сброса, если он установлен, нужно "парадоксально" установить его еще раз (записать 1).

Точно так же ведет себя и регистр ORC0B, только установлен будет флаг OCRF0B.

Таким образом, мы можем с помощью одно таймера получить сразу 3 различных временных интервала.

Сброс при совпадении

Если WGM02:WGM00=010, то регистр OCR0A задает верхний предел для регистра счетчика. В этом режиме таймер работает аналогично ранее рассмотренному (в предыдущей статье) TIMER2 в микроконтроллерах PIC. Там же я приводил графическую иллюстрацию такого режима работы.

Счетчик таймера в этом режиме считает от 0 в сторону увеличения. Когда счетчик достигнет значения заданного регистром OCR0A, счетчик будет сброшен в 0. И будет установлен флаг OCRF0A.

Но вот флаг TOV0 при этом не будет установлен. Так как переполнения счетчика не произойдет. Флаг OCRF0B будет установлен только если содержимое регистра OCR0B меньше содержимого OCR0A.

Усовершенствованный и "экологичный" 8-битный таймер Timer 0

Да, есть и такой. Например, в ATmega328. Он полностью идентичен описанному ранее, но для включения таймера (подачи на него питания) нужно сбросить бит PRTIM0 в регистре PRR.

Иных отличий нет.

Универсальный 16-битный таймер

Всем хорош усовершенствованный 8-битный таймер, но разрядность маловата... Да и режима захвата события в нем нет. И на помощь нам приходит универсальный 16-битный таймер. Такой таймер можно встретить, например, в ATmega328 и ATmega8A. И многих других. Там он называется Timer1.

А вот в ATtiny10 этот таймер называется Timer0, при этом базового таймера в этом микроконтроллере нет. Даже улучшенного. Что добавляет путаницы и вопросов у новичков.

Так же, обратите внимание, что в ATnega328 дополнительно потребуется сбросить бит PRTIM1 в регистре PRR, что бы включить питание таймера.

16-битный универсальный таймер в микроконтроллерах AVR. Из документации
16-битный универсальный таймер в микроконтроллерах AVR. Из документации
16-битный универсальный таймер в микроконтроллерах AVR. Из документации

Как видно, этот таймер является наследником усовершенствованного 8-битного таймера. Теперь все большинство регистров стали 16 разрядными.

Регистр-счетчик таймера теперь состоит из двух половин, TCNT1L и TCNT1H (TCNT0L и TCNT0H в ATtiny10). В дальнейшем, для уменьшения путаницы, я буду считать, что это Timer1. Регистры OCR1A и OCR1B тоже 16-разрядные. А вот управляющие регистры остались 8-битными. Добавился и регистр TCCR1C, который не показан на иллюстрации.

В целом, работа узлов таймера, которые похожи на таковые в улучшенном таймере, аналогична им. Но есть одна важная особенность, которой я уже уделял внимание в предыдущей статье "Микроконтроллеры для начинающих. Часть 53. Таймеры PIC". Речь идет об атомарности доступа к 16-битным регистрам со стороны 8-битного процессора (и шины).

В данном случае проблема решается с помощью дополнительного буферного регистра. Причем этот буфер один на все регистры каждого таймера. И используется он не всегда. Например, для чтения регистров OCR нет необходимости использовать буфер, так как содержимое этих регистров не может измениться само. А вот при их записи буфер используется.

Процедуры записи и чтения, с использованием этого буфера точно такие же, как и в PIC

Запись:

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

Чтение:

  • Чтение содержимого младшего байта. При этом содержимое старшего байта одновременно копируется во временный буфер.
  • Чтение содержимого старшего байта, которое ранее было сохранено во временном буфере.

Режимов работы таймера стало больше, теперь ими управляют не 3, а 4 бита WGM13:WGM10. Причем WGM13:WGM12 находятся в регистре TCCR1B, а два оставшихся в регистре TCCR1A.

При этом нам опять (пока!) интересны не все режимы.

Нормальный режим

Если WGM13:WGM10=0000, то таймер работает в нормальном режиме. Он почти полностью идентичен таковому в усовершенствованном 8-битном таймере. За исключением разрядности участвующих а процессе регистров.

Про работу блока захвата поговорим чуть позже.

Сброс при совпадении

А вот таких режимов теперь два. Если WGM13:WGM10=0100, режим аналогичен уже рассмотренному ранее. Только разрядность TCNT1 и OCR1A теперь 16.

А вот если WGM13:WGM10=1100, то вместо OCR1A в операциях участвует ICR1. И при условии TCNT1=ICR1 будет установлен флаг ICF1 в регистре TIFR1. Других отличий нет.

Блок захвата событий

Как и в PIC, захват событий подразумевает измерение времени между моментом запуска таймера и наступлением события. Или между двумя событиями.

Блок захвата события в универсальном 16-битном таймере. Из документации
Блок захвата события в универсальном 16-битном таймере. Из документации
Блок захвата события в универсальном 16-битном таймере. Из документации

Захват события выполняется очень просто. По сигналу на выводе ICP, или по сигналу внутренних модулей (компаратор на иллюстрации), содержимое регистра-счетчика таймера копируется в регистр ICR1. Эта операция атомарная. Одновременно устанавливается флаг ICF1. Теперь можно прочитать содержимое ICR1.

Обратите внимание, на этой иллюстрации показан тот самый буферный регистр, о котором я уже говорил.

Выбор источника сигнала захвата осуществляется битом ACIC в управляющем регистре модуля аналогового компаратора ACSR.

Блок подавления шума, Noise Canceler, просто считывает состояние источника сигнала захвата в каждом цикле. Если 4 отсчета подряд дадут одинаковый результат, то считается, что сигнал устойчив и он может быть передан на детектор перепадов. Это такое гашение дребезга. Я его описывал в статьях на канале уже не один раз. Нужно учитывать, что подавление шума вносит дополнительную задержку 4 такта.

И эта задержка складывается с задержкой детектора перепадов, которая равна одному такту. То есть, блок захвата работает синхронно.

Теперь мы можем сделать вывод, что из всех рассмотренных нами режимов работы таймера (не всех вообще, а только рассмотренных!), захват событий можно выполнять в нормальном режиме и в режиме "сброс при совпадении", когда WGM13:WGM10=0100.

А вот записывать в регистр ICR1 можно только в режиме "сброс при совпадении", когда WGM13:WGM10=1100.

Еще один 8-битный таймер, но со своим генератором. Или совсем улучшенный, да еще и асинхронный

Что будет, если мы возьмем уже рассмотренный улучшенный 8-битный таймер и добавим к нему отдельный тактовый генератор? А вот что получится

Улучшенный 8-битный таймер Timer2 с собственным генератором в ATmega328. Из документации
Улучшенный 8-битный таймер Timer2 с собственным генератором в ATmega328. Из документации
Улучшенный 8-битный таймер Timer2 с собственным генератором в ATmega328. Из документации

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

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

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

  1. Собственный генератор, в основном, предназначен для работы с "часовыми" кварцевыми резонаторами (32768 Гц)
  2. Тактовая частота, на которой работает микроконтроллер, должна не менее чем в 4 раза превышать частоту, на которой работает собственный генератор таймера.
  3. Переключение между синхронным и асинхронным режимами может привести к искажению содержимого регистров таймера. Поэтому новичкам лучше заранее определиться с необходимым режимом работы и настроить его в начале программы.
  4. При работе в асинхронном режиме запись в регистры TCNT2, OCR2, TCCR2 осуществляется не напрямую, а через дополнительные буферные регистры. Информация переписывается в аппаратные регистры только по следующему тактовому импульсу собственного генератора.

Переключение в асинхронный режим осуществляется установкой бита AS2 в регистре ASSR (на иллюстрации не показан). При работе в асинхронном режиме состояние бита EXCLK определяет, действительно ли к собственному генератору подключается кварцевый резонатор. Если EXCLK=0, то используется внешний кварцевый резонатор. Если EXCLK=1, то кварцевый резонатор не подключен, а вывод TOSC1 используется как вход для внешнего источника импульсов.

Следует учитывать, что бит EXCLK должен быть установлен в нужное состояние до переключения в асинхронный режим!

Так же следует учитывать, что при работе в асинхронном режиме выводы TOSC1 и TOSC2 невозможно использовать как выводы порта B.

Усекновенный совсем улучшенный

Да, такое тоже возможно. В ATmega 8A таймер обладает несколько меньшей функциональностью. Правда его не усекали, это просто старый вариант построения таймера.

Блок-схема старого 8-битного Timer2 в ATmega8A. Из документации
Блок-схема старого 8-битного Timer2 в ATmega8A. Из документации
Блок-схема старого 8-битного Timer2 в ATmega8A. Из документации

Здесь показан регистр ASSR. Но сразу видно отличие, которое и уменьшает функциональность. Имеется всего один регистр OCR2.

В остальном это точно такой же таймер, как ранее рассмотренный.

Заключение

Нельзя сказать что таймеры в AVR намного сложнее, чем в PIC. Они более функциональные, но и в управлении чуть сложнее. А вот их разновидностей ничуть не меньше. Причем даже одинаковые таймеры могут иметь свои особенности в разных моделях микроконтроллеров. А безусловно снижает совместимость и усложняет переносимость программ так же, как в PIC.

Да, я опять рассказал далеко не все. И зачастую упрощал изложение, так как этот цикл статей все таки для новичков. Да и не является учебником в полном смысле этого слова. Кроме прерываний и ШИМ я не сказал, например, о регистре GTCCR, с помощью которого можно сбросить триггеры предделителя.

Многое еще будет описано в последующих статьях. Что то мы увидим в учебных проектах. А что то так и останется за кадром, для самостоятельного изучения вами.

В следующей статье мы познакомимся с таймерами в STM8. Там все еще хитрее. Будет интересно.

До новых встреч!