Top.Mail.Ru

Таймеры с точки зрения железячника.

Режим ШИМ (Широтно-Импульсная Модуляция).

Попробуем последовательно разобраться с таймерами, так как всё сразу понять проблематично. Начнём с вопроса - что такое ШИМ? ШИМ характерен тем, что частота сигнала постоянна, а коэффициент заполнения меняется. Не правда ли страшно звучит?

Итак, что такое частота? Это характеристика, которая показывает, как часто сигнал переходит из состояния "0", в состояние "1" за секунду (это для цифры, аналоговики молчать). Берём сферического коня в вакууме (так как сигнал мы ещё не получили, и осциллограф некуда подключать) и представляем, как предыдущая фраза у нас нарисуется на осциллографе:

Мы договорились, что период - это время от одной "1" до следующей, ещё их называют передним фронтом, а переход из "1"->"0" задним фронтом или спадом. Период обозначают буквой T, и равен он T = 1 / f, где f - частота сигнала. Зададимся получить частоту 1000 Герц. Тогда по формуле получаем период T = 1 / f  = 1 / 1000 = 0.001 сек или 1 миллисекунду.

Теперь второе страшное слово - заполнение:

На рисунке хорошо видно координатную сетку. И мы видим, что высокий уровень занимает две клетки, низкий уровень тоже две клетки - получается 50% времени у нас состояние "1" и 50% времени состояние "0". Таким образом заполнение у нас 50%.

В этом случае период у нас не изменился, а заполнение стало 1 / 4, т. е. 25%. Нууу, в общем, ничего страшного, теперь посмотрим структуру базового таймера, нас интересует только эта часть:

Что бы не смущала куча стрелок, нарисуем упрощённо:

Основные узлы:

  1. PSC - предварительный делитель тактовой частоты контроллера. Подаваемая частота зависит от того, к какой шине тактового генератора МК подключен данный таймер;
  2. ARR - регистр в который пишется, до какого числа будет считать счётчик CNT;
  3. CNT - счётчик;
  4. CCRx - регистр, с которым сравнивается значение счётчика CNT. Таких регистров столько, сколько каналов у таймера.

Сигналы:

  1. CK_PSC - тактовая частота, подаваемая с тактового генератора;
  2. CK_CNT - тактовая частота, которая вываливается из предделителя. Предназначена для тактирования счётчика CNT;
  3. OCx_REF - выход канала, на котором будет "1" или "0" в зависимости от того, что нам там насравнивал регистр CCRx.

Как всегда начнём с конца, с регистра CCRx.

Примечание: не забываем, что все отсчёты в МК идут с нуля, поэтому во все регистры пишутся значения на единицу меньше.

Вспомним, что коэффициент заполнения ШИМ мы считали в процентах. Допустим, зададимся диапазоном от 0 до 30, тогда 50% будут соответствовать значению CNT равному 14. Если диапазон от 0 до 999, тогда значение CNT при 25% будет равно 254, что увеличит плавность регулировки ШИМ. Мы же сейчас зададимся значениями 0-99 (удобно когда числа целые). Допустим, хотим получить коэффициент заполнения 25%, значит в регистр CCRx записываем цифру 24 (не забываем 25-1, вычитаем единицу).

Дальше, при запуске нашего таймера, происходит игра в разведчика. Причём эта игра зависит от нескольких параметров. Счётчик CNT у нас умеет считать "вверх", "вниз" и "вверх потом вниз" (последний режим разделяется на несколько подрежимов). Каждый режим задаётся в его настройках. Мы возьмём режим счёта вверх, т. е. счётчик CNT считает от 0 до значения в регистре ARR, потом сбрасывается в ноль.

Во время счёта CCRx внимательно следит за CNT (разведчик). В начале слежки, в зависимости от того, какой тип ШИМ выбран, на выходе OCx_REF выставляется определенный уровень (1-й тип - на выходе OCx_REF единица, в случае ШИМ режима №2 - на выходе ноль). CCRx ждёт, когда значение в счётчике CNT достигнет того числа, что записан в нём самом (в блокнотик заглядывает). При совпадении этих чисел он в режиме ШИМ1 сбрасывает выход OCx_REF в ноль, или выставляет в 1 в режиме ШИМ2.

