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.

TMS320F280049C: ePWM interrupt doesn't occur

Part Number: TMS320F280049C


Hi there,

I'm desperately trying to get my ePWM module to fire an interrupt but it just doesn't seem to happen. The same ePWM module is also controlling the SOCA for the ADC which has an according ISR that works perfectly fine.

Here's what I consider the relevant code:

Main.c

    // Initialize device clock and peripherals
    InitSysCtrl();

    // Initialize GPIO
    InitGpio();
    GPIO_SetupPinMux(GPIO_DBG_ISR, GPIO_MUX_CPU1, 0);
    GPIO_SetupPinOptions(GPIO_DBG_ISR, GPIO_OUTPUT, GPIO_PUSHPULL);
    // 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.
    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).
    InitPieVectTable();

    adc_Init();

    // Configure the ePWM
    pwm_Init();
    
    
    // Enable global Interrupts and higher priority real-time debug events:
    IER |= M_INT1;  // Enable group 1 interrupts

    EINT;           // Enable Global interrupt INTM
    ERTM;           // Enable Global realtime interrupt DBGM

    // Sync ePWM
    EALLOW;
    CpuSysRegs.PCLKCR0.bit.TBCLKSYNC = 1;

    while(1)
}

adc.c

__interrupt void A1ISR(void)
{
    do stuff
}


