Найти тему
Разумный мир

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

Оглавление

Познакомившись с таймерами "в общем и целом", пора переходить к их реализации в различных моделях микроконтроллеров. И начну я с PIC, включая PIC18. Они, пожалуй, самые простые и понятные.

Все -битные микроконтроллеры PIC имеют минимум один 8-битный базовый таймер TIMER0. Этот таймер работает всегда и обладает небольшой функциональностью. Но хорошо подходит в качестве источника "системных тиков".

Кроме того, может быть до трех 16-битных таймеров. Это "нечетные" таймеры TIMER1/3/5. Кроме повышенной разрядности эти таймеры имеют управление входными импульсами счета и интеграцию с некоторыми другими модулями микроконтроллера для "захвата" событий. И могут иметь свой независимый тактовый генератор.

Так же, может быть до трех 8-битных таймеров с расширенными возможностями управления выходами. Это "четные" таймеры TIMER2/4/6. Эти таймеры тоже имеют интеграцию с другими модулями микроконтроллера. В частности, они могут использоваться для формирования ШИМ сигналов, в том числе, комплиментарных.

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

Базовый 8-битный TIMER0

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

TIMER0 в микроконтроллере PIC12F629. Из документации.
TIMER0 в микроконтроллере PIC12F629. Из документации.

Сам регистр-счетчик таймера занимает лишь малую часть этой схемы, в правом верхнем углу, красный прямоугольник, TMR0. Этот счетчик умеет работать только в сторону увеличения. Когда счетчик переполняется, то есть, его содержимое изменяется с 0xFF на 0x00, устанавливается флаг T0IF.

Этот флаг может быть проанализирован программно, и это единственный способ в микроконтроллерах BaseLine, где нет прерываний, или вызвать прерывание. Поскольку мы пока не изучали прерывания, будем воспринимать T0IF просто как флаг. Устанавливается он аппаратно, а вот сбрасывать его надо вручную. Располагается этот флаг в регистре INTCON.

Источник счетных импульсов выбирается мультиплексором, который управляется битом T0CS, который находится в регистре OPTION_REG. Если T0CS=0, то на выбран системный тактовый генератор. На схеме он показан как сигнал CLKOUT. Я уже говорил ранее, что в PIC машинный цикл состоит из 4 тактов. Поэтому частота CLKOUT и равняется частоте тактового генератора деленной на 4 (Fosc/4). В документации этот режим называется режимом таймера.

Если T0CS=1, то счетные импульсы поступают с вывода T0CKI. То есть, источник этих импульсов внешний. А для этого источника мы можем выбрать и фронт импульса, который будет обрабатываться регистром-счетчиком. Делает это бит T0SE, который тоже располагается в регистре OPTION_REG.

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

Пока все просто, но вот дальше появляется не слишком приятная особенность. Дело в том, что предварительный делитель (Prescaler) здесь общий для TIMER0 и WDT (сторожевой таймер). Сторожевыми таймерами мы будем заниматься отдельно. Но понятно, что эта особенность ограничивает "свободу маневра". Приходится выбирать, что важнее.

Назначение предварительного делителя определяется битом PSA, который расположен в опять таки в OPTION_REG. А коэффициент деления задается битами PS2:PS0 регистра OPTION_REG.

Причем одной и той же комбинации PS2:PS0 соответствую разные коэффициенты деления, в зависимости от назначения предделителя. Так комбинация 010 будет задавать коэффициент деления 1:8 для TIMER0 и 1:4 для WDT.

Давайте посмотрим, как с помощью TIMER0 получит задержку на 10 мс. Предположим, что тактовая частота равно 4 МГц, тогда частота CLKOUT будет равна 1МГц. Это соответствует периоду 1 мкс. Максимальная длительность, которая может быть получена без предделителя равно 256 импульсов, или 256 мкс. А нам надо 10000 мкс. Значит придется подключать предварительный делитель.

Для простоты примем, что WDT не используется. Нам нужен коэффициент деления 1:4. А в счетчик таймера должен будет отсчитать 250 импульсов. Значит, нам нужно загрузить в него значение 256-250=6. Или просто -250. Вот фрагмент программы, которая выполнит требуемую задержку

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

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

Графически можно представить работу таймера так

Работа базового TIMER0. Иллюстрация моя
Работа базового TIMER0. Иллюстрация моя

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

Более современный TIMER0

Основная неприятность с базовым таймером заключается в том, что предделитель общий с WDT. И первым усовершенствованием была попытка их разделить. Получилось примерно так

TIMER0 в микроконтроллере PIC16F886. Из документации
TIMER0 в микроконтроллере PIC16F886. Из документации