Исходя из сказанного, если мы в CNT запишем 99, а в CCRx 24, то при достижении счётчиком CNT значения 24, CCRx изменит состояние выхода OCx_REF на противоположное, и оставит его таким, пока CNT не досчитает до 99. Далее весь цикл повторяется и мы получаем на выходе OCx_REF ШИМ сигнал с заданной скважностью:

Теперь нам осталось разобраться с тактированием самого счётчика CNT. Вот с этим всё просто - перед ним стоит предделитель PSC, какое число в него запишешь, на столько он и поделит частоту на входе, а результат подаст на CNT. Для расчёта принимаем, что мы должны получить ШИМ с частотой 1000 Гц с возможностью регулировки заполнения от 0 до 99.

Так как CNT у нас и так считает до 100, значит, чтобы получить частоту 1000 Гц, нам нужно подать на CNT частоту 1000 * 100 = 100 000 Гц. Значит значение для предделителя, например при входной тактовой частоте 168 МГц, будет 168000000 / 100000 = 1680. Не забываем вычесть единицу 1680 - 1 = 1679 - это то число, которое мы должны записать в предделитель PSC.

Примечание: Не забываем смотреть, какую частоту можно подать на таймер, который вы используете в данный момент.

Для этого загружаем CubeMX (если используем STM32), выставляем тактовую на максимум и смотрим что идёт на таймеры:

Для STM32F103C8T все таймеры тактируются от 72 МГц. Значит делитель для PSC будет равен 72000000 / 100000 - 1 = 720 - 1 = 719. Полностью код выкладывать не буду, сгенерить проект можно с помощью CubeMX, а потом добавить следующий код в main.c, но только перед циклом.

Исходим из данных для этого МК:

  1. Частота ШИМ = 1000 Гц
  2. Частота, подаваемая на таймер = 72 000 000 Гц
  3. Коэффициент заполнения равен 25, при диапазоне 1 - 100
RCC->APB2ENR  |= RCC_APB2ENR_TIM1EN;                 // Врубаем тактирование таймера
TIM1->PSC      =  720 - 1;                           // Снижаем частоту перед таймером до 100 000 Гц
TIM1->ARR      =  100 - 1;                           // Длительность цикла 1 мс или 100 тактов счётчика CNT
TIM1->CCR1     =  25 - 1;                            // Заполнение 25%
TIM1->CCMR1   |= (0b110 << TIM_CCMR1_OC1M_Pos);      // ШИМ 1-го типа
TIM1->CCER    |= TIM_CCER_CC1E;                      // Включаем выход
TIM1->BDTR    |= TIM_BDTR_MOE                        // Разрешаем всем каналам плеваться на выход 
                                                     // (такое есть только у таймеров с синфазным выходом)
TIM1->CR1     |= TIM_CR1_CEN;                        // Запускаем таймер

Мы должны получить на выходе то же, что нарисовано на предыдущем рисунке для режима ШИМ1. Чтобы запустить все каналы, можно сделать так:

RCC->APB2ENR  |= RCC_APB2ENR_TIM1EN;                 // Врубаем тактирование таймера
TIM1->PSC      =  720 - 1;                           // Снижаем частоту перед таймером до 100 000 Гц
TIM1->ARR      =  100 - 1;                           // Длительность цикла 1мс или 100 тактов счётчика CNT

TIM1->CCR1     =  25 - 1;                            // Заполнение 25%
TIM1->CCR2     =  40 - 1;                            // Заполнение 40%
TIM1->CCR3     =  50 - 1;                            // Заполнение 50%
TIM1->CCR4     =  75 - 1;                            // Заполнение 75%

TIM1->CCMR1   |= (0b110 << TIM_CCMR1_OC1M_Pos);      // ШИМ 1-го типа
TIM1->CCMR1   |= (0b110 << TIM_CCMR1_OC2M_Pos);      // ШИМ 1-го типа
TIM1->CCMR2   |= (0b110 << TIM_CCMR2_OC3M_Pos);      // ШИМ 1-го типа
TIM1->CCMR2   |= (0b110 << TIM_CCMR2_OC4M_Pos);      // ШИМ 1-го типа

