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-F28379D: Completely random single PWM cycle is twice switching frequency.

Part Number: LAUNCHXL-F28379D

Hello, in previous forum here: LAUNCHXL-F28379D: Is there a way to synchronise the CMPSS, DC and TZ modules to the PWM frequency? - C2000 microcontrollers forum - C2000Tm︎ microcontrollers - TI E2E support forums

I had been attempting to stop the PWM trips from clipping the signal mid-cycle, such that I always had 50% duty cycle. 

Messing around with the rising and fall edge triggers of the GPIO enable signal, I have managed to get something working OK. 

However, at what seems like random times, the PWM frequency of EPWM2B (yellow) and EPWM1B (green) will often seem to "latch" their duty cycle, or something similar, such that the frequency appears approximately twice that of the desired duty cycle. Here is a photo of the problem:

Complete code is below. This may help you to see the problem yourself and to help debug. I am using the Launchpad. I believe this is easier, rather select the code snippet, as I am unsure where this is coming from.

// Included Files.
#include "driverlib.h"
#include "device.h"

// Set PWM Frequency defines
#define EPWM_TIMER_TBPRD   600

// This sets the delay from receiving ENABLE to PWM switching start.
// This avoids very small duty cycle at charge start.
// Start with 10 microsecond delay.
#define ENABLE_DELAY_US 10

// Set maximum comparator trip value globally for now.
#define USER_DAC_REF 2000

// Sets number of charge cycles to be executed.
// This will be typically set to around 10, but for debug keep high.
#define NUM_OF_CHARGES 10

// Set PWM Function Prototypes.
void initEPWM1(void);
void initEPWM2(void);

// Comparator initialisation.
void initCMPSS(void);

// Set PWM Interrupt Functions.
__interrupt void epwm1ISR(void);
__interrupt void epwm2ISR(void);
__interrupt void risingEdgeTrigger(void);
__interrupt void fallingEdgeTrigger(void);

// Functions that set-up the GPIO signals.
void setupGPIO(void);

// Function that implements active-high complementary FED/RED.
void setupEPWMActiveHighComplementary(void);

// Setup Comparator Based Digital Compare Trips.
void setupCMPSSDigitalTrips(void);

// Define any and all ISR counters here.
uint16_t epwm1TZIntCount = 0;
uint16_t interruptCount1 = 0;
uint16_t chargeCount = 0;
uint16_t epwm1ISRCount = 0;
uint16_t epwm2ISRCount = 0;
uint16_t modifyCounterVal = 0;

uint16_t cpuTimer2IntCount =0;
uint16_t risingEdgeCount = 0;
uint16_t fallingEdgeCount = 0;

// Controlled phase variable for slave PWM module.
uint16_t PHASE_MODULATE_VAL = 0;

// Toggle reset occurs between rising and falling edges of GPIO to allow soft-start in CPU ISR.
uint16_t SS_RESET = 0;

// CPU timer interrupt
__interrupt void cpuTimer2ISR(void);

void initCPUTimers(void);
void configCPUTimer(uint32_t, float, float);

#define SS_INCREMENT 20
#define MAX_SS_VALUE 4095
uint16_t SOFT_START_VALUE = 0;
uint16_t LAST_SS_VALUE = 0;

void configureDAC(void);

