Не буду особо вдаваться в теорию, в сети много ресурсов где все очень подробно описано. Но когда дело доходит до практики понимаешь, что все намного сложнее. Используется микроконтроллер stm32l152c-discovery. В статье будет описан процесс запуска ШИМ двух таймеров в одно и то же время (полная синхронизация):
А так же запуск с отставанием (на фото отставание в пол периода):
Создадим функцию для инициализации 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.
Настраиваем выходы в режим альтернативной фунции:
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