PWM output on 2 channels of the dsPIC30F4011 with equal duty cycle and 180 degrees out of phase

Reader Mo Hsen wrote in with an interesting query about generating PWM output from the dsPIC30F4011 on two channels with equal duty cycle but 180 degrees out of phase (see the waveforms below for an example). I’ve created a short sample program (for the C30 compiler) which is included in full below. Mo needs the PWM frequency to be 25kHz, so I’m running at the highest clock speed possible with the internal RC oscillator and I’ve set the PWM prescaler to a ratio of 1:1 in order to achieve fine-grained control of the pulse timing.

The basic approach is as follows:

  1. Enable up-down counting for centre-aligned PWM (“_PTMOD = 0b10;”).
  2. Enable PWM channels 1 and 2 in complementary mode  (“PWMCON1 = 0b00110011;”).
  3. Set your PWM period as normal (e.g. “_PTCKPS = 0; PTPER = 600”).
  4. Then, whenever you set the pulse width on channel 1, you set the pulse width on channel 2 to be the opposite, i.e. “PDC2 = (2*PTPER )- PDC1;”. In my example below, I’ve phrased this slightly differently, but basically the idea is the same: if you want a duty cycle of 20%, set PDC1 for 20% and PDC2 for 80%.
  5. Finally, your 180 degree phase separated outputs are on PWM1H and PWM2L (pins 37 and 36 respectively).

My example program sets up this type of output, then switches back and forth indefinitely between duty cycles of 25% and 75%. Here’s are screenshots of the output waveforms on both channels for each duty cycle:

In the next screenshot, the cursors are enabled so that the period and frequency are displayed above the waveforms (40us and 25 kHz respectively). Note also that due to the high PWM frequency, I have set the sample rate to 1 MHz so that the recorded signal will not be aliased.

I’m using the following software versions:

  • MPLAB IDE v8.50
  • Microchip C30 Toolsuite v3.23 (free version)
  • PICkit 2 Application v2.61.00
  • Windows XP (service pack 3, I think)

Here’s the complete code for the program. I’m using the C30 compiler. Also, in MPLAB I have specified Configuration Bits set in code (in MPLAB’s “Configure” menu, click “Configuration Bits…” and then tick the box).

//
// This is a dsPIC30F4011 program to generate PWM on two channels
// simultaneously with equal duty cycle but 180 degrees out of phase.
//
// Written by Ted Burke
// https://batchloaf.wordpress.com
// Last updated 6-4-2012
//

#include <libpic30.h>
#include <p30f4011.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

// Function prototypes
void configure_pins();
void set_pwm_duty_cycle(double duty_cycle);
unsigned int read_analog_channel(int n);

int main()
{
	// Set up digital i/o, analog input, PWM, UART and interrupt
	configure_pins();

	// Switch back and forth between 25% and 75% duty cycle
	while(1)
	{
		_LATD1 = 1; // LED on
		set_pwm_duty_cycle(0.75); // 75% duty cycle
		__delay32(15000000);

		_LATD1 = 0; // LED off
		set_pwm_duty_cycle(0.25); // 25% duty cycle
		__delay32(15000000);
	}

	return 0;
}

// Sets the duty cycle on PWM channels 1 & 2
void set_pwm_duty_cycle(double duty_cycle)
{
	PDC1 = duty_cycle * (2 * PTPER);
	PDC2 = (1 - duty_cycle) * (2 * PTPER);
}

// This function sets up digital i/o, analog input, PWM,
// UART and timer interrupt.
void configure_pins()
{
	// Configure all four port D pins (RD0, RD1, RD2, RD3)
	// as digital outputs
	LATD = 0;
	TRISD = 0b1111111111110000;

	// Configure all three port C pins (RC13, RC14, RC15)
	// as digital inputs
	TRISC = 0b1111111111111111;

	// Configure AN0-AN8 as analog inputs
	TRISB = 0x01FF;      // All 9 port B pins are inputs
	ADPCFG = 0xFE00;     // Lowest 9 PORTB pins are analog inputs
	ADCON1 = 0;          // Manually clear SAMP to end sampling, start conversion
	ADCON2 = 0;          // Voltage reference from AVDD and AVSS
	ADCON3 = 0x0005;     // Manual Sample, ADCS=5 -> Tad = 3*Tcy
	ADCON1bits.ADON = 1; // Turn ADC ON

	// Configure PWM for free running mode
	//
	//   PWM period = 2 * Tcy * prescale * PTPER = 0.66ns * 1 * PTPER
	//   PWM pulse width = Tcy * prescale * PDC
	//
	PWMCON1 = 0b00110011; // Enable channels 1 & 2 in complementary mode
	PTCON = 0;
	_PTCKPS = 0;      // prescale=1:1 (0=1:1, 1=1:4, 2=1:16, 3=1:64)
	_PTMOD = 0b10;    // Select up-down counting for centre-aligned PWM
	PTPER = 600;      // 25kHz in up/down counting mode
	PDC1 = PTPER;     // 50% duty cycle on PWM channel 1
	PDC2 = PTPER;     // 50% duty cycle on PWM channel 2
	PTMR = 0;         // Clear 15-bit PWM timer counter
	_PTEN = 1;        // Enable PWM time base
}