int main(void)
{
    // As per usual, do all of these initialisation functions.
    // Initialise system, device clock, and peripherals
    Device_init();

    // Initialise PIE and clear PIE registers. Disables CPU interrupts.
    // and clear all CPU interrupt flags.
    Interrupt_initModule();

    // Disable pin locks and enable internal pull-ups.
    Device_initGPIO();

    // Initialise the PIE vector table with pointers to the shell interrupt
    // Service Routines (ISR).
    Interrupt_initVectorTable();

    // Specific GPIO Setup
    setupGPIO();

    GPIO_setInterruptType(GPIO_INT_XINT1, GPIO_INT_TYPE_RISING_EDGE);
    GPIO_setInterruptPin(5, GPIO_INT_XINT1);
    GPIO_enableInterrupt(GPIO_INT_XINT1);

    GPIO_setInterruptType(GPIO_INT_XINT2, GPIO_INT_TYPE_FALLING_EDGE);
    GPIO_setInterruptPin(5, GPIO_INT_XINT2);
    GPIO_enableInterrupt(GPIO_INT_XINT2);

    // Specify which function is called when an interrupt is triggered by assigning to register.
    Interrupt_register(INT_EPWM1, &epwm1ISR);
    Interrupt_register(INT_EPWM2, &epwm2ISR);

    // GPIO Timer Interrupts
    Interrupt_register(INT_XINT1, &risingEdgeTrigger);
    Interrupt_register(INT_XINT2, &fallingEdgeTrigger);

    // CPU Timer Interrupt for incrementing Soft-Start to max value at constant slope.
    Interrupt_register(INT_TIMER2, &cpuTimer2ISR);

    // Start EPWM Initialisation here, first disable peripheral.
    SysCtl_disablePeripheral(SYSCTL_PERIPH_CLK_TBCLKSYNC);

    // Initialise PWM1 with zero phase shift, set as master. See function.
    initEPWM1();

    // Initialise PWM2 with variable phase-shift, set as slave. See function.
    initEPWM2();

    // Set up digital compare trip-zones.
    setupCMPSSDigitalTrips();

    // Synchronised shadow load mode.
    EPWM_selectPeriodLoadEvent(EPWM2_BASE, EPWM_SHADOW_LOAD_MODE_SYNC);

    // Set initial phase-shift and time-base counter as zero for soft-start.
    EPWM_setPhaseShift(EPWM2_BASE, 0);
    EPWM_setTimeBaseCounter(EPWM2_BASE, 0);

    // Set EPWM1 SYNCO to be generated when counter is zero.
    EPWM_setSyncOutPulseMode(EPWM1_BASE, EPWM_SYNC_OUT_PULSE_ON_COUNTER_ZERO);

    // EPWM2 will use the EPWM1 SYNCO signal as SYNCIN value
    // Do not need to call setSyncInputConfig as EPWM2  uses this by default.
    EPWM_setSyncOutPulseMode(EPWM2_BASE, EPWM_SYNC_OUT_PULSE_ON_EPWMxSYNCIN);

    // Disable phase shift load for EPWM1, enable for PWM2.
    EPWM_disablePhaseShiftLoad(EPWM1_BASE);
    EPWM_enablePhaseShiftLoad(EPWM2_BASE);

    // Setup rising and falling edge delays for both modules.
    setupEPWMActiveHighComplementary();

    // Call comparator initialisation.
    initCMPSS();

    // Enable the synch register and lock to the PWM.
    SysCtl_enablePeripheral(SYSCTL_PERIPH_CLK_TBCLKSYNC);

    // Finally, enable the PWM interrupts.
    Interrupt_enable(INT_EPWM1);
    Interrupt_enable(INT_EPWM2);

    // Trip zone interrupts. Only use one and allow it to handle both PWM modules.
    //Interrupt_enable(INT_EPWM1_TZ);
    Interrupt_enable(INT_XINT1);
    Interrupt_enable(INT_XINT2);

    // Initialise device peripheral.
    initCPUTimers();

    // Configure CPU-Timer 2 to interrupt at X second intervals, ten microseconds.
    configCPUTimer(CPUTIMER2_BASE, DEVICE_SYSCLK_FREQ, 10);
    CPUTimer_enableInterrupt(CPUTIMER2_BASE);
    Interrupt_enable(INT_TIMER2);
    CPUTimer_startTimer(CPUTIMER2_BASE);

    // Configure DAC that measures analogue quantities for external view.
    configureDAC();

    // Enable Global Interrupt (INTM) and real time interrupt (DBGM)
    EINT;
    ERTM;

    // Loop forever to allow debug. 400Hz signal, 80% duty.
    for(;;)
    {
        while(chargeCount < NUM_OF_CHARGES) {
        // Charge enabled for maximum of 2 milliseconds.
        GPIO_writePin(67, 1);
        DEVICE_DELAY_US(2000);
        // Charging disabled for 500 microseconds.
        GPIO_writePin(67, 0);
        DEVICE_DELAY_US(500);
        chargeCount++;
        }

        // Simulate random delay between trigger pulse trains.
        DEVICE_DELAY_US(50000);
        chargeCount = 0;
    }
}