void adc_Init(void)
{
    // allow adjusting registers
    // Enable internal 3.3V reference voltage for both ADCs
    // SetVREF also loads the correct offtrim value.
    SetVREF(ADC_ADCA, ADC_INTERNAL, ADC_VREF3P3);
    SetVREF(ADC_ADCB, ADC_INTERNAL, ADC_VREF3P3);
    SetVREF(ADC_ADCC, ADC_INTERNAL, ADC_VREF3P3);

    EALLOW;
    // configure ADCCLK prescale. Max according to ds is 50MHz.
    AdcaRegs.ADCCTL2.bit.PRESCALE = 2;
    AdcbRegs.ADCCTL2.bit.PRESCALE = 2;

    // Cause the interrupt to be triggered after conversion
    AdcaRegs.ADCCTL1.bit.INTPULSEPOS = 1;
    AdcbRegs.ADCCTL1.bit.INTPULSEPOS = 1;

    AdcaRegs.ADCSOC0CTL.bit.CHSEL = 0;
    AdcaRegs.ADCSOC1CTL.bit.CHSEL = 1;
    AdcaRegs.ADCSOC2CTL.bit.CHSEL = 2;
    AdcaRegs.ADCSOC3CTL.bit.CHSEL = 3;
    AdcaRegs.ADCSOC4CTL.bit.CHSEL = 4;
    AdcaRegs.ADCSOC5CTL.bit.CHSEL = 5;
    AdcaRegs.ADCSOC6CTL.bit.CHSEL = 6;
    AdcaRegs.ADCSOC7CTL.bit.CHSEL = 8; // channel A7 is not bonded out.
    AdcbRegs.ADCSOC0CTL.bit.CHSEL = 0;
    AdcbRegs.ADCSOC1CTL.bit.CHSEL = 1;
    AdcbRegs.ADCSOC2CTL.bit.CHSEL = 2;
    AdcbRegs.ADCSOC3CTL.bit.CHSEL = 3;
    AdcbRegs.ADCSOC4CTL.bit.CHSEL = 4;
    AdcbRegs.ADCSOC5CTL.bit.CHSEL = 6; // channel B5 is not bonded out.
    AdcbRegs.ADCSOC6CTL.bit.CHSEL = 8; // channel B7 is not bonded out.
    AdcbRegs.ADCSOC7CTL.bit.CHSEL = 7; // channel A7 is not bonded out.
    // Setup acquisition windows for ADC A and B and all required SOCs
    // (register holds value - 1)
    uint16_t u16Acqps = 24;
    AdcaRegs.ADCSOC0CTL.bit.ACQPS = u16Acqps -1;
    AdcaRegs.ADCSOC1CTL.bit.ACQPS = u16Acqps -1;
    AdcaRegs.ADCSOC2CTL.bit.ACQPS = u16Acqps -1;
    AdcaRegs.ADCSOC3CTL.bit.ACQPS = u16Acqps -1;
    AdcaRegs.ADCSOC4CTL.bit.ACQPS = u16Acqps -1;
    AdcaRegs.ADCSOC5CTL.bit.ACQPS = u16Acqps -1;
    AdcaRegs.ADCSOC6CTL.bit.ACQPS = u16Acqps -1;
    AdcaRegs.ADCSOC7CTL.bit.ACQPS = u16Acqps -1;
    AdcbRegs.ADCSOC0CTL.bit.ACQPS = u16Acqps -1;
    AdcbRegs.ADCSOC1CTL.bit.ACQPS = u16Acqps -1;
    AdcbRegs.ADCSOC2CTL.bit.ACQPS = u16Acqps -1;
    AdcbRegs.ADCSOC3CTL.bit.ACQPS = u16Acqps -1;
    AdcbRegs.ADCSOC4CTL.bit.ACQPS = u16Acqps -1;
    AdcbRegs.ADCSOC5CTL.bit.ACQPS = u16Acqps -1;
    AdcbRegs.ADCSOC6CTL.bit.ACQPS = u16Acqps -1;
    AdcbRegs.ADCSOC7CTL.bit.ACQPS = u16Acqps -1;

    // set all SOCs  to trigger on ePWM1 SOCA. Since no further
    // priorization is taking place the SOCs will be triggered in a
    // round robbing manner
    AdcaRegs.ADCSOC0CTL.bit.TRIGSEL = 5;
    AdcaRegs.ADCSOC1CTL.bit.TRIGSEL = 5;
    AdcaRegs.ADCSOC2CTL.bit.TRIGSEL = 5;
    AdcaRegs.ADCSOC3CTL.bit.TRIGSEL = 5;
    AdcaRegs.ADCSOC4CTL.bit.TRIGSEL = 5;
    AdcaRegs.ADCSOC5CTL.bit.TRIGSEL = 5;
    AdcaRegs.ADCSOC6CTL.bit.TRIGSEL = 5;
    AdcaRegs.ADCSOC7CTL.bit.TRIGSEL = 5;
    AdcbRegs.ADCSOC0CTL.bit.TRIGSEL = 5;
    AdcbRegs.ADCSOC1CTL.bit.TRIGSEL = 5;
    AdcbRegs.ADCSOC2CTL.bit.TRIGSEL = 5;
    AdcbRegs.ADCSOC3CTL.bit.TRIGSEL = 5;
    AdcbRegs.ADCSOC4CTL.bit.TRIGSEL = 5;
    AdcbRegs.ADCSOC5CTL.bit.TRIGSEL = 5;
    AdcbRegs.ADCSOC6CTL.bit.TRIGSEL = 5;
    AdcbRegs.ADCSOC7CTL.bit.TRIGSEL = 5;

    // Cause interrupt once SOC7 of ADC A is done
    AdcaRegs.ADCINTSEL1N2.bit.INT1SEL = 7;
    AdcaRegs.ADCINTSEL1N2.bit.INT1E = 1; // Enable INT1 flag
    AdcaRegs.ADCINTFLGCLR.bit.ADCINT1 = 1; // Make sure INT1 flag is cleared
    AdcbRegs.ADCINTSEL1N2.bit.INT1SEL = 7;
    AdcbRegs.ADCINTSEL1N2.bit.INT1E = 1; // Enable INT1 flag
    AdcbRegs.ADCINTFLGCLR.bit.ADCINT1 = 1; // Make sure INT1 flag is cleared

    // Enable PIE interrupt. INTx1 for ADC A INTx2 for ADC B
    // see f28004x_defaultisr.h
    PieCtrlRegs.PIEIER1.bit.INTx1 = 1;
    // Function for ADC-A interrupt 1. Since ADC-B is setup
    // identically to ADCA it should be done at the same time.
    PieVectTable.ADCA1_INT = &A1ISR;

    // Power up the ADC
    AdcaRegs.ADCCTL1.bit.ADCPWDNZ = 1;
    AdcbRegs.ADCCTL1.bit.ADCPWDNZ = 1;
    // stop to allow register changes
    EDIS;

    // delay for 1ms so the ADC have time to power up
    DELAY_US(1000);
}

pwm.c

__interrupt void pwm_ISR(void)
{
     do stuff
}

