Using a dsPIC30F4011 to generating 4 PWM signals with equal duty cycles but at 90 degree phase increments

In a recent comment on one of my blog posts, Saptarshi De posed an interesting problem: How can the dsPIC30F4011 be used to generate four PWM signals of equal (but variable) duty cycle at 90 degree phase increments? Saptarshi wants to control a 4-phase interleaved boost converter and he supplied an illustration similar to the following to show what he requires:


The required PWM frequency is 50kHz and the four PWM signals must have equal duty cycle, but that duty cycle is variable. Saptarshi didn’t specify maximum and minimum values for the duty cycle, so I worked on the assumption that it can vary all the way from 0-100%.

At first, I was stumped. The most obvious approaches all have seemed to have show-stopping snags:

  • The dsPIC30F PWM module does most of what’s needed, but sadly only provides three channels. Each channel has two outputs, so it may somehow be possible to coax two of the channels to produce complementary output with appropriate deadtime to yield the required four PWM signals, but I couldn’t figure out how.
  • The Output Compare module provides four channels which can be used to generate PWM signals, but each of those channels must use either Timer 2 or Timer 3 as its timebase, which imposes some major limitations.

Ultimately, I opted for a variation of the second approach above using the Output Compare channels. Unfortunately, my solution requires that two of the channels be inverted externally, for example using a CMOS logic IC. If the duty cycle range is more constrained, this can be avoided, but for arbitrary duty cycle anywhere between 0% and 100%, I couldn’t work out how to do it without externally inverting two of the signals.

My solution is very similar to what I did previously to generate two out of phase PWM signals having identical duty cycle.

My approach is as follows:

  • The dsPIC uses the internal fast RC oscillator with 16x PLL multiplier so that it runs at 30 MIPS. Fcy = 30e6 and Tcy = 33.33ns.
  • Timer 2 and Timer 3 are configured with the same period of 600 * Tcy = 20us.
  • TMR2 is initialised to 150 and TMR3 is initialised to 0. The result is that when the timers are enabled, TMR2 leads TMR3 by a quarter cycle.
  • PWM1 is obtained from OC1 which uses Timer 2 as its timebase.
  • PWM2 is obtained from OC2 which uses Timer 3 as its timebase (lagging OC1 by 25% of a cycle).
  • PWM3 is obtained by inverting OC3 which uses Timer 2 as its timebase. The pulses on OC3 become the gaps between the pulses in PWM3.
  • PWM4 is obtained by inverting OC4 which uses Timer 3 as its timebase. The pulses on OC4 become the gaps between the pulses in PWM4.
// This dsPIC30F4011 example program generates four
// interleaved PWM signals to control a 4-phase
// interleaved boost circuit.
// Written by Ted Burke - last updated 5-3-2015

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

// Configuration settings
_FOSC(CSW_FSCM_OFF & FRC_PLL16); // Fosc=16x7.5MHz, Fcy=30MHz
_FWDT(WDT_OFF);                  // Watchdog timer off
_FBORPOR(MCLR_DIS);              // Disable reset pin

