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.

TMS320F28379D: PWM Duty extending in multi-phase variable frequency operation

Part Number: TMS320F28379D
Other Parts Discussed in Thread: C2000WARE

Tool/software:

Hello I am working on a 3 phase 3 level Flying Capacitor Totem-pole Boost PFC in Inverter mode. We are operating in Triangle Conduction Mode (iTCM) where the switching frequency of each leg and duty cycles also vary. Since we need the PWMs to be correctly updated during each ISR interval, I have used the global load mode to latch the TBPRD, CMPA, CMPB and TBPHS registers. In phase A, I am using EPWM1 (inner switches) and EPWM2 (outer switches). I am facing an issue where if my TBPHS is larger than the CMPA for EPWM2, it keeps EPWM2A ON for a longer duration. I have attached waveforms of the same below. Please help me in resolving this.

  • Hello Pranav,

    So if I'm understanding the issue, you are missing an action qualifier due to the phase shift skipping over the action qualifier event. If this is the case, please see this related thread and let me know if this is applicable/can help resolve your issue: 

    https://e2e.ti.com/support/microcontrollers/c2000-microcontrollers-group/c2000/f/c2000-microcontrollers-forum/1312604/tms320f28379d-tms320f28379d-phase-shift-pwm-missing-pulse 

    Best Regards,

    Allison

  • Hello Allison,

    I am using the global load mode in one-shot configuration because my application is also variable frequency. Is this solution applicable for that?

    Regards,

    Pranav

  • Hi Pranav, 

    The loading scheme wouldn't affect this solution since the solution implements separate action qualifiers (T1/T2) on sync pulses to carry out actions that are missed under certain conditions (e.g. when the TBPHS > CMPA, causing the TBCTR to skip over an action qualifier event upon a sync pulse).

    To clarify, is the extended pulse occurring around when you have a sync pulse? (In case you didn't know or were curious, you can actually scope out the EPWM sync pulse "EXTSYNCOUT" by using the OUTPUT XBAR module which can be quite helpful in debug sometimes). In the thread I linked, you can read my description of the issue and assess if this applies to your situation.

    Best Regards,

    Allison

  • Hello Allison, can you verify if this logic in my code? I think I am missing out on something

    if(new_phase > new_cmp)
    {
        cmp = new_phase + 1;
        EPwm2Regs.AQTSRCSEL.bit.T1SEL = 7;              // EPWMxSYNCI chosen as T1 Event Source
        EPwm2Regs.AQCTLA.bit.CAU = AQ_SET;              
    }
    else
    {
        cmp = news_cmp;
        EPwm2Regs.AQTSRCSEL.bit.T1SEL = 0;              // Default source selected
        EPwm2Regs.AQCTLA.bit.CAU = AQ_CLEAR;
    }
    
    EPwm2Regs.CMPA.bit.CMPA = cmp;
    
    EPwm2Regs.TBPHS.bit.TBPHS = new_phase;
    
    EPwm1Regs.GLDCTL2.bit.OSHTLD = 1;        

  • Hi Pranav, 

    Thanks for following up. Please allow me another day to take a look and provide further guidance. 

    Best Regards,

    Allison

  • Hi Pranav, 

    Could you please send me your EPWM initializations so I can see the foundational settings (Time Base, Counter Compare values, Action Qualifiers etc.)? 

    Best Regards,

    Allison

  •     // PWM initialization
        
        EPwm1Regs.TBCTL.bit.CTRMODE = TB_COUNT_UP; // Count up
        EPwm1Regs.TBPRD = PWM_PERIOD;       // Set timer period
        EPwm1Regs.TBCTR = PWM_PERIOD/2;                 // Set time base counter to half period counts
        EPwm1Regs.TBCTL.bit.PHSEN = TB_DISABLE;    // Disable phase loading
        EPwm1Regs.TBCTL.bit.SYNCOSEL = 0x1;
        EPwm1Regs.TBPHS.bit.TBPHS = 0x0000;        // Phase is 0
    
        EPwm1Regs.CMPCTL.bit.SHDWAMODE = CC_SHADOW;
        EPwm1Regs.CMPCTL.bit.SHDWBMODE = CC_SHADOW;
        EPwm1Regs.CMPCTL.bit.LOADAMODE = CC_CTR_ZERO;
        EPwm1Regs.CMPCTL.bit.LOADBMODE = CC_CTR_ZERO;
        EPwm1Regs.TBCTL.bit.HSPCLKDIV = 0;   // Clock ratio to SYSCLKOUT
        EPwm1Regs.TBCTL.bit.CLKDIV = 0;
    
        EPwm1Regs.AQCTLA.bit.ZRO = AQ_SET;            // Set PWM1A on Zero
        EPwm1Regs.AQCTLA.bit.CAU = AQ_CLEAR;          // Clear PWM1A on event A,
        EPwm1Regs.DBCTL.bit.OUT_MODE = 0x3;
        EPwm1Regs.DBCTL.bit.POLSEL = 0x2;
        EPwm1Regs.DBFED.bit.DBFED = DT;
        EPwm1Regs.DBRED.bit.DBRED = DT;
    
        EPwm2Regs.TBCTL.bit.CTRMODE = TB_COUNT_UP; // Count up
        EPwm2Regs.TBPRD = PWM_PERIOD;       // Set timer period
        EPwm2Regs.TBCTR = PWM_PERIOD/2;           // Set time base counter to half period counts
        EPwm2Regs.TBCTL.bit.PHSEN = TB_ENABLE;    // Enable phase loading
        EPwm2Regs.TBCTL.bit.SYNCOSEL = 0x0;
        EPwm2Regs.TBPHS.bit.TBPHS= PWM_HALF_PERIOD;       // Phase is 180deg
    
        EPwm2Regs.CMPCTL.bit.SHDWAMODE = CC_SHADOW;
        EPwm2Regs.CMPCTL.bit.SHDWBMODE = CC_SHADOW;
        EPwm2Regs.CMPCTL.bit.LOADAMODE = CC_CTR_ZERO;
        EPwm2Regs.CMPCTL.bit.LOADBMODE = CC_CTR_ZERO;
        EPwm2Regs.TBCTL.bit.HSPCLKDIV = 0;   // Clock ratio to SYSCLKOUT
        EPwm2Regs.TBCTL.bit.CLKDIV = 0;
    
        EPwm2Regs.AQCTLA.bit.ZRO = AQ_SET;            // SET PWM2A on ZERO
        EPwm2Regs.AQCTLA.bit.CAU = AQ_CLEAR;              // CLEAR PWM2A on event A,
                                                        // up count
        EPwm2Regs.DBCTL.bit.OUT_MODE = 0x3;
        EPwm2Regs.DBCTL.bit.POLSEL = 0x2;
        EPwm2Regs.DBFED.bit.DBFED = DT;
        EPwm2Regs.DBRED.bit.DBRED = DT;
        
        EPwm1Regs.GLDCFG.bit.TBPRD_TBPRDHR = 1;
        EPwm1Regs.GLDCFG.bit.CMPA_CMPAHR = 1;
        EPwm1Regs.GLDCFG.bit.CMPB_CMPBHR = 1;
        EPwm1Regs.GLDCTL.bit.GLDMODE = 0;      // Global load on counter = 0
        EPwm1Regs.GLDCTL.bit.OSHTMODE = 1;     // One shot mode is active
        EPwm1Regs.GLDCTL.bit.GLD = 1;          // All shadow to active events are defined by GLDMODE
    
        EPwm2Regs.GLDCFG.bit.TBPRD_TBPRDHR = 1;
        EPwm2Regs.GLDCFG.bit.CMPA_CMPAHR = 1;
        EPwm2Regs.GLDCFG.bit.CMPB_CMPBHR = 1;
        EPwm2Regs.GLDCTL.bit.GLDMODE = 3;      // Global load on sync pulse
        EPwm2Regs.GLDCTL.bit.OSHTMODE = 1;     // One shot mode is active
        EPwm2Regs.GLDCTL.bit.GLD = 1;          // All shadow to active events are defined by GLDMODE
    
        EPwm2Regs.EPWMXLINK.bit.GLDCTL2LINK = 0;   // EPWM2 linked for global load to EPWM1
        EPwm2Regs.EPWMXLINK.bit.TBPRDLINK = 0;     // TBPRD write for EPWM1 simultaneously writes to EPWM2
        
    // In ISR
    
        EPwm1Regs.TBPRD = prd;
        EPwm1Regs.CMPA.bit.CMPA = new_cmp;              // Set PWM duty cycle
        
        EPwm2Regs.TBPRD = prd;
        
        if(new_phase > new_cmp)
        {
            cmp = new_phase + 1;
            EPwm2Regs.AQTSRCSEL.bit.T1SEL = 7;              // EPWMxSYNCI chosen as T1 Event Source
            EPwm2Regs.AQCTLA.bit.CAU = AQ_SET;              
        }
        else
        {
            cmp = news_cmp;
            EPwm2Regs.AQTSRCSEL.bit.T1SEL = 0;              // Default source selected
            EPwm2Regs.AQCTLA.bit.CAU = AQ_CLEAR;
        }
    
        EPwm2Regs.CMPA.bit.CMPA = cmp;
    
        EPwm2Regs.TBPHS.bit.TBPHS = new_phase;
    
        EPwm1Regs.GLDCTL2.bit.OSHTLD = 1;   

  • Thank you! Let me review and try to run a few test cases. I will follow up after.

    Best Regards,

    Allison

  • Hi Pranav,

    I stripped down the EPWM settings to isolate the issue and showcase a simple way you can to use the T1 Event to fulfill the missing action qualifier. The attached .c is taken from our DriverLib synchronization example ({C2000Ware}\driverlib\f2837xd\examples\cpu1\epwm\epwm_ex3_synchronization) and modified to do so. I left comments at the top explaining. Let me know if this is helpful or if you still have trouble implementing it!

    epwm_ex3_synchronization_MODIFIED.c
    // ePWM Phase Shift and T1 Workaround.
    //
    // This is the DriverLib epwm_ex3_synchronization example modified.
    //
    // This file configures EPWM1A to GPIO0 and and EPWM2A to GPIO2.
    // EPWM1A:
    //    - High on CTR = 0
    //    - Sync-Out pulse on CTR = CMPB = 500
    //    - Low on CMPA = 1000
    //    - PRD = 2000
    //
    // EPWM2A:
    //    - High on CTR = 0
    //    - Low on CMPA = 1000
    //    - PRD = 2000
    //    - TBPHS = 1500 (sync-in is EPWM1 sync-out = 500)
    //
    // The example demonstrates a missing EPWM2A action qualifier in the first cycle due to phase shift.
    // The workaround is implemented in main() by using the T1 AQ to carry out the missed action qualifier
    // due to TBPHS (1500) being greater than CMPA (1000) with phase shift (on CMPB - 500) occurring before
    // CMPA.
    //
    // To see the effect, place probes on GPIO0 and GPIO2.
    // Run the code with the workaround 'if' loop in main() commented out.
    // The result is an unwanted longer pulse in the first cycle of EPWM2A.
    // Un-comment the 'if' loop in main() and rerun to see the desired phase shift occur.
    //
    
    //
    // Included Files
    //
    #include "driverlib.h"
    #include "device.h"
    #include "board.h"
    
    #define EPWM_TIMER_TBPRD    2000
    #define EPWM_CMPA           1000
    #define EPWM_CMPB           1000
    #define EPWM_TBPHS          1500
    
    //
    // Function Prototypes
    //
    void initEPWM(uint32_t base);
    
    __interrupt void epwm1ISR(void);
    __interrupt void epwm2ISR(void);
    
    //
    // Main
    //
    void main(void)
    {
        //
        // Initialize device clock and peripherals
        //
        Device_init();
    
        //
        // Disable pin locks and enable internal pull ups.
        //
        Device_initGPIO();
    
        //
        // Initialize PIE and clear PIE registers. Disables CPU interrupts.
        //
        Interrupt_initModule();
    
        //
        // Initialize the PIE vector table with pointers to the shell Interrupt
        // Service Routines (ISR).
        //
        Interrupt_initVectorTable();
    
        //
        // Assign the interrupt service routines to ePWM interrupts
        //
        Interrupt_register(INT_EPWM1, &epwm1ISR);
        Interrupt_register(INT_EPWM2, &epwm2ISR);
    
        //
        // Configure GPIO0/1 , GPIO2/3 and GPIO4/5 and GPIO6/7 as
        // ePWM1A/1B, ePWM2A/2B, ePWM3A/3B, ePWM4A/4B pins respectively, set up output XBAR for extsyncout on GPIO24
        //
        Board_init();
    
        // Disable sync(Freeze clock to PWM as well). GTBCLKSYNC is applicable
        // only for multiple core devices. Uncomment the below statement if
        // applicable.
        //
        // SysCtl_disablePeripheral(SYSCTL_PERIPH_CLK_GTBCLKSYNC);
        SysCtl_disablePeripheral(SYSCTL_PERIPH_CLK_TBCLKSYNC);
    
        //
        // Initialize PWM1 without phase shift as master
        //
        initEPWM(myEPWM1_BASE);
    
        //
        // Initialize PWM2 with phase shift of 1500 TBCLKs
        //
        initEPWM(myEPWM2_BASE);
    
        EPWM_selectPeriodLoadEvent(myEPWM2_BASE, EPWM_SHADOW_LOAD_MODE_SYNC);
        EPWM_setPhaseShift(myEPWM2_BASE, EPWM_TBPHS);
        EPWM_setTimeBaseCounter(myEPWM2_BASE, 0);
    
        //
        // ePWM1 SYNCO is generated on CTR = CMPB = 500
        //
        EPWM_setSyncOutPulseMode(EPWM1_BASE, EPWM_SYNC_OUT_PULSE_ON_COUNTER_COMPARE_B);
    
        //
        // ePWM2 uses the ePWM 1 SYNCO as its SYNCIN.
        // ePWM2 SYNCO is generated from its SYNCIN, which is ePWM1 SYNCO
        //
        EPWM_setSyncOutPulseMode(myEPWM2_BASE, EPWM_SYNC_OUT_PULSE_ON_EPWMxSYNCIN);
    
        //
        // Enable all phase shifts.
        //
        EPWM_enablePhaseShiftLoad(myEPWM2_BASE);
    
        //
        // Enable sync and clock to PWM
        //
        SysCtl_enablePeripheral(SYSCTL_PERIPH_CLK_TBCLKSYNC);
    
        //
        // Enable ePWM interrupts
        //
        Interrupt_enable(INT_EPWM1);
        Interrupt_enable(INT_EPWM2);
    
        //
        // Enable Global Interrupt (INTM) and realtime interrupt (DBGM)
        //
        EINT;
        ERTM;
    
        //
        // IDLE loop.
        //
        for(;;)
        {
            //
            // WORKAROUND LOOP. Check if TBPHS is greater than CMPA.
            // Un-comment this section to implement the workaround.
            //
    //        if(EPWM_TBPHS > EPWM_CMPA)
    //        {
    //            //
    //            // If true, then use the sync-in pulse as the T1 action qualifier event and perform the action that would be skipped by the phase shift.
    //            //
    //            EPWM_setActionQualifierT1TriggerSource(myEPWM2_BASE, EPWM_AQ_TRIGGER_EVENT_TRIG_EPWM_SYNCIN);
    //            EPWM_setActionQualifierAction(myEPWM2_BASE, EPWM_AQ_OUTPUT_A, EPWM_AQ_OUTPUT_LOW, EPWM_AQ_OUTPUT_ON_T1_COUNT_UP);
    //        }
    //        else
    //        {
    //            //
    //            // If false, then T1 action is not needed, so do nothing on T1 events.
    //            //
    //            EPWM_setActionQualifierAction(myEPWM2_BASE, EPWM_AQ_OUTPUT_A, EPWM_AQ_OUTPUT_NO_CHANGE, EPWM_AQ_OUTPUT_ON_T1_COUNT_UP);
    //        }
    
        }
    }
    
    //
    // epwm1ISR - ePWM 1 ISR
    //
    __interrupt void epwm1ISR(void)
    {
        //
        // Clear INT flag for this timer
        //
        EPWM_clearEventTriggerInterruptFlag(myEPWM1_BASE);
    
        //
        // Acknowledge interrupt group
        //
        Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP3);
    }
    
    //
    // epwm2ISR - ePWM 2 ISR
    //
    __interrupt void epwm2ISR(void)
    {
        //
        // Clear INT flag for this timer
        //
        EPWM_clearEventTriggerInterruptFlag(myEPWM2_BASE);
    
        //
        // Acknowledge interrupt group
        //
        Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP3);
    }
    
    
    void initEPWM(uint32_t base)
    {
        //
        // Set-up TBCLK
        //
        EPWM_setTimeBasePeriod(base, EPWM_TIMER_TBPRD);
        EPWM_setPhaseShift(base, 0U);
        EPWM_setTimeBaseCounter(base, 0U);
    
        //
        // Set Compare values
        //
        EPWM_setCounterCompareValue(base,
                                    EPWM_COUNTER_COMPARE_A,
                                    EPWM_CMPA);
        EPWM_setCounterCompareValue(base,
                                    EPWM_COUNTER_COMPARE_B,
                                    EPWM_CMPB);
    
        //
        // Set up counter mode
        //
        EPWM_setTimeBaseCounterMode(base, EPWM_COUNTER_MODE_UP);
    
        EPWM_disablePhaseShiftLoad(base);
    
        EPWM_setClockPrescaler(base,
                               EPWM_CLOCK_DIVIDER_8,
                               EPWM_HSCLOCK_DIVIDER_1);
    
        //
        // Set up shadowing
        //
        EPWM_setCounterCompareShadowLoadMode(base,
                                             EPWM_COUNTER_COMPARE_A,
                                             EPWM_COMP_LOAD_ON_CNTR_ZERO);
        EPWM_setCounterCompareShadowLoadMode(base,
                                             EPWM_COUNTER_COMPARE_B,
                                             EPWM_COMP_LOAD_ON_CNTR_ZERO);
    
        //
        // Set actions
        //
        EPWM_setActionQualifierAction(base,
                                      EPWM_AQ_OUTPUT_A,
                                      EPWM_AQ_OUTPUT_HIGH,
                                      EPWM_AQ_OUTPUT_ON_TIMEBASE_ZERO);
        EPWM_setActionQualifierAction(base,
                                      EPWM_AQ_OUTPUT_B,
                                      EPWM_AQ_OUTPUT_HIGH,
                                      EPWM_AQ_OUTPUT_ON_TIMEBASE_ZERO);
        EPWM_setActionQualifierAction(base,
                                      EPWM_AQ_OUTPUT_A,
                                      EPWM_AQ_OUTPUT_LOW,
                                      EPWM_AQ_OUTPUT_ON_TIMEBASE_UP_CMPA);
        EPWM_setActionQualifierAction(base,
                                      EPWM_AQ_OUTPUT_B,
                                      EPWM_AQ_OUTPUT_LOW,
                                      EPWM_AQ_OUTPUT_ON_TIMEBASE_UP_CMPA);
    
    
        //
        // Interrupt where we will change the Compare Values
        // Select INT on Time base counter zero event,
        // Enable INT, generate INT on 1rd event
        //
        EPWM_setInterruptSource(base, EPWM_INT_TBCTR_ZERO);
        EPWM_enableInterrupt(base);
        EPWM_setInterruptEventCount(base, 1U);
    }
    

    Best Regards,

    Allison

  • Hello Allison,

    This helped me resolve the issue. Thank you so much for the timely help!