Перейти к содержанию

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

Как устроены таймеры-счётчики классических AVR: три таймера ATmega328P, их режимы и таблицы битов WGM, предделители, формулы частоты для CTC и ШИМ, 16-битный Timer1 с регистром ICR1 и захватом, асинхронный Timer2 с часовым кварцем. С примерами на C: прерывание раз в миллисекунду, ШИМ 20 кГц и сигнал для сервопривода.

Обновлено Редакция GAW

Таймер-счётчик — это счётчик, который увеличивается от тактового сигнала через предделитель, сравнивается с регистрами сравнения и по этим событиям вызывает прерывания и формирует сигналы на выводах. У ATmega328P их три: 8-битные Timer0 и Timer2 и 16-битный Timer1. Ниже — по даташиту DS40002061B (главы 15–18): режимы, биты, формулы и примеры. Готовые значения предделителя и регистров для нужной частоты считают калькулятор таймера и калькулятор ШИМ Arduino.

Коротко

  • Timer0 — 8 бит, каналы OC0A (PD6) и OC0B (PD5), внешний вход T0 (PD4), предделители 1, 8, 64, 256, 1024.
  • Timer1 — 16 бит, каналы OC1A (PB1) и OC1B (PB2), захват ICP1 (PB0), внешний вход T1 (PD5), 15 режимов, TOP из ICR1 или OCR1A.
  • Timer2 — 8 бит, каналы OC2A (PB3) и OC2B (PD3), предделители 1, 8, 32, 64, 128, 256, 1024, асинхронный режим от кварца 32,768 кГц на TOSC1/TOSC2.
  • Режимы: Normal, CTC, Fast PWM, Phase Correct PWM, у Timer1 ещё Phase and Frequency Correct.
  • Формулы: CTC с переключением вывода — f_clk / (2 · N · (1 + OCR)), Fast PWM — f_clk / (N · (1 + TOP)), Phase Correct — f_clk / (2 · N · TOP).

Как устроен таймер

Счётчик TCNTn считает импульсы clk_T — частоту ввода-вывода, поделённую предделителем, или внешние импульсы на входе Tn. Регистры сравнения OCRnA и OCRnB постоянно сравниваются со счётчиком; при совпадении устанавливается флаг OCFnx, может вызываться прерывание и меняться состояние вывода OCnx. Флаг TOVn ставится при переполнении. Максимальное значение счётчика называется MAX (0xFF или 0xFFFF), нижнее — BOTTOM (0), а TOP — значение, на котором счёт поворачивает или сбрасывается: в зависимости от режима это MAX, фиксированное 0x00FF, 0x01FF, 0x03FF или содержимое регистра OCRnA или ICR1.

Режим работы задают биты WGMn — генерации сигнала, а поведение вывода — биты COMnx1:0: в режимах без ШИМ вывод по совпадению устанавливается, сбрасывается или переключается, в режимах ШИМ — неинвертирующий (COMnx = 10) или инвертирующий (11) сигнал. Сигнал попадает на вывод, только если тот настроен на выход в DDRx.

Предделитель

CSn2:0Timer0 и Timer1Timer2
000таймер остановлентаймер остановлен
001clk / 1clk / 1
010clk / 8clk / 8
011clk / 64clk / 32
100clk / 256clk / 64
101clk / 1024clk / 128
110внешний вход Tn, спадclk / 256
111внешний вход Tn, фронтclk / 1024

Timer0 и Timer1 делят один предделитель, а у Timer2 свой, с дополнительными коэффициентами 32 и 128 (таблицы 15-9, 16-5 и 18-9). Таймер запускается записью ненулевого кода в CSn2:0.

Режимы Timer0 и Timer2

РежимWGM2:0ОписаниеTOPОбновление OCRФлаг TOV
0000Normal0xFFсразуMAX
1001Phase Correct PWM0xFFTOPBOTTOM
2010CTCOCRAсразуMAX
3011Fast PWM0xFFBOTTOMMAX
5101Phase Correct PWMOCRATOPBOTTOM
7111Fast PWMOCRABOTTOMTOP