int main()
    // Configure RD0, RD1, RD2, RD3 as digital outputs.
    // (Not sure if this is required when Output Compare is used)
    TRISD = 0b1111111111110000;

    // Set period and duty cycle
    float dutycycle = 0.2;      // 20% duty cycle for this example
    int pulse, space, period;   // pulse width, space width, period
    period = 600;               // f_m = 30e6 / 600 = 50 kHz
    pulse = dutycycle * period; // width of PWM pulses
    space = period - pulse;     // gap between PWM pulses

    // Configure Timers 2 & 3
    PR2 = period - 1;      // Set Timer 2 period for 50kHz
    PR3 = period - 1;      // Set Timer 3 period for 50kHz
    T2CONbits.TCKPS = 0;   // 1:1 prescale
    T3CONbits.TCKPS = 0;   // 1:1 prescale
    TMR2 = period / 4;     // Timer 2 leads Timer 3 by 25% of period.
    TMR3 = 1;              // Timer 3 lags Timer 2 by 25% of period. Give it a "head start" of 1 because Timer3 is enabled just after Timer2

    // Select timer for each channel
    OC1CONbits.OCTSEL = 0; // OC1 driven by Timer 2
    OC2CONbits.OCTSEL = 1; // OC2 driven by Timer 3
    OC3CONbits.OCTSEL = 0; // OC3 driven by Timer 2
    OC4CONbits.OCTSEL = 1; // OC4 driven by Timer 3

    // Output continuous pulses on all OC channels
    OC1CONbits.OCM = 0b101;
    OC2CONbits.OCM = 0b101;
    OC3CONbits.OCM = 0b101;
    OC4CONbits.OCM = 0b101;

    // Set OC1 to output continuous pulses of the desired width.
    // The pulses are positioned midway through the TMR2 up-count.
    OC1R = space / 2;       // pulse start time
    OC1RS = OC1R + pulse;   // pulse end time

    // Set OC2 to output continuous pulses of the desired width.
    // The pulses are positioned midway through the TMR3 up-count.
    OC2R = space / 2;       // pulse start time
    OC2RS = OC2R + pulse;   // pulse end time

    // Set OC3 to output continuous pulses. These will be inverted
    // to become the gaps between the pulses and vice versa. The
    // width of these pulses is therefore set to the width of the
    // gap between the final pulses in PWM3.
    OC3R = pulse / 2;       // pulse start time (start of gap in PWM3)
    OC3RS = OC3R + space;   // pulse end time (end of gap in PWM3)

    // Set OC4 to output continuous pulses. These will be inverted
    // to become the gaps between the pulses and vice versa. The
    // width of these pulses is therefore set to the width of the
    // gap between the final pulses in PWM4.
    OC4R = pulse / 2;       // pulse start time (start of gap in PWM4)
    OC4RS = OC4R + space;   // pulse end time (end of gap in PWM4)

    // Enable Timers 2 & 3
    // It might be better to use some inline assembly language here
    // to ensure that the delay between the two timers being enabled
    // is very short and that we know exactly how many instruction
    // cycles "head start" Timer 3 should get to make the two timers
    // exactly 90 degrees out of phase.
    T2CONbits.TON = 1; // Enable Timer 2
    T3CONbits.TON = 1; // Enable Timer 3

    // Now just let the Output Compare module to the work

    return 0;

I saved the program as “main.c” and compiled it using Microchip’s free XC16 C compiler, with the following simple build script:

xc16-gcc main.c -mcpu=30F4011 -Wl,--script=p30F4011.gld
if errorlevel 0 xc16-bin2hex a.out

My test circuit is shown below:



[ Download editable Inkscape SVG version (same SVG file as earlier illustration) ]

I didn’t have an inverter IC available when I was doing this test, so I just cobbled together a couple of crappy NPN transistor inverters (I used BC237 transistors). These don’t respond fast enough for the system to work at 50 kHz, so I temporarily reduced the dsPIC clock speed by a factor of 4 for this experiment.

I tested the circuit using a two channel digital oscilloscope, so I wasn’t able to view all four signals simultaneously. Instead, I displayed PWM1 on scope channel 2 (the blue signal in each of the photos below) and then used scope channel 1 to view PWM2, PWM3 and PWM4 one at a time so that I could verify the phase shift of each signal relative to PWM1.

The photo below shows the signals PWM1 (blue) and PWM2 (yellow).

The photo below shows the signals PWM1 (blue) and PWM3 (yellow). Note that PWM3 is obtained by inverting the signal from OC3.

The photo below shows the signals PWM1 (blue) and PWM4 (yellow). Note that PWM4 is obtained by inverting the signal from OC4.

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