void setupCMPSSDigitalTrips(void) {

    // Disable all CBC trips for EPWMA/B of both modules.
    EPWM_disableTripZoneSignals(EPWM1_BASE, EPWM_TZ_SIGNAL_DCAEVT2);
    EPWM_disableTripZoneSignals(EPWM2_BASE, EPWM_TZ_SIGNAL_DCAEVT2);
    EPWM_disableTripZoneSignals(EPWM1_BASE, EPWM_TZ_SIGNAL_DCBEVT2);
    EPWM_disableTripZoneSignals(EPWM2_BASE, EPWM_TZ_SIGNAL_DCBEVT2);

    // Enable OST trips for EPWMA/B of both modules.
    EPWM_enableTripZoneSignals(EPWM1_BASE, EPWM_TZ_SIGNAL_DCAEVT1);
    EPWM_enableTripZoneSignals(EPWM2_BASE, EPWM_TZ_SIGNAL_DCAEVT1);
    EPWM_enableTripZoneSignals(EPWM1_BASE, EPWM_TZ_SIGNAL_DCBEVT1);
    EPWM_enableTripZoneSignals(EPWM2_BASE, EPWM_TZ_SIGNAL_DCBEVT1);

    // Trips 9 and 10 together cause DCAH.
    EPWM_selectDigitalCompareTripInput(EPWM1_BASE, EPWM_DC_TRIP_TRIPIN9, EPWM_DC_TYPE_DCAH);
    EPWM_selectDigitalCompareTripInput(EPWM1_BASE, EPWM_DC_TRIP_TRIPIN10, EPWM_DC_TYPE_DCBH);
    EPWM_selectDigitalCompareTripInput(EPWM2_BASE, EPWM_DC_TRIP_TRIPIN9, EPWM_DC_TYPE_DCAH);
    EPWM_selectDigitalCompareTripInput(EPWM2_BASE, EPWM_DC_TRIP_TRIPIN10, EPWM_DC_TYPE_DCBH);

    // Generate DCAEVT1 when DCAH high.
    EPWM_setTripZoneDigitalCompareEventCondition(EPWM1_BASE, EPWM_TZ_DC_OUTPUT_A1, EPWM_TZ_EVENT_DCXH_HIGH);
    EPWM_setTripZoneDigitalCompareEventCondition(EPWM2_BASE, EPWM_TZ_DC_OUTPUT_A1, EPWM_TZ_EVENT_DCXH_HIGH);
    EPWM_setTripZoneDigitalCompareEventCondition(EPWM1_BASE, EPWM_TZ_DC_OUTPUT_B1, EPWM_TZ_EVENT_DCXH_HIGH);
    EPWM_setTripZoneDigitalCompareEventCondition(EPWM2_BASE, EPWM_TZ_DC_OUTPUT_B1, EPWM_TZ_EVENT_DCXH_HIGH);

    // DCAEVT1 uses unfiltered version of DCAEVT1 for now.
    EPWM_setDigitalCompareEventSource(EPWM1_BASE, EPWM_DC_MODULE_A, EPWM_DC_EVENT_1, EPWM_DC_EVENT_SOURCE_ORIG_SIGNAL);
    EPWM_setDigitalCompareEventSource(EPWM2_BASE, EPWM_DC_MODULE_A, EPWM_DC_EVENT_1, EPWM_DC_EVENT_SOURCE_ORIG_SIGNAL);
    EPWM_setDigitalCompareEventSource(EPWM1_BASE, EPWM_DC_MODULE_B, EPWM_DC_EVENT_1, EPWM_DC_EVENT_SOURCE_ORIG_SIGNAL);
    EPWM_setDigitalCompareEventSource(EPWM2_BASE, EPWM_DC_MODULE_B, EPWM_DC_EVENT_1, EPWM_DC_EVENT_SOURCE_ORIG_SIGNAL);

    // DCAEVT1 is asynchronous
    EPWM_setDigitalCompareEventSyncMode(EPWM1_BASE, EPWM_DC_MODULE_A, EPWM_DC_EVENT_1, EPWM_DC_EVENT_INPUT_SYNCED);
    EPWM_setDigitalCompareEventSyncMode(EPWM2_BASE, EPWM_DC_MODULE_A, EPWM_DC_EVENT_1, EPWM_DC_EVENT_INPUT_SYNCED);
    EPWM_setDigitalCompareEventSyncMode(EPWM1_BASE, EPWM_DC_MODULE_B, EPWM_DC_EVENT_1, EPWM_DC_EVENT_INPUT_SYNCED);
    EPWM_setDigitalCompareEventSyncMode(EPWM2_BASE, EPWM_DC_MODULE_B, EPWM_DC_EVENT_1, EPWM_DC_EVENT_INPUT_SYNCED);

    // Force EPWM low action on DCAEVT1.
    EPWM_setTripZoneAction(EPWM1_BASE, EPWM_TZ_ACTION_EVENT_DCAEVT1, EPWM_TZ_ACTION_LOW);
    EPWM_setTripZoneAction(EPWM2_BASE, EPWM_TZ_ACTION_EVENT_DCAEVT1, EPWM_TZ_ACTION_LOW);
    EPWM_setTripZoneAction(EPWM1_BASE, EPWM_TZ_ACTION_EVENT_DCBEVT1, EPWM_TZ_ACTION_LOW);
    EPWM_setTripZoneAction(EPWM2_BASE, EPWM_TZ_ACTION_EVENT_DCBEVT1, EPWM_TZ_ACTION_LOW);

    // Enable Trip Zone Interrupt
    EPWM_enableTripZoneInterrupt(EPWM1_BASE, EPWM_TZ_INTERRUPT_DCAEVT1);
    EPWM_enableTripZoneInterrupt(EPWM1_BASE, EPWM_TZ_INTERRUPT_DCBEVT1);
}

