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.

Problems with Changing RED/FED during PWM Period

Hello,

I using a 28027 Piccolo processor to control an AC-AC Cuk SMPS.  During the 60Hz cycle I change the PWM switching methodology 5 times through the period.  This is done to reduce the switching violations for the Cuk topology and reduce switching losses.  During two of the switching states I require 200nS RED/FED dead-band and the other three states require no dead-band.  I am using two PWM's configured as master slave, shadow register, and dead-band mode.  I control the switching states by loading the PWM's with a PWM period or 100% duty cycle for always "on" or 0% duty cycle for always "off".  When I do not require dead-band I set the dead-band register to 0.  When I require dead-band I set both to 200nS.  My problem I am having is during the last switching period of one of the phases that require dead-band, the first rising/falling edge will have dead-band.  The last rising/falling edge will not have any dead-band.  I believe the issue is due to the fact the PWM registers get set by the shadow register at the end of the period.  The RED/FED registers get set immediately and influence the next rising/falling edge.  Is their any way to set the PWM registers with a new PWM value and modify the RED/FED registers and guarantee that the RED/FED values will stay active for that last PWM period?  I have included 2 relevant snippets of my code.  The first is the set-up of the PWM's.  The second snippet is the ADC EOC isr routine that generates the compensation network results, loads the PWM value or sets high/low the PWM depending on the state, and sets up the RED/FED registers.

 

PWM Initialization

//=====================================================================
// Configure PWM1x

