Skip to main content
sanju
Associate III
July 27, 2020
Solved

3 Phase sine wave generation

  • July 27, 2020
  • 15 replies
  • 6565 views

Hi all,

i am trying to generate a 3 phase sine wave with 120 degree phase shift by using stm32f030r8t6 nucleo board.

i can see the generated 3 phase sine wave on oscilloscope but the freq is not stable of the sine wave. it varies continuously from 40hz to 110 hz. please have a look on it and suggest me what can i do.

please find the attachment for code.

this is code of main.c file

#include "stm32f0xx.h"

#include "stm32f030x8.h"

#include "math.h"

void GPIO_Init(void);

void EXTI_Init(void);

void TIM1_Init(void);

void TIM1I_Init(void);

void UpdatePulse(void);

unsigned int i,i1=0,i2=0,i3=0,count,Ch1_DutyCycle=0,Ch2_DutyCycle=0,Ch3_DutyCycle=0,Ch1_Dir=0,Ch2_Dir=0,Ch3_Dir=0,Max_Duty=0,Start_Duty=0,Duty_Diff=0,Period=0;

unsigned int SinTable[100],SineFreq=0;

double x=0;

int main()

{

/****start PLL setting for system clock at 48Mhz using 8Mhz HSI****/

RCC->CFGR |= ((1<<19)|(1<<21)); // PLL Multification factor = 12

RCC->CFGR |= (1<<1); // PLL selected as system clock

RCC->CR |= (1<<24); // Enable / ON PLL

while((RCC->CR & (1<<25)) == 0); // Wait until PLL gets Ready

/****PLL settigs end here****/

Period = 1199; // 40khz i.e. 20 khz in center aligned mode

Max_Duty = 1080; // 90%

Start_Duty = 600; // 50%

Duty_Diff = (Max_Duty-Start_Duty);

// Generating sine table , here x is angle

x=0;

for(i=0;i<=24;i++)

{

x=x+0.062832;

SinTable[i] = Start_Duty+(Duty_Diff*(sin(x)));

}

for(i=25;i<=49;i++)

{

x=x+0.062832;

SinTable[i] = Start_Duty+(Duty_Diff*(sin(x)));

}

for(i=50;i<=74;i++)

{

x=x+0.062832;

SinTable[i] = Start_Duty+(Duty_Diff*(sin(x)));

}

for(i=75;i<=99;i++)

{

x=x+0.062832;

SinTable[i] = Start_Duty+(Duty_Diff*(sin(x)));

}

// maintaining 120 deg phase shift between 3 sine waves.

i1 = 33; // 120 degree W

i2 = 66; // 240 degree V

i3 = 99; // 0/360 degree U

GPIO_Init(); // gpio initialization

TIM1_Init(); // timer1 initialization

TIM1I_Init(); // timer 1 interrupt initialization

while(1) // infinite loop

{

}

}

void GPIO_Init()

{

RCC->AHBENR |= (1<<17); // Enable clock for GPIOA

RCC->AHBENR |= (1<<18); // Enable clock for GPIOB

GPIOA->MODER |= (1<<17); // PA8 in alternate function mode TIM1_CH1

GPIOA->OTYPER &= ~(1<<8); // PA8 as output push pull

GPIOA->OSPEEDR |= ((1<<16)|(1<<17)); // PA8 at high speed

GPIOA->PUPDR &= ~((1<<16)|(1<<17)); // PA8 no pull-up, no pull-down

GPIOA->AFR[1] |= (1<<1); // PA8 as TIM1_CH1 alternate function

GPIOA->MODER |= (1<<19); // PA9 in alternate function mode TIM1_CH2

GPIOA->OTYPER &= ~(1<<9); // PA9 as output push pull

GPIOA->OSPEEDR |= ((1<<18)|(1<<19)); // PA9 at high speed

GPIOA->PUPDR &= ~((1<<18)|(1<<19)); // PA9 no pull-up, no pull-down

GPIOA->AFR[1] |= (1<<5); // PA9 as TIM1_CH1 alternate function

GPIOA->MODER |= (1<<21); // PA10 in alternate function mode TIM1_CH3

GPIOA->OTYPER &= ~(1<<10); // PA10 as output push pull

GPIOA->OSPEEDR |= ((1<<20)|(1<<21)); // PA10 at high speed

GPIOA->PUPDR &= ~((1<<20)|(1<<21)); // PA10 no pull-up, no pull-down

GPIOA->AFR[1] |= (1<<9); // PA10 as TIM1_CH1 alternate function

GPIOA->MODER |= (1<<15); // PA7 in alternate function mode TIM1_CH1N

GPIOA->OTYPER &= ~(1<<7); // PA7 as output push pull

GPIOA->OSPEEDR |= ((1<<14)|(1<<15)); // PA7 at high speed

GPIOA->PUPDR &= ~((1<<14)|(1<<15)); // PA7 no pull-up, no pull-down

GPIOA->AFR[0] |= (1<<29); // PA7 as TIM1_CH1 alternate function

GPIOB->MODER |= (1<<1); // PB0 in alternate function mode TIM1_CH2N

GPIOB->OTYPER &= ~(1<<0); // PB0 as output push pull

GPIOB->OSPEEDR |= ((1<<0)|(1<<1)); // PB0 at high speed

GPIOB->PUPDR &= ~((1<<0)|(1<<1)); // PB0 no pull-up, no pull-down

GPIOB->AFR[0] |= (1<<1); // PB0 as TIM1_CH1 alternate function

GPIOB->MODER |= (1<<3); // PB1 in alternate function mode TIM1_CH3N

GPIOB->OTYPER &= ~(1<<1); // PB1 as output push pull

GPIOB->OSPEEDR |= ((1<<2)|(1<<3)); // PB1 at high speed

GPIOB->PUPDR &= ~((1<<2)|(1<<3)); // PB1 no pull-up, no pull-down

GPIOB->AFR[0] |= (1<<5); // PB1 as TIM1_CH1 alternate function

}