void initEPWM1(void) {

    // Set up time-base clock.
    EPWM_setTimeBasePeriod(EPWM1_BASE, EPWM_TIMER_TBPRD);
    EPWM_setPhaseShift(EPWM1_BASE, 0);
    EPWM_setTimeBaseCounter(EPWM1_BASE, 0);

    // Counter compare config for EPWM1. May just need CMPA if use FED/RED.
    EPWM_setCounterCompareValue(EPWM1_BASE,
                                EPWM_COUNTER_COMPARE_A,
                                EPWM_TIMER_TBPRD/2);
    EPWM_setCounterCompareValue(EPWM1_BASE,
                                EPWM_COUNTER_COMPARE_B,
                                EPWM_TIMER_TBPRD/2);

    // Set up counter mode.
    EPWM_setTimeBaseCounterMode(EPWM1_BASE, EPWM_COUNTER_MODE_UP);
    EPWM_disablePhaseShiftLoad(EPWM1_BASE);
    EPWM_setClockPrescaler(EPWM1_BASE,
                           EPWM_CLOCK_DIVIDER_1,
                           EPWM_HSCLOCK_DIVIDER_1);

    // Set up PWM Shadowing. Load on CTR ZRO.
    EPWM_setCounterCompareShadowLoadMode(EPWM1_BASE,
                                         EPWM_COUNTER_COMPARE_A,
                                         EPWM_COMP_LOAD_ON_CNTR_ZERO);
    EPWM_setCounterCompareShadowLoadMode(EPWM1_BASE,
                                         EPWM_COUNTER_COMPARE_B,
                                         EPWM_COMP_LOAD_ON_CNTR_ZERO);

    // Set up the action qualifier.
    EPWM_setActionQualifierAction(EPWM1_BASE,
                                  EPWM_AQ_OUTPUT_A,
                                  EPWM_AQ_OUTPUT_HIGH,
                                  EPWM_AQ_OUTPUT_ON_TIMEBASE_ZERO);

    EPWM_setActionQualifierAction(EPWM1_BASE,
                                  EPWM_AQ_OUTPUT_B,
                                  EPWM_AQ_OUTPUT_LOW,
                                  EPWM_AQ_OUTPUT_ON_TIMEBASE_ZERO);

    EPWM_setActionQualifierAction(EPWM1_BASE,
                                  EPWM_AQ_OUTPUT_A,
                                  EPWM_AQ_OUTPUT_LOW,
                                  EPWM_AQ_OUTPUT_ON_TIMEBASE_UP_CMPA);

    EPWM_setActionQualifierAction(EPWM1_BASE,
                                  EPWM_AQ_OUTPUT_B,
                                  EPWM_AQ_OUTPUT_HIGH,
                                  EPWM_AQ_OUTPUT_ON_TIMEBASE_UP_CMPA);

    // // One-shot Active Low Trip ENABLE command - sets maximum charge time/minimum off time.
    EPWM_enableTripZoneSignals(EPWM1_BASE, EPWM_TZ_SIGNAL_OSHT1);
    EPWM_setTripZoneAction(EPWM1_BASE, EPWM_TZ_ACTION_EVENT_TZA, EPWM_TZ_ACTION_LOW);
    EPWM_setTripZoneAction(EPWM1_BASE, EPWM_TZ_ACTION_EVENT_TZB, EPWM_TZ_ACTION_LOW);
    EPWM_enableTripZoneInterrupt(EPWM1_BASE, EPWM_TZ_INTERRUPT_OST);

    // Clear latent trips.
    EPWM_clearTripZoneFlag(EPWM1_BASE, EPWM_TZ_INTERRUPT | EPWM_TZ_FLAG_OST);

    // Interrupt where we will change execute control code.
    // Select INT on time base counter zero event
    // Enable INT, but generate on the 15th event
    // Bandwidth is then FSW/15.
    EPWM_setInterruptSource(EPWM1_BASE, EPWM_INT_TBCTR_ZERO);
    EPWM_enableInterrupt(EPWM1_BASE);
    EPWM_setInterruptEventCount(EPWM1_BASE, 5);
}