18 Responses to Using a dsPIC30F4011 to generating 4 PWM signals with equal duty cycles but at 90 degree phase increments

  1. Saptarshi De says:

    Hi sir,
    Thanks a lot sir for the effort and the code. The pwm pulses are perfect and yes it does the trick. You are awesome sir.

    • batchloaf says:

      Hi Saptarshi,

      Great, I’m glad to hear that was useful. If you’re using the code for your boost converter, just be careful when you change the OCxR and OCxRS values for each of the four channels. If you change the duty cycle at just the wrong moment, you can cause a glitch in the output where a channel stays high (or low) for a complete cycle. For exampe, if TMR2 is counting up and currently has a value of, let’s say, 430, and then you change OC1RS from 440 to 420 the output compare will never match OC1RS that cycle, so the channel will be stuck high until the next cycle.

      I’m still thinking about what’s the best way to do this reliably. I may post an update in the coming days.


  2. Saptarshi De says:

    Hi sir, hope you are doing well. Sir, i am having some problem with the change notification pin of dspic30f4011. Actually i am using it for BLDC control, however i was able to develop the logic using change notification pin but the dspic30f4011 has no response for any change in state of change notification pin. Sir can you give me a demo program or tutorial on how to use change notification pin.
    The Code-
    #include “p30F4011.h”
    #define FCY 10000000 // xtal = 5.0Mhz; PLLx8
    void DelayNmSec(unsigned int N);
    void InitMCPWM(void);
    unsigned int HallValue;

    ////************************************************** RB 3 4 5 E0 E1 E2 E3 E4 E5
    ////TRUTH TABLE to load OVDCON register depending upon the ///*********************************************************0/1 0/1 0/1 : 0 0 0 0 0 0
    //// inputs from Change notification pin RB 3 4 5 :
    ////PWMs are generated on PORTE 0 0 1 : 0 1 1 0 0 0
    //// 0 1 0 : 1 0 0 1 0 0
    ////********************************************* 0 1 1 : 1 0 0 0 0 1
    //// 1 0 0 : 0 1 0 0 1 0
    //// 1 0 1 : 0 1 1 0 0 0
    //// 1 1 0 : 0 0 0 1 1 0

    unsigned int StateLoTableAntiClk[] = {0x0000, 0x0024, 0x0009, 0x0021,
    0x0012, 0x0006, 0x0018, 0x0000};
    void __attribute__((interrupt, no_auto_psv)) _CNInterrupt (void)
    IFS0bits.CNIF = 0; // clear flag
    HallValue = PORTB & 0x0038; // mask RB3,4 & 5
    HallValue = HallValue >> 3; // shift right 3 times
    OVDCON = StateLoTableAntiClk[HallValue];

    int main(void)
    TRISB = 0x0038; //Enabling port B as input
    CNEN1 = 0x00E0; // Change Notification on CN 5,6 and 7 enabled
    CNPU1 = 0x00E0; // enable internal pullup resistances
    LATE = 0x0000; //Clearing E PORT
    TRISE = 0xFFC0; // PWMs are outputs
    IFS0bits.CNIF = 0; // clear Change notification interrupt CNIF
    IEC0bits.CNIE = 1; // enable CN interrupt
    PWMCON1 = 0x0777;
    { // wait till key is released

    HallValue = PORTB & 0x0038; // mask RB3,4 & 5
    HallValue = HallValue >> 3; // shift right to get value 1, 2 … 6
    OVDCON = StateLoTableAntiClk[HallValue]; // Load the overide control register
    // enable PWM outputs
    } // end of while (1)

    void InitMCPWM(void)
    PTPER = 999;
    PWMCON1 = 0x0700; // PWM in free running mode
    PWMCON1 = 0x0777; // PWM in independent mode & configuring PWM on PORTE registers
    OVDCON = 0x0000; // allow control using OVD
    PDC1 = 500; // init PWM 1, 2 and 3 to 100
    PDC2 = 500;
    PDC3 = 500;
    PTCON = 0x8000; // start PWM

  3. Chetan says:

    Hi Sir.
    Can you please help me in generating two out of phase pwm with frequency of 40khz and 0.6 duty cycle. Two signals are 180 degrees out of phase.
    Please help i want to use it for my Boost converter

  4. An interesting article, but isn’t a dsPIC overkill for this. Is it just easier to code than bit manipulation ? I am looking for a 100kHz, 3-phase system, with an external SPI or IIC channel to change the duty cycle (0 – 50%) and to send o/p V&I back to a controller (Arduino).

    • batchloaf says:

      Hi Shane,

      The dsPIC30F4011 may not be the lowest priced microcontroller out there, but it’s still reasonably cheap and cheerful (5 euro approx?) so I don’t really think it’s overkill for this. For a robust 5V-friendly micro in a DIL package, this seems like a convenient option to me.

      When you say “easier to code than bit manipulation”, I’m guessing you’re referring to the fact that I used the dsPIC’s Output Compare module rather than just bit banging? If so, then yes and no. You can certainly use straightforward bit banging in a simple while loop (or whatever) to generate signals similar to those above. However, precise timing is difficult since the execution time of all the instructions you carry out needs to be taken into account. When you use the Output Compare module (or the PWM module), it’s much easier to control pulses durations in exact numbers of instruction cycles. It also means you can have other code running to modulate the pulse widths or whatever without interfering with your pulse timing. The pulses are all taken care of in the background by dedicated hardware.

      The dsPIC30F4011 can do most of what you specified – SPI, I2C, 3-phase control, chatting to Arduino, etc. but it may be a challenge doing it all at 100kHz. The maximum clock speed of the dsPIC30F4011 is 30 MIPS, so that would only give you 300 instructions per 100kHz cycle. I’m sure there are tons of newer chips that run at higher clock speeds. Maybe one of those would suit you better?


  5. Rony Chakraborty says:

    Dear Batchloaf
    This is a great tutorial. I just need one more thing 3Phase interleaved PWM. Can u plz help?

  6. Lakshmi says:

    Sir,i have doubt on dspic33fj32mc204.i can’t generate the PWMs with phase shift.Normal PWM signals that is without phase shift i generated.

  7. Thank you very much batchloaf ,i found most of your write-up very helpful and useful in most of my projects keep the good work on and remain bless; Plz can you explain on how to use symmetric(center aligned pwm) to drive H-BRIDGE configuration for inverter, show the four(4) signal output wave and how it is applied to THE MOSFETS (S1,S2,S3,S4.) in the bridge configuration .Thanks

    • batchloaf says:

      Hi Philip,

      Sorry, but I don’t have time to write up how to do this right now because I’m very busy with work.

      However, I will give you a couple of quick pointers.

      Firstly, look at Chapter 15 of the dsPIC30F Family Reference Manual which explains the PWM module in detail. In section 15.2, you’ll find the explanation of the PWM configuration registers.

      The most important part for centre aligned PWM is the PTMOD bit field in the PTCON register. Basically, you’re going to need to set it to “up/down” counting mode. In practice, that means including something like this…

      _PTMOD = 0b11;

      …or this…

      _PTMOD = 0b10;

      …in your program, depending whether you need double PWM interrupts or not.

      Once you enable PWM with the mode set this way, the PWM outputs should be centre aligned. Be advised though – I’ve never actually tried it!

      Best of luck and sorry I don’t have time to go into more detail.


  8. Henry Adams says:

    please can you modify your code to work with static ac voltage regulator (Dynamic voltage regulator)


    May i know the code for generating four pulses for gating a four phase IBC with same duty ratio but 90 degrees phase shifted


    Sorry…. I need the code in PIC 16f877a

    • batchloaf says:

      Hi Fousia,

      I’m not sure what an IBC is – is it some kind of bus controller?

      Anyway, it’s several years since I’ve programmed a PIC16F microcontroller and I’ve never used a 16F877a, so I don’t think I can help. I don’t even have the compiler for that series of microcontrollers.

      Sorry I can’t be more helpful.


  11. ashok says:

    hi i am ash.. i need help in generating gate pulses using the equations
    i am using dsPIC30F4011

    • ashok says:

      i am using cascaded H Bridge inverter concepts.i have two H bridges in my should operate @10khz and other one 50 Hz. If anyone ready to help me. will mail my pdf. there u can find equations

Leave a Reply

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

You are commenting using your 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