В предыдущей статье про базовые таймеры, мы в очередной раз мигали светодиодами, а в этот раз пойдем гораздо дальше и попробуем вкурить как заставить контроллер STM32 генерировать ШИМ. Для этого нам придётся использовать один из таймеров общего назначения, ведь именно у них есть всё что для этого нужно. Весь остальной функционал этих таймеров конечно впечатляет, но в моей практике он пока не пригодился. Хотя возможно, что в будущем мне пригодятся такие полезные фичи как функция подсчёта внешних импульсов и возможность аппаратно обрабатывать повороты энкодера. Но пока займемся ШИМом. Есть вот такая схема из контроллера, трех резисторов и RGB светодиода которым мы будем управлять. Управление заключается в том, чтоб плавно зажечь и погасить каждый цвет. Разумеется можно взять три разных светодиода если нет RGB.
Мы подключили светодиод к этим выводам не случайно. Таймеры общего назначения могут генерировать ШИМ только на определённых ножках. Поскольку мы будем использовать таймер 2, то в нашем распоряжении есть 4 ноги (PA0-PA3). Чтоб таймер мог их использовать нужно разрешить это аж в двух местах: Настроить три ноги (PA1-PA3) как выход с альтернативной функцией и разрешить в настройках таймера дергать эти ноги для генерации ШИМа. Для этого нам потребуется регистр CCER
Если установить в единицу один из битов выделенных синим цветом, то таймеру будет позволено использовать для ШИМа соответствующую ногу. Из схемы видно, что нам потребуется установить биты CC2E, CC3E и CC4E. Теперь нам нужно настроить режим ШИМа: Прямой или инверсный (я не претендую на правильность терминологии). Разница вполне очевидна — при прямом ШИМе чем больше число в регистре сравнения — тем больше коэффициент заполнения ШИМа. В случае инверсного ШИМа все наоборот. Записали ноль в регистр сравнения — коэффициент заполнения 100%. Для выбора режима используются два регистра CCMR1 и CCMR2:
На настройку каждого канала выделяется аж по 8 бит! Но к счастью нам интересны только три бита OCxM[2:0] которые я отметил синим. То что отмечено серым — это те же самые биты но с другим названием, они используются если канал таймера работает в режиме захвата. Рассматривать все комбинации битов я не буду, так как большинство из них к ШИМу отношения не имеют. Нам потребуются только две комбинации бит:
OCxM2 | OCxM1 | OCxM0 | Режим |
1 | 1 | 0 | Прямой ШИМ |
1 | 1 | 1 | Инверсный ШИМ |
RGB светодиод у меня с общим катодом и поэтому я использую инверсный ШИМ. Таким образом нам следует установить все три бита OCxM для трех каналов на которых висят светодиоды. И это всё! Настройка ШИМа закончена, теперь нужно только запустить таймер установив бит CEN в регистре CR1. Для управления скважностью просто пишем число от 0x0000 до 0xFFFF в регистры CCRx, где x номер канала. Собственно следующий код реализует то что было задумано в начале этой статьи: Наращивает яркость светодиода а потом снижает её до нуля и переходит к следующему. Процесс повторяется бесконечно, смотрится красиво 🙂
#include "stm32f10x.h" #include "stm32f10x_gpio.h" #include "stm32f10x_rcc.h" void delay(void) { volatile uint32_t i; for (i=1; i != 0xF000; i++); } int main() { //Включем порт А RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE); //Включаем Таймер 2 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); GPIO_InitTypeDef PORT; // Настроим ноги со светодиодами на выход PORT.GPIO_Pin = (GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3); //Будем использовать альтернативный режим а не обычный GPIO PORT.GPIO_Mode = GPIO_Mode_AF_PP; PORT.GPIO_Speed = GPIO_Speed_2MHz; GPIO_Init(GPIOA, &PORT); //Разрешаем таймеру использовать ноги PA1,PA2,PA3 для ШИМа TIM2->CCER |= (TIM_CCER_CC2E|TIM_CCER_CC3E|TIM_CCER_CC4E); // Для всех трех каналов задаем инверсный ШИМ. TIM2->CCMR1|=(TIM_CCMR1_OC2M_0| TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_2); TIM2->CCMR2|=(TIM_CCMR2_OC3M_0 | TIM_CCMR2_OC3M_1 | TIM_CCMR2_OC3M_2 | TIM_CCMR2_OC4M_0 | TIM_CCMR2_OC4M_1 | TIM_CCMR2_OC4M_2); //Запускаем таймер! TIM2->CR1 |= TIM_CR1_CEN; //После этого пишем данные в TIM2->CCRx - и яркость светодиодов меняется uint32_t pwm_arr[]={0,0,6553,13107,19660,26214,32768, 39321,45875,52428,58982,65535}; uint8_t i; while(1) { for (i=1;i<=11;i++) { TIM2->CCR3=pwm_arr[i]; delay(); } for (i=11;i>=1;i--) { TIM2->CCR3=pwm_arr[i]; delay(); } for (i=1;i<=10;i++) { TIM2->CCR2=pwm_arr[i]; delay(); } for (i=11;i>=1;i--) { TIM2->CCR2=pwm_arr[i]; delay(); } for (i=1;i<=10;i++) { TIM2->CCR4=pwm_arr[i]; delay(); } for (i=11;i>=1;i--) { TIM2->CCR4=pwm_arr[i]; delay(); } } }
Надеюсь, что этот код поможет вам запустить ШИМ на STM32 а мне он поможет не забыть то, что я раскуривал почти пол дня. Не сложные вопросы можно писать ниже.