Все управление таймером осталось неизменным, но WDT получил свой предварительный делитель. Теперь можно пользоваться и всем функционалом TIMER0, и всем функционалом WDT одновременно. Но сохранена совместимость со старым вариантом, PSA по прежнему может назначить предделитель WDT. И тогда WDT будет иметь сразу два предварительных делителя. И сможет формировать гораздо большие интервалы времени. Но WDT мы в рамках данной статьи не рассматриваем.

Современный TIMER0, универсальный

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

TIMER0 в микроконтроллере PIC16F18855. Из документации
TIMER0 в микроконтроллере PIC16F18855. Из документации

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

Теперь это могут быть конфигурируемые логические ячейки (LC1_out), это такой простой кусочек ПЛИС в микроконтроллере. Сегодня мы их не рассматриваем. Это может быть вторичный тактовый генератор (32 кГц, SOSC). Это могут быть высокочастотный и низкочастотный внутренние тактовые генераторы. Это может быть уже знакомая нам частота Fosc/4. Или сигнал с одного из настраиваемых внешних выводов (PPS).

Выбор источника по прежнему осуществляется T0CS, только теперь это не один бит, а три бита в регистре T0CON1.

По прежнему есть предварительный делитель, коэффициент деления которого можно менять. И управляют этим 4 бита T0CKPS в регистре T0CON1. Теперь коэффициентов деления стало больше, а предделитель принадлежит только таймеру.

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

А вот и новинка! Теперь у нас есть и делитель на выходе таймера (Postscaler). Коэффициент деления устанавливается битами T0OUTPS3:T0OUTPS0 ( 4 бита) регистра T0CON. Кроме того, мы можем вывести сигнал переполнения таймера (после постделителя) на один из настраиваемых выводов микроконтроллера (TMR0 через PPS). Причем скважность будет равна 2 благодаря триггеру.

Пока усовершенствования выглядят интересными, но не столь радикальными. Однако, сам таймер не зря прячется за блоком TMR0 body. Там много интересного! И управляется это битом T016BIT регистра T0CON0.

Внутренний мир Timer0 микроконтроллера PIC16F1885. Из документации
Внутренний мир Timer0 микроконтроллера PIC16F1885. Из документации

Слева показана работа в 8-битном режиме, а справа в 16 битном.

Поскольку таймер может работать как 16 битный, его счетчик состоит из двух регистров TMR0H старший байт и TMR0L младший байт. Но в 8 битном режиме как счетчик используется только младший байт. А старший задает "условие сравнения", о котором я говорил в предыдущей статье "Микроконтроллеры для начинающих. Часть 52. Таймеры".

Содержимое счетчика таймера на каждом цикле сравнивается с содержимым TMR0H. И при их совпадении счетчик таймера сбрасывается, а на выходе формируется сигнал переполнения. То есть, максимальное значение счетчика теперь не фиксированное (0xFF) а определяется TMR0H. Что бы запись нового значения в TMR0H не влияла на отсчет текущего интервала, добавлен промежуточный буфер (TMR0 High Byte), который и участвует в сравнении. Содержимое TMR0H копируется в этот буфер при формировании сигнала переполнения, одновременно со сбросом TMR0L.

Это можно графически представить так

Работа современного TIMER0 в 8-битном режиме. Иллюстрация моя
Работа современного TIMER0 в 8-битном режиме. Иллюстрация моя

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

Запись значения счетчика выполняется в такой последовательности:

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

Чтение значения счетчика выполняется в обратном порядке:

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

Есть и еще одно отличие от простого базового таймера. Теперь TIMER0 можно и остановить, и запустить. Отвечает за это бит T0EN регистра T0CON0.

Итак, современный TIMER0 уже нельзя назвать базовым таймером. Это универсальный таймер, который может работать, как мы скоро увидим, и как "четные таймеры", и как "нечетные таймеры".

Как и все "четные таймеры" он может автоматически формировать заданные интервалы времени. Причем периодичность в этом случае обеспечивается автоматически.

Как и все "нечетные таймеры" он может работать в 16 битном режиме. При этом автоматического обеспечения периодичности нет, но зато есть аппаратная поддержка целостности (атомарности доступа) данных при чтении/записи счетчика не остановленного таймера.

Не смотря на все эти усовершенствования и усложнения, работа с таймером не стала на много сложнее. Давайте посмотрим, как мы теперь можем отработать задержку на 10 мс

Поскольку у нас теперь таймер 16-битный, делители не нужны. После сброса они настроены на коэффициент деления 1:1, что нас как раз устраивает. Обратите внимание, что начальную установку счетчика таймера я выполнил именно так, как описывал ранее.

В остальном, все примерно так же, как и ранее.

16-битные таймеры ("нечетные")

Нечетными я называю их потому, что они имеют нечетные номера. TIMER1, TIMER3, TIMER5. Все эти таймеры устроены и работают абсолютно одинаково, поэтому рассмотрим их на примере TIMER1.

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

Вот функциональная схема типового варианта TIMER1, усовершенствования рассмотрим чуть позже

