В статье описаны методы запуска и формирования PWM (Pulse-Width Modulation, или по-русски ШИМ) в микроконтроллерах AVR с помощью встроенных таймеров/счетчиков T/C0, T/C1 и T/C2. ШИМ широко применяется для цифро-аналогового преобразования, генерации сигналов произвольной формы, регулирования.
[Использование T/C0]
1. T/C0 является 8-восьмибитным, сам счетчик находится в регистре TCNT0. При переполнении (переход 0xFF->0x00) может генерироваться прерывание TIMER0_OVF (Timer/Counter0 Overflow, флаг TOV0). Это прерывание запрещается (маскируется) битом TOIE0 (бит 0 регистра TIMSK). Флаг TOV0 автоматически очищается при выполнении процедуры прерывания.
2. Для счетчика TCNT0 определены 3 специальных значения, по которым могут происходить определенные события:
BOTTOM == 0x00 MAX == 0xFF TOP == MAX или OCR0
3. TCNT0 может считать как вверх (инкрементироваться), так и вниз (декрементироваться). Это определяется выбранным режимом работы.
4. Для TCNT0 источник тактирования и его частота выбирается битами CS02:0 (биты 0..2 регистра TCCR0). Можно даже совсем запретить работу TCNT0 (записав в CS02:0 все нули).
5. Имеется 8-битный регистр OCR0 - регистр цифрового компаратора. При совпадении TCNT0 == OCR0 устанавливается Output Compare Flag (OCF0) и может генерироваться прерывание TIMER0_COMP (Timer/Counter0 Compare Match). Флаг OCF0, как водится, автоматически очищается при выполнении процедуры прерывания. Прерывание TIMER0_COMP запрещается (маскируется) битом OCIE0 (бит 1 регистра TIMSK). Кроме того, это событие может генерировать аппаратный PWM-сигнал на выводе OC0 процессора (ножка 4 корпуса DIP40, порт PB3). Сигнал Timer/Counter0 Compare Match используется генератором сигнала PWM.
6. Тип PWM выбирается битами Waveform Generation Mode - WGM01:0 (биты 3 и 6 регистра TCCR0 соответственно). От них также зависит, когда устанавливается флаг The Timer/Counter Overflow (TOV0). По этому флагу может генерироваться прерывание TIMER0_OVF. Всего существует 4 режима:
Mode
WGM01 (CTC0)
WGM00 (PWM0)
Режим T/C0
TOP
Изменение OCR0
Установка TOV0
0
0
0
Normal (не PWM)
0xFF
сразу
MAX
1
0
1
Phase Correct PWM
0xFF
TOP
BOTTOM
2
1
0
CTC (не PWM)
OCR0
сразу
MAX
3
1
1
Fast PWM
0xFF
BOTTOM
MAX
Примечание: имена бит CTC0 и PWM0 устарели. Вместо них используйте имена WGM11:0. Однако функционал и размещение этих бит совместим с предыдущей версией таймера.
7. Генератор PWM использует сигнал OCF0 (Timer/Counter0 Compare Match) и установки битов WGM01:0 и Compare Output mode (COM01:0, биты 5 и 4 регистра TCCR0), а также сигналы BOTTOM и TOP.
8. Бит Force Output Compare (FOC0) может принудительно изменить состояние ножки OC0 процессора, при этом счетчик не сбрасывается и прерывание не генерируется.
9. Состояние ножки 4 OC0 принудительно переключается в режим PWM, если установлен хотя бы один бит COM01:0, однако вход будет ножка 4 или выход, по прежнему определяется регистром DDR, поэтому регистр DDR надо обязательно настроить на выход перед включением режима PWM.
10. Режим Normal, не PWM (WGM01:0 = 0), влияние события Compare Match на ножку 4 OC0:
COM01
COM00
Описание
0
0
Нормальная работа порта PB3, OC0 отсоединен.
0
1
Переключение OC0 в противоположное состояние при событии compare match.
1
0
Сброс OC0 при событии compare match.
1
1
Установка OC0 при событии compare match.
Режим Normal самый простой - в этом режиме TCNT0 всегда считает в инкремент (вверх, в сторону увеличения), и не происходит очистка TCNT0. При переполнении (переход 0xFF->0x00) Timer/Counter Overflow Flag (TOV0) устанавливается в 1, и возможен вызов прерывания TIMER0_OVF. Флаг TOV0 работает как девятый бит с тем отличием, что он только устанавливается. Однако можно программно увеличить разрядность таймера, если использовать вызов прерывания TIMER0_OVF (при этом TOV0 автоматически очищается).
11. В режиме Clear Timer on Compare или CTC mode (WGM01:0 = 2), регистр OCR0 используется для изменения разрешения (диапазона счета) счетчика TCNT0 - счетчик сбрасывается в ноль, когда он равен OCR0. Таким образом, OCR содержит предельную величину TOP счетчика, что определяет его разрешающую способность. В момент достижения счетчиком величины TOP может генерироваться прерывание (по флагу OCF0). В обработчике прерываний можно изменить величину TOP в регистре OCR0. Нужно быть внимательным с малыми величинами OCR, иначе возможна потеря срабатывания компаратора, и счетчик продолжит считать до 0xFF. Режим CTC хорош для генерирования меандра, для этого выход OC0 включается в режим переключения (COM01:0 = 1). Максимальная частота будет равна fOC0 = fclk_IO/2, когда OCR0==0x00. Общая формула вычисления частоты fOC0 = fclk_IO / (2 * N * (1 + OCR0)), где N равен коэффициенту деления (prescale factor 1, 8, 64, 256, или 1024). Флаг TOV0 устанавливается MAX -> 0x00.
12. Fast PWM Mode, влияние события Compare Match на ножку 4 OC0:
COM01
COM00
Описание
0
0
Нормальная работа порта PB3, OC0 отсоединен.
0
1
Зарезервировано.
1
0
Сброс OC0 при событии compare match, установка при BOTTOM (PWM без инверсии).
1
1
Установка OC0 при событии compare match, сброс при BOTTOM (PWM с инверсией).
Этот режим генерирует в 2 раза бОльшую частоту, чем режим Phase Correct PWM Mode. Как работает Fast PWM Mode, понятно из таблицы. Остается добавить, что Timer/Counter Overflow Flag (TOV0) устанавливается, когда TCNT0 достигает MAX (0xFF). Общая формула вычисления частоты PWM:
fOC0PWM = fclk_IO / N * 256, где N равен коэффициенту деления (prescale factor 1, 8, 64, 256, или 1024).
13. Phase Correct PWM Mode, влияние события Compare Match на ножку 4 OC0:
COM01
COM00
Описание
0
0
Нормальная работа порта PB3, OC0 отсоединен.
0
1
Зарезервировано.
1
0
Сброс OC0 при событии compare match когда счет вверх, установка при счете вниз (PWM без инверсии).
1
1
Установка OC0 при событии compare match когда счет вверх, сброс при счете вниз (PWM с инверсией).
Режим отличается вдвое меньшей частотой PWM, но имеет более высокое качество формирования сигнала, вплоть до предельных значений OCR0 (0x00 и 0xFF). Режим хорошо подходит для управления моторами. В этом режиме TCNT0 сначала считает вверх, достигает 0xFF, потом начинает считать вниз, достигает 0x00, начинает считать вверх и процесс повторяется. Timer/Counter Overflow Flag (TOV0) устанавливается каждый раз, когда TCNT0 достигает BOTTOM (0x00). Общая формула вычисления частоты PWM:
fOC0PWM = fclk_IO / N * 510, где N равен коэффициенту деления (prescale factor 1, 8, 64, 256, или 1024).
14. При работе от внутренней тактовой частоты 1 МГц (значение по умолчанию) максимальная частота ШИМ в режиме fast PWM будет равна 1000000/256 = 3906,25 Гц.
Такая частота подойдет для медленно меняющихся процессов. Если поднять тактовую частоту до 8 МГц (для этого надо записать fuses CKSEL3..0 в значение 0100), то частота ШИМ будет 8000000/256 = 31250 Гц
Такая частота не слышима человеческим ухом, и может применяться для генерации звуков.
После такой настройки частота ШИМ на выходе будет 3906.25 Гц, скважность сигнала 50%.
[Использование T/C1]
1. Счетчик T/C1 значительно отличается от T/C0 хотя бы тем, что 16-битный, и имеет два регистра цифрового компаратора OCR1A и OCR1B и 2 аппаратных выхода PWM - OC1A и OC1B, и имеет 2 регистра управления TCCR1A и TCCR1B.
2. Для счетчика TCNT1 определены 3 специальных значения, по которым могут происходить определенные события:
BOTTOM == 0x0000 MAX == 0xFFFF TOP == 0x00FF, 0x01FF, 0x03FF или OCR1A или ICR1
3. К 16-битным регистрам (TCNT1, OCR1A, OCR1B и ICR1) нужны специальные методы обращения, читаются и записываются через 2 восьмибитные операции с участием временного 8-бит регистра (он общий для всех 16-бит регистров одного таймера). Через этот промежуточный регистр передается значение старшего байта, а обращение к младшему (отдельному для каждого 16-бит регистра) запускает выполнение 16-бит операции. Не все 16-бит регистры используют временный регистр для high-байта, например OCR1A и OCR1B.
Для 16-бит операций при чтении low-байт должен быть прочитан первым, а при записи high-байт должен быть записан первым. Пример обращения к регистру TCNT1 (обращение к OCR1A, OCR1B и ICR1 производится так же), предполагается, что прерывания не обращаются к TCNT1 (иначе они должны быть запрещены во время операции из этого примера):
;[Пример кода на ассемблере]...; Установка TCNT1 в 0x01FF.ldir17,0x01ldir16,0xFFoutTCNT1H,r17outTCNT1L,r16; Чтение TCNT1 в регистровую пару r17:r16inr16,TCNT1Linr17,TCNT1H...
//[Пример кода на языке C]
unsignedint i;
...
/* Установка TCNT1 в значение 0x01FF */
TCNT1 =0x1FF;
/* Чтение TCNT1 в i */
i = TCNT1;
...
Вот тот же пример с запретом прерываний:
;[Пример кода на ассемблере]
TIM16_ReadTCNT1:; Сохранение флага глобальных прерываний.inr18,SREG; Запрет прерываний.cli; Чтение TCNT1 в регистровую пару r17:r16inr16,TCNT1Linr17,TCNT1H; Восстановление флага глобальных прерываний.outSREG,r18ret
/* Сохранение флага глобальных прерываний. */
sreg = SREG;
/* Запрет прерываний. */
_CLI();
/* Read TCNT1 into i */
i = TCNT1;
/* Восстановление флага глобальных прерываний. */
SREG = sreg;
return i;
}
Если записывается сразу несколько 16-битных регистра с одинаковым high-байтом, то high-байт можно записать только 1 раз.
4. Как и в T/C0, флаги прерываний (ICF1, OCF1A, OCF1B, TOV1) находятся в регистре Timer Interrupt Flag Register (TIFR). Как и в T/C0, каждое из прерываний может маскироваться через Timer Interrupt Mask Register (TIMSK), биты маскирования TICIE1, OCIE1A, OCIE1B, TOIE1.
5. Тактирование T/C1 такое же, как и у T/C0. Источник тактов выбирается регистром Timer/Counter Control Register B (TCCR1B).
[Использование T/C2]
Почти все написанное ранее про T/C0 справедливо и для T/C2, поскольку отличаются они только в том, что T/C2 имеет более точный предделитель для тактовой частоты (немного по-другому работают биты управления предделителем CS22:0 - биты 2..3 регистра TCCR2), а также если T/C0 мог тактироваться от внешнего сигнала на T0, ножка 1, порт PB0, то T/C2 может тактироваться от дополнительного кварцевого генератора, так называемого External 32 kHz Watch Crystal, независимого от основной тактовой частоты (не от ножек XTAL2 выв. 12 и XTAL1 выв. 13, а от TOSC2 порт PC7 ножка 29 и TOSC1 порт PC6 ножка 28).
Предположим, что нужно формировать прямоугольный сигнал частотой несколько десятков килогерц, чтобы генерировать отрицательное напряжение питания операционного усилителя. Частота не критична, должна быть в диапазоне 20..200 кГц. Обработчик прерывания не нужен, формирование меандра должно происходить чисто аппаратно, без участия процессора. Вот шаги, которые предстоит выполнить:
Шаг 1, тактовая частота таймера. Нужно подобрать значение для бит настройки предделителя тактовой частоты. Это биты CS12, CS11, CS10, находятся они в регистре TCCR1B. Выбор делается на основе тактовой частоты микроконтроллера и нужной генерируемой частоты ШИМ на выходе. Т. е. этими битами нужно грубо выбрать тактовую частоту для таймера. Вот таблица значений этих бит, и коэффициенты деления прескалера.
CS12
CS11
CS10
Описание
0
0
0
Тактовый сигнал отключен (Timer1 остановлен).
0
0
1
clkI/O/1 (прескалер отключен, тактовая частота подается без деления на Timer1. В нашем примере тактовая частота получится 16 МГц).
0
1
0
clkI/O/8 (для нашего примера тактовая частота 2 МГц).
0
1
1
clkI/O/64 (для нашего примера тактовая частота 250 кГц).
1
0
0
clkI/O/256 (для нашего примера тактовая частота 62.5 кГц).
1
0
1
clkI/O/1024 (для нашего примера тактовая частота 15.625 кГц).
1
1
0
Timer1 в качестве тактов будет использовать внешний сигнал, подаваемый на ножку T1. Счетчик будет считать спады лог. уровня (переходы от 1 к 0).
1
1
1
Timer1 в качестве тактов будет использовать внешний сигнал, подаваемый на ножку T1. Счетчик будет считать фронты лог. уровня (переходы от 0 к 1).
Для нашего случая подойдет тактовая частота 2 МГц, поэтому из таблицы для настройки бит можно выбрать третью строчку. Нужно установить бит CS11, а биты CS12 и CS10 должны быть сброшены.
Примечание: выбор тактовой частоты по таблице делается с учетом того, с какой частотой будет переполняться таймер. Частота переполнения таймера, кроме частоты тактирования, также зависит от выбранного значения верхнего предела счета и от режима счета.
//Тактовая частота 2МГц:
TCCR1B = (1 << CS11);
Шаг 2, диапазон счета. Выбрать диапазон счета, т. е. в каких пределах будет считать счетчик. Выберем для счетчика 32 значения, предел счета будет определяться значением регистра ICR1. В этом случае таймер будет считать от 0 до 31, и эти циклы будут повторяться переполняться с частотой 2 МГц / 32 = 62.5 кГц. Если выбрать режим ШИМ с корректной фазой (PWM Phase Correct, об этом далее), то частота на выходе OC1A получится 31.25 кГц, что вполне подходит.
//считать счетчик будет до 32:
ICR1 =32;
Примечание: подробнее про пределы счета Timer1 см. [3], "BOTTOM, MAX, TOP: предельные значения счета".
Шаг 3, установка порога счета. Записать в регистр OCR1A пороговое значение, при достижении которого счетчиком будет меняться уровень на выходе OC1A. Для получения меандра это должно быть число 16:
//Сравниваемая со счетчиком величина для меандра:
OCR1A =16;
Шаг 4, как должен переключаться выход OC1A. Поведение выхода ШИМ OC1A зависит от выбранного режима ШИМ, и от состояния бит Compare Output Mode. Это биты COM1A1 и COM1A0 в регистре TCCR1A. В таблице ниже приведены значения бит для режимов Compare Output Mode, режимы Phase Correct PWM и Phase and Frequency Correct PWM.
COM1A1
COM1A0
Описание
0
0
Обычная работа порта как GPIO, функционал OCnx отключен.
0
1
WGM13 = 9 или 11: переключение OC1A при событии Compare Match, OC1B и OC1C отключены (обычная работа порта как GPIO). Для всех других установок WGM1 обычная работа порта (OCnx отключены).
1
0
Очистка OCnx на событии Compare Match при счете вверх. Установка на событии Compare Match при счете вниз.
1
1
Установка OCnx на событии Compare Match при счете вверх. Сброс на событии Compare Match при счете вниз.
В нашем случае режим будет Phase and Frequency Correct PWM, поэтому можно выбрать предпоследнюю или последнюю строки таблицы. Выберем предпоследнюю строку COM1A1COM1A0 == 10, так что нужно установить бит COM1A1 и сбросить бит COM1A0:
//OC1A очистится, когда счетчик досчитает до OCR1A:
TCCR1A = (1 << COM1A1);
Примечание: подробнее про значение бит COMnx1:0 см. [3], "Описание регистров 16-битных Timer/Counter".
Шаг 5, выбор режима ШИМ. В таблице ниже приведено значение бит Waveform Generation Mode, WGM13..WGM10, которые влияют на режим работы Timer1 и выбранный режим работы ШИМ. Эти биты находятся в регистрах TCCR1A (WGM11 и WGM10) и TCCR1B (WGM13 и WGM12). Описание бит Waveform Generation Mode(1):
Mode
WGM13
WGM12 (CTC1)
WGM11 (PWM11)
WGM10 (PWM10)
Режим работы
TOP
Обновл. OCR1A
TOV1 установится при значении
0
0
0
0
0
Normal
0xFFFF
Немедленно
MAX
1
0
0
0
1
Phase Correct PWM, 8 бит
0x00FF
TOP
BOTTOM
2
0
0
1
0
Phase Correct PWM, 9 бит
0x01FF
TOP
BOTTOM
3
0
0
1
1
Phase Correct PWM, 10 бит
0x03FF
TOP
BOTTOM
4
0
1
0
0
CTC
OCRnA
Немедленно
MAX
5
0
1
0
1
Fast PWM, 8 бит
0x00FF
BOTTOM
TOP
6
0
1
1
0
Fast PWM, 9 бит
0x01FF
BOTTOM
TOP
7
0
1
1
1
Fast PWM, 10 бит
0x03FF
BOTTOM
TOP
8
1
0
0
0
Phase and Frequency Correct
ICRn
BOTTOM
BOTTOM
9
1
0
0
1
Phase and Frequency Correct
OCRnA
BOTTOM
BOTTOM
10
1
0
1
0
Phase Correct PWM
ICRn
TOP
BOTTOM
11
1
0
1
1
Phase Correct PWM
OCRnA
TOP
BOTTOM
12
1
1
0
0
CTC
OCRnA
Немедленно
MAX
13
1
1
0
1
Зарезервировано
-
-
-
14
1
1
1
0
Fast PWM
ICRn
BOTTOM
TOP
15
1
1
1
1
Fast PWM
OCRnA
BOTTOM
TOP
Примечание (1): имена бит CTCn и PWMn1:0 устарели. Вместо них используйте имена WGMn2:0. Однако функционал и размещение этих бит совместим с предыдущей версией таймера.
Для нашего случая выберем режим Phase and Frequency Correct (строка в таблице, соответствующая Mode 8), значение для бит WGM1n == 1000b. Для этого режима нужно установить бит WGM13 в регистре TCCR1B (бит WGM12 останется сброшенным), биты WGM11 и WGM10 в регистре TCCR1A останутся сброшенными.
//Режим Phase and Frequency Correct:
TCCR1B |= (1 << WGM13);
Шаг 6, настройка выходного порта для вывода ШИМ. Чтобы выходной сигнал OC1A появился на выводе ножки микроконтроллера, нужно порт PD5 настроить как выход (по умолчанию он работает как вход):
//Настройка ножки OC1A как выход:
DDRD |= (1 << PD5);
Вот полный код процедуры на языке C, которая выполняет все описанные действия:
//ШИМ на ножке OC1A используется для генерации отрицательного напряжения питания
voidpwmInit (void)
{
//Тактовая частота 2МГц, частота ШИМ 32 кГц:
TCCR1B = (1 << CS11);
//Считать счетчик будет до 32:
ICR1 =32;
//Сравниваемая величина для меандра:
OCR1A =16;
//OC1A очистится, когда счетчик досчитает до OCR1A:
TCCR1A = (1 << COM1A1);
//Режим Phase and Frequency Correct:
TCCR1B |= (1 << WGM13);
//Настройка ножки OC1A как выход:
DDRD |= (1 << PD5);
}
Примечание: маска, которая попадает в регистр, генерируется операцией сдвига влево константы номера бита. Поскольку биты могут быть разбросаны по разным регистрам, будьте осторожны с операцией присваивания маски регистру. Первое присваивание должно быть сделано операцией =, эта операция сбросит все биты регистра, которые не установлены в маске. Последующие установки бит в этом регистре по маске должны делаться операцией чтение-операция-запись, в данном случае |= (чтение - операция ИЛИ - запись). Будьте внимательны!