Закончив первый учебный проект мы почувствовали, что со счетом времени надо что то делать. Использование машинных циклов слишком неудобно. Мало того, что этот метод зависит от тактовой частоты, так в STM8 еще и последовательность расположения команд влияние оказывает. Кроме того, даже простая модификация программы может изменить длительность выполнения основного цикла, что вносит еще большую сумятицу. Да и микроконтроллер вынужден большую часть времени проводить в бесполезных циклах ожидания. А ведь мог бы заняться чем то полезным, или просто заснуть экономя энергию батареи (при питании от батареи).
Решить большинство этих проблем помогают таймеры, специализированные модули микроконтроллера предназначенные для счета времени. И сегодня мы познакомимся, пока в самых общих чертах, с таймерами и некоторыми (лишь некоторыми!) из их возможностей.
При дальнейшем изучении микроконтроллером мы будем не раз возвращаться к таймерам, что бы узнать о все новых их возможностях. Так как применение таймеров в микроконтроллерах весьма разнообразно. И во многих микроконтроллерах имеется несколько таймеров, причем разных.
Что такое таймер?
В общем и целом, изначально, это устройство для отсчета требуемых интервалов времени. То есть, мы устанавливаем требуемый временной интервал и запускаем таймер. Когда заданное время (с момента запуска таймера) истечет, таймер "сработает" выдав какой либо сигнал.
Здесь сразу возникают вопросы, что считать, и как считать?
Что будем считать?
В наш цифровой век обычно считают импульсы. Но вот какие могут быть источники этих импульсов? Раз речь идет о времени, то источник импульсов должен быть достаточно стабильным. На роль такого источника хорошо подходят генераторы с кварцевыми резонаторами.
В микроконтроллере такой обычно есть - системный тактовый генератор. Да, он не всегда использует кварцевый резонатор (мы это видели в учебном проекте бегущих огней). Но все таки этот источник использовать имеет смысл. Правда тут есть одна тонкость, связанная с режимами энергосбережения. Если системный тактовый генератор останавливается в режимах сна, то и наш таймер работать не будет.
Кроме того, можно использовать внешний источник импульсов. Это может что угодно, например, TCXO. Главное, что это внешний источник импульсов, не зависящий от микроконтроллера. А значит, он не будет остановлен во время засыпания микроконтроллера и таймер может продолжить работу.
И, наконец, мы можем предоставить таймеру свой собственный генератор, включая внешний кварцевый резонатор. Этот вариант тоже позволит таймеру не зависеть от системного тактового генератора и продолжить работу во время засыпания микроконтроллера.
Как будем считать?
Для счета импульсов вполне разумно использовать счетчик. Именно счетчики лежат в основе таймеров в микроконтроллерах. И опять возникают вопросы, какова разрядность счетчика и каково направление счета?
Мы рассматриваем 8-битные микроконтроллеры, поэтому естественной разрядностью счетчика будет 8. Но такой разрядности может оказаться недостаточно, поэтому в микроконтроллерах обычно ест и 8-разрядные, и 16-разрядные таймеры.
А если и этого не хватает? Наращивать разрядность? И да, и нет. В большинстве случаев на входе счетчика стоит предварительный делитель, или предделитель. Который в документации обычно называется prescaler.
В общем и целом, это тоже счетчик. И его можно считать расширением разрядности счетчика таймера. Но только с точки зрения деления частоты, так как по сравнению со счетчиком таймера функционал делителя сильно ограничен.
Счетчики, как всем известно, могут работать в двух направлениях. Увеличивая, инкрементируя (+1), или уменьшая, декрементируя (-1), свое содержимое. А могут быть и реверсивными, меняя направление счета в процессе работы.
В большинстве случаев счетчики таймеров микроконтроллеров работают в сторону увеличения. Однако, мы сразу, в самом первом варианте блок-схемы таймера предусмотрим возможность смены направления счета. Нам это понадобится в дальнейшем.
Первый вариант построения таймера - базовый таймер
Такое построение таймера действительно используется в большинстве микроконтроллеров. Обычно, таймеров несколько, но самый простой из них устроен примерно так. Даже еще проще, иногда.
Почти все, что изображено на этой функциональной схеме мы уже кратко обсудили. В основе таймера лежит счетчик (двоичный) TMR_CNT. Я не стал указывать разрядность счетчика, это не важно, в данном случае. Причем это счетчик можно прочитать или записать в любой момент времени, даже не останавливая таймер.
Если счетчик работает в направлении увеличения, то на его выходе возникает сигнал переполнения OF (overflow), а если в направлении уменьшения, то сигнал опустошения UF (underflow). Эти сигналы возникают при переходе счетчика через 0. Мультиплексор, стоящий после счетчика, выбирает один из этих сигналов, в зависимости от направления счета (DIR), для формирования сигнала TE - событие таймера.
Три возможных источника импульсов для счета, которые мы ранее рассматривали, выбираются мультиплексором. Main CLK это системный тактовый сигнал, а TI это вход внешнего сигнала. Сигнал с выбранного источника поступает на предварительный делитель.
При этом, дополнительный мультиплексор позволяет выбрать, нужно ли использовать делитель. Выход этого мультиплексора и является тактовым сигналом (CLK) для счетчика таймера.
Я специально использовал обозначения отличные от тех, которые используются в документации на рассматриваемые микроконтроллеры. Так как мы пока рассматриваем таймеры в самом общем виде.
Совершенно не обязательно в реальном таймере реального микроконтроллера есть все эти элементы. Но именно так, в общем случае, устроены их таймеры, которые называют базовыми.
Незапланированный бонус - расширенные функциональные возможности базового таймера
А теперь о небольшом "бонусе", который изначально нами не предполагался, если рассматривать таймер только как устройство отсчета временных интервалов. Вход Tin совершенно не обязательно использовать как источник импульсов времени! Мы можем подавать на него любые импульсы, например, с датчика прохождения объекта по ленте конвейера. Тогда таймер будет считать эти объекты, а событие таймера может использоваться, например, в качестве признака "коробка полная".
Итак, мы получили не просто таймер, а таймер-счетчик. И можем использовать его не только для отсчета временных интервалов. Но и для подсчета чего угодно! И для определения наполнения/опустошения чего либо на объекте автоматизации. Или для отсчета нужной дозы, например, лекарства. Или для определения того, что мы залили в бак автомобиля оплаченное клиентом АЗС количество топлива. Всего и не перечислить.
Расширяем функциональность для измерения интервалов времени. Захват.
Чем таймер отличается от секундомера? Только тем, что первый отсчитав требуемый интервал времени подает сигнал, а второй нужно остановить вручную, что позволит измерить временной интервал. То есть, просто небольшие отличия в функционале. А значит, имеет смысл попробовать усовершенствовать наш таймер, что бы он мог использоваться и для отсчета, и для измерения временных интервалов.
Тут нужно сделать небольшое уточнение. Да, в большинстве случаев мы можем остановить таймер сбросив/установив специальный бит в управляющем регистре. Но то ли это, что нам требуется? Даже если останов таймера просто отключает поступление импульсов счета, а не отключает питание таймера.
А это не совсем то. Хотя бы по той причине, что останов таймера будет выполняться программно, с непредсказуемой задержкой. Даже при использовании прерываний. А это снижает точность. Поэтому нужно именно аппаратное решение. Примерно такое
Даже функциональная схема показана очень упрощенно. В данном случае мы добавили управляющий вход (вывод TG - Timer Gate) и мультиплексор, который позволяет выбрать источник сигнала управляющего прохождением счетных импульсов.
Для сигнала TG, для разных микроконтроллеров, можно задать работу по уровню (0 или 1) или по фронту (нарастающий спадающий). Я не стал показывать на иллюстрации, как это делается. Для нас это не важно, в данном случае.
Внутренние сигналы EV1 и EV2 это формируемые модулями микроконтроллера сигналы, их может быть разное количество. И для них тоже можно задать различные условия, как и для TG. Эти сигналы соответствуют каким то событиям, которые фиксируются модулями микроконтроллера. Например, изменение состояния вывода порта. Или превышение порогового уровня напряжения на аналоговом входе.
Таким образом, становится возможным, например, определение емкости конденсатора измерением времени его заряда или разряда. Или размера предмета, проезжающего по ленте конвейера мимо датчика, что можно использовать для сортировки.
На этой иллюстрации есть еще одно упрощение. Дело в том, что задание периода счета (через TG) это одно, а вот останов счета по событию, которое может быть кратковременным, это немного другое. Поэтому управление поступлением импульсов счета не ограничивается одним элементом И. Оно сложнее, и может включать в себя триггеры. Более подробно все условия и их обработку мы будем рассматривать в последующих статьях, для каждого семейства микроконтроллеров в отдельности.
Два варианта обработки событий
Выше мы рассмотрели решение, при котором таймер останавливает счет при наступлении события. Это позволяет легко прочитать остановленный счетчик таймера. Но такой останов не всегда приемлем. Иногда нужно продолжить счет, или перезапустить таймер.
Такой вариант тоже возможен, просто нужно добавить еще один регистр, в который и будет копироваться содержимое счетчика таймера при наступлении события. Вместо остановки счета. Примерно так.
Здесь регистр EV_CNT используется для хранения копии содержимого TMR_CNT при наступлении события. И его можно прочитать в любой момент времени, главное, успеть до наступления очередного события.
Захват (capture)
В таком использовании таймера собственно таймер, его счетчик, не является главным действующим лицом. Он используется совместно с другими модулями для измерения продолжительности какого либо события.
В документации это часто называют захватом события. Такой способ использования таймера может описываться или в разделах описания собственно таймера, или в разделах описаний других модулей, которые и генерируют события. Например, в микроконтроллере может быть отдельный модуль Capture/Compare/PWM, который в своей работе используется один из таймеров.
Использование таймеров для захвата событий мы изучим позднее. Уже после изучения таймеров. Так как для этого потребуется знать модули, которые эти события формируют. Но мы это сделаем обязательно.
Что происходит после окончания счета
Начав рассматривать таймер я сказал, что после окончания счета таймер выдает сигнал. Или событие. Но что происходит дальше? Таймер останавливается или продолжает счет?
Немного мы коснулись этого вопроса рассматривая режим захвата, когда добавили регистр EV_CNT. Но как ведет себя таймер в обычном режиме?
В большинстве случаев, как мы уже знаем, счетчики таймеров работают в сторону увеличения. Давайте рассмотрим 8-битный таймер, в который загрузили байт 0xC5 (десятичное 197). С каждым импульсом счета содержимое счетчика будет увеличиваться на 1. И через 58 импульсов счетчик будет равен 0xFF. А еще через один импульс он переполнится и и будет сформировано событие таймера (TE).
Но таймер при этом не остановится, а продолжит счет. И следующее событие таймера возникнет через 256 импульсов. Для 16 битного таймера, разумеется, через 65536 импульсов.
Такой режим счета называется обычным. Он самый простой и вполне подходит для формирования одиночный временных интервалов. А для периодических требуется повторная установка начального значения счетчика, программная.
При этом в период будет входить и время переустановки начального значения счетчика. А это снижает точность формирования периодических временных интервалов.
Расширяем функциональность формирования интервалов времени. Сравнение.
Для периодических временных интервалов нам надо как то обеспечить автоматическую перезагрузку начального значения счетчика. Для этого можно добавить еще один регистр и сравнивать содержимое счетчика таймера а этим регистром. Примерно так
Я показал лишь часть таймера, так нас, в данном случае, не интересует формирование импульсов счета. Здесь есть уже знакомый нам счетчик таймера TMR_CNT. Регистр TMR_RR (Reload Register), в который записывается максимальное значение счетчика таймера для формирования заданного временного интервала. И компаратор (цифровой), который сравнивает содержимое счетчика и регистра TMR_RR.
В данном случае принято, что таймер может считать только вверх, а начальное значение счетчика равно 0. Как только счетчик таймера достигнет записанного в TMR_RR значения, он будет сброшен на 0 и сформируется событие таймера. И счет начнется с начала.
Например, для уже рассматриваемого нами примера 8-битного таймера и временного интервала 59 импульсов, нам достаточно записать в TMR_RR значение 59 и таймер будет автоматически отсчитывать периодические интервалы времени из 59 импульсов.
Вот такое сравнение с содержимого счетчика и дополнительного регистра и дало название режиму работы - сравнение (compare). И точно так же, как в случае режима захвата, таймер может быть лишь частью отдельного модуля, например, того самого Capture/Compare/PWM.
Можно сэкономить один регистр если вспомнить, что режимы захвата и сравнения не могут использоваться одновременно. А значит, в качестве EV_CNT и TMR_RR моно использовать один и тот же аппаратный регистр. И так действительно делают.
Альтернативные режимы сравнения и автоперезагрузки
Таймер может работать не на увеличение, а на уменьшение. В этом случае содержимое TMR_RR будет автоматически записываться в счетчик таймера, как начальное значение. При этом для нашего примера с временным интервалом из 59 импульсов ничего не изменится. Абсолютно ничего. Хотя схемотехника модуля будет иная. Зачем это нужно?
Это влияет на момент синхронизации сигнала таймера и других модулей микроконтроллера. Например, на модуль ШИМ. Когда мы до него доберемся, мы и рассмотрим этот момент более подробно.
Возможен и режим, когда таймер сначала считает от 0 до значения в TMR_RR, а потом направление счета меняется и таймер начинает считать вниз, до 0. И так до бесконечности, с постоянной сменой направления счета. Этот режим возможен далеко не во всех микроконтроллерах. И мы тоже рассмотрим его подробнее при знакомстве с модулями ШИМ.
Дополнительные усовершенствования
Рассмотреть все варианты дело не такое простое. Да и особого смысла не имеет. Но все таки одно дополнение хочу затронуть. Мы можем установить еще один делитель, но уже после таймера. Поэтому его называют postscaler. Этот делитель может использоваться для формирования сигнала TE не на каждое переполнение таймера, а лишь через определенное их число. Иногда этот делитель называют счетчиком повторений.
Такой счетчик повторений может оказаться полезным, если нужно сформировать определенное количество одинаковых импульсов. И мы обязательно познакомимся с этим делителем/счетчиком повторений при рассмотрении реализации таймеров в конкретных моделях микроконтроллеров.
Заключение
Мы, пока довольно кратко и в общих чертах, познакомились с таймерами, которые есть в подавляющем большинстве микроконтроллеров. Простейший счетчик, на первый взгляд, оказался довольно сложным и многофункциональным устройством. Настолько многофункциональным, что мы будем знакомиться с его возможностями при изучении различных модулей микроконтроллеров. И не раз убедимся в их пользе и удобстве.