void initEPWM2(void) {

    // Set up time-base clock.
    EPWM_setTimeBasePeriod(EPWM2_BASE, EPWM_TIMER_TBPRD);

    // Initially zero, but test with different TBPHS.
    // Ensure Time base is set to same value as phase shift.
    EPWM_setPhaseShift(EPWM2_BASE, 0);
    EPWM_setTimeBaseCounter(EPWM2_BASE, 0);

    // Counter compare configuration for EPWM2. May just need CMPA if use FED/RED.
    EPWM_setCounterCompareValue(EPWM2_BASE,
                                EPWM_COUNTER_COMPARE_A,
                                EPWM_TIMER_TBPRD/2);
    EPWM_setCounterCompareValue(EPWM2_BASE,
                                EPWM_COUNTER_COMPARE_B,
                                EPWM_TIMER_TBPRD/2);

    // Set up counter mode.
    EPWM_setTimeBaseCounterMode(EPWM2_BASE, EPWM_COUNTER_MODE_UP);
    EPWM_disablePhaseShiftLoad(EPWM2_BASE);
    EPWM_setClockPrescaler(EPWM2_BASE,
                           EPWM_CLOCK_DIVIDER_1,
                           EPWM_HSCLOCK_DIVIDER_1);

    // Set up PWM Shadowing. Load on CTR ZRO.
    EPWM_setCounterCompareShadowLoadMode(EPWM2_BASE,
                                         EPWM_COUNTER_COMPARE_A,
                                         EPWM_COMP_LOAD_ON_CNTR_ZERO);
    EPWM_setCounterCompareShadowLoadMode(EPWM2_BASE,
                                         EPWM_COUNTER_COMPARE_B,
                                         EPWM_COMP_LOAD_ON_CNTR_ZERO);

    // Set up the action qualifier.
    // Scope these and check if right, most likely need to be different to EPWM1.
    EPWM_setActionQualifierAction(EPWM2_BASE,
                                  EPWM_AQ_OUTPUT_A,
                                  EPWM_AQ_OUTPUT_HIGH,
                                  EPWM_AQ_OUTPUT_ON_TIMEBASE_ZERO);

    EPWM_setActionQualifierAction(EPWM2_BASE,
                                  EPWM_AQ_OUTPUT_B,
                                  EPWM_AQ_OUTPUT_LOW,
                                  EPWM_AQ_OUTPUT_ON_TIMEBASE_ZERO);

    EPWM_setActionQualifierAction(EPWM2_BASE,
                                  EPWM_AQ_OUTPUT_A,
                                  EPWM_AQ_OUTPUT_LOW,
                                  EPWM_AQ_OUTPUT_ON_TIMEBASE_UP_CMPA);

    EPWM_setActionQualifierAction(EPWM2_BASE,
                                  EPWM_AQ_OUTPUT_B,
                                  EPWM_AQ_OUTPUT_HIGH,
                                  EPWM_AQ_OUTPUT_ON_TIMEBASE_UP_CMPA);

    // One-shot Active Low Trip ENABLE command - sets maximum charge time/minimum off time.
    EPWM_enableTripZoneSignals(EPWM2_BASE, EPWM_TZ_SIGNAL_OSHT1);
    EPWM_setTripZoneAction(EPWM2_BASE, EPWM_TZ_ACTION_EVENT_TZA, EPWM_TZ_ACTION_LOW);
    EPWM_setTripZoneAction(EPWM2_BASE, EPWM_TZ_ACTION_EVENT_TZB, EPWM_TZ_ACTION_LOW);
    EPWM_disableTripZoneInterrupt(EPWM2_BASE, EPWM_TZ_INTERRUPT_OST);

    // Clear any latent trips.
    EPWM_clearTripZoneFlag(EPWM1_BASE, EPWM_TZ_INTERRUPT | EPWM_TZ_FLAG_OST);

    // Interrupt where we will change execute control code.
    // Select INT on time base counter zero event
    // Enable INT, but generate on the 15th event
    // Bandwidth is then FSW/15.
    EPWM_setInterruptSource(EPWM2_BASE, EPWM_INT_TBCTR_ZERO);
    EPWM_enableInterrupt(EPWM2_BASE);
    EPWM_setInterruptEventCount(EPWM2_BASE, 5);
}

void setupEPWMActiveHighComplementary() {

    // As always, use EPWMA as the input for both RED and FED.
    EPWM_setRisingEdgeDeadBandDelayInput(EPWM1_BASE, EPWM_DB_INPUT_EPWMA);
    EPWM_setRisingEdgeDeadBandDelayInput(EPWM2_BASE, EPWM_DB_INPUT_EPWMA);
    EPWM_setFallingEdgeDeadBandDelayInput(EPWM1_BASE, EPWM_DB_INPUT_EPWMA);
    EPWM_setFallingEdgeDeadBandDelayInput(EPWM2_BASE, EPWM_DB_INPUT_EPWMA);

    // Set the rising and falling edge delay values. One percent of switching period, for now.
    EPWM_setFallingEdgeDelayCount(EPWM1_BASE, EPWM_TIMER_TBPRD/100);
    EPWM_setFallingEdgeDelayCount(EPWM2_BASE, EPWM_TIMER_TBPRD/100);
    EPWM_setRisingEdgeDelayCount(EPWM1_BASE, EPWM_TIMER_TBPRD/100);
    EPWM_setRisingEdgeDelayCount(EPWM2_BASE, EPWM_TIMER_TBPRD/100);

    // Invert only the falling edge delayed output for active-high complementary mode.
    EPWM_setDeadBandDelayPolarity(EPWM1_BASE, EPWM_DB_RED, EPWM_DB_POLARITY_ACTIVE_HIGH);
    EPWM_setDeadBandDelayPolarity(EPWM1_BASE, EPWM_DB_FED, EPWM_DB_POLARITY_ACTIVE_LOW);
    EPWM_setDeadBandDelayPolarity(EPWM2_BASE, EPWM_DB_RED, EPWM_DB_POLARITY_ACTIVE_HIGH);
    EPWM_setDeadBandDelayPolarity(EPWM2_BASE, EPWM_DB_FED, EPWM_DB_POLARITY_ACTIVE_LOW);

    // Use the delayed signals instead of the original signals.
    EPWM_setDeadBandDelayMode(EPWM1_BASE, EPWM_DB_RED, true);
    EPWM_setDeadBandDelayMode(EPWM1_BASE, EPWM_DB_FED, true);
    EPWM_setDeadBandDelayMode(EPWM2_BASE, EPWM_DB_RED, true);
    EPWM_setDeadBandDelayMode(EPWM2_BASE, EPWM_DB_FED, true);

    // Importantly, do not switch output A with output B.
    EPWM_setDeadBandOutputSwapMode(EPWM1_BASE, EPWM_DB_OUTPUT_A, false);
    EPWM_setDeadBandOutputSwapMode(EPWM1_BASE, EPWM_DB_OUTPUT_B, false);
    EPWM_setDeadBandOutputSwapMode(EPWM2_BASE, EPWM_DB_OUTPUT_A, false);
    EPWM_setDeadBandOutputSwapMode(EPWM2_BASE, EPWM_DB_OUTPUT_B, false);
}

