Чуть ранее (в этой статье) мы рассмотрели в общих чертах таймеры в STM32 и написали простенькую программку. Теперь, как и обещал, поподробнее покопаем генерацию ШИМ при помощи все того же таймера TIM4. Итак, начинаем!
Время традиционной вставки: поскольку компания STMicroelectronics прекратила поддержку библиотеки SPL, которая использовалась в этом курсе, я создал новый, посвященный работе уже с новыми инструментами, так что буду рад видеть вас там - STM32CubeMx. Кроме того, вот глобальная рубрика по STM32, а также небольшая подборка статей с ШИМ и таймерами:
- ПИД-регулятор. Пример ПИД-регулятора температуры на STM32.
- STM32 и Timer Input Capture. Режим захвата сигнала.
Честно говоря, писать-то особо нечего. Думаю многие знают что такое ШИМ и с чем его едят, а если нет то об этом можно прочитать, например, в Википедии, так что нет, наверное, смысла отдельно описывать то, что уже многократно и хорошо описано...
Давайте создадим пример для генерации ШИМ. Просто сгенерировать такой сигнал не так интересно, так что давайте хоть немного усложним себе задачу. Будем генерировать ШИМ с разной длительностью импульса в зависимости от состояния кнопки. Если кнопка нажата – генерируем сигнал с периодом 2.5 мс и длительностью импульса 1.5 мс, а если кнопка не нажата – то 2.5 мс и 0.5 мс соответственно.
Так что создаем новый проект и пишем код. Для начала набор includ’ов:
#include "stm32f10x.h" #include "stm32f10x_rcc.h" #include "stm32f10x_gpio.h" #include "stm32f10x_tim.h"
Добавили все файлы, необходимые нам для работы. Объявим переменные:
#define TIM_CCER_CC3NE ((uint16_t)0x0400) #define TIMER_PRESCALER 720 #define EXT_TIM_PULSE 150 #define TIM_PULSE 50
- TIMER_PRESCALER мы уже упоминали - это предделитель частоты.
- EXT_TIM_PULSE – увеличенная длительность импульса (то есть при нажатой кнопке), аналогично TIM_PULSE – обычная длительность (кнопка не нажата).
Продолжаем:
uint16_t previousState; GPIO_InitTypeDef port; TIM_TimeBaseInitTypeDef timer; TIM_OCInitTypeDef timerPWM; uint16_t buttonPreviousState;
Просто переменные, которые нам понадобятся в проекте. Пока все просто! И вот наконец-то кое-что поинтереснее, а именно наша разросшаяся функция инициализации:
void initAll()
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
GPIO_StructInit(&port);
port.GPIO_Mode = GPIO_Mode_AF_PP;
port.GPIO_Pin = GPIO_Pin_6;
port.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOB, &port);
port.GPIO_Mode = GPIO_Mode_IPD;
port.GPIO_Pin = GPIO_Pin_3;
port.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOA, &port);
TIM_TimeBaseStructInit(&timer);
timer.TIM_Prescaler = TIMER_PRESCALER;
timer.TIM_Period = 250;
TIM_TimeBaseInit(TIM4, &timer);
TIM_OCStructInit(&timerPWM);
timerPWM.TIM_Pulse = 50;
timerPWM.TIM_OCMode = TIM_OCMode_PWM1;
timerPWM.TIM_OutputState = TIM_OutputState_Enable;
TIM_OC1Init(TIM4, &timerPWM);
}
Давайте прямо по строчкам смотреть, что тут происходит.
Вначале уже привычное включение тактирования необходимой периферии. Шестую ножку порта GPIOB настраиваем на работу в режиме альтернативной функции(!). Лезем в даташит на контроллер и видим, что альтернативной функцией у этого вывода является первый канал таймера TIM4. То что надо:
Вывод PA3 настраиваем на вход – там будет наша воображаемая кнопка. Итак, с инициализацией портов закончили, идем настраивать таймер. Поначалу все конфигурируем, как и в предыдущем проекте. А вот дальше кое-что новенькое:
TIM_OCStructInit(&timerPWM); timerPWM.TIM_Pulse = 50; timerPWM.TIM_OCMode = TIM_OCMode_PWM1;