//=====================================================================

    //Disable Clocks
    CLK_disableTbClockSync(myClk);

    // Set up clock source for EPWM1x
    CLK_enablePwmClock(myClk, PWM_Number_1);

    // Configure GPIO 0 & 1 as PWM1A/PWM1B
    GPIO_setPullUp(myGpio, GPIO_Number_0, GPIO_PullUp_Enable);
    GPIO_setPullUp(myGpio, GPIO_Number_1, GPIO_PullUp_Enable);
    GPIO_setMode(myGpio, GPIO_Number_0, GPIO_0_Mode_EPWM1A);
    GPIO_setMode(myGpio, GPIO_Number_1, GPIO_1_Mode_EPWM1B);

    // Set-up PWM1x
    PWM_setPeriod(myPwm1, EPWM_PERIOD);                                        // Set timer period
    PWM_setPhase(myPwm1, 0x0000);                                           // Phase is 0
    PWM_setCount(myPwm1, 0x0000);                                           // Clear counter
    PWM_setCounterMode(myPwm1, PWM_CounterMode_UpDown);                     // Count up/down
    PWM_disableCounterLoad(myPwm1);                                         // Disable phase loading
    PWM_setHighSpeedClkDiv(myPwm1, PWM_HspClkDiv_by_1);                     // Clock ratio to SYSCLKOUT /1
    PWM_setClkDiv(myPwm1, PWM_ClkDiv_by_1);                                 // Set internal clock /1 --  One Clock Tick Equals 16.666nS
    PWM_setSyncMode(myPwm1, PWM_SyncMode_CounterEqualZero);                    // Sets PWM1x as the master
    PWM_setShadowMode_CmpA(myPwm1, PWM_ShadowMode_Shadow);                    // Load registers every zero
    PWM_setShadowMode_CmpB(myPwm1, PWM_ShadowMode_Shadow);
    PWM_setLoadMode_CmpA(myPwm1, PWM_LoadMode_Zero);
    PWM_setLoadMode_CmpB(myPwm1, PWM_LoadMode_Zero);

    // Set up Action Qualifier for PWM1
    PWM_setDeadBandInputMode(myPwm1, PWM_DeadBandInputMode_EPWMxA_Rising_EPWMxB_Falling);
    PWM_setDeadBandPolarity(myPwm1, PWM_DeadBandPolarity_EPWMxB_Inverted);
    PWM_setDeadBandOutputMode(myPwm1, PWM_DeadBandOutputMode_EPWMxA_Rising_EPWMxB_Falling);

    // Set compare actions for EPWM1A
    PWM_setActionQual_CntUp_CmpA_PwmA(myPwm1, PWM_ActionQual_Clear);
    PWM_setActionQual_CntDown_CmpA_PwmA(myPwm1, PWM_ActionQual_Set);

    // Set compare actions for EPWM1B
    PWM_setActionQual_CntUp_CmpB_PwmB(myPwm1, PWM_ActionQual_Clear);
    PWM_setActionQual_CntDown_CmpB_PwmB(myPwm1, PWM_ActionQual_Set);

    // Set Dead-Band Delay
    PWM_setDeadBandRisingEdgeDelay(myPwm1, EPWM1_ZERO_DEADBAND);
    PWM_setDeadBandFallingEdgeDelay(myPwm1, EPWM1_ZERO_DEADBAND);

    //=====================================================================
    // Configure PWM2x
    //=====================================================================

    // Enable master clock for PWM2x
    CLK_enablePwmClock(myClk, PWM_Number_2);

    // Configure GPIO 2 & 3 as PWM2A/PWM2B
    GPIO_setPullUp(myGpio, GPIO_Number_2, GPIO_PullUp_Enable);
    GPIO_setPullUp(myGpio, GPIO_Number_3, GPIO_PullUp_Enable);
    GPIO_setMode(myGpio, GPIO_Number_2, GPIO_2_Mode_EPWM2A);
    GPIO_setMode(myGpio, GPIO_Number_3, GPIO_3_Mode_EPWM2B);

    // Set-up PWM2x
    PWM_setPeriod(myPwm2, EPWM_PERIOD);                                         // Set timer period
    PWM_setPhase(myPwm2, 0x0000);                                           // Phase is 0
    PWM_setCount(myPwm2, 0x0000);                                           // Clear counter
    PWM_setCounterMode(myPwm2, PWM_CounterMode_UpDown);                     // Count up/down
    PWM_disableCounterLoad(myPwm2);                                         // Disable phase loading
    PWM_setHighSpeedClkDiv(myPwm2, PWM_HspClkDiv_by_1);                     // Clock ratio to SYSCLKOUT /1
    PWM_setClkDiv(myPwm2, PWM_ClkDiv_by_1);                                 // Set internal clock /1 -- One clock tick equals 16.666nS
    PWM_setSyncMode(myPwm2, PWM_SyncMode_EPWMxSYNC);                        // Sets PWM2x as a slave
    PWM_setShadowMode_CmpA(myPwm2, PWM_ShadowMode_Shadow);                    // Load registers every zero
    PWM_setShadowMode_CmpB(myPwm2, PWM_ShadowMode_Shadow);
    PWM_setLoadMode_CmpA(myPwm2, PWM_LoadMode_Zero);
    PWM_setLoadMode_CmpB(myPwm2, PWM_LoadMode_Zero);

    // Set up Action Qualifier for PWM2
    PWM_setDeadBandInputMode(myPwm2, PWM_DeadBandInputMode_EPWMxA_Rising_EPWMxB_Falling);
    PWM_setDeadBandPolarity(myPwm2, PWM_DeadBandPolarity_EPWMxB_Inverted);
    PWM_setDeadBandOutputMode(myPwm2, PWM_DeadBandOutputMode_EPWMxA_Rising_EPWMxB_Falling);

    // Set compare actions for EPWM2A
    PWM_setActionQual_CntUp_CmpA_PwmA(myPwm2, PWM_ActionQual_Clear);
    PWM_setActionQual_CntDown_CmpA_PwmA(myPwm2, PWM_ActionQual_Set);

    // Set compare actions for EPWM2B
    PWM_setActionQual_CntUp_CmpB_PwmB(myPwm2, PWM_ActionQual_Clear);
    PWM_setActionQual_CntDown_CmpB_PwmB(myPwm2, PWM_ActionQual_Set);

    // Set Dead-Band Delay
    PWM_setDeadBandRisingEdgeDelay(myPwm2, EPWM2_ZERO_DEADBAND);
    PWM_setDeadBandFallingEdgeDelay(myPwm2, EPWM2_ZERO_DEADBAND);

