cancel
Showing results for 
Search instead for 
Did you mean: 

stm32f407 pwm control both dutycycle and phase shift

zarkscon
Associate II
Posted on December 14, 2014 at 11:26

I'm new with the stm32f407 discovery board and I'm trying to make 4 signals which I can control phase shift and dutycycle. The thing is, when i set my timer on PWM mode I can't control phase shift between channel_1 and channel_2. I know I have to use interrupts but I can't figure out what i should code inside it. I would be grateful if you could help me.

Here is my code, I use one interrupt which switches on/off a LED every some time (the delay is not well synchronized yet).

/* Includes ------------------------------------------------------------------*/
#include ''stm32f4xx.h''
#include ''stm32f4xx_gpio.h''
#include ''stm32f4xx_rcc.h''
#include ''misc.h''
#include ''stm32f4xx_tim.h''
#include ''stm32f4xx_usart.h''
#include ''delay.h''
/* Private typedef -----------------------------------------------------------*/
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
/* Private define ------------------------------------------------------------*/
#define frequency 42500
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
int Prescaler = 0;
int Period = 84000000 / frequency; // ~5KHz
int pulse_width;
/* Private function prototypes -----------------------------------------------*/
void GPIO_Config(void);
void PWM_Config(void);
void PWM_SetDC(int channel,int dutycycle);
void Delay(__IO int nCount);
void LED_Config(void);
/* Private functions ---------------------------------------------------------*/
void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus(TIM2, TIM_IT_CC1) != RESET)
{
Delay(10000000);
GPIO_ToggleBits(GPIOD, GPIO_Pin_15);
TIM_ClearITPendingBit(TIM2, TIM_IT_CC1);
}
}
/**
* Main program
*/
int main(void)
{
/* GPIO Configuration */
GPIO_Config();
LED_Config();
/* PWM Configuration */
PWM_Config();
PWM_SetDC(1,80);
PWM_SetDC(2,40);
PWM_SetDC(3,0);
PWM_SetDC(4,0);
while (1)
{
}
}
/**
* Configure the TIM2 Output Channels.
*/
void GPIO_Config(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
/* Enable the TIM2 global Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
/* TIM2 clock enable */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
/* GPIOA clock enable */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA , ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
/* GPIOC Configuration: TIM2 CH1 (PA0), TIM2 CH2 (PA1), TIM2 CH3 (PA2), TIM2 CH4 (PA3) */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP ;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* Connect TIM2 pins to AF2 */
GPIO_PinAFConfig(GPIOA, GPIO_PinSource0, GPIO_AF_TIM2);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource1, GPIO_AF_TIM2);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_TIM2);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_TIM2);
}
void PWM_Config(void)
{
/* Time base configuration */
TIM_TimeBaseStructure.TIM_Period = Period - 1;
TIM_TimeBaseStructure.TIM_Prescaler = 0;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
TIM_ARRPreloadConfig(TIM2, ENABLE);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
/* PWM1 Mode configuration: Channel1 */
TIM_OCInitStructure.TIM_Pulse = 0;
TIM_OC1Init(TIM2, &TIM_OCInitStructure);
TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Enable);
////////////////////////* TIM INTERRUPT enable *////////////////////////////
TIM_ITConfig(TIM2, TIM_IT_CC1, ENABLE);
////////////////////////////////////////////////////////////////////////////
/* PWM1 Mode configuration: Channel2 */
// TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 0;
TIM_OC2Init(TIM2, &TIM_OCInitStructure);
TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Enable);
/* PWM1 Mode configuration: Channel3 */
// TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 0;
TIM_OC3Init(TIM2, &TIM_OCInitStructure);
TIM_OC3PreloadConfig(TIM2, TIM_OCPreload_Enable);
/* PWM1 Mode configuration: Channel4 */
// TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 0;
TIM_OC4Init(TIM2, &TIM_OCInitStructure);
TIM_OC4PreloadConfig(TIM2, TIM_OCPreload_Enable);
/* TIM2 enable counter */
TIM_Cmd(TIM2, ENABLE);
}
void PWM_SetDC(int channel,int dutycycle)
{
if (dutycycle <= 100 && dutycycle >= 0)
{
pulse_width=(Period*dutycycle)/100;
if (channel == 1)
{
TIM2->CCR1 = pulse_width;
}
else if (channel == 2)
{
TIM2->CCR2 = pulse_width;
}
else if (channel == 3)
{
TIM2->CCR3 = pulse_width;
}
else
{
TIM2->CCR4 = pulse_width;
}
}
}
void LED_Config(void)
{
/* GPIOD Peripheral clock enable */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
/* Configure PD12, PD13, PD14 and PD15 in output push-pull mode */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13| GPIO_Pin_14| GPIO_Pin_15;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOD, &GPIO_InitStructure);
}
/* Delay Function.
* nCount:specifies the Delay time length.
*/
void Delay(__IO int nCount)
{
while(nCount--)
{
}
}