TIMER1 в микроконтроллере PIC16F886. Из документации
TIMER1 в микроконтроллере PIC16F886. Из документации

Здесь нам многое уже знакомо. 16-разрядный счетчик таймера состоящий из двух регистров, TMR1L:TMR1L. При переполнении счетчика устанавливается флаг TMR1IF.

На вход таймера могут поступать импульсы от разных источников: системного тактового генератора Fosc/4, собственного кварцевого генератора с внешним резонатором, вывода T1CKI. За выбор источника отвечают два бита. Уже знакомый нам TMR1CS (как в TIMER0) и T1OSCEN. Если T1OSCEN=0, то собственный кварцевый генератор не работает, а таймер считает импульсы со входа T1CKI. Если T1OSCEN=1, то импульсы генерирует собственный генератор.

Все управление осуществляется через регистр T1CON. И уже упомянутые биты, и все упомянутые далее, находятся именно в этом регистре.

Собственного кварцевого генератора, иногда, может и не быть. Но если он есть, то позволяет таймеру работать в режиме сна.

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

Как видите, все очень похоже на современный TIMER0 при работе в 16-битном режиме. Но вот аппаратной поддержки атомарного доступа нет.

Основным отличием, и самым важным (не считая разрядности), является возможность разрешения/запрещения счета без выключения/включения таймера. Если посмотрите на регистр счетчика, то увидите дополнительный вход EN.

Разумеется, самый высокий приоритет имеет бит TMR1ON, который включает/выключает таймер. А вот дальнейшее влияние можно или запретить, тогда таймер будет считать всегда, пока включен, или разрешить. За это отвечает бит TMR1GE. Если бит сброшен, то таймер работает непрерывно. Если установлен, счетом можно управлять.

В показанном простом случае есть два возможных управляющих сигнала. Первый поступает из внешнего мира через вывод T1G. Второй с встроенного в микроконтроллер модуля компаратора.

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

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

Поскольку аппаратной поддержки атомарного доступа к счетчику нет, для его чтения или изменения рекомендуется останавливать счет таймера. Если это не возможно по каким то причинам, то нужно учитывать, что возможен перенос между TMR1L и TMR1H в процессе доступа.

Усовершенствования TIMER1

Как и TIMER0, TIMER1 в более современный моделях микроконтроллеров подвергся некоторым усовершенствованиям. Но они были не столь радикальными. Вот функциональная схема из микроконтроллера PIC12F1840.

TIMER1 в микроконтроллере PIC12F1840. Из документации
TIMER1 в микроконтроллере PIC12F1840. Из документации

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

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

8-битные таймеры ("четные")

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

Давайте, как всегда, сначала познакомимся с базовым вариантом таких таймеров

TIMER2 в микроконтроллере PIC16F886. Из документации
TIMER2 в микроконтроллере PIC16F886. Из документации

Как видите, структура TIMER2 очень похожа на структуру TMR0 body в 8-битном режиме, которую мы уже видели ранее. И работает точно так же. Только вместо двух половинок 16 битного счетчика здесь есть два обычных 8 битных регистра.

TMR2 это регистр-счетчик таймера, а PR2 это регистр периода. Он максимальное значение, которого может достигнуть счетчик. После чего счетчик будет сброшен в 0.

Этот таймер работает так же, как современный TIMER0, поэтому я не буду приводить отдельную иллюстрацию. Просто замените TMR0H на PR2. Есть и еще одно, более важное отличие - нет дополнительного буфера для PR2. А значит, изменение содержимого этого регистра немедленно отразится на работе таймера.

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

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

Кроме такого автоматического формирования периодических временных интервалов, четные таймеры активно используются модулями формирования ШИМ. Но это тема отдельной статьи.

Усовершенствования TIMER2

В большей степени подобные усовершенствования касаются выбора источника счетных импульсов. Вот пример функциональной схемы

TIMER2 в микроконтроллере PIC16F18855. Из документации
TIMER2 в микроконтроллере PIC16F18855. Из документации

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

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

Специализированные таймеры

В микроконтроллерах PIC, в некоторых моделях, есть и специальные таймеры. Например, таймер для измерения сигналов - SIGNAL MEASUREMENT TIMER (SMT). В некотором роде, этот таймер является гибридом TIMER1 в одноимпульсном режиме, и TIMER2. Его разрядность 24 бита. Но он предназначен именно для измерения, а не для формирования временных интервалов. Поэтому сегодня я его не буду рассматривать. Как и другие экзотические таймеры.

Заключение

Сегодня мы познакомились лишь с частью возможностей таймеров в микроконтроллерах PIC. Хотя я постарался более полно показать основные варианты их реализации. При этом использование таймеров для отсчета времени довольно простое. Не смотря на множество вариаций самих таймеров.

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

В следующей статье мы рассмотрим таймеры в микроконтроллерах AVR.

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