Хабрахабр

Синхронизация ШИМ на STM32

Не буду особо вдаваться в теорию, в сети много ресурсов где все очень подробно описано. Но когда дело доходит до практики понимаешь, что все намного сложнее. Используется микроконтроллер stm32l152c-discovery. В статье будет описан процесс запуска ШИМ двух таймеров в одно и то же время (полная синхронизация):

image

А так же запуск с отставанием (на фото отставание в пол периода):

image

Создадим функцию для инициализации initPWM, объявим в ней переменные для удобства

void initPWM()
{ const uint32_t Period = 20 - 1;//переменная периода const uint32_t prescaler = 1 - 1;//Предделитель //-1 потому что счет начинается с 0 const uint32_t pulse = 15;//Длина импульса
}

Для начала необходимо провести инициализацию структур для конфигурирования таймеров:

GPIO_InitTypeDef port; TIM_TimeBaseInitTypeDef timer; TIM_OCInitTypeDef timerPWM;

Далее активируем необходимую нам периферию. Будем использовать TIM3 и TIM4, просто потому что так захотелось:

RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);

GPIOB активируем, т.к. выходы таймеров сидят на этой шине PB5 и PB9. Эту информацию можно найти в юзер мануале к микроконтроллеру в таблице MCU pin description versus board function.

image

Настраиваем выходы в режим альтернативной фунции:

GPIO_StructInit(&port); port.GPIO_Mode = GPIO_Mode_AF;//Режим альтернативной функции, нужен для ШИМ port.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_9; port.GPIO_Speed = GPIO_Speed_2MHz;//Частота до 2Мгц port.GPIO_OType = GPIO_OType_PP;//режим работы "push-pull" "двухтактный выход" GPIO_Init(GPIOB, &port);
//активация альтернативной функции выхода PB5 GPIO_PinAFConfig(GPIOB,GPIO_PinSource5,GPIO_AF_TIM3);
//активация альтернативной функции выхода PB9 GPIO_PinAFConfig(GPIOB,GPIO_PinSource9,GPIO_AF_TIM4);

Настраиваем таймер в режим работы ШИМ:

TIM_TimeBaseStructInit(&timer); timer.TIM_ClockDivision = TIM_CKD_DIV1; timer.TIM_CounterMode = TIM_CounterMode_Up;//Счет вверх timer.TIM_Prescaler = prescaler;//Предделитель timer.TIM_Period = Period;//Период TIM_TimeBaseInit(TIM4, &timer);//Записываем настройки в оба таймера TIM_TimeBaseInit(TIM3, &timer); TIM_OCStructInit(&timerPWM); timerPWM.TIM_Pulse = pulse;//Длина импульса timerPWM.TIM_OCMode = TIM_OCMode_PWM1;//Выравнивание по границе timerPWM.TIM_OutputState = TIM_OutputState_Enable;//активируем выход timerPWM.TIM_OCPolarity = TIM_OCPolarity_High;//Импульс это 3.3В

Инициализируем нужные каналы для таймеров, каналы ищем в юзер мануале:

TIM_OC2Init(TIM3, &timerPWM); TIM_OC4Init(TIM4, &timerPWM);

А теперь самое важное, настройка синхронизации. Необходимо сделать один таймер мастером, другой слейвом:

//Настройка синхронизации TIM_SelectOutputTrigger(TIM4, TIM_TRGOSource_Enable);//Конфигурируем мастера TIM_SelectMasterSlaveMode(TIM4, TIM_MasterSlaveMode_Enable);//Задаем режим TIM_SelectInputTrigger(TIM3, TIM_TS_ITR3);//Конфигурируем подчиненного,
//используем триггер TIM4 канал 4, но нумерация с нуля, поэтому TIM_TS_ITR3 TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Gated);//режим стробирования

Если нам нужно сделать отставание, допустим на пол периода, то необходимо задать начальное значение счета подчиненного таймера равное половине периода. Для полной синхронизации счет начинаем с 0:

TIM3->CNT = 10;//Период 20, поэтому чтоб начать с половины пишем 10
//отставание в четверть это 5, в три четверти это 15

Активируем таймеры:

TIM_Cmd(TIM3, ENABLE);//Обязательно сначала слейв TIM_Cmd(TIM4, ENABLE);//А потом уже мастер

Выкладываю весь код полностью:

#include "stm32l1xx.h" //Для ШИМ
const uint32_t Period = 20 - 1;//переменная периода
const uint32_t prescaler = 1 - 1;
const uint32_t pulse = 15; void initPWM()
{ GPIO_InitTypeDef port; TIM_TimeBaseInitTypeDef timer; TIM_OCInitTypeDef timerPWM; //активация переферии RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); // настройка выхода PB5, PB9 GPIO_StructInit(&port); port.GPIO_Mode = GPIO_Mode_AF; port.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_9; port.GPIO_Speed = GPIO_Speed_2MHz; port.GPIO_OType = GPIO_OType_PP; GPIO_Init(GPIOB, &port); //активация альтернативной функции выхода PB5 GPIO_PinAFConfig(GPIOB,GPIO_PinSource5,GPIO_AF_TIM3);
//активация альтернативной функции выхода PB9 GPIO_PinAFConfig(GPIOB,GPIO_PinSource9,GPIO_AF_TIM4); //настройка таймера TIM_TimeBaseStructInit(&timer); timer.TIM_ClockDivision = TIM_CKD_DIV1; timer.TIM_CounterMode = TIM_CounterMode_Up; timer.TIM_Prescaler = prescaler; timer.TIM_Period = Period; TIM_TimeBaseInit(TIM4, &timer); TIM_TimeBaseInit(TIM3, &timer); TIM_OCStructInit(&timerPWM); timerPWM.TIM_Pulse = pulse; timerPWM.TIM_OCMode = TIM_OCMode_PWM1; timerPWM.TIM_OutputState = TIM_OutputState_Enable; timerPWM.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OC2Init(TIM3, &timerPWM); TIM_OC4Init(TIM4, &timerPWM);
//Настройка синхронизации TIM_SelectOutputTrigger(TIM4, TIM_TRGOSource_Enable);//Конфигурируем мастера TIM_SelectMasterSlaveMode(TIM4, TIM_MasterSlaveMode_Enable); TIM_SelectInputTrigger(TIM3, TIM_TS_ITR3);//Конфигурируем подчиненного TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Gated); TIM3->CNT = 5;
// TIM_Cmd(TIM4, ENABLE); TIM_Cmd(TIM3, ENABLE); TIM_Cmd(TIM4, ENABLE);
} int main()

}

Вот собственно и все. В моей задаче нужно было управлять двумя шаговыми двигателями. Дополнительно еще писался код для плавного разгона, начинал работу с более длинным периодом и постепенно его уменьшал функцией TIM_SetAutoreload.

Показать больше

Похожие публикации

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»