#discovery #stm32f4
5 REPLIES 5
Posted on December 14, 2014 at 16:11

Well, one approach to placing the edges at different phases is to use Toggle mode. Left alone it will function at 50/50 duty, or you can baby sit each edge at an interrupt by advancing/retarding the next CCRx value.

Posted an example previously to the forum.
Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Posted on December 14, 2014 at 16:24

https://my.st.com/public/STe2ecommunities/mcu/Lists/STM32Discovery/flat.aspx?RootFolder=/public/STe2ecommunities/mcu/Lists/STM32Discovery/%5bHELP%5d%20STM32F4%20generate%20PWM%203%20channel%20shift%20phase%20120%20degree%21&FolderCTID=0x01200200770978C69A1141439FE559EB459D75800084C20D8867EAD444A5987...

[DEAD LINK /public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/Flat.aspx?RootFolder=/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/quadrature%20output%20-%20STM32F407&FolderCTID=0x01200200770978C69A1141439FE559EB459D7580009C4E14902C3CDE46A77F0FFD06506F5B&currentviews=325]https://my.st.com/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/Flat.aspx?RootFolder=%2Fpublic%2FSTe2ecommunities%2Fmcu%2FLists%2Fcortex_mx_stm32%2Fquadrature%20output%20-%20STM32F407&FolderCTID=0x01200200770978C69A1141439FE559EB459D7580009C4E14902C3CDE46A77F0FFD06506F5B¤tviews=325

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
zarkscon
Associate II
Posted on December 14, 2014 at 20:56

Thanks a lot for your reply. I've seen your posts before but still can't figure out what to code inside the interrupt. I changed the timer on toggle mode and experimentally found the delay I was looking for between ch_1 and ch_2 which is a value of 980, aprox 250ns.

/* Includes ------------------------------------------------------------------*/
#include ''stm32f4xx.h''
#include ''stm32f4xx_gpio.h''
#include ''stm32f4xx_rcc.h''
#include ''misc.h''
#include ''stm32f4xx_tim.h''
#include ''stm32f4xx_usart.h''
#include ''delay.h''
/* Private typedef -----------------------------------------------------------*/
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
/* Private define ------------------------------------------------------------*/
#define frequency 42000
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
int Prescaler = 0;
int Period = 42000000 / frequency; // ~42KHz
int pulse_width;
/* Private function prototypes -----------------------------------------------*/
void GPIO_Config(void);
void PWM_Config(void);
void PWM_SetDC(int channel,int dutycycle);
void Delay(__IO int nCount);
void LED_Config(void);
/* Private functions ---------------------------------------------------------*/
void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus(TIM2, TIM_IT_CC1) != RESET)
{
TIM_ClearITPendingBit(TIM2, TIM_IT_CC1);
}
}
/**
* Main program
*/
int main(void)
{
/* GPIO Configuration */
GPIO_Config();
LED_Config();
/* PWM Configuration */
PWM_Config();
// PWM_SetDC(1,80);
// PWM_SetDC(2,40);
// PWM_SetDC(3,0);
// PWM_SetDC(4,0);
while (1)
{
}
}
/**
* Configure the TIM2 Output Channels.
*/
void GPIO_Config(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
/* Enable the TIM2 global Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
/* TIM2 clock enable */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
/* GPIOA clock enable */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA , ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
/* GPIOC Configuration: TIM2 CH1 (PA0), TIM2 CH2 (PA1), TIM2 CH3 (PA2), TIM2 CH4 (PA3) */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP ;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* Connect TIM2 pins to AF2 */
GPIO_PinAFConfig(GPIOA, GPIO_PinSource0, GPIO_AF_TIM2);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource1, GPIO_AF_TIM2);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_TIM2);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_TIM2);
}
void PWM_Config(void)
{
/* Time base configuration */
TIM_TimeBaseStructure.TIM_Period = Period - 1;
TIM_TimeBaseStructure.TIM_Prescaler = Prescaler;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
TIM_ARRPreloadConfig(TIM2, ENABLE);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
/* PWM1 Mode configuration: Channel1 */
TIM_OCInitStructure.TIM_Pulse = 0 + (980); // 250ns
TIM_OC1Init(TIM2, &TIM_OCInitStructure);
TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Enable);
////////////////////////* TIM INTERRUPT enable *////////////////////////////
TIM_ITConfig(TIM2, TIM_IT_CC1, ENABLE);
////////////////////////////////////////////////////////////////////////////
/* PWM1 Mode configuration: Channel2 */
TIM_OCInitStructure.TIM_Pulse = 0 ;
TIM_OC2Init(TIM2, &TIM_OCInitStructure);
TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Enable);
/* PWM1 Mode configuration: Channel3 */
TIM_OCInitStructure.TIM_Pulse = 0;
TIM_OC3Init(TIM2, &TIM_OCInitStructure);
TIM_OC3PreloadConfig(TIM2, TIM_OCPreload_Enable);
/* PWM1 Mode configuration: Channel4 */
TIM_OCInitStructure.TIM_Pulse = 0;
TIM_OC4Init(TIM2, &TIM_OCInitStructure);
TIM_OC4PreloadConfig(TIM2, TIM_OCPreload_Enable);
/* TIM2 enable counter */
TIM_Cmd(TIM2, ENABLE);
}
void PWM_SetDC(int channel,int dutycycle)
{
if (dutycycle <= 100 && dutycycle >= 0)
{
pulse_width=(Period*dutycycle)/100;
if (channel == 1)
{
TIM2->CCR1 = pulse_width;
}
else if (channel == 2)
{
TIM2->CCR2 = pulse_width;
}
else if (channel == 3)
{
TIM2->CCR3 = pulse_width;
}
else
{
TIM2->CCR4 = pulse_width;
}
}
}
void LED_Config(void)
{
/* GPIOD Peripheral clock enable */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
/* Configure PD12, PD13, PD14 and PD15 in output push-pull mode */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13| GPIO_Pin_14| GPIO_Pin_15;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOD, &GPIO_InitStructure);
}
/* Delay Function.
* nCount:specifies the Delay time length.
*/
void Delay(__IO int nCount)
{
while(nCount--)
{
}
}