Code that modifies the PWM's and FED/RED's

//=====================================================================
// ADC/PWM Duty Cycle Manager Thread
//
// Note: Discard ADCRESULT0 as part of workaround to silicon errata rev0
//=====================================================================
interrupt void adc_eoc_isr(void) {

    if (SystemSynchronization) {

        // Get ADC input value & convert to q24 format
        iq_AdcOut = _IQabs(((_iq)AdcResult.ADCRESULT2 << 12) - iq_VOFFSET);

        // Multiply by 2 to get full range (000 - FFF) and save for 3p3z method
        Fdbk = _IQmpy2(iq_AdcOut);

        // Get Sin Table Value and muliply by desired reference voltage constant
        iq_SinValue = _IQabs(_IQmpy(IQ24_HEX_nSIN_TABLE[SinTablePointer], iq_VOUT));

        if (SoftStart) {

            // In SoftStart so slow ramp the Reference Value & save for 3p3z method
            Ref = _IQmpy(iq_SinValue, iq_SoftRatio);
        }
        else {

            // No SoftStart so just update normal Reference Value & save for 3p3z method
            Ref = iq_SinValue;
        }

        // Go execute 3p3z Gc
        CNTL_3P3Z();

        // Generate PWM
        iq_D1 = _IQmpy(Out, iq_PWM_MAXIMUM);

        // Convert Fixed Point Fractional PWM to Integer PWM
        EpwmDutyCycle = (int)(iq_D1 >> 12);

        // Make sure Duty Cycle is not violating the maximum voltage boost
        if (EpwmDutyCycle >= MAX_SATURATION) {
                EpwmDutyCycle = MAX_SATURATION;
        }

        DINT;
        switch (AcPhaseState) {

        case 0:    // State 0 -- Diagnostic Flip-Flop

            // No Dead-band for Flip-Flop
            EPwm2Regs.DBFED = EPWM2_ZERO_DEADBAND;
            EPwm2Regs.DBRED = EPWM2_ZERO_DEADBAND;

            // Set up commutation for Flip-Flop
            EPwm1Regs.CMPA.half.CMPA = EpwmDutyCycle;
            EPwm1Regs.CMPB = EpwmDutyCycle;
            EPwm2Regs.CMPA.half.CMPA = EpwmDutyCycle;
            EPwm2Regs.CMPB = EpwmDutyCycle;
            break;

        case 1:    // State 1 -- Flip-Flop

            // Update PWM's for Flip-Flop
            EPwm1Regs.CMPA.half.CMPA = EpwmDutyCycle;
            EPwm1Regs.CMPB = EpwmDutyCycle;
            EPwm2Regs.CMPA.half.CMPA = EpwmDutyCycle;
            EPwm2Regs.CMPB = EpwmDutyCycle;
            break;

        case 2: // State 2 -- +Synchronous

            // Update Dead-band
            EPwm1Regs.DBFED = EPWM1_FED;
            EPwm1Regs.DBRED = EPWM1_RED;

            // Set comutation to +Synchronous
            EPwm2Regs.CMPA.half.CMPA = EPWM2A_ON;
            EPwm2Regs.CMPB = EPWM2B_ON;

            // Update PWM's
            EPwm1Regs.CMPA.half.CMPA = EpwmDutyCycle;
            EPwm1Regs.CMPB = EpwmDutyCycle;
            break;

        case 3:    // State 3 -- Flip-Flop

            // No Dead-Band for Flip-Flop
            EPwm1Regs.DBFED = EPWM1_ZERO_DEADBAND;
            EPwm1Regs.DBRED = EPWM1_ZERO_DEADBAND;

            // Set up PWM's for Flip-Flop
            EPwm1Regs.CMPA.half.CMPA = EpwmDutyCycle;
            EPwm1Regs.CMPB = EpwmDutyCycle;
            EPwm2Regs.CMPA.half.CMPA = EpwmDutyCycle;
            EPwm2Regs.CMPB = EpwmDutyCycle;
            break;

        case 4:    // State 4 -- -Synchronous

            // Update Dead-band
            EPwm2Regs.DBFED = EPWM2_FED;
            EPwm2Regs.DBRED = EPWM2_RED;

            // Set comutation to +Synchronous
            EPwm1Regs.CMPA.half.CMPA = EPWM1A_ON;
            EPwm1Regs.CMPB = EPWM1B_ON;

            // Update PWM's
            EPwm2Regs.CMPA.half.CMPA = EpwmDutyCycle;
            EPwm2Regs.CMPB = EpwmDutyCycle;
            break;

        }
        EINT;

        // Bump Sin Table Pointer
        ++SinTablePointer;

        // Reset pointer if at end of array
        if (SinTablePointer == 399) SinTablePointer = 0;

    }

    // Clear ADCINT1 flag reinitialize for next SOC
    AdcRegs.ADCINTFLGCLR.bit.ADCINT1 = 1;

    // Acknowledge interrupt to PIE
    PieCtrlRegs.PIEACK.all = PIEACK_GROUP10;

}

  • Stephen,

    I have one easy thing to try and a second that will require some work.

    1) I would recommend adding the following (I am guessing at the exact syntax, but I should be pretty close)
    PWM_setActionQual_Zero_PwmA(myPwm1, PWM_ActionQual_Set);
    PWM_setActionQual_Zero_PwmB(myPwm1, PWM_ActionQual_Set);
    PWM_setActionQual_Zero_PwmA(myPwm2, PWM_ActionQual_Set);
    PWM_setActionQual_Zero_PwmB(myPwm2, PWM_ActionQual_Set);

    This reduces the risk of issues that can occur around 0% and 100% duty cycle.  Note that most of the time this code will change nothing in your waveform.


    2) If (1) doesn't change anything, I would estimate that the difference between how RED/FED and CMP/PRD values update is what is causing your issue (as you suggest).  My first step would be to profile how long your interrupt is in relation to the amount of cycles in your PWM period and do it for each of your cases.  This should show you where in the PWM period you are updating in each AcPhaseState.  Ideally, you'd then shift your updating to some point in the PWM period where updating is safe (after your last CMP edge, ideally near 0% or 100% duty cycle, outside of your converter's specs).

    Hopefully this helps some.  Respond back if it's not clear.


    Thank you,
    Brett

  • Hello Bret,

    Sorry for the long delay in getting back to you.  I went on vacation and when I came back I had a pile software I had to get done before getting back to this problem.  I tried your 1st recommendation -- PWM_setActionQual_Zero_PwmA(myPwm1, PWM_ActionQual_Set); et. al.  It did not help.  I measured the time through the EOC thread like you recommended and found it to run between 4.1 and 4.5 uSec worst case.  My period for this SMPS is 1/24,000Hz or 41.6 Usec.  I have the Piccolo set up to give the EOC interrupt at the top of the up/down pwm period count.  Assuming worst cast of 4.5 uSec, I am finished updating all of the PWM duty cycles and RED/FED's with 16.3 uSec left over before the next period starts.  I tried a couple of other action qualifier load scenarios with no success.  It appears that the RED/FED's don't have a shadow feature.  Once you set them, they apply to the next rising/falling edge sequence.  I eventually fixed the problem by setting a "FirstTimeRedFedFlag" each time I change switching states.  Each time I update the PWM's, I check the FirstTimeRedFedFlag.  If it is set, I leave the RED/FED unchanged.  In my case the RED/FED values are zero.  I reset the flag and next time through the PWM update routine the flag is not set so I update the RED/FED registers.  Not pretty but it works!  Thanks for your help and recommendations.