// Call this function to set-up the pin-out of the GPIOs.
void setupGPIO(void)
{
    // Enable PWM1A/B-2A/B on GPIO0-GPIO4
    GPIO_setPadConfig(0, GPIO_PIN_TYPE_STD);     // Standard push-pull output for PWM.
    GPIO_setPadConfig(1, GPIO_PIN_TYPE_STD);
    GPIO_setPadConfig(2, GPIO_PIN_TYPE_STD);
    GPIO_setPadConfig(3, GPIO_PIN_TYPE_STD);

    GPIO_setPinConfig(GPIO_0_EPWM1A);            // GPIO0 = PWM1A
    GPIO_setPinConfig(GPIO_1_EPWM1B);            // GPIO1 = PWM1B
    GPIO_setPinConfig(GPIO_2_EPWM2A);            // GPIO2 = PWM2A
    GPIO_setPinConfig(GPIO_3_EPWM2B);            // GPIO3 = PWM2B

    // Enable a GPIO67 output on GPIO67, set it high
    GPIO_setPadConfig(67, GPIO_PIN_TYPE_PULLUP); // Enable pullup on GPIO67
    GPIO_writePin(67, 1);                            // Load output latch
    GPIO_setPinConfig(GPIO_67_GPIO67);                // GPIO67 = GPIO67
    GPIO_setDirectionMode(67, GPIO_DIR_MODE_OUT);    // GPIO67 = output

    // Enable a GPIO output on GPIO8, set it high
    GPIO_setPadConfig(5, GPIO_PIN_TYPE_PULLUP);     // Enable pullup on GPIO5
    GPIO_setPinConfig(GPIO_5_GPIO5);                // GPIO5 = GPIO5 MUX SELECT
    GPIO_setDirectionMode(5, GPIO_DIR_MODE_IN);     // GPIO5 = input (output overvolts latch)

    // Enable Trip Zone input on GPIO5
    GPIO_setQualificationMode(5, GPIO_QUAL_SYNC); // sync input

    // Input pins trigger XBAR TZs
    XBAR_setInputPin(XBAR_INPUT1, 5);              // GPIO5 = TZ1

}

void initCMPSS(void) {

    // Enable the CMPSS and configure the negative reference to be fed from the DAC.
    // CMPSS1 is not used as it's CMPIN is shorted to the DACREF.
    // Input/Output Over-voltage
    CMPSS_enableModule(CMPSS2_BASE);
    CMPSS_configHighComparator(CMPSS2_BASE, CMPSS_INSRC_DAC);
    CMPSS_enableModule(CMPSS3_BASE);
    CMPSS_configHighComparator(CMPSS3_BASE, CMPSS_INSRC_DAC);

    // Input/Output Over-current.
    CMPSS_enableModule(CMPSS5_BASE);
    CMPSS_configHighComparator(CMPSS5_BASE, CMPSS_INSRC_DAC);
    CMPSS_enableModule(CMPSS6_BASE);
    CMPSS_configHighComparator(CMPSS6_BASE, CMPSS_INSRC_DAC);

    // Set DAC reference as VDDA, and set the required trip value.
    CMPSS_configDAC(CMPSS2_BASE, CMPSS_DACREF_VDDA
                    | CMPSS_DACVAL_SYSCLK | CMPSS_DACSRC_SHDW);
    CMPSS_setDACValueHigh(CMPSS2_BASE, USER_DAC_REF);
    CMPSS_configDAC(CMPSS3_BASE, CMPSS_DACREF_VDDA
                    | CMPSS_DACVAL_SYSCLK | CMPSS_DACSRC_SHDW);
    CMPSS_setDACValueHigh(CMPSS3_BASE, USER_DAC_REF);
    CMPSS_configDAC(CMPSS5_BASE, CMPSS_DACREF_VDDA
                    | CMPSS_DACVAL_SYSCLK | CMPSS_DACSRC_SHDW);
    CMPSS_setDACValueHigh(CMPSS5_BASE, USER_DAC_REF);
    CMPSS_configDAC(CMPSS6_BASE, CMPSS_DACREF_VDDA
                    | CMPSS_DACVAL_SYSCLK | CMPSS_DACSRC_SHDW);
    CMPSS_setDACValueHigh(CMPSS6_BASE, USER_DAC_REF);

    // Configure now the digital filter. Use max clock pre-scale, sample window size, and threshold.
    CMPSS_configFilterHigh(CMPSS2_BASE, 0x3FF, 32, 31);
    CMPSS_configFilterHigh(CMPSS3_BASE, 0x3FF, 32, 31);
    CMPSS_configFilterHigh(CMPSS5_BASE, 0x3FF, 32, 31);
    CMPSS_configFilterHigh(CMPSS6_BASE, 0x3FF, 32, 31);

    // Initialise filter logic and begin filtering
    CMPSS_initFilterHigh(CMPSS2_BASE);
    CMPSS_initFilterHigh(CMPSS3_BASE);
    CMPSS_initFilterHigh(CMPSS5_BASE);
    CMPSS_initFilterHigh(CMPSS6_BASE);

    // Configure the output signals. CTRIPH, which feeds into the PWM X-BAR, need only be generated.
    // CTRIPH will be fed with the asynchronous comparator output.
    CMPSS_configOutputsHigh(CMPSS2_BASE, CMPSS_TRIP_FILTER);
    CMPSS_configOutputsHigh(CMPSS3_BASE, CMPSS_TRIP_FILTER);
    CMPSS_configOutputsHigh(CMPSS5_BASE, CMPSS_TRIP_FILTER);
    CMPSS_configOutputsHigh(CMPSS6_BASE, CMPSS_TRIP_FILTER);

    // Here is the important part, set the output mux configs.
    XBAR_setEPWMMuxConfig(XBAR_TRIP9, XBAR_EPWM_MUX02_CMPSS2_CTRIPH);
    XBAR_setEPWMMuxConfig(XBAR_TRIP10, XBAR_EPWM_MUX04_CMPSS3_CTRIPH);
    XBAR_setEPWMMuxConfig(XBAR_TRIP11, XBAR_EPWM_MUX08_CMPSS5_CTRIPH);
    XBAR_setEPWMMuxConfig(XBAR_TRIP12, XBAR_EPWM_MUX10_CMPSS6_CTRIPH);

    XBAR_enableEPWMMux(XBAR_TRIP9, XBAR_MUX02);
    XBAR_enableEPWMMux(XBAR_TRIP10, XBAR_MUX04);
    XBAR_enableEPWMMux(XBAR_TRIP11, XBAR_MUX08);
    XBAR_enableEPWMMux(XBAR_TRIP12, XBAR_MUX10);

    // Set typical hysteresis on comparator inputs.
    CMPSS_setHysteresis(CMPSS2_BASE, 4);
    CMPSS_setHysteresis(CMPSS3_BASE, 4);
    CMPSS_setHysteresis(CMPSS5_BASE, 4);
    CMPSS_setHysteresis(CMPSS6_BASE, 4);

}

