Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Микропроцессорные системы пособие.doc
Скачиваний:
0
Добавлен:
12.02.2026
Размер:
2.53 Mб
Скачать

5. Таймеры-счётчики

В предыдущей главе мы рассмотрели способ организации временных задержек с помощью пустых циклов. Совершенно очевидно, что для организации точных временных интервалов этот способ не подходит, к тому же он целиком загружает микроконтроллер, мешая реализации других функций. Так что нам не обойтись без некоторого таймера, который бы отсчитывал время независимо от работы микроконтроллерного ядра и позволял в любое время пользоваться результатами счёта. В рассматриваемой в данном курсе линейке микроконтроллеров функцию такого счетчика как раз и выполняет таймер-счётчик, входящий в состав стандартной периферии любого восьмибитного микроконтроллера семейства AVR. Их количество может варьироваться от одного (как в случае младшей линейке Tiny) до 6 (в старшей линейке, Atmega256, например).

В целом, во всех более старших моделях микроконтроллеров присутствуют, как минимум, два таймера/счетчика — T0 и T1. Таймер T0 обычно имеет минимальный набор функций, зависящий от модели микроконтроллера. В одних моделях он может использоваться только для отсчета и измерения временных интервалов или как счетчик внешних событий. В других же к этим функциям добавляется возможность генерации сигналов с широтно-импульсной модуляцией (ШИМ) фиксированной разрядности, а также возможность работать в асинхронном режиме в качестве часов реального времени.

Таймер T1 также может использоваться для отсчета временных интервалов и как счетчик внешних событий. Кроме того, он может выполнять запоминание своего состояния по внешнему сигналу. Как и таймер T0, он может работать в качестве широтно-импульсного модулятора, но уже переменной разрядности и к тому же многоканального. Таймер T2 полностью аналогичен таймеру T0. При наличии в микроконтроллере обоих таймеров, один из них может работать в асинхронном режиме, а другой — в качестве счетчика внешних событий. Таймер T3 по функциональным возможностям идентичен таймеру T1. В составе всех микроконтроллеров семейства имеется также сторожевой таймер (Watch Dog Timer), являющийся непременным атрибутом всех современных микроконтроллеров. Этот таймер позволяет избежать несанкционированного зацикливания программы, возникающего по тем или иным причинам.

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

Источники тактовой частоты.

Для работы любого микроконтроллера необходимо обеспечить его стабильным источником тактовых импульсов. В случае микроконтроллеров фирмы Atmel возможно несколько вариантов.

1) Первый – воспользоваться внутренним генератором на RC-цепочке. Этот вариант самый простой - не требует дополнительных внешних компонентов, но и особой точности от такого генератора ждать не стоит, его частота может меняться в зависимости от внешних условий в пределах 3-5 процентов. Для приложений, не требующих точного измерения временных интервалов, этого вполне достаточно. Для тех случаев, когда важна каждая микросекунда, стоит обратиться ко второму варианту.

2) В качестве источника тактовых импульсов может выступать внешний кварцевый резонатор (или керамический, для любителей экзотики). Максимальная поддерживаемая частота – 16МГц (здесь уместно вспомнить о том, что энергопотребление микроконтроллера возрастает с увеличением рабочей частоты, причём значительно). Для подключения внешнего кварцевого резонатора используются специальные выводы – XTAL1 и XTAL2, также для подключения необходимо использовать дополнительные конденсаторы емкостью в 15-30 пФ, в зависимости от частоты (хотя существуют изощренные трёхвыводные модели резонаторов с уже встроенными конденсаторами, что весьма удобно, но купить их, особенно в розницу, не так просто). Пример подключения кварцевого резонатора приведен на рисунке 5.1.

Рис. 5.1. Различные схемы тактирования микроконтроллера (часть 1)

Столь неровное значение частоты кварца (11.76 МГц) на рисунке объясняется просто – оно необходимо для реализации передачи данных по последовательному интерфейсу UART на высоких скоростях (об этом будет рассказано подробнее в соответствующем разделе).

3) Вариант, нетребовательный к точности задаваемой системной частоты – воспользоваться вместо резонатора обычной RC-цепочкой, при этом для её подключения используются те же самые выводы, что и в предыдущем случае. Тактовая частота, которую даёт такая RC-цепочка, приблизительно описывается следующим выражением:

Емкость конденсатора С должна быть не ниже 22пФ. Также, с помощью фьюз-бита CKOPT мы можем подключить между выводом XTAL1 и землёй внутренний конденсатор емкостью 36 пФ, что позволяет избавиться от внешнего конденсатора. Более подробно информация о тактировании МК с помощью RC-цепочки описана в фирменной документации.

4) Последний вариант – использование внешнего генератора. Здесь всё предельно просто, подключаем выход генератора к выводу XTAL1. Подобный способ тактирования микроконтроллера обеспечивает максимальную точность, единственный недостаток – подобные генераторы имеют достаточно высокую стоимость.

5) Для некоторых приложений (например, часов реального времени) нам может понадобиться низкочастотный кварц на 32,768 кГц. Почему значение именно такое? Очевидно, что шестнадцатибитный таймер может считать до 65536 тиков, а частота часового кварца, как нетрудно подсчитать, как раз составляет половину этого значения. Получается, что с помощью такого кварца можно с высокой точностью отсчитывать секундные интервалы. Для подключения часового кварца используются отдельные выводы TOSC1 и TOSC2, причём использование дополнительных внешних конденсаторов не требуется (рис. 5.2).

Рис. 5.2. Различные схемы тактирования микроконтроллера (часть 2)

Теперь рассмотрим регистры, определяющие работу таймера. Основными управляющими регистрами являются регистры TCCRxn (Timer/Counter Control Register, для микроконтроллера AtTiny104 предусмотрено 3 управляющих регистра – TCCR0A, TCCR0B и TCCR0C, которые определяет режим работы таймера и значение коэффициента предделителя.

Возникает вопрос, что такое предделитель? Тактовые импульсы нашего микроконтроллера (при работе от внешнего кварца или внутренней RC-цепочки, например) перед тем, как они попадут в счётный регистр таймера, можно пропустить через предделитель с фиксированными значениями - 8, 32, 64, 128, 256 и 1024. Где это может пригодиться? Вспомним часовой кварц с частотой в 32,768 кГц. Если эту частоту пропустить через делитель с коэффициентом 128 и подать на восьмибитный таймер, то точно раз в секунду будет происходить прерывание таймера по переполнению (32768/128=256), что позволяет быстро и эффективно организовать часы реального времени.

Значение предделителя можно изменить в регистре TCCR0B, за него отвечают три бита: CS02:0 (Clock Select), которые как раз и определяют коэффициент деления тактовых импульсов микроконтроллера. Здесь возможны следующие варианты (таблица 5.1).

Таблица 5.1. Задание предделителя таймера

CS02

CS01

CS01

Описание

0

0

0

нет источника импульсов (Таймер остановлен)

0

0

1

clkIO/1 (No prescaling)

0

1

0

clkIO/8 (From prescaler)

0

1

1

clkIO/64 (From prescaler)

1

0

0

clkIO/256 (From prescaler)

1

0

1

clkIO/1024 (From prescaler)

1

1

0

External clock source on T0. Clock on falling edge.

1

1

1

External clock source on T0. Clock on rising edge.

Режим работы таймера T0 определяется состоянием четырех битов WGM01WGMx03 регистра TCCR0A и TCCR0B. Зависимость режима работы таймера от состояния этих разрядов показана в таблице 5.2.

Таблица 5.2. Режимы работы таймера

WGM0

[3:0]

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

TOP

Обновление бита OCR0x

Установка прерыв. TOV0

0

0000

Normal

0xFFFF

Immediate

MAX

1

0001

PWM, Phase Correct, 8-bit

0x00FF

TOP

BOTTOM

2

0010

PWM, Phase Correct, 9-bit

0x01FF

TOP

BOTTOM

3

0011

PWM, Phase Correct, 10-bit

0x03FF

TOP

BOTTOM

4

0100

CTC (Clear Timer on Compare)

OCR0A

Immediate

MAX

5

0101

Fast PWM, 8-bit

0x00FF

TOP

TOP

6

0110

Fast PWM, 9-bit

0x01FF

TOP

TOP

7

0111

Fast PWM, 10-bit

0x03FF

TOP

TOP

8

1000

PWM, Phase and Frequency Correct

ICR0

BOTTOM

BOTTOM

9

1001

PWM, Phase and Frequency Correct

OCR0A

BOTTOM

BOTTOM

10

1010

PWM, Phase Correct

ICR0

TOP

BOTTOM

11

1011

PWM, Phase Correct

OCR0A

TOP

BOTTOM

12

1100

CTC (Clear Timer on Compare)

ICR0

Immediate

MAX

13

1101

Reserved

-

-

-

14

1110

Fast PWM

ICR0

TOP

TOP

15

1111

Fast PWM

OCR0A

TOP

TOP

Режим Normal

Это наиболее простой режим работы таймеров, он же единственный режим в младших моделях семейства. В этом режиме счетный регистр функционирует как обычный суммирующий счетчик. По каждому импульсу тактового сигнала осуществляется инкремент счетного регистра. При переходе через значение $FF возникает переполнение, и счет продолжается со значения $00. В том же такте сигнала, в котором обнуляется регистр TCNTn, устанавливается в «1» флаг переполнения счетного регистра TOVn (Timer Overflow).

При равенстве счетного регистра и регистра сравнения устанавливается флаг прерывания OCFn и, если разряд OCIEn регистра TIMSK установлен в «1», генерируется соответствующее прерывание. Наряду с установкой флага при равенстве счетного регистра и регистра сравнения может изменяться состояние вывода OC0x микроконтроллера. Каким образом оно будет изменяться, определяется разрядами COM0x1и COM0x1 регистра TCCR0A в соответствии с таблицей 5.3.

Таблица 5.3. Регистры сравнения.

COM0x1

COM0x0

Описание

0

0

Таймер Tn отключен от вывода OCn

0

1

Состояние вывода меняется на противоположное

1

0

Вывод сбрасывается в «0»

1

1

Вывод устанавливается в «1»

Режим CTC (сброс при совпадении)

В этом режиме счетный регистр функционирует как обычный суммирующий счетчик, инкремент которого осуществляется по каждому импульсу тактового сигнала на входе таймера. Однако максимально возможное значение счетного регистра и, следовательно, разрешающая способность счетчика определяется регистром сравнения OCR0A и OCR0B. После достижения значения, записанного в регистре сравнения, счет продолжается со значения 0x0000. В том же такте сигнала, в котором обнуляется счетный регистр, устанавливается флаг прерывания TOV0 регистра TIFR0. Временные диаграммы для этого режима работы таймера/счетчика приведены на рисунке 5.3.

Рис. 5.3. Временные диаграммы для режима CTC

При достижении счетчиком максимального значения устанавливается флаг OCF0x регистра TIFR. Одновременно с установкой флага может изменяться состояние вывода OC0x микроконтроллера. Поведение выводов определяется разрядами COM0х1 и COM0х0 регистра TCCR0A, и полностью совпадает с табличными данными, приведёнными для предыдущего режима работы таймера.

Режим Fast PWM

Режим Fast PWM («Быстродействующий ШИМ») позволяет генерировать высокочастотный сигнал с широтно-импульсной модуляцией. В связи с высокой частотой генерируемого сигнала данный режим с успехом может использоваться в таких приложениях, как регулирование мощности, выпрямление, цифроаналоговое преобразование и другие. Более подробно данный режим работы таймера (вместе с режимом Phase Corect PWM) будет рассмотрен в отдельном разделе.

Счётный регистр TCNT0 – именно в нём накапливаются тактовые импульсы. Счетный регистр может быть как восьмибитным, так и шестнадцатибитным, как в случае нашего микроконтроллера AtTiny104. В последнем случае он делится на два регистра – младший (TCNT0L) и старший (TCNT0H). И здесь возникает одна небольшая проблема. В счётные регистры в любой момент времени можно как записывать нужные численные значения, так и считывать их. Но так как регистр состоит из двух половинок, эти операции невозможно провести за одну команду – а при чтении/записи отдельных половинок может измениться значение одной из них. Чтобы избежать этого, был предложен специальный алгоритм.

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

CLI // Запрещаем прерывания

OUT TCNT1H, R16 //Старший байт записан в регистр TEMP

OUT TCNT1L,R17 // Запись сразу в обе половинки

SEI // Разрешаем прерывания

При чтении регистра TCNTx операции выполняются в обратном порядке – сначала читаем младший регистр, потом старший. Эти правила чтения/записи распространяются и на все остальные шестнадцатибитные регистры, состоящие из двух частей.

Прерывания таймера.

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

За прерывания от таймеров отвечают регистры TIMSK (Timer/Counter Interrupt Mask) и TIFR (Timer/Counter Interrupt Flag Register).

TIMSK – это регистр масок, биты, находящиеся в нем, локально разрешают прерывания. Если бит установлен, значит конкретное прерывание разрешено. Если бит сброшен в ноль, значит данное прерывание запрещено. По умолчанию после системного сброса все биты регистра TIMSK сброшены в ноль. Описание разрядов регистра TIMSK0 для используемого контроллера приведено в таблице ниже, их всего четыре:

Таблица 5.4. Прерывания таймера.

Название

Описание источника прерывания

ICIE0

Флаг по событию «Захват» таймера

OCIE0A

Флаг по событию «Совпадение A» таймера

OCIE0B

Флаг по событию «Совпадение B» таймера

TOIE0

Флаг по переполнению таймера

Наиболее интересующее нас на данный момент прерывание – TOIE0, то есть прерывание по переполнению таймера. Задавая коэффициент предделителя таймера и используя данное прерывание, мы можем с достаточно большой точность отсчитывать необходимые нам временные интервалы.

Регистр TIFR – это непосредственно флаговый регистр. Когда какое-то прерывание срабатывает, устанавливается соответствующий ему флаг (то есть каждому из четырех прерываний регистра TIMSK0 соответствует свой собственный флаг регистра TIFR0). Этот флаг сбрасывается на аппаратном уровне, когда программа совершает переход по вектору прерываний. Если прерывания запрещены, то флаг так и будет стоять до тех пор, пока прерывания не разрешат, и программа не уйдет на их обработку.

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

// Лабораторная работа №2. Использование таймера.

//******************************************************//

// Подключаем внешние модули и макросы

//***********************************************************//

// Задаём переменные, определяем имена используемых регистров

.def Count = R17

.def Temp = R18 // Рабочая переменная

//***********************************************************//

// вектор прерываний

ORG 0x000

RJMP RESET // Reset Handler

ORG 0x001

RETI // IRQ0 Handler

ORG 0x002

RETI //PCINT0 Handler

ORG 0x003

RETI // PCINT1 Handler

ORG 0x004

RETI // Timer/Counter0 Capture Handler

ORG 0x005

RJMP Tim_OVF // Timer/Counter0 Overflow Handler

ORG 0x006

RETI // Timer/Counter0 Compare Match A Handler

ORG 0x007

RETI // Timer/Counter0 Compare Match B Handler

ORG 0x008

RETI // Analog Comparator Handler

ORG 0x009

RETI // Watchdog Timer Handler

ORG 0x00A

RETI // Voltage Level Monitor Handler

ORG 0x00B

RETI // ADC Conversion Handler

ORG 0x00C

RETI // USART0 Rx Start Handler

ORG 0x00D

RETI // USART0 Rx Complete Handler

ORG 0x00E

RETI // USART0 Data Register Empty Handler

ORG 0x00F

RETI // USART0 Tx Complete Handler

ORG INT_VECTORS_SIZE // Конец таблицы прерываний

//***********************************************************//

// Обработка прерываний

Tim_OVF: // Прерывание по переполнению таймера

INC Count

// Если счётчик нечётный, пропускаем следующую команду

SBRS Count, 0

LDI Temp, 0b00100000 // Иначе устанавливаем бит 6

// Если счётчик чётный, пропускаем следующую команду

SBRC Count, 0

LDI Temp, 0b00000000 // Иначе сбрасываем бит 6

OUT PortA, Temp

RETI // Выходим из прерывания

*****************************************************//

RESET:

// Инициализация стека

LDI R16, Low(RAMEND)

OUT SPL, R16

LDI R16, High(RAMEND)

OUT SPH, R16

//Инициализация периферии

//*********************************************//

LDI R16, 0xD8

OUT CCP, R16

LDI R16, 0x00

OUT CLKPSR, R16

LDI R16, 0b00100000

OUT DDRA, R16

OUT PORTA, R16

CLR Count

CLR Temp // Очищаем регистры

LDI Temp, (1<<CS00) | (1<<CS01) // Задаем предделитель

OUT TCCR0B, Temp

// разрешаем прерывание по переполнению таймера

LDI Temp, (1<<TOIE0)

OUT TIMSK0, Temp

SEI // Разрешаем глобальные прерывания

//***********************************************************//

// Основной цикл программы

Main:

// Бесконечный цикл, вся работа выполняется в прерывании

RJMP Main //***********************************************************//

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