void TIM1_Init()

{

RCC->APB2ENR |= (1<<11); // Enable clock for TIM1

TIM1->PSC = 0;

TIM1->ARR = Period; // period

TIM1->CR1 |= ((1<<5)); // Center-aligned mode 1.

TIM1->CR1 |= ((1<<2)); // Only counter overflow/underflow generates an update interrupt

TIM1->DIER = 1; // Update interrupt enable

TIM1->RCR = 5; // update event after RCR+1 period

TIM1->CCMR1 |= ((1<<4)|(1<<5)|(1<<6)|(1<<3)); // TIM1_CH1 is set to pwm mode-2

TIM1->CCMR1 |= ((1<<12)|(1<<13)|(1<<14)|(1<<11)); // TIM1_CH2 is set to pwm mode-2

TIM1->CCMR2 |= ((1<<4)|(1<<5)|(1<<6)|(1<<3)); // TIM1_CH3 is set to pwm mode-2

Ch1_DutyCycle = SinTable[i1]; // 120 degree W @ start

Ch2_DutyCycle = SinTable[i2]; // 240 degree V @ start

Ch3_DutyCycle = SinTable[i3]; // 0/360 degree U @ start

TIM1->CCR1 = Ch1_DutyCycle; // Ch1 duty cycle

TIM1->CCR2 = Ch2_DutyCycle; // Ch2 duty cycle

TIM1->CCR3 = Ch3_DutyCycle; // Ch3 duty cycle

TIM1->CCER |= ((1<<0)|(1<<2)); // TIM1_CH1, TIM1_CH1N is Active, active high 

TIM1->CCER |= ((1<<4)|(1<<6)); // TIM1_CH2, TIM1_CH2N is Active, active high 

TIM1->CCER |= ((1<<8)|(1<<10)); // TIM1_CH3, TIM1_CH3N is Active, active high 

TIM1->BDTR |= (1<<15); // main output enable

TIM1->BDTR |= (1<<14); // automatic output enable

TIM1->BDTR |= ((1<<5)|(1<<4)|(1<<3)); // dead time

TIM1->EGR = 1; // Reinitialize the counter and generates an update of the registers

TIM1->CR1 |= 1; // TIM1 Counter enable

}

void TIM1I_Init()

{

 // interrupt enabling settings

__NVIC_SetPriority(TIM1_BRK_UP_TRG_COM_IRQn, 0);

__NVIC_EnableIRQ(TIM1_BRK_UP_TRG_COM_IRQn);

}

void ISR()

{

UpdatePulse();

TIM1->SR = 0; // clear update interrupt flag

}

void UpdatePulse()

{

i1++;

if(i1>99)

{

i1=0;

}

Ch1_DutyCycle = SinTable[i1];

i2++;

if(i2>99)

{

i2=0;

}

Ch2_DutyCycle = SinTable[i2];

i3++;

if(i3>99)

{

i3=0;

}

Ch3_DutyCycle = SinTable[i3];

TIM1->CCR1 = Ch1_DutyCycle; // duty cycle

TIM1->CCR2 = Ch2_DutyCycle; // duty cycle

TIM1->CCR3 = Ch3_DutyCycle; // duty cycle

}

this is code of it.c file

void ISR(void);

void TIM1_BRK_UP_TRG_COM_IRQHandler(void)

{

ISR();

}

This topic has been closed for replies.
Best answer by TDK

Hmm, I'm not sure. If ISR() is getting called at a constant rate I can't see any reason why the sine wave frequency would be changing.

15 replies

prain
Visitor II
July 27, 2020

There is a better way for doing what you need. Check the reference​ manual; multiple timers can be synchronized to achieve exact phase difference. Or you can use a timer in conjunction with DMA to bit bang the GPIO BSRR register.

sanju
sanjuAuthor
Associate III
July 28, 2020

Thanks for your reply.

i am getting almost correct phase shift of 120 deg. my problem is that freq of sine wave is not constant and i am not updating freq in program.

In current program freq is varying continuously.

sanju
sanjuAuthor
Associate III
August 10, 2020

@TDK​ 

Dear TDK,

Can you help me here to sort out this problem.

TDK
August 10, 2020

@prain​ 

DMA doesn't have access to GPIO on the STM32F0 series.

@sanju​ 

