This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

LAUNCHXL-F28069M: Building a Three Phase VSI - Generating the Reference Waveform

Part Number: LAUNCHXL-F28069M
Other Parts Discussed in Thread: CONTROLSUITE

Hello,

I am trying to build a simple three phase voltage source inverter to drive a three phase induction machine using a six switch inverter module. So far, I have configured ePWM4, 5 and 6 to produce complementary pairs of PWM signals with a deadband in between and I am going to use these to control the three legs of my inverter respectively.

At the moment, I have simply specified a duty cycle for these PWM signals so that I could verify them on my oscilloscope.

Uint32 ePWMdutyA = 500;
Uint32 ePWMdutyB = 600;
Uint32 ePWMdutyC = 700; 

......

	EPwm4Regs.CMPA.half.CMPA = ePWMdutyA;     // adjust duty for output ePWM4A
	EPwm5Regs.CMPA.half.CMPA = ePWMdutyB;     // adjust duty for output ePWM5A
	EPwm6Regs.CMPA.half.CMPA = ePWMdutyC;     // adjust duty for output ePWM6A

I do this within main() and it behaves as I would expect. 

Now though, I want to modulate this duty cycle to produce a sinusoidal output. I am planning on using the sine function from the math.h library. I want to do something like this:

ePWMdutyA = modulationIndex * sin(2 * pi * f * t)

ePWMdutyB = modulationIndex * sin(2 * pi * f * t + 120)

ePWMdutyC = modulationIndex * sin(2 * pi * f * t - 120)

I am not sure how to go about doing this over time though, ie where to get this t term from. Would it be best to set up an interrupt to fire when my PWM timer hits its maximum and change the duty cycle at this maximum point? Would it be best to set up a separate CPU timer to do this?

What I am eventually planning to do is take feedback from an encoder and use this to change the modulationIndex and the frequency to implement a very basic v/f control scheme.

