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

в 10:31, , рубрики: C, stm32, stm32l152c-discovery, программирование микроконтроллеров

Не буду особо вдаваться в теорию, в сети много ресурсов где все очень подробно описано. Но когда дело доходит до практики понимаешь, что все намного сложнее. Используется микроконтроллер 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()
{
    initPWM();
    while(1)
    {
         
    }
}

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

Автор: kovaltsov

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js