// Configure DAC - Setup the reference voltage and output value for the DAC
void configureDAC(void)
{
    // Use ADC reference voltage.
    DAC_setReferenceVoltage(DACA_BASE, DAC_REF_ADC_VREFHI);

    // Enable the DAC output
    DAC_enableOutput(DACA_BASE);

    // Set the DAC shadow output to 0
    DAC_setShadowValue(DACA_BASE, 0);

    // Load on next SYSCLOCK
    DAC_setLoadMode(DACA_BASE, DAC_LOAD_SYSCLK);

    // Delay for buffered DAC to power up
    DEVICE_DELAY_US(10);
}

void initCPUTimers(void) {
    // Initialise timer period to max
    CPUTimer_setPeriod(CPUTIMER2_BASE, 0xFFFFFFFF);

    // Initialise pre-scale counter to divide by 1 SYSCLKOUT
    CPUTimer_setPreScaler(CPUTIMER2_BASE, 0);

    // Stop timer
    CPUTimer_stopTimer(CPUTIMER2_BASE);

    // Reload counter register with period value
    CPUTimer_reloadTimerCounter(CPUTIMER2_BASE);

    // Reset any latent interrupt counts
    cpuTimer2IntCount = 0;
}

void configCPUTimer(uint32_t cpuTimer, float freq, float period)
{
    uint32_t temp;

    // Initialise timer period:
    temp = (uint32_t)(freq / 1000000 * period);
    CPUTimer_setPeriod(cpuTimer, temp);

    // Set pre-scale counter to divide by 1 (SYSCLKOUT):
    CPUTimer_setPreScaler(cpuTimer, 0);

    // Initialises timer control register. The timer is stopped, reloaded,
    // free run disabled, and interrupt enabled.
    // Additionally, the free and soft bits are set
    CPUTimer_stopTimer(cpuTimer);
    CPUTimer_reloadTimerCounter(cpuTimer);
    CPUTimer_setEmulationMode(cpuTimer,
                              CPUTIMER_EMULATIONMODE_RUNFREE);
    CPUTimer_enableInterrupt(cpuTimer);
}


__interrupt void epwm1ISR() {
epwm1ISRCount++;

// Modulate phase-shift register and time-base shift too.
PHASE_MODULATE_VAL = PHASE_MODULATE_VAL + 5;
if(PHASE_MODULATE_VAL > EPWM_TIMER_TBPRD/2) {
    PHASE_MODULATE_VAL = EPWM_TIMER_TBPRD/2;
}

EPWM_setPhaseShift(EPWM2_BASE, PHASE_MODULATE_VAL);

// Clear INT flag for this timer
EPWM_clearEventTriggerInterruptFlag(EPWM1_BASE);

// Acknowledge interrupt group
Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP3);
}

__interrupt void epwm2ISR() {
epwm2ISRCount++;

// Clear INT flag for this timer
EPWM_clearEventTriggerInterruptFlag(EPWM2_BASE);

// Acknowledge interrupt group
Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP3);
}