Any advice would be appreciated as I am very new to this field!

  • Hello

    You're right, the correct way to do it is to set up an interrupt. There is an example in control suite (C:\ti\controlSUITE\device_support\f2806x\v151\F2806x_examples_ccsv5\cpu_timer) how to set up an interrupt based on a CPU timer.

    In that interrupt you can do smth like:

    // Speed ramp with limitation
    speed = speed + 1 * INTERRUPT_PERIOD;	// INTERRUPT_PERIOD= 0.001 for 1 ms interrupt
    if (speed > maxSpeed)
    	speed = maxSpeed;
    
    // Calculate angle for this speed
    referenceAngle = referenceAngle + speed * INTERRUPT_PERIOD;
    
    // Wrap angle in [0; 2pi]
    if (referenceAngle > _2pi) referenceAngle -= _2pi;
    else if (referenceAngle < 0) referenceAngle += _2pi;
    
    // Calc duty cycles
    dutyCycle1 = sin(referenceAngle);
    dutyCycle2 = sin(referenceAngle + _2pi_div_3);
    dutyCycle3 = sin(referenceAngle - _2pi_div_3);
    
    // Load CMPAs
    EPwm1Regs.CMPA.half.CMPA = (Uint16)(dutyCycle1 * (float)TBPRD);

    You would like to create a macro "INTERRUPT_PERIOD" to convert time-related physical values like seconds, rpm's to discrete domain. I hope this helps.

  • Thanks for the speedy reply! I'll check out that example and see how I get on. I had been thinking about how I could implement a slow startup but your speed ramp would seem to be an elegant solution.

    I'll report back if I get stuck ;)
  • Update....

    I have set up a CPU timer to interrupt at 20kHz and trigger the following ISR:

    interrupt void TINT0_ISR(void)                  // PIE1.7 @ 0x000D4C  TINT0 (CPU TIMER 0)
    {
        PieCtrlRegs.PIEACK.all = PIEACK_GROUP1;     // Must acknowledge the PIE group
        GpioDataRegs.GPATOGGLE.bit.GPIO0 = 1;     //toggle
    
        theta = theta + (2 * Pi * outputFrequency * 0.00005); //where duty cycle is proportional to sin(theta)
        // Wrap angle in [0; 2pi]
        if (theta > (2 * Pi)) theta -= (2 * Pi);
        else if (theta < 0) theta += (2 * Pi);
    
        // Calc duty cycles
        float A = ((modulationIndex * sin(theta)) + 1) / 2;                 //Reference waveform magnitude for Phase A
        float B = (modulationIndex * sin(theta + ((2 * Pi) / 3)) + 1) / 2;  //Reference waveform magnitude for Phase B
        float C = (modulationIndex * sin(theta - ((2 * Pi) / 3)) + 1) / 2;  //Reference waveform magnitude for Phase C
    
        Uint16 dutyA = (Uint16)(A * (float)PWM_HALF_PERIOD);
        Uint16 dutyB = (Uint16)(B * (float)PWM_HALF_PERIOD);
        Uint16 dutyC = (Uint16)(C * (float)PWM_HALF_PERIOD);
    
        if (dutyA > PWM_MIN_DUTY) dutyA = PWM_MIN_DUTY;
        else if (dutyA < PWM_MAX_DUTY) dutyA = PWM_MAX_DUTY;
        if (dutyB > PWM_MIN_DUTY) dutyA = PWM_MIN_DUTY;
        else if (dutyB < PWM_MAX_DUTY) dutyA = PWM_MAX_DUTY;
        if (dutyC > PWM_MIN_DUTY) dutyA = PWM_MIN_DUTY;
        else if (dutyC < PWM_MAX_DUTY) dutyA = PWM_MAX_DUTY;
    
        // Load CMPAs
        EPwm4Regs.CMPA.half.CMPA = dutyA;
        EPwm5Regs.CMPA.half.CMPA = dutyB;
        EPwm6Regs.CMPA.half.CMPA = dutyC;
    }

    My theory here is this:

    1) Toggle a GPIO pin every time the ISR is called so that I can see it on the scope and reassure myself that the ISR is being called!

    2) The variable theta is initialised outside the ISR to zero. I then create a new value for theta based on the elapsed time.

    3) As suggested in a previous post, I then constrain theta to between 0 and 2pi

    4) I multiply by a modulation index to control the maginitude of my output waveform and I take sin of theta. I then add one and divide by two such that the result varies between 0 and 1.

    5) I constrain the duty cycle between 10% and 90%

    6) I update the ePWM compare registers

    This code would appear to me to make sense however the output from my inverter is far from sinusoidal. I end up with a pretty nasty sawtooth waveform with a large DC offset. Are there any obvious flaws in my logic above? I am struggling to understand what is going on and I don't see any way to verify that my PWM signals are correct without applying them to an inverter leg and seeing what happens!

  • Hmm... he-he-he

    Maybe...

     if (dutyA > PWM_MIN_DUTY) dutyA = PWM_MIN_DUTY;
        else if (dutyA < PWM_MAX_DUTY) dutyA = PWM_MAX_DUTY;
        if (dutyB > PWM_MIN_DUTY) dutyA = PWM_MIN_DUTY;
        else if (dutyB < PWM_MAX_DUTY) dutyA = PWM_MAX_DUTY;
        if (dutyC > PWM_MIN_DUTY) dutyA = PWM_MIN_DUTY;
        else if (dutyC < PWM_MAX_DUTY) dutyA = PWM_MAX_DUTY;

    Take a closer look here. Copypasting dutyA lead you to failure =)

  • I think we need a "bang head off wall emoji". That's what happens when you spend too long staring at a problem.

    I changed that piece of code and passed the three top gate PWM signals through a simple RC low pass filter. This was the result:

    4152.TEK00001.TIF

  • Nice waveforms =) Glad to hear you got it working =)