Режимы 4 и 6 зарезервированы (таблица 15-8, у Timer2 — 18-8 с теми же кодами). В режимах 5 и 7 TOP задаёт OCRnA, поэтому частота ШИМ настраивается, но ШИМ с регулируемой скважностью остаётся только у канала B, а вывод OCnA может лишь переключаться по совпадению.

  • Normal — счёт от 0 до 0xFF с переполнением; время отмеряют прерыванием по переполнению или по совпадению с OCRnx.
  • CTC — счётчик сбрасывается при совпадении с OCRnA, период задаётся точно. Переключение вывода OCnA при каждом совпадении (COMnA = 01) даёт меандр с частотой f_clk / (2 · N · (1 + OCRnA)), до f_clk / 2 при OCRnA = 0. Регистр OCRnA здесь не буферизован: если записать значение меньше текущего TCNTn, совпадение пропустится и счётчик дойдёт до 0xFF.
  • Fast PWM — однонаклонный счёт от BOTTOM до TOP. Частота f_clk / (N · 256) при TOP = 0xFF; при 16 МГц и N = 64 это 976,6 Гц — частота ШИМ Arduino на выводах 5 и 6.
  • Phase Correct PWM — счёт вверх и вниз, частота вдвое ниже: f_clk / (N · 510) при TOP = 0xFF, но импульс симметричен относительно середины периода, что предпочтительно для управления двигателями.

Timer1: 16 бит и 15 режимов

РежимWGM13:0ОписаниеTOPОбновление OCR1xФлаг TOV1
00000Normal0xFFFFсразуMAX
10001Phase Correct PWM, 8 бит0x00FFTOPBOTTOM
20010Phase Correct PWM, 9 бит0x01FFTOPBOTTOM
30011Phase Correct PWM, 10 бит0x03FFTOPBOTTOM
40100CTCOCR1AсразуMAX
50101Fast PWM, 8 бит0x00FFBOTTOMTOP
60110Fast PWM, 9 бит0x01FFBOTTOMTOP
70111Fast PWM, 10 бит0x03FFBOTTOMTOP
81000Phase and Frequency Correct PWMICR1BOTTOMBOTTOM
91001Phase and Frequency Correct PWMOCR1ABOTTOMBOTTOM
101010Phase Correct PWMICR1TOPBOTTOM
111011Phase Correct PWMOCR1ATOPBOTTOM
121100CTCICR1сразуMAX
141110Fast PWMICR1BOTTOMTOP
151111Fast PWMOCR1ABOTTOMTOP

Режим 13 зарезервирован (таблица 16-4). Биты WGM11:10 находятся в TCCR1A, WGM13:12 — в TCCR1B. Для ШИМ с произвольной частотой удобнее всего режим 14: период задаёт ICR1, а оба канала OC1A и OC1B свободны для скважности. Phase and Frequency Correct отличается от Phase Correct тем, что новые OCR1x и TOP применяются в нижней точке счёта — период и импульс остаются симметричными даже при смене частоты на ходу.

Регистры TCNT1, OCR1A/B и ICR1 16-битные, а шина данных 8-битная, поэтому обращение к ним идёт через общий временный регистр: при записи сначала старший байт, затем младший, при чтении — наоборот, сначала младший (раздел 16.3). Компилятор C соблюдает этот порядок сам; если 16-битный регистр трогают и основная программа, и прерывание, доступ в основной программе делают при запрещённых прерываниях.

Захват

Блок захвата копирует TCNT1 в ICR1 по фронту или спаду на выводе ICP1 (выбирает бит ICES1) и ставит флаг ICF1. Так измеряют период и длительность импульсов: разность двух захваченных значений, умноженная на период clk_T, — это время между событиями. Бит ICNC1 включает подавитель шума: событие засчитывается после четырёх одинаковых выборок подряд, ценой задержки на четыре такта. Пока ICR1 используется как TOP (режимы 8, 10, 12, 14), захват недоступен.

Timer2 и часовой кварц

Timer2 устроен как Timer0, но может считать от отдельного генератора на выводах TOSC1 и TOSC2 с кварцем 32,768 кГц — для этого устанавливают бит AS2 в регистре ASSR. Тогда таймер работает и в режиме Power-save, отсчитывая время, пока кристалл спит, — получаются часы реального времени с током около микроампера (подробности — в статье о режимах сна AVR). Частота ядра при этом должна быть больше учетверённой частоты кварца.

В асинхронном режиме запись в TCNT2, OCR2x и TCCR2x попадает сначала во временный регистр и переносится в рабочий через два фронта TOSC1; о завершении переноса говорят биты TCN2UB, OCR2xUB и TCR2xUB в ASSR. Перед засыпанием после такой записи нужно дождаться сброса этих битов, иначе прерывание по совпадению может не прийти и кристалл не проснётся (раздел 18.9). При переключении между синхронным и асинхронным тактированием регистры таймера могут испортиться — их перезаписывают после смены AS2.

Примеры на C

Прерывание раз в 1 мс на Timer1 в режиме CTC при 16 МГц: N = 8, OCR1A = 16 000 000 / (8 · 1000) − 1 = 1999.

#include <avr/interrupt.h>
#include <avr/io.h>

volatile uint32_t ms;

ISR(TIMER1_COMPA_vect)
{
    ms++;
}

