Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

Sine wave with PWM

Status
Not open for further replies.

sarah kandil

New Member
Hi,
I am trying to generate a sine wave using the PWM technique.
Using the below code with the Stellaris LM3S1968, the output was a square wave, probably because I didn't use an interrupt to update the value of the duty cycle.
Would anyone help?
//

unsigned char counter; // Current location in wave array
unsigned char wave[256] = { // Wave array, preset to values of sine
127,130,133,136,139,143,146,149,152,155,158,161,164,167,170,173,176,178,181,184,187,190,192,195,198,200,203,205,208,210,212,215,217,219,221,223,225,227,229,231,233,234,236,238,239,240,
242,243,244,245,247,248,249,249,250,251,252,252,253,253,253,254,254,254,254,254,254,254,253,253,253,252,252,251,250,249,249,248,247,245,244,243,242,240,239,238,236,234,233,231,229,227,225,223,
221,219,217,215,212,210,208,205,203,200,198,195,192,190,187,184,181,178,176,173,170,167,164,161,158,155,152,149,146,143,139,136,133,130,127,124,121,118,115,111,108,105,102,99,96,93,90,87,84,81,78,
76,73,70,67,64,62,59,56,54,51,49,46,44,42,39,37,35,33,31,29,27,25,23,21,20,18,16,15,14,12,11,10,9,7,6,5,5,4,3,2,2,1,1,1,0,0,0,0,0,0,0,1,1,1,2,2,3,4,5,5,6,7,9,10,11,12,14,15,16,18,20,21,23,25,27,29,31,
33,35,37,39,42,44,46,49,51,54,56,59,62,64,67,70,73,76,78,81,84,87,90,93,96,99,102,105,108,111,115,118,121,124

};

unsigned int i; // Used for 'for' loops.
unsigned long ulPeriod , dutyCycle;
int main(void)
{
SysCtlClockSet(SYSCTL_SYSDIV_5|SYSCTL_USE_PLL|
SYSCTL_XTAL_16MHZ|SYSCTL_OSC_MAIN);
SysCtlPeripheralEnable(SYSCTL_PERIPH_PWM0);
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOG);
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOH);
GPIOPinTypePWM(GPIO_PORTG_BASE, GPIO_PIN_2);
GPIOPinTypePWM(GPIO_PORTH_BASE, GPIO_PIN_0);
PWMGenConfigure(PWM0_BASE, PWM_GEN_0,
PWM_GEN_MODE_UP_DOWN | PWM_GEN_MODE_NO_SYNC);
PWMGenPeriodSet(PWM0_BASE, PWM_GEN_0, ulPeriod);
PWMGenConfigure(PWM0_BASE, PWM_GEN_1,
PWM_GEN_MODE_UP_DOWN | PWM_GEN_MODE_NO_SYNC);
PWMGenPeriodSet(PWM0_BASE, PWM_GEN_1, ulPeriod/2);
PWMPulseWidthSet(PWM0_BASE, PWM_OUT_0, dutyCycle);
PWMPulseWidthSet(PWM0_BASE, PWM_OUT_2, dutyCycle);
PWMOutputState(PWM0_BASE, PWM_OUT_0_BIT | PWM_OUT_2_BIT, true);
PWMGenEnable(PWM0_BASE, PWM_GEN_0);
PWMGenEnable(PWM0_BASE, PWM_GEN_1);


ulPeriod = 256;
dutyCycle = wave[counter];
counter += 1;
if ( counter == 255) // If counter is at the end of the array
{
counter = 0; // Reset counter
}
}
//
 
Your program runs only once without updating the duty cycle.
It needs a continous loop or an interrupt routine to update the duty cycle.
 
Your program runs only once without updating the duty cycle.
It needs a continous loop or an interrupt routine to update the duty cycle.
What happens is... as the pic resets they don't see what gives??? Once he notices that the ouput doesn't change THEN he will see that a forever loop is needed...
 
what I am trying to do now is to insert an ISR in the code, but I don't know how yet
Interrupt can do the job but you don't need one, a simple loop can do it. Your main function repeats all the functions of setting up the pic and after you increase the counter to next PWM you go back to start and setup the pic again.

Put all the functions that setup the pic at the start of main.
Add a loop (inside main) that includes the functions for updating the PWM only.
Add to the loop the routine you have for advancing the counter.

This way the program will setup the pic once and after that will enter a continues loop that changes the PWM to 256 different levels and then repeats itself.

I don't know what each function does exactly, I guess it will look like this;

int main(void)
{
ulPeriod = 256;
dutyCycle = 1;
SysCtlClockSet(SYSCTL_SYSDIV_5|SYSCTL_USE_PLL|
SYSCTL_XTAL_16MHZ|SYSCTL_OSC_MAIN);
SysCtlPeripheralEnable(SYSCTL_PERIPH_PWM0);
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOG);
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOH);
GPIOPinTypePWM(GPIO_PORTG_BASE, GPIO_PIN_2);
GPIOPinTypePWM(GPIO_PORTH_BASE, GPIO_PIN_0);
PWMGenConfigure(PWM0_BASE, PWM_GEN_0,
PWM_GEN_MODE_UP_DOWN | PWM_GEN_MODE_NO_SYNC);
PWMGenPeriodSet(PWM0_BASE, PWM_GEN_0, ulPeriod);
PWMGenConfigure(PWM0_BASE, PWM_GEN_1,
PWM_GEN_MODE_UP_DOWN | PWM_GEN_MODE_NO_SYNC);
PWMGenPeriodSet(PWM0_BASE, PWM_GEN_1, ulPeriod/2);
PWMPulseWidthSet(PWM0_BASE, PWM_OUT_0, dutyCycle);
PWMPulseWidthSet(PWM0_BASE, PWM_OUT_2, dutyCycle);
PWMOutputState(PWM0_BASE, PWM_OUT_0_BIT | PWM_OUT_2_BIT, true);
PWMGenEnable(PWM0_BASE, PWM_GEN_0);
PWMGenEnable(PWM0_BASE, PWM_GEN_1);