__interrupt void risingEdgeTrigger(void)
{
    risingEdgeCount++;

    // Blank a switching period after trip
    DEVICE_DELAY_US(10);

    PHASE_MODULATE_VAL = 0;

    // Before initiation of new soft-start cycle, reset to zero phase-shift and zero counter.
    // Reset counters for both EPWM1 and EPWM2.
    EPWM_setTimeBasePeriod(EPWM1_BASE, EPWM_TIMER_TBPRD);
    EPWM_setTimeBasePeriod(EPWM2_BASE, EPWM_TIMER_TBPRD);
    EPWM_setTimeBaseCounter(EPWM1_BASE, 0);
    EPWM_setTimeBaseCounter(EPWM2_BASE, 0);
    EPWM_setPhaseShift(EPWM2_BASE, 0);

    // For one-shot trips, we must re-enable all of the trip-zone flags within both PWM modules.
    EPWM_clearTripZoneFlag(EPWM1_BASE, (EPWM_TZ_INTERRUPT | EPWM_TZ_FLAG_OST | EPWM_TZ_FLAG_DCAEVT1 | EPWM_TZ_FLAG_DCBEVT1));
    EPWM_clearTripZoneFlag(EPWM2_BASE, (EPWM_TZ_INTERRUPT | EPWM_TZ_FLAG_OST | EPWM_TZ_FLAG_DCAEVT1 | EPWM_TZ_FLAG_DCBEVT1));

    // Re-enable CPU timer at rising edge to allow soft-start
    CPUTimer_startTimer(CPUTIMER2_BASE);

    // Acknowledge this interrupt to receive more interrupts from group 2
    Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP2);
    Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP1);
}

__interrupt void fallingEdgeTrigger(void) {

    // Small delay...
    DEVICE_DELAY_US(10);

    fallingEdgeCount++;
    SS_RESET = 1;

    PHASE_MODULATE_VAL = 0;

    // Set soft-start DAC value to zero
    DAC_setShadowValue(DACA_BASE, 0);

    // Disable CPU timer 2 which so it doesn't increment during pulse disable
    CPUTimer_stopTimer(CPUTIMER2_BASE);

    // Clear GPIO falling edge interrupt.
    Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP1);
}

__interrupt void cpuTimer2ISR(void) {
    cpuTimer2IntCount++;

    // Reset the soft-start to zero, reset controller phase-shift to zero.
    if(SS_RESET == 1) {
        SOFT_START_VALUE = 0;
    }

    SS_RESET = 0;

    // At every counter event, increment soft-start value.
    SOFT_START_VALUE = SOFT_START_VALUE + SS_INCREMENT;

    // Keep at maximum in CPU interrupt until external trigger reset.
    if(SOFT_START_VALUE  > MAX_SS_VALUE) {
        SOFT_START_VALUE = 4095;
    }

    // Pass value to DAC-A here, soft-start on rising-edge enable.
    DAC_setShadowValue(DACA_BASE, SOFT_START_VALUE);
}

Since I use active-high complementary PWM dead-band, this unfortunately also messes up my EPWMxA/xB relations as shown below:

Best regards,

JMH.

  • Hi JMH,

    I'll see if I can recreate the issue on my end using your code and will get back to you tomorrow with my findings. In the mean time, can I ask if you saw any related issue like this before implementing the rising/falling edge trigger with the GPIO enable signal? Or was it just the clipping of 50% duty cycle that was the issue you were trying to resolve?

    Regards,

    Allison

  • Hello Allison, 

    I cannot really say, as I always had this operating at fixed duty, phase, and frequency. It seems to be when implementing modifying the registers I get this problem. I can try again tomorrow without the phase control to confirm. 

    If it helps, this problem originally seemed to have something to do with my interrupt from the PWM, which was originally generated every 15 cycles. This “double frequency” cycle artifact would then occur every 15 cycles or so. If I changed it to 5, then it would occur every 5. I can’t remember what I changed to get to this point, as I forgot to commit changes to our systems version control. But now it seems to occur just this once, and then for the rest of the ENABLE period, the phase control works fine. Strange. 

    I think if you can recreate on your end you’ll spot it with your expertise quickfast! 

    Best, 

    JMH.

  • Hi JMH,

    I was able to recreate the issue you are seeing with the PWM signals. I did see that you had posted earlier today about changing the action qualifiers- just wanted to double check if adjusting the action qualifiers did resolve the issue? On my end it also seems that the latching only occurs once on EPWM2, do you still see it on both EPWM2B and EPWM1B? The pattern does hint at an issue with the interrupt timing which I'll look into further.

    Best regards,

    Allison

  • Hi yes, I apologise I do not know where my reply has went. I have attached as photo as I cannot paste text here. Your thoughts would be appreciated. I cannot recall what else I changed, but modifying the AQ cleaned the issue right up. I am still running the interrupt at the same frequency it was originally, I had just set up an additional PWM module at 15kHz to update the phase value, rather than doing this on the 15th interrupt of a 150kHz PWM (my main inverter frequency).

    I can’t recall if I had the issue on both PWM modules 1 and 2, but their action qualifiers are set exactly the same.  

  • No worries at all, glad to hear that the issue has resolved! It's definitely worth looking deeper into to find the real culprit. Would you mind sending your updated code, so I can compare?

    Best regards,

    Allison