TIM1->CCER    |= TIM_CCER_CC1E;                      // Включаем выход 1
TIM1->CCER    |= TIM_CCER_CC2E;                      // Включаем выход 2
TIM1->CCER    |= TIM_CCER_CC3E;                      // Включаем выход 3
TIM1->CCER    |= TIM_CCER_CC4E;                      // Включаем выход 4

TIM1->BDTR    |= TIM_BDTR_MOE                        // Разрешаем всем каналам плеваться на выход 
                                                     // (такое есть только у таймеров с синфазным выходом)
TIM1->CR1     |= TIM_CR1_CEN;                        // Запускаем таймер

Получим такую осциллограмму (масштаб не соблюдён):

Рис. 8

Самым неожиданным для многих будет то, что все каналы начинают работу одновременно (красная черта). И получить таким способом сигнал для управления трёхфазным двигателем, т. е. сигналы, сдвинутые на 120 градусов, возможно только с помощью хитрых манипуляций. Однако не всё потеряно - есть STM32Gxxx, у которых это возможно.  Есть правда один фокус, заменить строку:

TIM1->CCMR2   |= (0b110 << TIM_CCMR2_OC4M_Pos);      // ШИМ 1-го типа

на

TIM1->CCMR2   |= (0b111 << TIM_CCMR2_OC4M_Pos);      // ШИМ 2-го типа

Тогда получим сигнал такого типа:

Правда его можно получить ещё некоторыми способами, но это уже другой узел и в этой статье не рассматривается. И как всегда вишенку на торте: если посмотреть рисунок со структурной схемой таймера, то там нарисовано не до конца. В действительности выглядит так:

У регистров ARR и CCRx оказывается есть тень. Что это значит?

Всё просто. При записи значений в регистры они сразу попадают по месту назначения. Понятно, что при записи в начале инициализации таймера, это ничего не значит. Но мы же не хотим вечно давать на выходе ШИМ с заполнением 25%, мы же его запускали чтобы, допустим, двигателем управлять или сервоприводом. Значит мы должны менять значение в CCRx, а иногда и CNT в процессе работы.

Если во время работы таймера он будет где-то на полпути, допустим, отыграет только 50% того, что в него заложено, то, записав новое значение, мы добьёмся только сбоя в работе. Он бросит всё и начнёт работать опять от красной черты, как на рисунке, но уже с новыми значениями. И у нас будет срыв сигнала, что может сказаться на нагрузке или управляющей схеме, если они большой мощности, например, при управлении трёхфазным двигателем на 3 КВт. У вас какие-то транзисторы будут открыты, а перезапись счётчика вызовет одновременное открытие транзисторов в другом плече - фейерверк будет ещё тот.

Чтобы этого не происходило, есть специальные биты разрешения предзагрузки в регистрах CCMRx->OCxPE для каждого канала и CR1->ARPE для регистра ARR. Если их поставить в "1", то запись значений в ARR или CCRx не вызовет моментальной перезаписи регистров. Они как бы уйдут в тень и только после того, как цикл закончится, они выйдут из тени и займут законное место в соответствующих регистрах. Правда тут опять есть одно но - при подаче питания всё записанное в теневые регистры будет там и находиться, а в самих регистрах может оказаться мусор, который также может привести к непоправимым последствиям (в случае управления мощной нагрузкой). Чтобы этого не произошло, нужно подать команду "обновления" этих регистров через регистр и бит EGR->UG. После этого желательно почистить флаги в регистре SR и только после этого можно разрешать таймеру работать. Про ШИМ пока на сегодня на этом всё.

Подписаться
Уведомить о
guest

3 комментариев
Старые
Новые
Межтекстовые Отзывы
Посмотреть все комментарии
b707
b707
2 месяцев назад

Самым неожиданным для многих будет то, что все каналы начинают работу одновременно

А что в этом неожиданного? Счетчик-то у таймера один общий на все каналы, значит все каналы всегда обновляются синхронно.

3
0
Оставьте комментарий! Напишите, что думаете по поводу статьи.x