PWM Sinewave generation using output compare on dsPIC30F4011

Reader Manu Abraham wrote in with an interesting query about sinusoidal variation of PWM duty cycle using the output compare feature of the dsPIC30F4011. The 30F4011 has two output compare channels which can be used to very easily generate periodic rectangular waveforms (i.e. rectangular pulse trains) driven by a single timer. The two waveforms must have the same period, but within each period, the start and stop time of each channel’s pulse can be controlled independently.

Manu is effectively trying to generate two PWM signals, each of which has a sinusoidally varying duty cycle. The two modulating sine waves should be 180 degrees out of phase and the two waveforms should never be high simultaneously, since he’s driving a push-pull MOSFET circuit. Also, a short deadtime needs to be included in between one MOSFET turning off and the other turning on.

This is a rough sketch of the required code. The frequency of the PWM signals is 100kHz. The frequency of the modulating sine waves is 25Hz. I’m using an external 10MHz crystal, together with the internal 8x PLL multiplier. This gives an instruction cycle, Tcy = 50ns (i.e. Fcy = 20000000).

//
// dsPIC30F4011 Push-pull PWM sinewave with deadtime
// Written by Ted Burke
// Last updated 12-10-2012
//
// This example generates sinusoidally modulated PWM
// waveforms on both output compare channels of a
// dsPIC30F4011. The two modulating sine waves are
// both at 25Hz frequency, but are 180 degrees out
// of phase with each other. The PWM frequency is
// 100kHz (seems high to me!). These PWM waveforms
// are intended to drive a pair of MOSFETs in a
// push-pull circuit (e.g. in a power inverter),
// so a short deadtime is included between the end
// of one channel's pulse and the beginning of the
// other channel's pulse.
//

#include <xc.h>
#include <libpic30.h>
#include <math.h>

// Configuration settings
_FOSC(CSW_FSCM_OFF & XT_PLL8);	// Fosc=80MHz (10MHz crystal, 8xPLL)
_FWDT(WDT_OFF);					// Watchdog timer off
_FBORPOR(MCLR_DIS);				// Disable reset pin

int main()
{
	// Variables
	long Fcy = 20000000;	// Fcy = Fosc/4
	double Tsin = 0.04;		// Sine wave period in seconds (40ms -> 25Hz)
	int deadtime = 10;		// Deadtime in instruction cycles
	int Tpwm = 200;			// PWM period in instruction cycles
	double dc = 0.5;		// duty cycle of OC1, initially 50%

	// Make all port D pins outputs
	TRISD = 0;

	// Set OC channel 1 & 2 start and stop times
	OC1R = 0;
	OC1RS = dc * (Tpwm - 2 * deadtime);
	OC2R = OC1RS + deadtime;
	OC2RS = Tpwm - deadtime;

	// Set output compare mode for continuous pulses
	OC1CONbits.OCM = 0b101;
	OC2CONbits.OCM = 0b101;

	// Configure timer 2 (default timer for output compare)
	PR2 = Tpwm;			// 100kHz PWM frequency
	T2CONbits.TON = 1;	// Enable timer 2

	// Move through sine wave with 100 steps
	int n = 0, N=100;

	// Now vary the duty cycle sinusoidally
	while(1)
	{
		// delay for Tsin/N seconds
		__delay32(Tsin * Fcy / N);

		// Update duty cycle
		n = n + 1;
		if (n >= N) n = 0;
		dc = 0.5 * (1 + sin(n*6.2831853/N));
		if (dc < 0) dc = 0;

		// Set pulse start and stop times for both OC channels.
		// It seems that OCxRS needs to be at least OCxR + 2.
		OC1R = 0;
		OC1RS = 2 + dc * (Tpwm - 2 * deadtime);
		OC2R = OC1RS + deadtime;
		OC2RS = 2 + 2 + Tpwm - deadtime;
	}

	return 0;
}

Advertisements
This entry was posted in Uncategorized and tagged , , , , , , , , , , , , , , , , , , , . Bookmark the permalink.