void timer1_1ms(void)
{
    TCCR1A = 0;
    TCCR1B = (1 << WGM12) | (1 << CS11);   /* CTC (mode 4), clk/8 */
    OCR1A = 1999;
    TIMSK1 = (1 << OCIE1A);
    sei();
}

ШИМ 20 кГц на OC1A (PB1) в режиме 14: TOP = ICR1 = 16 000 000 / (1 · 20 000) − 1 = 799. В неинвертирующем Fast PWM выход находится в единице OCR1A + 1 тактов — недаром при OCR1A = 0 даташит описывает узкий импульс длиной в один такт, — поэтому для скважности 25 % OCR1A = 800 · 0,25 − 1 = 199.

void pwm_20khz(void)
{
    DDRB |= (1 << PB1);                                   /* OC1A as output */
    ICR1 = 799;                                           /* 20 kHz at 16 MHz */
    OCR1A = 199;                                          /* (199 + 1) / 800 = 25 % */
    TCCR1A = (1 << COM1A1) | (1 << WGM11);                /* non-inverting, mode 14 */
    TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS10);   /* clk/1 */
}

Сигнал для сервопривода — 50 Гц, импульс 1–2 мс — в том же режиме 14 с N = 8: такт таймера 0,5 мкс, ICR1 = 16 000 000 / (8 · 50) − 1 = 39 999, а импульс 1,5 мс — это 3000 тактов, то есть OCR1A = 2999.

void servo_50hz(void)
{
    DDRB |= (1 << PB1);
    ICR1 = 39999;                                         /* 20 ms period */
    OCR1A = 2999;                                         /* 1.5 ms pulse, center */
    TCCR1A = (1 << COM1A1) | (1 << WGM11);
    TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS11);   /* clk/8 */
}

Таймеры других AVR устроены так же, но число таймеров, каналов и их выводы различаются — их перечисляют страницы кристаллов в разделе AVR, например ATmega328P. Общие формулы ШИМ без привязки к микроконтроллеру — в калькуляторе ШИМ.

Частые вопросы

Как рассчитать частоту ШИМ AVR?

Для Fast PWM: f = f_clk / (N · (1 + TOP)), для Phase Correct PWM: f = f_clk / (2 · N · TOP), где N — предделитель, TOP — 0xFF у 8-битного режима или значение OCR0A, OCR1A, ICR1. Например, Timer0 в Fast PWM при 16 МГц и N = 64 даёт 16 000 000 / (64 · 256) ≈ 976,6 Гц, а Timer1 в режиме 14 с ICR1 = 799 и N = 1 — ровно 20 кГц.

Какой таймер AVR выбрать для задачи?

Для точных интервалов, длинных периодов и ШИМ с произвольной частотой — 16-битный Timer1: у него TOP можно задать регистром ICR1, оставив оба канала сравнения свободными, и есть захват. Для коротких периодов хватит 8-битных Timer0 и Timer2. Timer2 единственный умеет считать от часового кварца 32,768 кГц и будить кристалл из режима Power-save. В Arduino учтите: Timer0 занят millis() и delay(), Timer1 — библиотекой Servo, Timer2 — функцией tone().

Почему ШИМ AVR не выходит на вывод?

Сигнал сравнения появляется на выводе, только если в COMnx1:0 выбран режим вывода и вывод настроен на выход в регистре DDRx. Кроме того, таймер должен быть запущен — в CSn2:0 выбран предделитель, а не 000. У Timer0 и Timer2 переключение вывода по совпадению (COMnA = 01) в режимах ШИМ работает только для канала A и только при WGMn2 = 1.

Как читать и писать 16-битные регистры Timer1?

Через общий временный регистр: при записи сначала старший байт, затем младший, при чтении — сначала младший, затем старший. Компилятор C делает это сам, когда вы пишете TCNT1 = 0 или читаете ICR1. Если же регистр читается и в основной программе, и в прерывании, доступ в основной программе выполняют при запрещённых прерываниях, иначе обработчик испортит временный регистр.

Источники

  1. Microchip. ATmega48A/PA/88A/PA/168A/PA/328/P Data Sheet (DS40002061B) — гл. 15 — Timer/Counter0, таблицы 15-8 (WGM) и 15-9 (CS); гл. 16 — Timer/Counter1, доступ к 16-битным регистрам, захват, таблица 16-4; гл. 17 — предделители; гл. 18 — Timer/Counter2, асинхронный режим, таблица 18-9; назначение выводов OC0A–OC2B, ICP1, T0, T1
  2. Arduino. ArduinoCore-avr, коммит 11b9130, и библиотека Servo (github.com/arduino) — wiring.c — Timer0 в Fast PWM с делителем 64 и счёт millis(); Tone.cpp — tone() на Timer2 у Uno; Servo, src/avr/ServoTimers.h — Timer1 у ATmega328P

Нашли ошибку?