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.

TMS320F28388D: How to avoid the jitter when EPWMxSYNCO source is set to CTR = 0.

Part Number: TMS320F28388D


Hello,

In Technical Reference Manual, it is mentioned “when high-resolution period mode is enabled,

an EPWMxSYNC pulse will introduce +/- 1 - 2 cycle jitter to the PWM. For this reason,

EPWMxSYNCO source should not be set to CTR = 0 or CTR = CMPB. Otherwise, the jitter will occur on every PWM cycle with the synchronization pulse.”

According to this explanation, could you answer me the following questions?

1) Is there a method to avoid the jitter? if it is needed to set EPWMxSYNCO source to CTR = 0 for updating a parameter every PWM cycle in my experiment,

2) If high-resolution period mode is disabled, will the jitter occur when EPWMxSYNCO is set to CTR = 0.

Thank you.

Best regards,

Wenjing

  • 1) Is there a method to avoid the jitter? if it is needed to set EPWMxSYNCO source to CTR = 0 for updating a parameter every PWM cycle in my experiment,

    2) If high-resolution period mode is disabled, will the jitter occur when EPWMxSYNCO is set to CTR = 0.

    1) If HR mode is enabled, on every SYNCIN there will be a jitter. You have to send the SYNC PULSE ONCE, then continue to operate everything. It will all stay in perfect sync. Once absolutely necessary due to a phase shift or change in the system requirement, you can send another SYNCIN pulse.

    2) Without HR, you are good to sync as much as you want!

  • Hi, Nima

    Thank you for reply.

    1) I will try it.

    2) Without HR, you are good to sync as much as you want!

    I tested the example of hrpwm_ex4_deadband_sfo_v8.c in ”… device_support\f2838x\examples\cpu1\hrpwm” with the following changes.

    As a result, 2 PWM cycle jitters occurred. please see the picture and attached C source.

    #define HR_ENABLED          0   // disable HR behavior

     (*ePWM[PWM2]).TBPHS.bit.TBPHS = 0x00; // in interrupt function

     

    Could you let me know what caused the jitter.

    Thanks.

    //###########################################################################
    //
    // FILE:    hrpwm_ex4_deadband_sfo_v8.c
    //
    // TITLE:   f2838x Device HRPWM SFO V8 High-Resolution Dead-Band
    //          (up-down count) example
    //
    //! \addtogroup cpu01_example_list
    //! <h1> HRPWM Dead-Band Example (hrpwm_deadband_sfo_v8)</h1>
    //! This program requires the f2838x header files, including the
    //! following files required for this example:
    //! sfo_v8.h and SFO_v8_fpu_lib_build_c28_eabi.lib
    //!
    //! Monitor ePWM1 & ePWM2 A/B pins on an oscilloscope
    //!
    //! DESCRIPTION:
    //!
    //! This example sweeps the ePWM frequency while maintaining a duty cycle of
    //! ~50% in ePWM up-down count mode. In addition, this example demonstrates
    //! ePWM high-resolution dead-band (HRDB) capabilities utilizing the HRPWM
    //! extension of the respective ePWM module.
    //!
    //! This example calls the following TI's micro-edge positioner (MEP) Scale
    //! Factor Optimizer (SFO) software library V8 functions:
    //!
    //! \b int \b SFO(); \n
    //! updates MEP_ScaleFactor dynamically when HRPWM is in use
    //! updates HRMSTEP register (exists only in EPwm1Regs register space)
    //! which updates MEP_ScaleFactor value
    //! - returns 0 if not complete for the specified channel
    //! - returns 1 when complete for the specified channel
    //! - returns 2 if error: MEP_ScaleFactor is greater than maximum value of 255
    //! (Auto-conversion may not function properly under this condition)
    //!
    //! This example is intended to demonstrate the HRPWM capability to control
    //! the dead-band falling edge delay (FED) and rising edge delay (RED).
    //!
    //! ePWM1 and ePWM2 A/B channels will have fine edge movement due to HRPWM
    //! control.
    //!
    //!=======================================================================
    //! NOTE: For more information on using the SFO software library, see the
    //! f2838x High-Resolution Pulse Width Modulator (HRPWM) Chapter in the
    //! Technical Reference Manual.
    //!=======================================================================
    //!
    //! To load and run this example:
    //! -# **!!IMPORTANT!!**
    //! -# Run this example at maximum SYSCLKOUT
    //! -# Activate Real time mode
    //! -# Run the "AddWatchWindowVars_HRPWM.js" script from the scripting console
    //!    (View -> Scripting Console) to populate watch window by using the
    //!    command:
    //!    loadJSFile <path_to_JS_file>/AddWatchWindowVars_HRPWM.js
    //! -# Run the code
    //! -# Watch ePWM A / B channel waveforms on an oscilloscope
    //! -# In the watch window:
    //!    Change the variable InputPeriodInc to increase or decrease the frequency
    //!    sweep rate. Setting InputPeriodInc = 0 will stop the sweep while
    //!    allowing other variables to be manipulated and updated in real time.
    //! -# In the watch window:
    //!    Change values for registers EPwm1Regs.DBRED/EPwm2Regs.DBRED to see
    //!    changes in rising edge dead-bands for ePWM1 and ePWM2 respectively.
    //!    Alternatively, changing values for registers
    //!    EPwm1Regs.DBFED/EPwm2Regs.DBFED will change falling edge dead-bands for
    //!    ePWM1 and ePWM2. Changing these values will alter the duty cycle
    //!    percentage for their respective ePWM modules.
    //!    **!!NOTE!!** - DBRED/DBFED values should never be set below 4.
    //!                   Do not set these values to 0, 1, 2 or 3.
    //! -# In the watch window:
    //!    Change values for registers
    //!    EPwm1Regs.DBREDHR.bit.DBREDHR/EPwm2Regs.DBREDHR.bit.DBREDHR to increase
    //!    or decrease number of resolvable high-resolution steps at the
    //!    dead-band rising edge. Alternatively, change values for
    //!    EPwm1Regs.DBFEDHR.bit.DBFEDHR/EPwm2Regs.DBFEDHR.bit.DBFEDHR to change
    //!    the number of resolvable steps at the dead-band falling edge for ePWM1
    //!    and ePWM2 respectively.
    //
    //###########################################################################
    // $TI Release: F2838x Support Library v3.02.00.00 $
    // $Release Date: Tue May 26 17:21:56 IST 2020 $
    // $Copyright:
    // Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com/
    //
    // Redistribution and use in source and binary forms, with or without 
    // modification, are permitted provided that the following conditions 
    // are met:
    // 
    //   Redistributions of source code must retain the above copyright 
    //   notice, this list of conditions and the following disclaimer.
    // 
    //   Redistributions in binary form must reproduce the above copyright
    //   notice, this list of conditions and the following disclaimer in the 
    //   documentation and/or other materials provided with the   
    //   distribution.
    // 
    //   Neither the name of Texas Instruments Incorporated nor the names of
    //   its contributors may be used to endorse or promote products derived
    //   from this software without specific prior written permission.
    // 
    // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
    // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
    // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
    // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
    // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
    // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
    // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
    // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    // $
    //###########################################################################
    
    //
    // Included Files
    //
    #include "f28x_project.h"
    #include "sfo_v8.h"
    
    //
    // Defines
    //
    #define PWM_CH              2   // # of PWM channels used in example
    #define HR_ENABLED          0   // 1 = HR behavior
                                    // 0 = non-HR behavior
    
    //
    // Globals
    //
    Uint16 UpdateFine, status;
    Uint16 temp_REM2 = 0, temp_PHS2, PhaseFine2;
    
    Uint32 CountUpdatefine = 0, CountUpdateMax = 0;
    Uint16 Period = 0, PeriodFine = 0, PeriodOdd = 0;
    Uint16 PeriodIncrement = 0, PeriodFineIncrement = 0;
    Uint32 InputPeriodInc = 0;
    Uint32 PeriodFine_temp = 0;
    Uint16 PWM1 = 1;
    Uint16 PWM2 = 2;
    Uint16 PeriodMax = 600, PeriodMin = 360;
    
    //
    // Array of pointers to EPwm register structures:
    // *ePWM[0] is defined as dummy value not used in the example
    //
    volatile struct EPWM_REGS *ePWM[PWM_CH + 1] = {0, &EPwm1Regs, &EPwm2Regs};
    
    int MEP_ScaleFactor;
    
    //
    // Function Prototypes
    //
    void HRPWM1_Config(int);
    void HRPWM2_Config(int);
    void FreqCtl_func(void);
    interrupt void PRDEQfix_ISR(void);
    void error(void);
    
    //
    // Main
    //
    void main(void)
    {
        //
        // Initialize System Control for Control and Analog Subsystems.
        // Enable Peripheral Clocks.
        // This example function is found in the f2838x_SysCtrl.c file.
        //
        InitSysCtrl();
    
        //
        // Disable CPU interrupts
        //
        DINT;
    
        //
        // Initialize the PIE control registers to their default state.
        // The default state is all PIE interrupts disabled and flags
        // are cleared.
        // This function is found in the f2838x_PieCtrl.c file.
        //
        InitPieCtrl();
    
        //
        // Disable CPU interrupts and clear all CPU interrupt flags
        //
        IER = 0x0000;
        IFR = 0x0000;
    
        //
        // Initialize the PIE vector table with pointers to the shell Interrupt
        // Service Routines (ISR).
        // This will populate the entire table, even if the interrupt
        // is not used in this example. This is useful for debug purposes.
        // The shell ISR routines are found in f2838x_defaultisr.c.
        // This function is found in f2838x_PieVect.c.
        //
        InitPieVectTable();
    
        //
        // Set address of ISR in PIE vector table
        //
        EALLOW;
        PieVectTable.EPWM1_INT = &PRDEQfix_ISR;
        EDIS;
    
        //
        // Calling SFO() updates the HRMSTEP register with calibrated
        // MEP_ScaleFactor. HRMSTEP must be populated with a scale factor value
        // prior to enabling high resolution period control.
        //
        status = SFO_INCOMPLETE;
        while(status == SFO_INCOMPLETE)
        {
            //
            // Call until complete
            //
            status = SFO();
            if(status == SFO_ERROR)
            {
                error();
            }
        }
    
        EALLOW;
        CpuSysRegs.PCLKCR0.bit.TBCLKSYNC = 0;
        EDIS;
    
        //
        // Init HRPWM1/HRPWM2
        //
        InitEPwm1Gpio();
        InitEPwm2Gpio();
        HRPWM1_Config(360);  // EPwm1 target, Period = 360
        HRPWM2_Config(360);  // EPwm2 target, Period = 360
    
        EALLOW;
        CpuSysRegs.PCLKCR0.bit.TBCLKSYNC = 1;  // Resync PWM timebase clock
        (*ePWM[PWM1]).GLDCTL2.bit.OSHTLD = 1;  // This should also write to
                                               // GLDCTL2 of PWM2
        EDIS;
    
        //
        // Configure ePWM1 to generate interrupts on period match
        //
        (*ePWM[PWM1]).ETSEL.bit.INTSEL = 1;   // Interrupt on counter zero match
        (*ePWM[PWM1]).ETSEL.bit.INTEN = 1;    // Enable peripheral interrupt
        (*ePWM[PWM1]).ETPS.bit.INTPRD = 1;    // Generate interrupt on every event
        PieCtrlRegs.PIEIER3.bit.INTx1 = 1;    // Enable ePWM1 interrupt in PIE
    
        IER |= 0x0004;                        // Enable core INT #3
        EINT;                                 // Clear global interrupt mask
    
        UpdateFine = 0;                       // Disable continuous updates
        Period = 360;
        PeriodFine = 0x0;
        CountUpdateMax = 0x0FFFF;
        CountUpdatefine = CountUpdateMax;
    
        //
        // Watch window variable to modify the rate of the frequency sweep
        //
        InputPeriodInc = 6553;
    
        for(;;)
        {
            while(UpdateFine == 0)
            {
                if(CountUpdatefine >= CountUpdateMax)
                {
                    if(Period < PeriodMax)
                    {
                        //
                        // Perform sweep
                        //
                        PeriodIncrement = InputPeriodInc >> 16;
                        PeriodFineIncrement = (Uint16)InputPeriodInc;
                        Period = Period + PeriodIncrement;
                        PeriodFine_temp = (Uint32)PeriodFine +
                                                     (Uint32)PeriodFineIncrement;
                        if(PeriodFine_temp >= 0x10000)
                        {
                            PeriodFine_temp = PeriodFine_temp - 0x10000;
                            Period = Period + 1;
                        }
    
                        //
                        // Period is odd - CMP is divide by 2 for 50% duty
                        //
                        if(Period % 2 == 1)
                        {
                            PeriodOdd = 1;
                        }
                        else
                        {
                            PeriodOdd = 0;
                        }
    
                        PeriodFine = (Uint16) PeriodFine_temp;
    
                        //
                        // Update PWM values for non-zero increment
                        //
                        if(InputPeriodInc != 0)
                        {
                            FreqCtl_func();
                        }
                    }
                    else
                    {
                        Period = PeriodMin;
                        PeriodFine = 0;
                        if(InputPeriodInc != 0)
                        {
                            FreqCtl_func();
                        }
                    }
                    CountUpdatefine = 0;
                }
                CountUpdatefine++;
            }
        }
    }
    
    //
    // HRPWM1_Config - ePWM1 register configuration with HRPWM
    //                 ePWM1A toggle low/high with MEP control on Rising edge
    //
    void HRPWM1_Config(PeriodConfig)
    {
        (*ePWM[PWM1]).TBCTL.bit.PRDLD = TB_SHADOW;  // Set shadow load
    
        //
        // PWM frequency = 1 / PeriodConfig
        //
        (*ePWM[PWM1]).TBPRD = PeriodConfig;
    
        //
        // Set duty 50% initially and initialize HRPWM extension
        //
        (*ePWM[PWM1]).CMPA.bit.CMPA = PeriodConfig / 2;
        (*ePWM[PWM1]).CMPA.bit.CMPAHR = (1 << 8);
        (*ePWM[PWM1]).CMPB.bit.CMPB = PeriodConfig / 2;
        (*ePWM[PWM1]).CMPB.bit.CMPBHR = (1 << 8);
        (*ePWM[PWM1]).TBPHS.all = 0;
        (*ePWM[PWM1]).TBCTR = 0;
    
        (*ePWM[PWM1]).TBCTL.bit.CTRMODE = TB_COUNT_UPDOWN;
        (*ePWM[PWM1]).TBCTL.bit.PHSEN = TB_DISABLE;   // ePWM1 is the Master
        (*ePWM[PWM1]).EPWMSYNCINSEL.all = SYNC_IN_SRC_DISABLE_ALL;
        (*ePWM[PWM1]).EPWMSYNCOUTEN.all = 2;          // syncout on ctr = zero
        (*ePWM[PWM1]).TBCTL.bit.HSPCLKDIV = TB_DIV1;
        (*ePWM[PWM1]).TBCTL.bit.CLKDIV = TB_DIV1;
    
        //
        // LOAD CMPA on CTR = ZERO_PRD
        //
        (*ePWM[PWM1]).CMPCTL.bit.LOADAMODE = CC_CTR_ZERO_PRD;
        (*ePWM[PWM1]).CMPCTL.bit.LOADBMODE = CC_CTR_ZERO_PRD;
    
        (*ePWM[PWM1]).CMPCTL.bit.SHDWAMODE = CC_SHADOW;
        (*ePWM[PWM1]).CMPCTL.bit.SHDWBMODE = CC_SHADOW;
    
        (*ePWM[PWM1]).AQCTLA.bit.CAU = AQ_SET;
        (*ePWM[PWM1]).AQCTLA.bit.CAD = AQ_CLEAR;
    
        EALLOW;
    #if HR_ENABLED
        (*ePWM[PWM1]).HRCNFG.all = 0x1353;
    
        //
        // Turn on high-resolution period control
        //
        (*ePWM[PWM1]).HRPCTL.bit.HRPE = 1;
    
        //
        // Synchronize high resolution phase to start HR period
        //
        (*ePWM[PWM1]).TBCTL.bit.SWFSYNC = 1;
    #endif
    
        (*ePWM[PWM1]).GLDCFG.bit.CMPA_CMPAHR = 1;
        (*ePWM[PWM1]).GLDCFG.bit.CMPB_CMPBHR = 1;
    
        //
        // Load on CTR = ZERO_PRD (2) / ZERO (1)
        //
        (*ePWM[PWM1]).GLDCTL.bit.GLDMODE = 2;
    
        //
        // One shot mode and global load enabled
        //
        (*ePWM[PWM1]).GLDCTL.bit.OSHTMODE = 1;
        (*ePWM[PWM1]).GLDCTL.bit.GLD = 1;
    
        //
        // Write to PWM1 GLDCTL2 will result in simultaneous write to PWM2 GLDCTL2
        //
        (*ePWM[PWM1]).EPWMXLINK.bit.GLDCTL2LINK = PWM1 - 1;
        (*ePWM[PWM1]).DBCTL.bit.OUT_MODE = DB_FULL_ENABLE;
        (*ePWM[PWM1]).DBCTL.bit.POLSEL = DB_ACTV_HIC;
        (*ePWM[PWM1]).DBCTL.bit.IN_MODE = DBA_ALL;
        (*ePWM[PWM1]).DBCTL.bit.SHDWDBREDMODE = 1;
        (*ePWM[PWM1]).DBCTL.bit.SHDWDBFEDMODE = 1;
        (*ePWM[PWM1]).DBCTL.bit.LOADREDMODE = 0;    // Load on Counter == 0
        (*ePWM[PWM1]).DBCTL.bit.LOADFEDMODE = 0;    // Load on Counter == 0
        (*ePWM[PWM1]).DBCTL.bit.HALFCYCLE = 1;
        (*ePWM[PWM1]).DBRED.bit.DBRED = 4;
        (*ePWM[PWM1]).DBREDHR.bit.DBREDHR = 0x0;
        (*ePWM[PWM1]).DBFED.bit.DBFED = 4;
        (*ePWM[PWM1]).DBFEDHR.bit.DBFEDHR = 0x0;
    
        (*ePWM[PWM1]).HRCNFG2.bit.EDGMODEDB = HR_BEP;    // DBREDHR and DBFEDHR
        (*ePWM[PWM1]).HRCNFG2.bit.CTLMODEDBRED = 0;      // Load on ZRO
        (*ePWM[PWM1]).HRCNFG2.bit.CTLMODEDBFED = 0;      // Load on ZRO
        (*ePWM[PWM1]).DBREDHR.bit.DBREDHR = (0 << 9);
    
        EDIS;
    }
    
    //
    // HRPWM2_Config - ePWM2 register configuration with HRPWM
    //                 ePWM2A toggle low/high with MEP control on Rising edge
    //
    void HRPWM2_Config(PeriodConfig)
    {
        (*ePWM[PWM2]).TBCTL.bit.PRDLD = TB_SHADOW;  // Set shadow load
    
        //
        // PWM frequency = 1 / PeriodConfig
        //
        (*ePWM[PWM2]).TBPRD = PeriodConfig;
    
        //
        // Set duty 50% initially and initialize HRPWM extension
        //
        (*ePWM[PWM2]).CMPA.bit.CMPA = PeriodConfig / 2;
        (*ePWM[PWM2]).CMPA.bit.CMPAHR = (1 << 8);
        (*ePWM[PWM2]).CMPB.bit.CMPB = PeriodConfig / 2;
        (*ePWM[PWM2]).CMPB.bit.CMPBHR = (1 << 8);
        (*ePWM[PWM2]).TBPHS.all = 0;
        (*ePWM[PWM2]).TBCTR = 0;
    
        (*ePWM[PWM2]).TBCTL.bit.CTRMODE = TB_COUNT_UPDOWN;
        (*ePWM[PWM2]).TBCTL.bit.PHSEN = TB_DISABLE;   // ePWM1 is the Master
        (*ePWM[PWM2]).EPWMSYNCINSEL.all = SYNC_IN_SRC_SYNCOUT_EPWM1;
        (*ePWM[PWM2]).EPWMSYNCOUTEN.all = 1;          // syncout on ctr = zero
        (*ePWM[PWM2]).TBCTL.bit.HSPCLKDIV = TB_DIV1;
        (*ePWM[PWM2]).TBCTL.bit.CLKDIV = TB_DIV1;
    
        //
        // LOAD CMPA on CTR = 0
        //
        (*ePWM[PWM2]).CMPCTL.bit.LOADAMODE = CC_CTR_ZERO_PRD;
        (*ePWM[PWM2]).CMPCTL.bit.LOADBMODE = CC_CTR_ZERO_PRD;
        (*ePWM[PWM2]).CMPCTL.bit.SHDWAMODE = CC_SHADOW;
        (*ePWM[PWM2]).CMPCTL.bit.SHDWBMODE = CC_SHADOW;
    
        (*ePWM[PWM2]).AQCTLA.bit.CAU = AQ_SET;
        (*ePWM[PWM2]).AQCTLA.bit.CAD = AQ_CLEAR;
    
        EALLOW;
    #if HR_ENABLED
        (*ePWM[PWM2]).HRCNFG.all = 0x1353;
    
        //
        // Turn on high-resolution period control
        //
        (*ePWM[PWM2]).HRPCTL.bit.HRPE = 1;
    
        //
        // Synchronize high resolution phase to start HR period
        //
        (*ePWM[PWM2]).TBCTL.bit.SWFSYNC = 1;
    #endif
    
        (*ePWM[PWM2]).TBCTL.bit.PHSDIR = 1;        // Count up after SYNC event
        (*ePWM[PWM2]).TBPHS.bit.TBPHS = 180;
    
        (*ePWM[PWM2]).GLDCFG.bit.CMPA_CMPAHR = 1;
        (*ePWM[PWM2]).GLDCFG.bit.CMPB_CMPBHR = 1;
    
        //
        // Load on CTR = ZERO_PRD (2) / ZERO (1)
        //
        (*ePWM[PWM2]).GLDCTL.bit.GLDMODE = 2;
    
        //
        // One shot mode and global load enabled
        //
        (*ePWM[PWM2]).GLDCTL.bit.OSHTMODE = 1;
        (*ePWM[PWM2]).GLDCTL.bit.GLD = 1;
    
        //
        // Write to PWM1 GLDCTL2 will result in simultaneous write to PWM2 GLDCTL2
        //
        (*ePWM[PWM2]).EPWMXLINK.bit.GLDCTL2LINK = PWM1 - 1;
        (*ePWM[PWM2]).DBCTL.bit.OUT_MODE = DB_FULL_ENABLE;
        (*ePWM[PWM2]).DBCTL.bit.POLSEL = DB_ACTV_HIC;
        (*ePWM[PWM2]).DBCTL.bit.IN_MODE = DBA_ALL;
        (*ePWM[PWM2]).DBCTL.bit.SHDWDBREDMODE = 1;
        (*ePWM[PWM2]).DBCTL.bit.SHDWDBFEDMODE = 1;
        (*ePWM[PWM2]).DBCTL.bit.LOADREDMODE = 0;    // Load on Counter == 0
        (*ePWM[PWM2]).DBCTL.bit.LOADFEDMODE = 0;    // Load on Counter == 0
        (*ePWM[PWM2]).DBCTL.bit.HALFCYCLE = 1;
        (*ePWM[PWM2]).DBRED.bit.DBRED = 4;
        (*ePWM[PWM2]).DBREDHR.bit.DBREDHR = 0x0;
        (*ePWM[PWM2]).DBFED.bit.DBFED = 4;
        (*ePWM[PWM2]).DBFEDHR.bit.DBFEDHR = 0x0;
    
        (*ePWM[PWM2]).HRCNFG2.bit.EDGMODEDB = HR_BEP;    // DBREDHR and DBFEDHR
        (*ePWM[PWM2]).HRCNFG2.bit.CTLMODEDBRED = 0;      // Load on ZRO
        (*ePWM[PWM2]).HRCNFG2.bit.CTLMODEDBFED = 0;      // Load on ZRO
        (*ePWM[PWM2]).DBREDHR.bit.DBREDHR = (0 << 9);
    
        EDIS;
    }
    
    //
    // FreqCtl_func - Frequency modulation & phase sync function
    // This function is called only if frequency sweep is enabled
    //
    void FreqCtl_func(void)
    {
        if(PeriodOdd)
        {
            //
            // Add 0.5 if period is odd
            //
            (*ePWM[PWM1]).CMPA.bit.CMPAHR = (PeriodFine >> 1) + 0x7FFF;
            (*ePWM[PWM2]).CMPA.bit.CMPAHR = (PeriodFine >> 1) + 0x7FFF;
            (*ePWM[PWM1]).CMPB.bit.CMPBHR = (PeriodFine >> 1) + 0x7FFF;
            (*ePWM[PWM2]).CMPB.bit.CMPBHR = (PeriodFine >> 1) + 0x7FFF;
        }
        else
        {
            (*ePWM[PWM1]).CMPA.bit.CMPAHR = PeriodFine >> 1;
            (*ePWM[PWM2]).CMPA.bit.CMPAHR = PeriodFine >> 1;
            (*ePWM[PWM1]).CMPB.bit.CMPBHR = PeriodFine >> 1;
            (*ePWM[PWM2]).CMPB.bit.CMPBHR = PeriodFine >> 1;
        }
    
        (*ePWM[PWM1]).CMPA.bit.CMPA = (Period >> 1) + 1;
        (*ePWM[PWM2]).CMPA.bit.CMPA = (Period >> 1) + 1;
        (*ePWM[PWM1]).CMPB.bit.CMPB = (Period >> 1) + 1;
        (*ePWM[PWM2]).CMPB.bit.CMPB = (Period >> 1) + 1;
    
        temp_PHS2 = (Period >> 1);
    
        switch(PeriodOdd)
        {
            case 1:
                //
                // Accounting for divide by 2 = 0.5
                //
                PhaseFine2 =  0xFF - (PeriodFine >> 9) - 0x7F;
                break;
    
            default:
                PhaseFine2 =  0xFF - (PeriodFine >> 9);
                break;
        }
    
        //
        // No fractional phase shift to account for
        //
        temp_REM2 =  (Uint16) 0x100 + PhaseFine2;
        UpdateFine = 1;
    }
    
    //
    // PRDEQfix_ISR - ISR for Translator remainder calculations
    //
    interrupt void PRDEQfix_ISR(void)
    {
        EALLOW;
        if(UpdateFine == 1)
        {
            //
            // This should also write to GLDCTL2 of PWM2, PWM3 and PWM4
            //
            (*ePWM[PWM1]).GLDCTL2.bit.OSHTLD = 1;
    
            //
            // TBCTR phase load on SYNC (required for updown count HR control)
            //
            (*ePWM[PWM2]).TBCTL.bit.PHSEN = TB_ENABLE;
    
            //
            // Coarse phase offset relative to ePWM1
            //
            (*ePWM[PWM2]).TBPHS.bit.TBPHS = 0x00;
            (*ePWM[PWM2]).TRREM.bit.TRREM = temp_REM2;
    
            (*ePWM[PWM2]).TBPRDHR = PeriodFine;
            (*ePWM[PWM2]).TBPRD = Period;
    
            (*ePWM[PWM1]).TBPRDHR = PeriodFine;
            (*ePWM[PWM1]).TBPRD = Period;
            (*ePWM[PWM1]).TRREM.bit.TRREM = 0x100;
            UpdateFine = 0;
        }
        else
        {
            (*ePWM[PWM2]).TBCTL.bit.PHSEN = TB_DISABLE;
        }
    
        //
        // Re-initialize for next PWM interrupt
        //
        PieCtrlRegs.PIEACK.all = PIEACK_GROUP3;     // Acknowledge PIE interrupt
        (*ePWM[PWM1]).ETCLR.bit.INT = 1;            // Clear interrupt bit
        EDIS;
    }
    
    void error (void)
    {
        ESTOP0;                                     // Stop here and handle error
    }
    
    //
    // End of file
    //
    

  • That is expected. Thats not JITTER but the logic delay inside the chip. Please enable the PHASE SHIFT LOAD and compensate the delay cycles in the TBPHS value.

    Nima

  • Thank you for your answer.
    But followings are not clear to me.

    1. Does it mean the logic delay always exists?
    2. Does the logic delay occur in the slave PWM modules from the mater PWM module when using synchronization?
    3. Is the logic delay a constant as 2 cycles? Or the amount of delay depends on the type of the microcontroller individually?

    I really appreciate your help.

    Wenjing

  • Yes there is always a logic delay.

    If you have PHSEN, you need to set the TBPHS value to compensate the logic delay. It's very simple. The delay is in the sync receivers.

    The delay is constant.

    Nima

  • Thank you for reply.

    Yes, I am using the way you mentioned as setting TBPHS to compensate the delay. It is also working fine.

    In my experiment, the logic delay is 2 cycles.

    You replied the delay is constant. Can I understand that the constant is always 2 cycles with C2000 series such as F28388, F28379, F28335, etc. ?

    Is there any document for explaining this issue?

    Wenjing

  • Yes in my code I also had the value of 2, which mean the delay is actually 3! 0-1-2

    Nima

  • Sorry, I did not get your point. Could you make it clear?

    I would be grateful if you could provide any detailed explanation for this issue officially?

    Since this issue is quite important to us.

  • There is a 2 CYCLE delay! Hence, you have to set the TBPHS value to 1. (Remember the counter goes ZERO, THEN ONE!)

  • Thank you. I got it.

    I believe you have an official description manual about the logic delay.

    Could you share it?

     

    Wenjing

  • Wenjing,

    Here is the code for perfect Sync without delay.

    Nima

    //#############################################################################
    //
    // FILE:   epwm_ex14_zero_phase.c
    //
    // TITLE:  ePWM Using The Synch Chain and Phase Shift.
    //
    //! \addtogroup driver_example_list
    //! <h1>ePWM Synchronization</h1>
    //!
    //! This example configures ePWM1, ePWM2 as follows
    //!  - ePWM1 without phase shift as master
    //!  - ePWM2 with phase shift of 0 TBCLKs
    //!
    //! \b External \b Connections \n
    //! - GPIO0 EPWM1A
    //! - GPIO1 EPWM1B
    //! - GPIO2 EPWM2A
    //! - GPIO3 EPWM2B
    //!
    //! \b Watch \b Variables \n
    //! - None.
    //
    //#############################################################################
    // $TI Release: $
    // $Release Date: $
    // $Copyright: $
    //#############################################################################
    
    //
    // Included Files
    //
    #include "driverlib.h"
    #include "device.h"
    #include "board.h"
    
    #define USE_UPDOWN_COUNT    1
    
    #define EPWM_TIMER_TBPRD    2000
    
    //
    // 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
        //
        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 300 TBCLKs
        //
        initEPWM(myEPWM2_BASE);
        EPWM_selectPeriodLoadEvent(myEPWM2_BASE, EPWM_SHADOW_LOAD_MODE_SYNC);
    #if USE_UPDOWN_COUNT == 0
        EPWM_setPhaseShift(myEPWM2_BASE, 1);
    #else
        EPWM_setPhaseShift(myEPWM2_BASE, 1);
        EPWM_setCountModeAfterSync(myEPWM2_BASE, EPWM_COUNT_MODE_UP_AFTER_SYNC);
    #endif
        EPWM_setTimeBaseCounter(myEPWM2_BASE, 0);
    
    
        //
        // ePWM1 SYNCO is generated on CTR=0
        //
        EPWM_setSyncOutPulseMode(EPWM1_BASE, EPWM_SYNC_OUT_PULSE_ON_COUNTER_ZERO);
    
        //
        // 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. Just sit and loop forever (optional):
        //
        for(;;)
        {
    
        }
    }
    
    //
    // 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_TIMER_TBPRD/2);
        EPWM_setCounterCompareValue(base,
                                    EPWM_COUNTER_COMPARE_B,
                                    EPWM_TIMER_TBPRD/4);
    
        //
        // Set up counter mode
        //
    #if USE_UPDOWN_COUNT == 0
        EPWM_setTimeBaseCounterMode(base, EPWM_COUNTER_MODE_UP);
    #else
        EPWM_setTimeBaseCounterMode(base, EPWM_COUNTER_MODE_UP_DOWN);
    #endif
    
        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_CMPB);
    
    
        //
        // 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);
    }

  • The values are different for UP-COUNT and UP-DOWN COUNT

    I will add this to the TRM.

    UP COUNT Results

    UP-DOWN COUNT Results

  • You need to set the PHS to 0x1. For both UP-COUNT and UP-DOWN-COUNT.

    Sorry I just tested to get the values. I will update the TRM to reflect these values as I cant find it in the documentation.

    The logic delay is 2. So the phs value must be 0x1.

  • Hi, Nima

     

    Thank you for your support.

     

    Your test result is to set the PHS to 0x1 For both UP-COUNT and UP-DOWN-COUNT.

    But, I tested the example of hrpwm_ex4_deadband_sfo_v8.c in “… device_support\f2838x\examples\cpu1\hrpwm”

    with the following changes.

    #define HR_ENABLED          0   // disable HR behavior

     (*ePWM[PWM2]).TBPHS.bit.TBPHS = 0x02; // in interrupt function

    It results I need to set the PHS to 0x2 for UP-DOWN-COUNT.

    It also results the same as setting the PHS to 0x2 For both UP-COUNT and UP-DOWN-COUNT in my experiment.

    Could you double check the example and let me know your test result?

  • You are using F28388 device?

  • Yes, I am using  F28388D.

  • Sorry for the confusion. Yes your experiment is correct. For F2837xD, F2837xS and F28004x device, the delay between EPWM1 and EPWM2 is the 2 cycle delay hence the value written is 0x1.

  • Hi, Nima

    Thank you for confirmation.

    I have 2 further questions.

    1) The example of hrpwm_ex4_deadband_sfo_v8.c, it shows 2 cycle delay between epwm1 and epwm2,

     and It will be perfectly compensated by writing PHS = 0x2. Please see the waveform.

    Does it mean PHS = 0x2 for 2 cycle delay not 3 cycle?

    2)  Is it a fixed amount of delay regardless of the individual of  F28388?

    Wenjing

  • Please let me explain more about 2).

    We need product specification(official specification) about the amount of delay.

    Because it is a problem for us if there is a difference between F28388D#(S/N xxxx1) and F28388D#(S/N xxxx2) .

    If the delay is constant value, we will apply that setting. However if not, we need other solutions to sync PWM.

  • No they delay will be the same on ALL F28388D devices. No change within the device family.

    That's correct for F28388D device it will always be PHS=0x1.

    For F28004x, F2837xD, F2837xS, between EPWM1 and EPWM2, the PHS=0x1.