You can't generate sine waves with a timer. You can generate square waves with 120 degree shift. Use the timer in PWM mode, set it to toggle when compare is met (OCxM=0b011). Then you just need to update the ARR and CCRx registers appropriately.

"If you feel a post has answered your question, please click ""Accept as Solution""."
sanju
sanjuAuthor
Associate III
August 11, 2020

@TDK​ 

Yes, PWM can generate only square waves with desired duty cycle.

Here first i am calculating duty cycles values as per sine wave. and storing in array of size 100.

then initially i set timer1 ch1, ch2, ch3 at 120 phase shift duty cycle. and enabled the timer1 update interrupt.

my timer is set in center aligned mode. so i set rcr to odd value 5 so that every 6(5+1) pulses update interrupt generates. and in interrupt subroutine i updates the CCR!, CCR2, CCR3 values from array next index.

by this method (above programme) i can see generated 3 phase sine wave with 120 phase shift on oscilloscope .

but the issue which i am facing is that freq of generated sine wave is not stable it varies 50hz to 200hz. why it is happening even i am not changing the update interrupt rate (RCR) and ARR value. i have stuck at this point.

TDK
August 11, 2020

Okay, so duty cycle is varying between 0% and 100% over many timer updates to approximate a sine wave.

Can you measure the frequency your ISR is getting called? Is it varying between 50 Hz and 200Hz as well? It should be at a fixed rate.

Try in upcounting mode (as opposed to center aligned) to see if that's causing something odd.

"If you feel a post has answered your question, please click ""Accept as Solution""."
sanju
sanjuAuthor
Associate III
August 11, 2020

@TDK 

Okay, so duty cycle is varying between 0% and 100% over many timer updates to approximate a sine wave.

Yes​(10% to 90% it is my range forming a sine wave and generated sine wave is fine with 120 phase shift only problem is it is not maintaining stable freq.)

Can you measure the frequency your ISR is getting called? Is it varying between 50 Hz and 200Hz as well? It should be at a fixed rate

ISR freq is different from sine wave because for each cycle of sine wave I have 100 times timer1 update interrupt (for first 25 interrupt duty cycle goes to 50% to 90% then next 25 duty cycle goes from 90% to 50% then next 25 (50% to 10%) and then next 25 ( 10% to 50%)) to form (positive cycle then negative cycle) and and it repeats

for debugging purpose I have used a pin PA5 as it toggles whenever ISR is called

i will measure by tomorrow that at what freq it toggles, Yes it should be stable.

Try in upcounting mode (as opposed to center aligned) to see if that's causing something odd.

T​hanks for this suggestions I was also thinking about it to try.

I will try it by tomorrow.​

Thank you once again for your helping nature.​

TDK
TDKBest answer
August 12, 2020

Hmm, I'm not sure. If ISR() is getting called at a constant rate I can't see any reason why the sine wave frequency would be changing.

"If you feel a post has answered your question, please click ""Accept as Solution""."
waclawek.jan
Super User
August 13, 2020

In the ISR, check if TIMx_SR has the update flag set, and execute rest of ISR only if it has.

JW

sanju
sanjuAuthor
Associate III
August 13, 2020

@Community member​ 

Ok i will try it.

With your help in the past my problems was solved on stm32f0, Thanks for this reply.

Msd.1
Visitor II
August 15, 2020

Sanju, There's two point in your code:

First, define variables that used in interrupt routines as volatile.

Second, for a 50 Hz sine wave with 100 samples, your sampling rate is 5000 Hz. and in your pictures your ISR frequency is very low. And in reality you should have 15 to 20 PWM pulse for each sample to have a stable PWM , if you have 16 pulse for each sample you need 5000 x 16 = 80,000 PWM frequency. For test you reduce Period to 599 so PWM frequency is 80,000 Hz. Adjust samples value by divide them by 2, and test again. also send us PWM signal picture too if possible.

sanju
sanjuAuthor
Associate III
August 17, 2020

@Msd.1​ 

in my program whenever the ISR is called it toggles a pin PA5 (which is shown by green pulses in image) to debug the ISR calling freq.

So to be noted here that ISR toggles PA5 at each calling and update PWM duty cycle to form the sine wave.

to form a single pulse of sine wave there are 100 samples means ISR will be callled 100 times for one pulse of sine wave and PA5 will be toggled 100 times (100 times toggles makes 50 pulses).

so it means for every 50 pulses of PA5 (green color waveform) one pulse of sine wave will be generated.

if i notice from images there is two types of images (one for RCR=5 and Second one for RCR = 15)

CASE 1:

for RCR =5 freq is showing 3.32khz i.e. 3320hz.

so sine wave freq should be 3320/50 = 66.4hz

CASE 2:

for RCR =15 freq is showing 1.25khz i.e. 1250hz.

so sine wave freq should be 1250/50 = 25hz.

when this will be ok i will set program for different freq. my project is not to generate sine wave of 50hz it is to drive DC servo motor.

Note: PA5 is not in above program but it is in the conversation held already.

sanju
sanjuAuthor
Associate III
September 11, 2020

@Laurent Ca...​ 

Can you have a look at this.