Широтно-импульсная модуляция в микроконтроллерах STM32F1 реализована через таймеры общего назначения TIM1, TIM2, TIM3. К каждому из этих таймеров можно «подцепить» до четырех выходных каналов. Принцип работы ШИМ очень прост:
1) таймер тактируется от своей шины через Prescaler (делитель частоты) и считает, допустим, «вверх».
2) Как только счетчик таймера достигает значения равного значению регистра ARR (Auto-reload register), то он (счетчик) перезагружается, или, простыми словами, обнуляется и считает заново.
3) У каждого канала есть регистр CCR (Capture/compare register). Когда значение счетчика меньше CCR, на канале PWM (на выходе) высокий уровень. Когда значение счетчика равно CCR или больше, то на канале низкий уровень.
4) Работа таймеров, и, следовательно ШИМ, не занимает процессорного времени, они работают независимо друг от друга.
В этом уроке мы задействуем аж 7 каналов ШИМ на разных таймерах. На рисунке показано, как бы это выглядело в Cube.

В моем случае я переназначил расположение выводов выходных каналов TIM2 и TIM3 через регистр AFIO->MAPR. Возможно вам это пригодится, если вы тоже захотите перекинуть ноги ШИМ выходов с позиций по умолчанию.

В предыдущих уроках этого цикла я показывал, как конфигурирую линии GPIO для ШИМ, но не заострял на этом внимания. Я настраиваю все GPIO сразу для всей используемой периферии, не разнося эти настройки по разным функциям.
RCC->APB2ENR |= RCC_APB2ENR_IOPBEN; // Разрешить тактирование GPIOB
__NOP();
// PB7 - AF SDA, PB6 - AF SCL (OpenDrain CNF=11, MODE=11) ; PB5 - in (CNF0=01 MODE0=00); PB4,PB3,PB1 - AF PWM (CNF=10, MODE=11); PB2 - out (CNF1=00 MODE1=01), PB0 - analog
GPIOB->CRL = 0xFF4BB1B0; // 1111 1111 0100 1011 1011 0001 1011 0000
// PB15, PB12 - out (CNF1=00 MODE1=01); PB14, PB13, PB9, PB8 - in (CNF0=01 MODE0=00); PB11, PB10 - AF PWM (CNF=10, MODE=11)
GPIOB->CRH = 0x1441BB44; // 0001 0100 0100 0001 1011 1011 0100 0100
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN; // Включить тактирование GPIOA
__NOP();
// PA8 - AF PWM (CNF=10, MODE=11), PA9 - AF TX (CNF=10, MODE=11), PA10-PA12 Floating inputs (CNF0=01 MODE0=00), PA13-PA14 Debug (CNF0=01 MODE0=00), PA15 - AF PWM (CNF=10, MODE=11)
GPIOA->CRH = 0xB44444BB; // 1011 0100 0100 0100 0100 0100 1011 1011
Нужные пины нужных портов настроены как альтернативные выходы (CNF = 01, MODE = 11).
Таймеры TIM2 и TIM3 находятся на шине APB1 (медленная, у меня работает на 32 МГц), а таймер TIM1 на APB2 (быстрая, у меня работает на 64 МГц).
Традиционно мы разрешаем тактирование желаемой периферии. Указываем период перезагрузки, указываем режим работы ШИМ, разрешаем работу таймера и разрешаем выходные каналы.
// Разрешение тактирования таймеров
RCC->APB1ENR |= RCC_APB1ENR_TIM2EN; __NOP();
RCC->APB1ENR |= RCC_APB1ENR_TIM3EN; __NOP();
RCC->APB2ENR |= RCC_APB2ENR_TIM1EN; __NOP();
// TIM2 реле
TIM2->ARR = RELAY_TURN_ON_PWM_VALUE; // Период 3000+1 тиков (это 10663 кГц)
// Установить режим ШИМ №1 (OC1M = 110) для всех каналов
TIM2->CCMR1 |= ((TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1) | (TIM_CCMR1_OC2M_2 | TIM_CCMR1_OC2M_1));
TIM2->CCMR2 |= ((TIM_CCMR2_OC3M_2 | TIM_CCMR2_OC3M_1) | (TIM_CCMR2_OC4M_2 | TIM_CCMR2_OC4M_1));
TIM2->CR1 |= TIM_CR1_CEN; // Разрешить счет
TIM2->CCER |= (TIM_CCER_CC1E | TIM_CCER_CC2E | TIM_CCER_CC3E | TIM_CCER_CC4E); // Все выходы разблокируем
// TIM3 реле
TIM3->ARR = RELAY_TURN_ON_PWM_VALUE;
// Режим ШИМ №1 для каналов 1 и 4
TIM3->CCMR1 |= (TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1);
TIM3->CCMR2 |= (TIM_CCMR2_OC4M_2 | TIM_CCMR2_OC4M_1);
TIM3->CR1 |= TIM_CR1_CEN; // Разрешить счет
TIM3->CCER |= (TIM_CCER_CC1E | TIM_CCER_CC4E); // Разрешить выходы 1 и 4
// TIM1 зуммер, только канал 1
TIM1->ARR = 64000000U / 2400U - 1; // Задаю частоту 2400 Гц
TIM1->BDTR = TIM_BDTR_MOE | TIM_BDTR_BKP; // Разрешить основной выход
TIM1->CCMR1 = (TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1); // Режим ШИМ 1
TIM1->CR1 |= TIM_CR1_CEN; // Разрешить счет
TIM1->CCER |= TIM_CCER_CC1E; // Разрешить выход 1
В дальнейшем остается лишь указывать период модуляции через регистры CCRx.
Допустим, у меня есть 4 ШИМ канала на TIM2. И я хочу задать Duty cycle для канала 1 — 10%, 2 — 20%, 3 — 30%, 4 — 40%.
На TIM2 ARR = 3000 (Auto-reload register), а тактируется таймер частотой 32 000 000 Гц. Значит, 10663 раз в секунду таймер будет перезагружаться. Для того чтобы ширина импульса на канале 1 была равна 10%, нужно регистру TIM2->CCR1 присвоить 1/10 от ARR (3000 / 10 = 300).
TIM2->CCR1 = TIM2->ARR / 100 * 10;
аналогично поступим с другими каналами
TIM2->CCR1 = TIM2->ARR / 100 * 10;
TIM2->CCR2 = TIM2->ARR / 100 * 20;
TIM2->CCR3 = TIM2->ARR / 100 * 30;
TIM2->CCR4 = TIM2->ARR / 100 * 40;
По такой вот аналогии можно управлять и другими каналами других таймеров.
Обсудить эту статью в сообществе в Telegram
Подпишитесь на канал, где я делюсь своими знаниями и практическими советами