Posted on December 14, 2014 at 23:41

The thing to understand is there is a single counting element, you are trying to set the compare to future values in this time line.

For non 50/50 duty you have to actively place the phase edges.

// You want a long period for the timebase, you are advancing the compare
// point, and it's the ticks of the high and low duty time which determine
// the output frequency, NOT the update period of the timers timebase.
void TIM2_IRQHandler(void)
{
static int x = 0;
if (TIM_GetITStatus(TIM2, TIM_IT_CC1) != RESET) // Validate source
{ 
TIM_ClearITPendingBit(TIM2, TIM_IT_CC1); // Clear first
if (x)
TIM2->CCR1 = (TIM2->CCR1 + (Period / 8)) % Period; // Duty A
else
TIM2->CCR1 = (TIM2->CCR1 + (Period / 4)) % Period; // Duty B
x ^= 1;
}
}

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
zarkscon
Associate II
Posted on December 22, 2014 at 20:24

Hello clive and thanks for your reply, it might've been a long time but I had exams.

I liked your code though it wasn't exactly what I meant. Here is the oscilloscope picture https://drive.google.com/file/d/0B4pu-9T2Fws1UUdvUW1WM3FhRG8/view?usp=sharing I changed the code a bit like this:

void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus(TIM2, TIM_IT_CC1) != RESET)
{
static int x = 0;
if (TIM_GetITStatus(TIM2, TIM_IT_CC1) != RESET) // Validate source
{
TIM_ClearITPendingBit(TIM2, TIM_IT_CC1); // Clear first
if (x)
{
TIM2->CCR1 = (TIM2->CCR1 + (Period / 2)) % Period; // Duty A
}
else
{
x ^= 1;
}
}
}
}

and got this oscilloscope result https://drive.google.com/file/d/0B4pu-9T2Fws1Y0hfTkdWSDZ3bXc/view?usp=sharing I still don't know if it's achievable by using one timer. Well to sum up, what I want to achieve is something like this https://drive.google.com/file/d/0B4pu-9T2Fws1N1huZERkWU9vRnc/view?usp=sharing The first 4 pulses will be produced by one timer (TIM2) and the last 4 by a a second timer (TIM5)after synchronizing them. Again thanks a lot for your reply, it was helpfull!