Three PWM outputs with three different frequencies using the dsPIC30F4011 microcontroller

Arising out of an interesting online discussion with Jaime Mora in the Costa Rica Institute of Technology, I wrote this example program to show how three PWM outputs, each with a different frequency, can be generated using the dsPIC30F4011 microcontroller. Generating three or four PWM outputs with the same frequency is very straightforward, but mutliple PWM outputs with different frequencies is more complicated because each PWM signal must be generated using a different clock source.

Jaime is using the three PWM signals to control IGBT’s in a high-current buck converter. His chosen frequencies are 17 kHz, 19kHz and 23 kHz. My understanding is that these particular frequencies were chosen to avoid harmonic interference. Since 17, 19 and 23 are prime numbers, I think I’m right in saying that the first harmonic frequency that’s common to any two of these PWM signals will be at 323 kHz (because 17 x 19,000 = 19 x 17,000 = 323,000). The three PWM outputs used in this program are:

  • A: PWM1H on pin 37. This 17 kHz PWM signal is generated by the “Motor Control PWM” module. The clock source is the dedicated PWM clock in that module.
  • B: OC1 on pin 23. This 19 kHz PWM signal is generated using Output Compare channel 1 (in PWM mode), with Timer 2 as its clock source.
  • C: OC2 on pin 18. This 23 kHz PWM signal is generated using Output Compare channel 2 (in PWM mode), with Timer 3 as its clock source.

I included a handy function called set_duty_cycles, which allows all three channels’ duty cycles to be set at once. Each duty cycle should be specified as a floating point value between 0 and 1. This is the function prototype:

void set_duty_cycles(float a, float b, float c);
  • a is the duty cycle (between 0 and 1) for PWM1H on pin 37.
  • b is the duty cycle (between 0 and 1) for OC1 on pin 23.
  • c is the duty cycle (between 0 and 1) for OC2 on pin 18.

I’ve tested this example program with LEDs attached to the three output pins so that I could confirm that PWM was working and that the duty cycle really was varying on each channel (as shown in the video below). Jaime was kind enough to send me the following screen captures from his oscilloscope when he was testing his circuit. They seem to confirm that the PWM frequencies are 17 kHz, 19 kHz and 23 kHz, as expected.

Here’s a video of the three LEDs pulsating:

Here’s the complete source code:

//
// This example shows how the dsPIC30F4011 can generate three PWM
// signals, each with a different frequency (17000, 19000, 23000 Hz)
//
// To generate three different frequencies, three separate clock sources
// must be used, which necessitates some mixing and matching:
//
//   PWM signal A (17 kHz) is generated using the PWM module with its
//   own timebase (PTMR) used as the clock source.
//
//   PWM signal B (19 kHz) is generated using Output Compare channel 1
//   with Timer 2 as the clock source.
//
//   PWM signal C (23 kHz) is generated using Output Compare channel 2
//   with Timer 2 as the clock source.
//
// I compiled this with Microchip's XC16 C compiler, using the
// following commands:
//
//   xc16-gcc main.c -mcpu=30F4011 -Wl,--script=p30F4011.gld
//   xc16-bin2hex a.out
//
// Written by Ted Burke, Last updated 24-9-2014
//
 
#include <xc.h>
#include <libpic30.h>
#include <math.h>
 
// Configuration settings
_FOSC(CSW_FSCM_OFF & FRC_PLL16); // Fosc=16x7.5MHz, i.e. 30 MIPS
_FWDT(WDT_OFF);                  // Watchdog timer off
_FBORPOR(MCLR_DIS);              // Disable reset pin
 
// Function prototype
void set_duty_cycles(float a, float b, float c);
 
int main(void)
{
    // Use PWM module for PWM output A with PTMR as clock source
    PWMCON1 = 0x0011;       // Enable PWM1 (high and low pins)
    PTCONbits.PTCKPS = 0;   // prescale=1:1 (0=1:1, 1=1:4, 2=1:16, 3=1:64)
    PTPER = 1764;           // 17 kHz PWM frequency (PTPER + 1 = 30000000 / 17000)
    PDC1 = 1200;            // 50% duty cycle on PWM channel 1
    PTMR = 0;               // Clear 15-bit PWM timer counter
    PTCONbits.PTEN = 1;     // Enable PWM time base
 
    // Use OC1 for PWM output B with Timer 2 as clock source
    PR2 = 1578;             // 19 kHz PWM frequency (PR2 + 1 = 30000000 / 19000)
    OC1R = PR2 / 2;         //
    OC1RS = PR2 / 2;        // Select 50% duty cycle initially
    OC1CONbits.OCTSEL = 0;  // Select Timer 2 as clock source for OC1
    OC1CONbits.OCM = 0b110; // PWM mode
    T2CONbits.TON = 1;      // Turn on Timer 2
 
    // Use OC2 for PWM output C with Timer 3 as clock source
    PR3 = 1303;             // 23 kHz PWM frequency (PR3 + 1 = 30000000 / 23000)
    OC2R = PR3 / 2;         //
    OC2RS = PR3 / 2;        // Select 50% duty cycle initially
    OC2CONbits.OCTSEL = 1;  // Select Timer 3 as clock source for OC2
    OC2CONbits.OCM = 0b110; // PWM mode
    T3CONbits.TON = 1;      // Turn on Timer 3
    
    // Make OC1 and OC2 outputs (same pins as RD0 and RD1)
    TRISD = 0b1111111111111100;
    
    //
    // Now, vary duty cycles on the three PWM outputs so that the
    // three LEDs pulsate.
    //
    // I'm using an inverted squared sine waveform to pulsate
    // each LED. Between each pair of LEDs, there's a phase shift
    // of 2*pi/3 radians.
    //
    // It doesn't really matter what's actually happening in this part
    // of the program - it's just something to show that the duty
    // cycle really is variable on each PWM output.
    //
    float t=0, pi=3.1428, f=0.25, a, b, c;
    while(1)
    {
        a = sin(2 * pi * f * t);
        a = 1 - a * a;
        b = sin(2 * pi * f * t + 2 * pi / 3);
        b = 1 - b * b;
        c = sin(2 * pi * f * t + 4 * pi / 3);
        c = 1 - c * c;
        
        set_duty_cycles(a, b, c);
        
        __delay32(300000); // 10 ms delay
        t += 0.01; // t is in seconds
    }
}
 
//
// This function provides a way to set the duty cycle on all three
// PWM output channels in one go. Arguments a, b and c are the three
// desired duty cycle values - each should be in the range from 0 to 1.
//
void set_duty_cycles(float a, float b, float c)
{
    PDC1 = a * 2 * PTPER;
    OC1RS = b * PR2;
    OC2RS = c * PR3;
}
This entry was posted in Uncategorized. Bookmark the permalink.

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