dsPIC/PIC24 PWM (using XC16 and MPLAB X)

Pulse Width Modulation (PWM) in dsPIC/PIC24 works from output compare section (OC). OC is simply a comparator. One input is from a timer and another from OC register where we set the value. The timer is used to set the frequency of the PWM and OC register for setting the duty cycle. There are actually 2 OC register. One where we set the value and another is a read only register that takes the copy of the value in the input register and update it in a glitch free manner. In order to know what timer can be used for PWM please refer the data sheet of the part you are using.

Select your part based on your application. Here I am using dsPIC33FJ32GP202. It is a General Purpose (GP) controller and does not have any special PWM ability. Wish I could have picked a MC series for Motor Control. May be next time.

Even with GP series you can control DC motor’s without a problem in one direction. But for bi-directional control it is better to use MC series. Available PWM options

  • OC PWM (Using Output Compare)
  • MC PWM (for motor control – control both H/L H-bridge)
  • SMPS PWM (for SMPS design – Power electronics application)
    Here I’m using OC PWM.
    Note: For OC PWM use “outcompare.h” Similarly for other PWM’s you have pwm.h and pwm12.h. Select according to the documentations listed below.

 

Documents to read

 

PWM Sample

 

dsPIC33/PIC24 has ‘F’ series and ‘E’ series. ‘E’ series is relatively newer than ‘F’ series and has more features to it. So you have to read the library documentation to know how to use it. I don’t have any ‘E’ series with me.

    Note: PWM function prototypes are located in outcompare.h

Steps to setup PWM

 

  1. Setup Timer for required PWM frequency
  2. Use the function OpenOCx (x= 1,2…) to start your PWM.

That simple!

Here is another important boundary parameters you will see in the document 70005157a.pdf

boundarycondition

 

Resolution of PWM

 

The resolution is hugely dependent on the Frequency and prescale used in the timer. The example code I’m going to put below is for generating a PWM of freqency 50Hz or say 20ms. 20ms is perfect for driving a servo motor or may be brushless motor controller. So I did end up using a prescale value 256 for the timer. So now my calculated value is 3125. How did I get that 3125? check https://singularengineer.com/timers-using-xc16-and-mplab-x/

 

PWM resolution (in bits) = log (3125)/ log(2)

                                       = 11.60964 bits

 

So now the duty cycle of 0-100%  = [ 0 – 3125 ]

config

 

dsPIC/PIC24 PWM Code

 

/* * File: dsPICtest.c * Author: Singular Engineer * * Created on January 19, 2015 */ #define FCY 40000000ULL //Fcy = (Fosc/2); Fosc = 80MHz #include <xc.h> #include "config.h" #include <libpic30.h> #include <pps.h> #include <timer.h> #include <outcompare.h> //for PWM unsigned int i = 0; unsigned int dutycycle = 0; void SetupClock(void); int main(int argc, char** argv) { SetupClock(); unsigned int pwmperiodval = 3125; //20ms (50Hz) //---------- Assigning PPS function------------------ PPSUnLock; PPSOutput(OUT_FN_PPS_OC1,OUT_PIN_PPS_RP9); //PWM 1 pin to RP 9 PPSLock; //----------------------------------------------------- OpenTimer2(T2_ON & //Timer2 ON T2_GATE_OFF & //Gated mode OFF T2_IDLE_STOP & //Stop when controller is in sleep T2_PS_1_256 & //Prescale 256 T2_SOURCE_INT, pwmperiodval); // Interal source unsigned int OC1Config1 = OC_IDLE_STOP & // Stop when controller sleeps OC_TIMER2_SRC & // Timer 2 as source OC_PWM_FAULT_PIN_DISABLE; // not using PWM fault OpenOC1(OC1Config1, 0x00, 0x00); while(1) { if(i == 0) { dutycycle++; SetDCOC1PWM(dutycycle); if(dutycycle > 3124) i = 1; } if(i == 1) { dutycycle--; SetDCOC1PWM(dutycycle); if(dutycycle < 1 ) i = 0; } __delay_ms(1); } return 0; } void SetupClock() { //Setting N1 to 4 (N1 = n+2); n = N1-2 CLKDIVbits.PLLPRE0 = 0; CLKDIVbits.PLLPRE1 = 1; CLKDIVbits.PLLPRE2 = 0; CLKDIVbits.PLLPRE3 = 0; CLKDIVbits.PLLPRE4 = 0; //Setting M to 32 (M = m+2); m = M-2 PLLFBDbits.PLLDIV0 = 0; PLLFBDbits.PLLDIV1 = 1; PLLFBDbits.PLLDIV2 = 1; PLLFBDbits.PLLDIV3 = 1; PLLFBDbits.PLLDIV4 = 1; PLLFBDbits.PLLDIV5 = 0; PLLFBDbits.PLLDIV6 = 0; PLLFBDbits.PLLDIV7 = 0; PLLFBDbits.PLLDIV8 = 0; //Setting N2 to 2 CLKDIVbits.PLLPOST0 = 0; CLKDIVbits.PLLPOST1 = 0; }