while(1){

PWMGenPeriodSet(PWM0_BASE, PWM_GEN_0, ulPeriod);
PWMGenConfigure(PWM0_BASE, PWM_GEN_1,
PWM_GEN_MODE_UP_DOWN | PWM_GEN_MODE_NO_SYNC);
PWMGenPeriodSet(PWM0_BASE, PWM_GEN_1, ulPeriod/2);
PWMPulseWidthSet(PWM0_BASE, PWM_OUT_0, dutyCycle);
PWMPulseWidthSet(PWM0_BASE, PWM_OUT_2, dutyCycle);
PWMOutputState(PWM0_BASE, PWM_OUT_0_BIT | PWM_OUT_2_BIT, true);
PWMGenEnable(PWM0_BASE, PWM_GEN_0);
PWMGenEnable(PWM0_BASE, PWM_GEN_1);

//ulPeriod = 256;
dutyCycle = wave[counter];
counter += 1;
if ( counter == 255) // If counter is at the end of the array
{
counter = 0; // Reset counter
}

}
}
 
Last edited:
Once he notices that the ouput doesn't change THEN he will see that a forever loop is needed...
Same as you I often refer to microcontroller as "he". I'm sure microcontrollers are based on the brain of a man and this is why they can do only one thing at a time.
 
I tested it yesterday in the lab and it worked perfectly !
One more thing need to be done, which is a periodic interrupt to trigger the loop for a certain period so that the sine wave will have a 50 Hz frequency.
Any Ideas or examples that I can follow?
Thanks all
 
Hello Sarah,
I'm working on very similar project in my university in Caen. I'm using AVR. To get 50Hz I added a delay of 50 us to the loop after increasing the counter. This way every time the duty cycle changes the processor waits before moving to the next duty cycle. Maybe in your M/C you need to adjust the delay until you get 50Hz.
In AVR the delay macro is: Delay_us(50);
I hope it will work or that you get help from others, moty22 isn't a member anymore he was kicked out of the forum for speaking rubbish.
 

Sorry Mirel
But I didn't get how would you get a 50 hz wave by delaying 50 us?
My English is weak this is why I don't explain well.
In your code the counter advances and sends the new PWM value to the output. Immediately after that the counter advances and send a new value to the PWM, this give you output as fast as the processor can give. If you add a delay before each time that you advance the counter the output of the PWM will sustain for that delay before it changes again. For 50 Hz your cycle is 20 ms, you have 256 values of PWM, it means the delay is 20 ms divided by 256, it is 78 us. In my microcontroller it takes about 20 us to do 1 loop so I added 56 us delay. If you add 50 us delay to the loop you will see how the output changes to lower frequency. You have to make the delay longer for lower frequency or shorter delay for higher output frequency. It is possible to calculate the exact delay but it is difficult because you have to know much about the microcontroller, it is easy to try different delay until it is ok.
Your compiler uses macros or functions for delay. If you look at the manual of the compiler you will see how to format a delay. another way is by a simple code:

unsigned int delay;
for(delay=0;delay<1000;delay++){}

Add this before the line
counter += 1;

You have to change the 1000 to another number until you get 50 Hz at the output.
I hope I helped.
 
Thanks a lot you explained it perfectly !
I used the SysCtlDelay(); function for my microcontroller using the calculations you gave me above
I will test the output again in the lab and update you
Thanks again
 
My English is weak this is why I don't explain well.
In your code the counter advances and sends the new PWM value to the output. Immediately after that the counter advances and send a new value to the PWM, this give you output as fast as the processor can give. If you add a delay before each time that you advance the counter the output of the PWM will sustain for that delay before it changes again. For 50 Hz your cycle is 20 ms, you have 256 values of PWM, it means the delay is 20 ms divided by 256, it is 78 us. In my microcontroller it takes about 20 us to do 1 loop so I added 56 us delay. If you add 50 us delay to the loop you will see how the output changes to lower frequency. You have to make the delay longer for lower frequency or shorter delay for higher output frequency. It is possible to calculate the exact delay but it is difficult because you have to know much about the microcontroller, it is easy to try different delay until it is ok.
Your compiler uses macros or functions for delay. If you look at the manual of the compiler you will see how to format a delay. another way is by a simple code:

unsigned int delay;
for(delay=0;delay<1000;delay++){}

Add this before the line
counter += 1;

You have to change the 1000 to another number until you get 50 Hz at the output.
I hope I helped.

So now I tested the code and the output of the pwm after the filter has that shape shown in the attached image
image.jpg



My questions now:
1- Why are those spikes formed and how can I get a smooth sine wave?
2- It is a control method so I need to add an interrupt to the code with a specific time/Frequency to adjust the frequency and amplitude of the wave but I don't know how to make this. (the delay works but whenever the code changes I still need to adjust the delay besides I need to get a more accurate frequency)

Thanks in advance
 
Actually that looks like the waveform is trying to go negative (below 0 pwm) and rolling over to max PWM.

Perhaps your min PWM needs to be error checked.
 
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top