10 Responses to PWM Sinewave generation using output compare on dsPIC30F4011

  1. manu says:

    Hi Ted,

    Thanks for the help. Some slight differences in the setup I have here. I am using MPLAB 7.5 rather than MPLAB X. I am using a dsPIC30F2010 instead of a 4011, the difference that I can programatically is that it lacks a PLL16 on the FRC input, while the FRC can run at a maximum of 7.5Mhz, I use a 10MHz XTAL oscillator and use the PLL8 which gives 80Mhz and 20MIPS respectively.

    So, I made these changes accordingly.

    #include
    #include
    #include
    #include

    // Configuration settings
    _FOSC(CSW_FSCM_OFF & XT_PLL8); /* Fosc=8x10MHz, Fcy=20MHz */
    _FWDT(WDT_OFF); // Watchdog timer off
    _FBORPOR(MCLR_DIS); // Disable reset pin

    int main()
    {
    long Fcy = 20000000; // ?
    ..
    }

    I guess you meant to change the CPU speed with Fcy. I connected 2 LED’s instead of the MOSFET’s to OC1 and OC2 to view the ouput. A Green LED is OC2 while a Red LED is connected to OC1. The 2 LED’s seem to alternate in a complementary mode at about 1Hz each (both of them thus amount to 2Hz as expected), while the Green LED shows a large brightness surge while it is just about to go OFF.

    I guess this should be most likely due to the change in the Clock speed that you have calculated and the one that I am using ?

    Regards,
    Manu

    • batchloaf says:

      Hi Manu,

      I was scribbling that code as I ran out the door of my office yesterday, so I didn’t have a chance to set the frequency to the correct value. I had it set low while I was writing the code because I too was using LEDs to see the modulation happening.

      I’ll have another look at it later and I’ll keep in mind that you’ll be using Fosc = 80MHz. Hence, I assume Tcy = 50ns?

      Ted

      • manu says:

        Hi Ted,

        Good to know that something else wasn’t wrong at my side. Great that we are both testing it the same way. 🙂

        I am using a 10Mhz XTAL oscillator with a PLL_8 = 80Mhz. At 4 cycles per instruction, Fcy = 80MHz/4 = 20MIPS; Tcy = 1/20,000,000 = 50nS.

        Regards,
        Manu

  2. manu says:

    Hi Ted,

    While playing with the code for a while; This thought crossed my mind: I think Tsin should be correleated to OCxR, OCxRS; Since If we increase Tsin, then OCxR, OCxRS still correspond to the previous precomputed values, rather than for the updated Tsin value.

    Regards,
    Manu

    • batchloaf says:

      Hi Manu,

      As it happened, I updated the code in the post to my finished version before I read your comment about the OCxR and OCxRS being related to Tsin, so it’s possible that what you said was true about the previous version of the code. However, I don’t think it’s the case for the code as it is now.

      The way I’ve written the code, the sinusoidal modulation of the PWM signals always happens in N steps (currently set as 100 steps). In other words, the duty cycle of the PWM signal changes 100 times during each cycle of the sine wave. What controls the frequency of the sine wave is the time delay in the while loop. That delay occurs between one update of the duty cycle and the next. The longer that delay, the longer it will take to complete one complete cycle of the sine wave. Basically, the period of the sine wave will be N times that time delay.

      The time delay in the while loop is calculated from Tsin, so that’s how Tsin controls the frequency of the sine wave. I checked the timing roughly on my system and it seemed to be working properly.

      Give my new code a try and see if it’s working for you. You’ll need to swap the “xc.h” header file at the top for whichever one you’re using (is it “p30F4011.h”?). I put a 10MHz crystal on my dsPIC and enabled the 8x PLL, so apart from the different header file things should hopefully be pretty much the same on both of our systems.

      Let me know how you get on.

      Ted

      • manu says:

        Hi Ted,

        The updated version looks better. 🙂 I modified:
        __delay32(Tsin * Fcy / N);
        to instead:
        __delay32(Fcy / (N * Fsin));

        so, that I am able to play a bit more with frequency variations faster, than with period variations.

        I played around a bit with the code. The visual output on the LED’s looked much exactly as what I wanted. Well, that was short lived.. I was quite happy with with what I saw and connected the MOSFET’s and a HF transformer with a lamp load. The load flickered much as though it was running at a lower frequency than expected. Impression was that something was wrong with the code/frequency. While playing with the variables/deadtime the MOSFET’s blew up, causing some reverse current into the microcontroller port, where I smelt a strong odour of a cake being baked…

        I am not too upset about the baked cake. I wanted to understand what really happened. Had a 2 more microcontrollers around me, disconnected the MOSFET and it’s drivers from the test setup and I was back to my original visual monitoring setup with LED’s. At some point while staring into the LED’s, had a better thought about listening to the audible 50Hz “mains hum”, with a headphone.

        I was able to hear the switching steps of the PWM as expected, but was able to notice an unusual distortion some place at the lower end (likely 3rd or 4th quadrants) of the waveform.With this in mind, wanted to view the waveform under test: without an oscilloscope nearby, had an idea to make use of the PC’s microphone input with some additional software to view the wave form with

        http://www.sillanumsoft.org/prod01.htm

        I was able to view the waveform with it. It looks thus:
        http://www.flickr.com/photos/79743192@N07/8084989467/

        Now that I understand what could be wrong.

        1. The frequency. The frequency counter shows about 15Hz instead of 25Hz (but this is the least one of the problems). I guess instead of a loop in a dead delay, maybe we can use a timer instead so that we can avoid a possible overhead of the CPU blindly loaded by the delay (the reasoning follows)
        2. The major issue that exists now is that each Output Compare should output is only 2 quadarants. (OC1 = 0+deadtime -to- PI, OC2=PI+deadtime -to- 2PI) This would look like a half wave rectified waveform on each of the OC’s. When both OC’s are put together, they would resemble a full wave rectified waveform (The transformer in push pull mode will have 2 quadrants of the waveform being pumped to it in a given cycle).
        3. After this incident, I feel the need that I need to monitor the current flowing through the MOSFET’s and the voltage at the output, so that I can control DC. I thought about using an ADC to setup to read the values at the peak of the PWM step. But ran into another thought that I would be disturbing the frequency by disturing that while loop and the delay within it. This made me to think that an additional timer could be of a better help in there.

        Regards,
        Manu

  3. manu says:

    Hi Ted,

    I just had some more additional thoughts: We are really interested only in 2 quadrants of the sinewave. OC1 follows Q1 and Q2; OC2 follows Q3 and Q4. Also it would be interesting to think that Q1 and Q2 are most likely a mirror image. In such a case, do we really need to calculate out the sine ? ie, maybe we can use a table instead for the first quadrant alone and reverse it and use for the 2nd quadrant ?

    An additional thought was that, additionally we could thus increase the number of steps in each cycle a bit more effeciently, with lesser CPU usage using a table instead ?

    Regards,
    Manu

  4. Van says:

    Hi Mr. Burke,

    Thank you so much for all your postings. I am trying to read them all, a lot of useful information for a beginner like me. At least I can use one of your examples for generating square wave with dsPIC. I saw another example on generate a sine wave using output compare, I haven’t tried it yet. I’d like to have a few questions: Do you need any additional hardware like DAC or R-2R ladder to output the sine wave ? Once again, thank you so much

    • batchloaf says:

      Hi Van,

      When you’re using PWM to output an analog waveform (either using the PWM module or output compare), the only external hardware you need is a simple RC low pass filter to remove the high frequency digital “chopping” of the PWM waveform and leave only the lower frequency component of the signal, i.e. the analog waveform you’re trying to generate. The component values of your RC filter depend on the frequency of your PWM waveform, and possibly the frequency of (or range of frequencies present in) the waveform you’re generating. In the above example, I’m generating a 25Hz sine wave using 100kHz PWM, so a filter cutoff frequency of about 100Hz might be appropriate – high enough to let the 25Hz signal through, but well below the PWM frequency, so that all the digital chopping gets completely smoothed out.

      Depending where the signal is going after the RC filter, you may need something like a unity gain buffer so that the filter can operate as intended without being loaded by whatever it’s connected to. However, if you’re driving something like a loudspeaker (i.e. an audio waveform) or an LED (varying it’s brightness), you probably don’t need to bother with the filter at all, because the high frequency of the PWM won’t have any obvious effect on the output.

      Ted

  5. Very useful method for generating PWM [output compare] because In my design I have used PWM interrupt method to update Sine value in PDC2 & PDC3 register, so again I can not use PWM1 channel for battery charging IGBT from Grid Charging beacuse PWM1 will disturb sine table array values so I’m following this method.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s