Программирование AVR ATmega16 - PWM с помощью T/C0, T/C1, T/C2 Thu, March 28 2024  

Поделиться

Нашли опечатку?

Пожалуйста, сообщите об этом - просто выделите ошибочное слово или фразу и нажмите Shift Enter.

ATmega16 - PWM с помощью T/C0, T/C1, T/C2 Печать
Добавил(а) microsin   

В статье описаны методы запуска и формирования 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 Гц

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

15. Пример настройки PWM в режиме fast PWM:

//Скважность импульсов будет 50%:
OCR0 = 127;
//Очистка OC0 при compare match (1<<COM01),
// режим fast PWM mode ((1<<WGM01)|(1<<WGM00))
// clkI/O (1<<CS00):
TCCR0 = (1<<COM01)|(1<<WGM01)|(1<<WGM00)|(1<<CS00);
//PB3 (OC0) настроен как выход:
DDRB = 1<<DDB3;

После такой настройки частота ШИМ на выходе будет 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.
      ldi r17,0x01
      ldi r16,0xFF
      out TCNT1H,r17
      out TCNT1L,r16
      ; Чтение TCNT1 в регистровую пару r17:r16
      in r16,TCNT1L
      in r17,TCNT1H
      ...
//[Пример кода на языке C]
unsigned int i;
 
...
/* Установка TCNT1 в значение 0x01FF */
TCNT1 = 0x1FF;
/* Чтение TCNT1 в i */
i = TCNT1;
...

Вот тот же пример с запретом прерываний:

;[Пример кода на ассемблере]
TIM16_ReadTCNT1:
      ; Сохранение флага глобальных прерываний.
      in r18,SREG
      ; Запрет прерываний.
      cli
      ; Чтение TCNT1 в регистровую пару r17:r16
      in r16,TCNT1L
      in r17,TCNT1H
      ; Восстановление флага глобальных прерываний.
      out SREG,r18
      ret
//[Пример кода на языке C]
unsigned int TIM16_ReadTCNT1( void )
{
  unsigned char sreg;
  unsigned int i;
 
  /* Сохранение флага глобальных прерываний. */
  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 используется для генерации отрицательного напряжения питания
// операционного усилителя OP291 (микрофонный усилитель).
void pwmInit (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);
}

Примечание: маска, которая попадает в регистр, генерируется операцией сдвига влево константы номера бита. Поскольку биты могут быть разбросаны по разным регистрам, будьте осторожны с операцией присваивания маски регистру. Первое присваивание должно быть сделано операцией =, эта операция сбросит все биты регистра, которые не установлены в маске. Последующие установки бит в этом регистре по маске должны делаться операцией чтение-операция-запись, в данном случае |= (чтение - операция ИЛИ - запись). Будьте внимательны!

[Ссылки]

1. Использование 16-bit Timer/Counter1 для измерения и подсчета импульсов.
2. AVR130: настройка и использование таймеров AVR.
3. Таймеры-счетчики ATmega2560.
4. ATmega32: 16-битный таймер/счетчик 1.

 

Добавить комментарий


Защитный код
Обновить

Top of Page