// This function reads a single sample from the specified
// analog input. It should take less than 2.5us if the chip
// is running at about 30 MIPS.
// Because the dsPIC30F4011 has a 10-bit ADC, the value
// returned will be between 0 and 1023.
unsigned int read_analog_channel(int channel)
{
	ADCHS = channel;          // Select the requested channel
	ADCON1bits.SAMP = 1;      // Start sampling
	__delay32(30);            // 1us delay @ 30 MIPS
	ADCON1bits.SAMP = 0;      // Start Converting
	while (!ADCON1bits.DONE); // Should take 12 * Tad = 1.2us
	return ADCBUF0;
}

Finally, here are two images of the breadboard circuit I used to test the code. I took these using my phone, so unfortunately the image quality is not very good. However, the main details should hopefully be visible. Note that AVdd and AVss (pins 39 and 40) should be connected to the positive and negative supply voltages too, but I ran out of bits of wire!

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

50 Responses to PWM output on 2 channels of the dsPIC30F4011 with equal duty cycle and 180 degrees out of phase

  1. george skep says:

    Greetings mr.Ted Burke from Greece.
    Your example is the only one we have tried that worked with our dsPIC30F4011 concerning the pwm and we are really happy about this.We are students at technological educational institute and would like to make an inverter using SPWM.Specifically we want to produce a pulse to drive 4 mosfets in order to get a pure sine wave.Our 1st try was to compile a source code with flowcode to make a sinus change of the duty cycle but no chance at all.We are newbies at C code and we try by our owns to find a solution to make the code works and really we have no chance.
    Is there a possibility to make some additions to your source code to achieve our goal??
    Thanks in advance.

  2. George Skepas says:

    Greetings mr.Ted.
    Did you checked my latest reply about using your c source code and the additions i would like to make?

    • batchloaf says:

      Hi George. I only just saw your comments this minute. I’m delighted to hear that you found my code useful, and I’ll do my best to help you solve your problem. Yes, it should be possible to make some changes to the code to produce pulses that vary their width over time in a sinusoidal fashion. I’ll have a go, but I’ll need some more details from you, such as:

      1. What frequency of sinusoid do you want to produce?

      2. Does the frequency of the PWM signal matter for the MOSFETs? (bearing in mind that it will certainly need to be much higher than the frequency of the sinusoid).

      3. What are the maximum and minimum duty cycles that you want? In other words, what should the pulse width be at the high and low point of the sinusoid?

      If you can provide this information, I’ll put together some sample code and post it on my blog.

      Ted

    • batchloaf says:

      Hello again George. I’ve just posted some sample code which I hope is similar to what you’re looking for. Here’s the link:

      https://batchloaf.wordpress.com/2012/05/02/sinusoidal-variation-of-pwm-duty-cycle-with-dspic30f4011/

      I set the PWM frequency to 10kHz and the sinusoidal variation to 50Hz, which I hope is appropriate, but if not I can modify it to suit your requirements. It just varies the duty cycle on PWM channel 1 (pins 37 and 38). The PWM channel is in complementary mode, so whenever one pin is high the other is low. If I’m picturing your circuit correctly, I think you may need to use the output from both pins. It’s also possible that you need to introduce some “dead time” in between one pin going low and the other going high. It’s possible to do this automatically with the dsPIC, but I’ll have to look up the datasheet to find out exactly how.

      Anyway, have a look and try out the code. Let me know if it does the trick for you.

      Ted

  3. jil says:

    Dear Mr Burke,
    Hello there. I am working on a project of making bi-directional dc dc converter,wherein i need to generate 180 degree phase shifted PWM for triggering the IGBTS. Can this same concept be used if i were to use edge triggered waveforms?
    i need to trigger two legs of IGBTs ,both 180 degree phase shifted, and the upper and lower IGBT of the same leg have complementry switching. The period and the duty cycle of the IGBT remain same.
    I am using dsPIC30f4011, and MPLABIDE 8.46, HI-TECH C compiler. The switching frequency is 20kHz, duty cycle remains 73 % during Boost mode and 27 % degree during Buck mode of operation.
    Kindly let me know if this program can work with little addons?
    Thanks in advance

    • batchloaf says:

      Hi Jil,
      I’m pretty sure it’s possible to do what you want, but I’m not 100% clear yet on the waveforms you need to produce. A few questions:

      • Am I right in thinking that you need to use two outputs for each IGBT and that there are two IGBTs in total? i.e. You need to produce waveforms from four output pins of the dsPIC in total?
      • When you say “edge triggered” do you mean that the waveforms will be triggered by the rising or falling edge of a signal coming from somewhere else?
      • Do you have a circuit diagram, or a waveform graph?

      From what you’ve described so far, I suspect that it might be easier to do this using the dsPIC’s output compare pins rather than the PWM outputs. That’s not too hard though. Here are a couple of output compare example that I posted before:

      • Jil Sutaria says:

        Dear Mr Burke,
        Thank you for your earliest reply.The answer to the questions you asked are below:
        I am using four IGBTs in total,two in each leg, and the upper and lower IGBTs will work in complementary ,mode. Thus i will need four outputs, for eg if using PWM pins,i will need PWM1H,PWM1L,PWM2H,PWM2L.
        By edge triggered waveform i meant sawtooth waveform, as in the above example you have used center aligned waveform. The concept is that i need to compare the sawtooth waveform with the constant 0.73 to generate the PWM pulses.
        I do hae a circuit diagram and a sketch of waveforms, but i am not able to post it here. Could you please let me know how to do it so that the idea would be clear to you.
        I was told that it would be difficult using output compare so i thought of using PWM motor module.
        Thanking you,
        jil sutaria

    • batchloaf says:

      Thanks Jil. I’m just writing up some example code, which I’ll post here on the blog. If you have a picture of the four waveforms you want, it would be great to see that too just in case I’m picturing the IGBT activation sequence correctly.

      Regards,
      Ted

    • batchloaf says:

      Hi Jil,
      I’ve just posted a first draft of some code to generate the IGBT control waveforms. It’s not complete yet, but hopefully it will give you some idea of the direction I’m going with it. if you could try compiling it and let me know how you get on, that would be great. I don’t actually have the compiler installed on this PC, so I haven’t been able to compile it myself yet. I’m using the XC16 compiler by the way, but hopefully this will compile for you without too much modification.

      Here’s the link:

      https://batchloaf.wordpress.com/2012/12/26/igbt-control-in-a-buck-boost-dc-dc-converter/

      Ted

  4. Jil Sutaria says:

    Hello Mr Burke,
    thank you for the code.i have compiled the code and checked the output on a scope. I am not getting the phase shift.
    Also i have given the link to the waveforms below the code for Buck-Boost converter.
    Thanking you,
    jil

  5. Mote says:

    HI Mr Burke,
    I had look at some of your dsPic30f4011 examaples, you’ve done a great job.
    I am working on a SRM drive system for my Phd project and I am interested to do “2DLinear Interpolation” by using a microprocessor which I prefer dsPic30f4011 cause I am slightly familiar with it. Could you please advice me how can I compute the time that the PIC needed to do a 2Dlinear Interpolation. The link for interpolation are below and in this stage I just need to know can I do a 2dinterpolation in less than 1mSec ( somwhere between 10usec-100usec). Or in the other hand I need to know the time that the PIC needed to Interpolate one value. Please advice me.

    http://en.wikipedia.org/wiki/Bilinear_interpolation
    http://en.wikipedia.org/wiki/Linear_interpolation

    Regards
    Mote

    • Mote says:

      Please refer to linke below which explained Piecewise linear interpolation for PIC12/14/16 is maximum 137 instruction per cycle and I need this for dspic30f4011 to workout the time required for linear interpolation, cause I need to do cubic spilne interpolation which definitely needs more time than linear interpolation for latter taslk.

      http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&nodeId=1824&appnote=en020511

      I highly appreciatye your help in advance Mr Bruke

      • batchloaf says:

        Hi Mote,

        Ok, let’s consider the bilinear interpolation first.

        1. Are you interpolating between just four fixed points, or do you have a two dimensional grid of points and you need to interpolate between the nearest four points in each calculation?
        2. If it’s a whole grid of points, what size is it?
        3. What data types are you working with? int, unsigned int, float, double?
        4. For each interpolation, is the data type the same for the input and the output?

        If you can supply some more information, perhaps including more details of the application (e.g. where the input to the interpolation process is coming from and where the output value is being used) hopefully we can get a clear answer to how fast it can be done.

        Under certain circumstances, it should certainly be possible to do a bilinear interpolation within 1ms (and probably within 10-100us), but the time taken could vary considerably depending on the details of the application (data types, number of fixed points, etc).

        Ted

  6. Mote says:

    Deat Ted
    Fisr I have to appologies for late reply, my grandma passed away and I went to my country for her funeral thats why I could not reply on time.

    Lets clarify the points you mentioned above:

    1- I have to have a two dimentional grid of points, in the case of linear interpolation the grid size will be 20 columns and 61 rows (61×20=1220) and for cubic spline interpolation the grid size will reduce to 10 columns and 21 rows (21×10=210).I need to interpolate between nearest 4 points in each calculation for bilinear interpolation.

    2-For bilinear interpolation the grid size is 61Rows*20Columns=1220

    3-Data type that I would like to work with is minimum 5 digit number with 4decimal points (e.c 0.0000).

    4-Data type are the same for all the intyerpolation process.

    5-Input to the interpolation comes from a table which is a measured value of torque of a SRM machine.

    6-The outpute value will store in a table to plote the torque of the machine.

    Mote

    • batchloaf says:

      Hi Mote,

      I’m very sorry to hear about your grandmother.

      Thanks for the additional details on the interpolation problem. I’ll need to have a think about this and try it out for myself to measure the time taken. At first glance, I can see that the lookup table will need to be stored in program memory, since each stored value will need to be a 16-bit number, and there isn’t enough RAM in the 30F4011 to store the grid values in data memory. That shouldn’t be a problem, but I suppose it will affect the execution time in some way.

      Anyway, give me a day or two to think about this and I’ll get back to you.

      Ted

      • mote says:

        Hi Ted

        hope you are doing well. Just wanted to remind you about the computation of the time requires for interpolation in dsPic, I really appreciate if you have any idea please let me know.

        Regards
        Mote

      • Mote says:

        Hi ted
        Hope you are safe and sound, just want to know if you have thought about the interpolation’s time and if you couldn’t please advice me how can I estimate time requires for interpolations in dsPIC30f4011

        Regards
        Mohsen

      • batchloaf says:

        Hi Mote,

        Sorry I haven’t responded in full yet to your query, but I just haven’t had the time to get to the bottom of it because I’ve been very busy in work and there’s a backlog of queries on my blogs that I’ve been trying to get through. I have done some research on your question and I’m hoping to perform a complete experiment over the weekend to measure some actual computation times. I’ll let you know how I get on. Once I have my code written, I’ll probably put it up as a new blog post here.

        Regards,
        Ted

  7. Mote says:

    Hi Ted

    I appreciate How busy you are as you have a very usefull site. Thanks again for your great help buddy.

    Regards
    Mote

    • batchloaf says:

      Hi Mote,
      I’m working on the interpolation problem now. I have a question though. I was re-reading your answers to my questions above and I’m still a bit confused about the input and output of the interpolation process. You said:

      5-Input to the interpolation comes from a table which is a measured value of torque of a SRM machine.
      6-The outpute value will store in a table to plote the torque of the machine.

      …but I think maybe you might have misunderstood my question. Am I right in thinking that you’re using the interpolation process to estimate torque in the SRM? If so, I assume that the grid of values has been populated with measured values that were obtained by varying two “input” parameters (current, speed, or something else?). Presumably, in your final application, you will measure the actual values of these two parameters using some kind of sensors and then use interpolation to estimate the torque?

      The reason I ask is because the “input” values for the interpolation (the two measured parameters) will need to be scaled before being used as indices to the table. This is not a problem, but it’s an extra step that needs to be factored into the computation time.

      I’ll keep working on it for the time being without assuming anything specific about the two parameter value ranges.

      Ted

  8. Saptarshi De says:

    Hello Sir, thank you for the code. I am new at dspic and so i am unable to understand the logic.If PWM1H is having 25% duty then PWM2H will have 75% duty and PWM2L will have 25% duty, but PWM2L will start after PWM2H is low that is after 270 degrees from start of PWM1H/PWM2H. So how the phase shift is 180 degrees. Thank You.

    https://plus.google.com/photos/111038221676542745551/albums/6118361690316013793/6118361697329459234?pid=6118361697329459234&oid=111038221676542745551

    • batchloaf says:

      Hi Saptarshi,

      Yes, I know it’s a bit confusing!

      The way this works is that PWM channels 1 and 2 operate in a complementary fashion, so PWM1 generates positive pulses (i.e. it’s high during a pulse and low when there’s no pulse), whereas PWM2 generates negative pulses (i.e. it’s low during a pulse and high when there’s no pulse). So what “looks like” a positive pulse on channel 2 is actually the gap between two negative pulses!

      The pulses on both channels (positive on PWM1 and negative on PWM 2) are centre-aligned, and the centre of the pulses on both channels happen at exactly the same time. However, the pulse on PWM2 is in the negative direction and we set its width so that the gap between pulses on PWM2 is the same length and the positive pulses on PWM1.

      The net result of this is that we effectively end up with positive pulses of equal duration on both channels, but exactly 180 degrees out of phase.

      Hopefully that helps to explain it!

      Ted

  9. Memo Pérez says:

    Hi Ted, I am working on dspic30f2010 and I don´t have enough experience on C30 compiler. Generally I used to work with CCS and Assembler. I am doing an audio filter and I want to understand the next code if you want to support me:
    Can you tell me, what is the output pwm in this code (PWM1 or PWM2 or PWM3)?
    What is the meaning of XT_PLL8?
    What more can you tell me about this code?
    Anything else you can tell me is important for me…Greetings

    #include
    #include
    #include
    #include
    #include
    #include
    #include “fdacoefs.h”
    //////////////////////////////////////////////////
    _FOSC(CSW_FSCM_OFF & XT_PLL8);
    _FWDT(WDT_OFF);
    _FBORPOR(PBOR_OFF & MCLR_EN);
    _FGS(CODE_PROT_OFF);
    //////////////////////////////////////////////////
    // xtal 7.327MHz
    #define FOSC 7372000LL
    #define FCY (FOSC/4)
    #define milis (FCY/1000L)
    #define micros (FCY/1000000LL)
    //////////////////////////////////////////////////////
    #define CicloTrabajo OC1RS
    int X[32] = {};
    int XP[10]={};
    int PROM = 0;
    int Y = 0;
    int max=1023/2;
    int min=1023/2;
    unsigned int resultado = 0;
    void ConfTimer();
    void ConfADC();
    void ConfPWM();
    void ConfInt();
    int ADCOK = 0;
    int ERROR = 0;
    int i = 0;
    int j = 0;

    void __attribute__((__interrupt__)) _T1Interrupt(void)
    {
    WriteTimer1(0);
    IFS0bits.T1IF = 0;
    if(ADCOK){
    ERROR = 1;
    }
    else{
    ERROR = 0;
    ADCOK = 1;
    resultado = ReadADC10(0);
    ConvertADC10();
    }
    j++;
    if(j>2756){
    max=511;
    min=511;
    j=0;
    }
    }
    int main(void){

    TRISD = 0;
    TRISE = 0;
    ConfADC();
    ConfPWM();
    for(i = 0; i++; i0; i–) X[i]=X[i-1];
    X[0]=(resultado>>2)-PROM;
    //
    Y=VectorDotProduct(BL, B, X);
    CicloTrabajo = (Y+PROM) & 255;
    ADCOK = 0;
    }
    }
    }
    void ConfTimer(){
    //config timer – 11025Hz
    ConfigIntTimer1(T1_INT_PRIOR_1 & T1_INT_ON);
    WriteTimer1(0);
    OpenTimer1(T1_ON & T1_GATE_OFF & T1_IDLE_STOP &
    T1_PS_1_1 & T1_SYNC_EXT_OFF &
    T1_SOURCE_INT, 1335);
    }
    void ConfADC(){
    // AN0 versus -Vref
    ADCON1 = 0b0010000000000111;
    ADCON2 = 0x0000;
    ADCON3 = 0x0180;
    ADCHS = 0x0000;
    ADPCFG = 0xFFFE;
    ADCON1bits.ADON = 1;
    }
    void ConfPWM(){
    PR2 = 0xFF;
    OC1RS = PR2>>1;
    OC1R = OC1RS;
    OC1CON = 0x0206; //PWM – TIMER 2
    //conf TIMER2
    T2CON = 0xA000;
    }
    void calcularPromedio(){
    if(resultado>max) max = resultado;
    if(resultado>3;
    }

    • Memo Pérez says:

      sorry…the headers are:
      #include
      #include
      #include
      #include
      #include
      #include
      #include “fdacoefs.h”

      • batchloaf says:

        Hi Memo, WordPress is filtering out your header file names because they’re wrapped in angle brackets which makes them look like HTML tags. Just remove the angle brackets and post again – I’ll know the brackets are supposed to appear there anyway.

        Ted

  10. Memo Pérez says:

    Hi Ted… I get the code from this link: http://www.udb.edu.sv/udb/archivo/guia/electronica-ingenieria/senales-y-sistemas-discretos/2012/ii/guia-10.pdf

    The tutorial is in spanish and show me a digital filter. I can get fdacoefs.h with matlab and I can build this code with C30 compiler … my problem is that I can´t understand some instructions as XT_PLL8 and where is the PWM output? and what is doing exactly this code because there are many things I wish to do with this project … Greetings

    #include p30f2010.h
    #include libpic30.h
    #include stdio.h
    #include dsp.h
    #include timer.h
    #include adc10.h
    #include “fdacoefs.h”
    //////////////////////////////////////////////////
    _FOSC(CSW_FSCM_OFF & XT_PLL8);
    _FWDT(WDT_OFF);
    _FBORPOR(PBOR_OFF & MCLR_EN);
    _FGS(CODE_PROT_OFF);
    //////////////////////////////////////////////////
    #define FOSC 7372000LL
    #define FCY (FOSC/4)
    #define milis (FCY/1000L)
    #define micros (FCY/1000000LL)
    //////////////////////////////////////////////////////
    #define CicloTrabajo OC1RS
    int X[32] = {};
    int XP[10]={};
    int PROM = 0;
    int Y = 0;
    int max=1023/2;
    int min=1023/2;
    unsigned int resultado = 0;
    void ConfTimer();
    void ConfADC();
    void ConfPWM();
    void ConfInt();
    int ADCOK = 0;
    int ERROR = 0;
    int i = 0;
    int j = 0;

    void __attribute__((__interrupt__)) _T1Interrupt(void)
    {
    WriteTimer1(0);
    IFS0bits.T1IF = 0;
    if(ADCOK){
    ERROR = 1;
    }
    else{
    ERROR = 0;
    ADCOK = 1;
    resultado = ReadADC10(0);
    ConvertADC10();
    }

    j++;
    if(j>2756){
    max=511;
    min=511;
    j=0;
    }
    }

    int main(void){
    TRISD = 0;
    TRISE = 0;
    ConfTimer();
    ConfADC();
    ConfPWM();
    for(i = 0; i++; i0; i–) X[i]=X[i-1];
    X[0]=(resultado>>2)-PROM;
    Y=VectorDotProduct(BL, B, X);
    CicloTrabajo = (Y+PROM) & 255;
    ADCOK = 0;
    }
    }
    }

    void ConfTimer(){
    ConfigIntTimer1(T1_INT_PRIOR_1 & T1_INT_ON);
    WriteTimer1(0);
    OpenTimer1(T1_ON & T1_GATE_OFF & T1_IDLE_STOP &
    T1_PS_1_1 & T1_SYNC_EXT_OFF &
    T1_SOURCE_INT, 1335);
    }

    void ConfADC(){
    ADCON1 = 0b0010000000000111;
    ADCON2 = 0x0000;
    ADCON3 = 0x0180;
    ADCHS = 0x0000;
    ADPCFG = 0xFFFE;
    ADCON1bits.ADON = 1;
    }

    void ConfPWM(){
    PR2 = 0xFF; OC1RS = PR2>>1;
    OC1R = OC1RS;
    OC1CON = 0x0206;
    T2CON = 0xA000;
    }

    void calcularPromedio(){
    if(resultado>max) max = resultado;
    if(resultado>3;
    }

  11. gunz says:

    Hi,Ted
    How can i set PWM for Push-Pull by only use PWM1H/1L one channel ,i use yor code in “PWM output on 2 channels of the dsPIC30F4011 with equal duty cycle and 180 degrees out of phase”

    • batchloaf says:

      Hi Gunz,

      Please give me some more details and I’ll write a short example.

      1. What PWM frequency?
      2. How does the duty cycle vary?
      3. What are the max and min duty cycle values?
      4. What is the required dead time (between one transistor switching off and the other one switching on)?

      Ted

      • gunz says:

        1.25kHz
        2.I use Lookup Table MPPT for vary duty cycle.
        3.min0.1,max0.45
        4.In push-pull use equal duty cycle and 180 degrees out of phase

        thank you

      • batchloaf says:

        Hi Gunz,

        I’ve just posted an example that does more or less what you want (minus the lookup table). Here’s the link:

        Generating antiphase PWM signals with the dsPIC30F4011

        When I was programming this example, I was working without an internet connection and unfortunately I misremembered your requested PWM frequency, so I wrote the example for 15kHz rather than 25kHz. However, it should be straightforward to change the frequency to 25kHz by changing the value of PTPER from 999 to 599. You might also want to change PDC1 from 400 to 240 to keep the initial duty cycle at 0.1

        Hope that helps!

        Ted

  12. gunz says:

    thank you very much Ted 🙂 i will try it

  13. Margo says:

    Hello Mr Burke,

    i have try your upper code with the dspic33fj32mc204, but it is not working very well. I mean the duty cycles on the scope are not equals and the dead time as well.

    I will be very please for your reply.

    • batchloaf says:

      Hi Margo,

      Sorry for the delay responding. I’ve been away on holidays and now I’m up to my eyes with other work, so I don’t have time to investigate your query. Sorry I can’t be more helpful!

      Ted

      • batchloaf says:

        Hi Margo,

        Did you work out what the problem was? Perhaps you could explain a little bit more about how exactly the timing is different from what you expect on the scope?

        On most of the PIC microcontrollers I’ve used, the number of clock cycles per instruction is 4. However, that’s definitely different on certain PICs. So, for example, if you’re seeing times that are all out by a factor of 2, then that could be the reason.

        Ted

  14. Chetan says:

    Hi Mr.Ted,
    Please help me, am new to PIC family & using a dsPIC30F4011 for my project.
    In my project i want to generate two 180 degree out of phase pulses as above but i want the frequency to be 40KHz annd duty cycle to be 0.6.
    Please help me with the code modification. Am unable to understand what parameters should be modified to get 40KHz waveform.

    Thank You,

    • batchloaf says:

      Hi Chetan,

      Please try the modified version below and see if it does what you need. The first change is lines 77 and 78, which set the period (and frequency) and the duty cycle:

      PTPER = 375;             // Set period so that frequency is 40kHz
      set_pwm_duty_cycle(0.6); // Set duty cycle to 0.6 (this function sets PDC1 and PDC2)
      

      The second change is in the for loop in the main function – I changed lines 31-34 so that the duty cycle remains constant at 0.6 rather than changing back and forth between two values as it did originally.

      // Blink LED
      while(1)
      {
          _LATD1 = 1;          // LED on
          __delay32(15000000); // 0.5 second delay
          _LATD1 = 0;          // LED off
          __delay32(15000000); // 0.5 second delay
      }
      

      The full modified program is below.

      Ted

      //
      // This is a dsPIC30F4011 program to generate 40kHz PWM on two channels
      // simultaneously, both with 0.6 duty cycle but 180 degrees out of phase.
      //
      // Written by Ted Burke
      // https://batchloaf.wordpress.com
      // Last updated 26-8-2015
      //
       
      #include <libpic30.h>
      #include <p30f4011.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
       
      // Function prototypes
      void configure_pins();
      void set_pwm_duty_cycle(double duty_cycle);
      unsigned int read_analog_channel(int n);
       
      int main()
      {
          // Set up digital i/o, analog input, PWM, UART and interrupt
          configure_pins();
       
          // Blink LED
          while(1)
          {
              _LATD1 = 1;          // LED on
              __delay32(15000000); // 0.5 second delay
              _LATD1 = 0;          // LED off
              __delay32(15000000); // 0.5 second delay
          }
       
          return 0;
      }
       
      // Sets the duty cycle on PWM channels 1 and 2
      void set_pwm_duty_cycle(double duty_cycle)
      {
          PDC1 = duty_cycle * (2 * PTPER);
          PDC2 = (1 - duty_cycle) * (2 * PTPER);
      }
       
      // This function sets up digital i/o, analog input, PWM,
      // UART and timer interrupt.
      void configure_pins()
      {
          // Configure all four port D pins (RD0, RD1, RD2, RD3)
          // as digital outputs
          LATD = 0;
          TRISD = 0b1111111111110000;
       
          // Configure all three port C pins (RC13, RC14, RC15)
          // as digital inputs
          TRISC = 0b1111111111111111;
       
          // Configure AN0-AN8 as analog inputs
          TRISB = 0x01FF;      // All 9 port B pins are inputs
          ADPCFG = 0xFE00;     // Lowest 9 PORTB pins are analog inputs
          ADCON1 = 0;          // Manually clear SAMP to end sampling, start conversion
          ADCON2 = 0;          // Voltage reference from AVDD and AVSS
          ADCON3 = 0x0005;     // Manual Sample, ADCS=5 sets Tad = 3*Tcy
          ADCON1bits.ADON = 1; // Turn ADC ON
       
          // Configure PWM for free running mode
          //
          //   PWM period = 2 * Tcy * prescale * PTPER = 0.66ns * 1 * PTPER
          //   PWM pulse width = Tcy * prescale * PDC
          //
          PWMCON1 = 0b00110011; // Enable channels 1 and 2 in complementary mode
          PTCON = 0;
          _PTCKPS = 0;             // prescale=1:1 (0=1:1, 1=1:4, 2=1:16, 3=1:64)
          _PTMOD = 0b10;           // Select up-down counting for centre-aligned PWM
          PTPER = 375;             // Set period so that frequency is 40kHz
          set_pwm_duty_cycle(0.6); // Set duty cycle to 0.6 (this function sets PDC1 and PDC2)
          PTMR = 0;                // Clear 15-bit PWM timer counter
          _PTEN = 1;               // Enable PWM time base
      }
       
      // This function reads a single sample from the specified
      // analog input. It should take less than 2.5us if the chip
      // is running at about 30 MIPS.
      // Because the dsPIC30F4011 has a 10-bit ADC, the value
      // returned will be between 0 and 1023.
      unsigned int read_analog_channel(int channel)
      {
          ADCHS = channel;          // Select the requested channel
          ADCON1bits.SAMP = 1;      // Start sampling
          __delay32(30);            // 1us delay @ 30 MIPS
          ADCON1bits.SAMP = 0;      // Start Converting
          while (!ADCON1bits.DONE); // Should take 12 * Tad = 1.2us
          return ADCBUF0;
      }
      
    • batchloaf says:

      PS Actually, since you only want a fixed frequency and duty cycle, a much simpler program will suffice. Here’s a simpler version of the previous program, but this time I stripped out most of the code you don’t need. Hopefully this will be a little easier to understand!

      //
      // This is a dsPIC30F4011 program to generate 40kHz PWM on two channels
      // simultaneously, both with 0.6 duty cycle but 180 degrees out of phase.
      //
      // Written by Ted Burke
      // https://batchloaf.wordpress.com
      // Last updated 26-8-2015
      //
       
      #include <libpic30.h>
      #include <p30f4011.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 PWM for free running mode
          PWMCON1 = 0b00110011;           // Enable channels 1 & 2 in complementary mode
          PTCON = 0;
          _PTCKPS = 0;                    // prescale=1:1 (0=1:1, 1=1:4, 2=1:16, 3=1:64)
          _PTMOD = 0b10;                  // Select up-down counting for centre-aligned PWM
          PTPER = 375;                    // Set period so that frequency is 40kHz
          PDC1 = 0.6 * (2 * PTPER);       // Set PWM duty cycle for channel 1
          PDC2 = (1 - 0.6) * (2 * PTPER); // Set PWM duty cycle for channel 2 (which is inverted!)
          PTMR = 0;                       // Clear 15-bit PWM timer counter
          _PTEN = 1;                      // Enable PWM time base
       
          // Now just do nothing while PWM is generated in the background
          while(1);
      
          // The program never actually reached this point!
          return 0;
      }
      

      Ted

      • Chetan says:

        Hi Ted, first of all thank you so much for the code.
        But there is a small problem in the output waveform, the pulses are not exactly 180 degrees apart.
        F = 40Khz -> T = 25us.
        D = 0.6 -> Ton = 15us & Toff = 10us
        Pulses being 180degress out of phase -> the time difference between them should be 2.5us between rising edge of one waveform & the falling edge of other.
        But its not happening, please help me.

      • Chetan says:

        Hii again, I think the problem is with the logic analyzer it is not displaying values on points like 2.5…
        Thank you for the code.

      • batchloaf says:

        Hi again Chetan,

        If you’re having trouble capturing the fine details on your logic analyser, you could try slowing everything down by a factor of 16 for testing by changing the following line…

        _FOSC(CSW_FSCM_OFF & FRC_PLL16); // Fosc=16x7.5MHz, Fcy=30MHz
        

        …to this…

        _FOSC(CSW_FSCM_OFF & FRC); // Fosc=7.5MHz, Fcy=1.875MHz
        

        That might make it easier to see the pulses clearly and once you’re happy that they’re being generated correctly you can speed everything back up to normal frequency by putting back the original line in the program.

        The difference between the two lines is that the original one enables the 16x PLL multiplier on the internal fast RC oscillator, whereas the second version uses the same oscillator but with the PLL multiplier.

        Hopefully that helps.

        Ted

      • Chetan says:

        Thank You So Much…
        I will check it.
        Can you please recommend me some links or materials regarding dsPIC programming where I can learn some basics and can understand what all these “FSCM, FBORPOR, PTPER, PTCKPS etc etc.,” means. It will be very helpful for me & my project.

      • batchloaf says:

        Hi Chetan,

        The information you probably want is distributed through several different documents. I’ll try to point you to the ones I find myself looking at most often.

        • First, you need the data sheet for whichever PIC you’re using. For example, here’s a link to the dsPIC30F4011 Data Sheet.
        • Secondly, you need what they call the “Family Reference Manual” for the family of devices your PIC belongs to. This is probably the most important document since it contains the most complete description of the registers etc in your device and explains how to use the different peripherals such as analog-to-digital converter, serial port, etc. For example, here’s a link to the dsPIC30F Family Reference Manual.
        • You should also download a copy of the XC16 C Compiler User’s Guide, which contains useful information about some of the cryptic PIC-specific lines you’ll see in C programs for the PIC.
        • Finally, some of the macros you mentioned (e.g. FSCM) are defined in the header file for your specific PIC microcontroller, which will be in a folder somewhere within the installation directory of the XC16 compiler. For example, I’m normally using the dsPIC30F4011 which has a header file called “p30F4011.h”. In that header file, you will find information about some of these compiler specific macros, including some examples of how to use them. Here’s a link to an online copy of the p30F4011.h header file in case you want to take a quick look without digging through your XC16 installation folder.

        Ted

  15. Mounika says:

    Hello Sir,
    I working on a DC-DC converter, which requires two 180 degrees out of phase pulses with 40Khz frequency & duty cycle should be more than 60%. I want PI control for my project.
    Please help me with the code.

  16. sandeep says:

    Hello sir,
    Im working on a project push-pull converter, which requiered two PWM pulse with equal duty cycle and 180 degree phase shift. The duty ratio have to varies between 10% to 45% depend upon requirement. The switching frequency is 25 kHz. How can I do this work,plz help me.

  17. sandeeppv says:

    Hello sir,
    I would like to know, how can we do simultaneous ADC sampling. I want to read current and voltage at same time and after a small interval need to take 2nd sample and compare with the first.

    • batchloaf says:

      Hi Sandeeppv,

      Although the dsPIC30F4011 only has one ADC, it has four sample-and-hold blocks, so it presumably is possible to sample up to four pins simultaneously, then convert them into digital values one at a time. I haven’t actually done it myself though, so I’m afraid I don’t have an example I can offer you.

      Ted

  18. Abir says:

    Dear Sir,
    I am currently working with dsPIC30F4011. dsPIC has 6 PWM pins (RE0 – RE1). I would like to ,make those pin working as input output pins. Please inform me how the configuration bits are to be set for that purpose.

Leave a reply to Jil Sutaria Cancel reply