static void pwm_Init(void)
{

    PieCtrlRegs.PIEIER3.bit.INTx1 = 1;
    PieVectTable.EPWM1_INT = &pwm_ISR;
    PieCtrlRegs.PIECTRL.bit.ENPIE = 1;

    // Time-Base Control
    EPwm1Regs.TBCTL.bit.CTRMODE = TB_FREEZE; // Stop - freeze counter operation
    EPwm1Regs.TBCTL.bit.PHSEN = TB_DISABLE; // Do not load the time-base counter (TBCTR) from the time-base phase register (TBPHS)
    EPwm1Regs.TBCTL.bit.PRDLD = TB_SHADOW; // The period register (TBPRD) is loaded from its shadow register when the time-base counter, TBCTR, is equal to zero.
    EPwm1Regs.TBCTL.bit.SYNCOSEL = TB_SYNC_DISABLE; // Disable EPWMxSYNCO signal
    EPwm1Regs.TBCTL.bit.HSPCLKDIV = TB_DIV1; // High Speed Time-base Clock Prescale Bits: /1
    EPwm1Regs.TBCTL.bit.CLKDIV = TB_DIV1; // Time-base Clock Prescale Bits: /1
    EPwm1Regs.TBCTL.bit.FREE_SOFT = TB_FREERUN; // Emulation Mode Bits. These bits select the behavior of the ePWM time-base counter during emulation events


    EPwm1Regs.TBPRD = IVPWM_PRD;  // Time-Base Period
    EPwm1Regs.TBPHS.all = 0;      // Time-Base Phase, not used
    EPwm1Regs.TBCTR = 0;          // Time-Base Counter reset

    // HRPWM Configuration
    // MEP control is on a CMPA-generated edge, as recommended by TI (..\Doku\HRPWM_dutycycle_limitations.doc)
    EALLOW;
    EPwm1Regs.HRCNFG.all = 0; // Reset HRCNF REG. Necessary for MEP on Falling Edge
    EPwm1Regs.HRCNFG.bit.EDGMODE = HR_FEP; // MEP control of falling edge
    EPwm1Regs.HRCNFG.bit.CTLMODE = HR_CMP; // CMPAHR(8) Register controls the edge position
    EPwm1Regs.HRCNFG.bit.HRLOAD = HR_CTR_ZERO; // Shadow mode bit; set this to match CMPCTL.LOADAMODE!
    EDIS;

    // Counter-Compare A/B
    EPwm1Regs.CMPA.all = ((uint32_t)IVPWM_MIN << 16) | (1<<8); // Counter-Compare A
    EPwm1Regs.CMPB.all = 0;         // Counter-Compare B, not used. Epwm out B is generated in dead-band gen as inverted outp A

    //Counter-Compare Control
    EPwm1Regs.CMPCTL.bit.LOADAMODE = CC_CTR_ZERO; // Active Counter-Compare A (CMPA) Load From Shadow Select Mode: Load on CTR = Zero
    //EPwm1Regs.CMPCTL.bit.LOADBMODE = CC_CTR_ZERO; // CMPB not used
    EPwm1Regs.CMPCTL.bit.SHDWAMODE = CC_SHADOW; // Counter-compare A (CMPA) Register Operating Mode
    //EPwm1Regs.CMPCTL.bit.SHDWBMODE = CC_SHADOW; // CMPB not used

    // Action-Qualifier
    EPwm1Regs.AQCTLA.all = AQ_NO_ACTION;   // Action-Qualifier Output A Control: Do nothing (all actions disabled)
    EPwm1Regs.AQCTLA.bit.CAU = AQ_CLEAR; // Action when the counter equals the active CMPA register and the counter is incrementing: Clear: force EPWMxA output low.
    EPwm1Regs.AQCTLA.bit.CAD = AQ_SET;   // Action when the counter equals the active CMPA register and the counter is decrementing: Set: force EPWMxA output high.
    EPwm1Regs.AQCTLB.all = AQ_NO_ACTION;   // Action-Qualifier Output B Control: Do nothing (all actions disabled)
    EPwm1Regs.AQSFRC.all = AQ_NO_ACTION;   // Action-Qualifier Software Force: disabled

    // Action-Qualifier Continuous Software Force
    EPwm1Regs.AQCSFRC.bit.CSFA = AQ_NO_ACTION;  // Continuous Software Force on Output A: Forcing disabled, i.e., has no effect
    EPwm1Regs.AQCSFRC.bit.CSFB = AQ_NO_ACTION;  // Continuous Software Force on Output A: Forcing disabled, i.e., has no effect

    // Dead-Band Generator Control
    EPwm1Regs.DBCTL.bit.HALFCYCLE = 0; // counter runs on TBCLK rate (high res dead band not used)
    EPwm1Regs.DBCTL.bit.DEDB_MODE = 0; // Rising Edge Delay on ePWM1A, Falling Edge Delay on ePWM1B
    EPwm1Regs.DBCTL.bit.OUTSWAP = 0; // no swapping of outputs
    EPwm1Regs.DBCTL.bit.SHDWDBFEDMODE = 0; // Immediate mode
    EPwm1Regs.DBCTL.bit.SHDWDBREDMODE = 0; // Immediate mode
    EPwm1Regs.DBCTL.bit.IN_MODE = DBA_ALL;      // EPWMxA In is the source for both falling-edge and rising-edge delay.
    EPwm1Regs.DBCTL.bit.POLSEL = DB_ACTV_HIC;   // Active high complementary (AHC). EPWMxB is inverted.
    EPwm1Regs.DBCTL.bit.OUT_MODE = DB_FULL_ENABLE; // Dead-band is fully enabled for both rising-edge delay on output EPWMxA and falling-edge delay on output EPWMxB.
    EPwm1Regs.DBRED.all = IVPWM_DBRED;              // Dead-Band Generator Rising Edge Delay
    EPwm1Regs.DBFED.all = IVPWM_DBFED;              // Dead-Band Generator Falling Edge Delay


    // PWM-Chopper, not used
    EPwm1Regs.PCCTL.all = 0;                   // PWM-Chopper Control

    // Trip-Zone Submodule, not used
    EALLOW;
    EPwm1Regs.TZSEL.all = 0;               // Trip-Zone Select
    EPwm1Regs.TZCTL.all = 0;               // Trip-Zone Control
    EPwm1Regs.TZEINT.all = 0;              // Trip-Zone Enable Interrupt
    EPwm1Regs.TZCLR.all = BIT0|BIT1|BIT2;  // Trip-Zone Clear
    EDIS;

    // Event-Trigger Prescale
    EPwm1Regs.ETPS.bit.INTPRD = ET_3RD;         // Generate interrupt on third event
    EPwm1Regs.ETPS.bit.SOCAPRD = ET_1ST;        // Generate the EPWMxSOCA pulse on the first event

    // Event-Trigger Selection
    EPwm1Regs.ETSEL.bit.INTSEL = ET_CTR_ZERO;   // Enable event time-base counter equal to zero.
    EPwm1Regs.ETSEL.bit.SOCASEL = ET_CTR_ZERO | ET_CTR_PRD;  // Enable event time-base counter equal to zero.

    // Event-Trigger Clear
    EPwm1Regs.ETCLR.bit.INT = 1;       // clear INT
    EPwm1Regs.ETCLR.bit.SOCA = 1;       // clear SOCA

    // enable Event-Trigger
    EPwm1Regs.ETSEL.bit.SOCAEN = 1;             // Enable EPWMxSOCA pulse (for ADC trigger)
    EPwm1Regs.ETSEL.bit.INTEN = 1;              // Enable EPWMx_INT generation
    EPwm1Regs.TBCTL.bit.CTRMODE = TB_COUNT_UPDOWN; // Finally let counter run
}

