Top.Mail.Ru

STM32 с нуля. Timer. Использование таймеров для генерации ШИМ.

Чуть ранее (в этой статье) мы рассмотрели в общих чертах таймеры в STM32 и написали простенькую программку. Теперь, как и обещал, поподробнее покопаем генерацию ШИМ при помощи все того же таймера TIM4. Итак, начинаем!

Время традиционной вставки: поскольку компания STMicroelectronics прекратила поддержку библиотеки SPL, которая использовалась в этом курсе, я создал новый, посвященный работе уже с новыми инструментами, так что буду рад видеть вас там - STM32CubeMx. Кроме того, вот глобальная рубрика по STM32, а также небольшая подборка статей с ШИМ и таймерами:

Честно говоря, писать-то особо нечего. Думаю многие знают что такое ШИМ и с чем его едят, а если нет то об этом можно прочитать, например, в Википедии, так что нет, наверное, смысла отдельно описывать то, что уже многократно и хорошо описано...

Давайте создадим пример для генерации ШИМ. Просто сгенерировать такой сигнал не так интересно, так что давайте хоть немного усложним себе задачу. Будем генерировать ШИМ с разной длительностью импульса в зависимости от состояния кнопки. Если кнопка нажата – генерируем сигнал с периодом 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. То что надо:

Функции выводов STM32.

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

TIM_OCStructInit(&timerPWM);
timerPWM.TIM_Pulse = 50;
timerPWM.TIM_OCMode = TIM_OCMode_PWM1;