Looking at the registers I can see that the Epwm1Regs.ETFLG.INT got set. Furthermore the EPwm1Regs.ETPS.INTCNT = 3. Writing a zero in here or clearing the interrupt flag in i.e. the main while(1) loops has no effect.
Any help to debug this would be highly appreciated! Please let me know if you need any more information.

Thanks in advance!

  • I have written many examples with EPWM interrupt support. Take a look at this one for example.

    If you wish you start from th example and add your own code to it. OR you can review the configuration, compare it to your own and see what is different that is causing your interrupt to not fire.

    https://dev.ti.com/tirex/explore/node?node=AINABydsuZL0ie43X3rCHA__gYkahfz__LATEST

    Also, try to use driverlib becuase it reduces the chances of configuring the device incorrectly.

    Nima

  • Hi Nima,

    unfortunately that example uses the driverlib functions which we do not want to use for several reasons.

    I figured out the solution myself. I was disabling all CPU interrupt and didn't add group 3 back to IER. Now it works all fine.

    Anywas, speeking about the driverlib. I'm wondering how many TI customers actually use the driverlib. Is there any kind of certification so that the driverlib can be used in the automotive, medical and military field? If not they either have to do that by themselfes or they are simply not using it (which is what we (medical field) decided for)

    Thanks,

    Lennart