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.

TMS320F280039C: Clarification on ADC Interrupt Timing in TMS320F280039C

Part Number: TMS320F280039C


Tool/software:

Hello,

I’m working with the TMS320F280039C LaunchPad and using the ADC module based on the adc_ex2_soc_epwm example. In my setup:

ADC SOC0 is triggered by ePWM1 ADCSOCA at counter = zero (Up-count mode).
Once the ADC conversion completes, an interrupt is generated (Occurs at the end of the acquisition window).
ePWM1 is configured for 100 kHz in Up-count mode.
I’m recording the ePWM counter value at the time of interrupt and consistently observe values between 90 to 93.
Given the SYSCLK = 120 MHz, each ePWM tick is 8.33 ns, so the interrupt occurs approximately 750 ns (=90 * 8.33ns) after the trigger.

I understand the ADC timing as follows:

ADCCLK = 60 MHz, Prescaler = 2
ADC Conversion Time =(9 * (1/120E6)) + (11 * (1/60E6)) = 258.3E-9 seconds

However, I’m seeing a delay of around 750 ns, which is significantly longer than the calculated 258 ns. That leaves a gap of approximately 492 ns.

Could you help me understand what contributes to this additional delay?\

Also, here are the relevant disassembly instructions:

237 {
adcA1ISR():
008800: 761B ASP
008801: FFF0 PUSH RB
008802: 0005 PUSH AR1H:AR0H
008803: ABBD MOVL *SP++, XT
008804: A8BD MOVL *SP++, XAR4
008805: A0BD MOVL *SP++, XAR5
008806: C2BD MOVL *SP++, XAR6
008807: C3BD MOVL *SP++, XAR7
008808: E20000BD MOV32 *SP++, STF
00880a: E20300BD MOV32 *SP++, R0H
00880c: E20301BD MOV32 *SP++, R1H
00880e: E20302BD MOV32 *SP++, R2H
008810: E20303BD MOV32 *SP++, R3H
008812: E6300600 SETFLG RNDF32=1,RNDF64=1
008814: FF69 SPM #0
008815: 2942 CLRC OVM|PAGE0
008816: 5616 CLRC AMODE
239 isrTick_Entry = EPWM_getTimeBaseCounterValue(myEPWM0_BASE);
008817: FF204000 MOV ACC, #16384
008819: 764092F1 LCR EPWM_getTimeBaseCounterValue
00881b: 761F02A0 MOVW DP, #0x2a0
00881d: 9609 MOV @0x9, AL
243 GPIO_togglePin(myGPIO0);
00881e: 0216 MOVB ACC, #22
00881f: 76408E69 LCR GPIO_togglePin

Thanks & Regards,

Prathamesh.

  • Hi Prathamesh,

    Appologies for late reply. Can you share the C code snippet for the above mentioned code (that will be easier to decode)  ?

    BR,

    Nilesh

  • Also what is the value of ACQPS kept for this configuration ?

  • /*
     * Modifications made by Prathamesh on 28th July, 2025:
     *
     * 1. Removed storage of ADC results in a 256-value buffer.
     * 2. Removed CPU halt and reset logic.
     * 3. Implemented continuous ADC result reading within the ADC ISR.
     *
     * Additional Notes:
     * - ADC is triggered at TBCTR zero value.
     * - Upon completion of ADC SOC/EOC, an interrupt is generated.
     * - Observed ADC conversion time is approximately 1 µs.
     *
     * Date: 29th July 2025
     * Learned that ADC interrupt was incorrectly configured—triggered
     * at SOC0 instead of final SOCN (where N = 1, 2, 3, ..., N).
     * Each SOC takes ~30 PWM counts, which equals ~250 ns (30 × 8.33 ns).
     * Raised a TI E2E forum query to understand why an additional ~500 ns delay
     * occursfor the first SOC0.
     */
     void main(void)
    {
        //
        // Initialize device clock and peripherals
        //
        Device_init();
    
        //
        // Disable pin locks and enable internal pullups.
        //
        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();
    
        // 
        // Board Initialization
        // - Set up the ADC and initialize the SOC
        // - Enable ADC interrupt
        //
        Board_init();
    
        //
        // Enable Global Interrupt (INTM) and realtime interrupt (DBGM)
        //
        EINT;
        ERTM;
    
        //
        // Loop indefinitely
        //
        while(1)
        {
    
        }
    }
    
    //
    // adcA1ISR - ADC A Interrupt 1 ISR
    //
    __interrupt void adcA1ISR(void)
    {
        isrTick_Entry = EPWM_getTimeBaseCounterValue(myEPWM0_BASE);
    
        GPIO_togglePin(myGPIO0);
    //    isrTick_Exist = EPWM_getTimeBaseCounterValue(myEPWM0_BASE);
    
    
        //
        // Add the latest result to the buffer
        //
        myADC0Results = ADC_readResult(ADCARESULT_BASE, ADC_SOC_NUMBER0);
        myADC1Results = ADC_readResult(ADCBRESULT_BASE, ADC_SOC_NUMBER0);
    
        //
        // Clear the interrupt flag
        //
        ADC_clearInterruptStatus(myADC0_BASE, ADC_INT_NUMBER1);
    
        //
        // Acknowledge the interrupt
        //
        Interrupt_clearACKGroup(INT_myADC0_1_INTERRUPT_ACK_GROUP);
    }
    
    void myADC0_init(){
    	//
    	// ADC Initialization: Write ADC configurations and power up the ADC
    	//
    	// Set the analog voltage reference selection and ADC module's offset trims.
    	// This function sets the analog voltage reference to internal (with the reference voltage of 1.65V or 2.5V) or external for ADC
    	// which is same as ASysCtl APIs.
    	//
    	ADC_setVREF(myADC0_BASE, ADC_REFERENCE_INTERNAL, ADC_REFERENCE_3_3V);
    	//
    	// Configures the analog-to-digital converter module prescaler.
    	//
    	ADC_setPrescaler(myADC0_BASE, ADC_CLK_DIV_2_0);
    	//
    	// Sets the timing of the end-of-conversion pulse
    	//
    	ADC_setInterruptPulseMode(myADC0_BASE, ADC_PULSE_END_OF_ACQ_WIN);
    	//
    	// Sets the timing of early interrupt generation.
    	//
    	ADC_setInterruptCycleOffset(myADC0_BASE, 0U);
    	//
    	// Powers up the analog-to-digital converter core.
    	//
    	ADC_enableConverter(myADC0_BASE);
    	//
    	// Delay for 1ms to allow ADC time to power up
    	//
    	DEVICE_DELAY_US(5000);
    	//
    	// SOC Configuration: Setup ADC EPWM channel and trigger settings
    	//
    	// Disables SOC burst mode.
    	//
    	ADC_disableBurstMode(myADC0_BASE);
    	//
    	// Sets the priority mode of the SOCs.
    	//
    	ADC_setSOCPriority(myADC0_BASE, ADC_PRI_THRU_SOC1_HIPRI);
    	//
    	// Start of Conversion 0 Configuration
    	//
    	//
    	// Configures a start-of-conversion (SOC) in the ADC and its interrupt SOC trigger.
    	// 	  	SOC number		: 0
    	//	  	Trigger			: ADC_TRIGGER_EPWM1_SOCA
    	//	  	Channel			: ADC_CH_ADCIN0
    	//	 	Sample Window	: 9 SYSCLK cycles
    	//		Interrupt Trigger: ADC_INT_SOC_TRIGGER_NONE
    	//
    	ADC_setupSOC(myADC0_BASE, ADC_SOC_NUMBER0, ADC_TRIGGER_EPWM1_SOCA, ADC_CH_ADCIN0, 9U);
    	ADC_setInterruptSOCTrigger(myADC0_BASE, ADC_SOC_NUMBER0, ADC_INT_SOC_TRIGGER_NONE);
    	//
    	// Start of Conversion 1 Configuration
    	//
    	//
    	// Configures a start-of-conversion (SOC) in the ADC and its interrupt SOC trigger.
    	// 	  	SOC number		: 1
    	//	  	Trigger			: ADC_TRIGGER_EPWM1_SOCA
    	//	  	Channel			: ADC_CH_ADCIN1
    	//	 	Sample Window	: 9 SYSCLK cycles
    	//		Interrupt Trigger: ADC_INT_SOC_TRIGGER_NONE
    	//
    	ADC_setupSOC(myADC0_BASE, ADC_SOC_NUMBER1, ADC_TRIGGER_EPWM1_SOCA, ADC_CH_ADCIN1, 9U);
    	ADC_setInterruptSOCTrigger(myADC0_BASE, ADC_SOC_NUMBER1, ADC_INT_SOC_TRIGGER_NONE);
    	//
    	// Start of Conversion 2 Configuration
    	//
    	//
    	// Configures a start-of-conversion (SOC) in the ADC and its interrupt SOC trigger.
    	// 	  	SOC number		: 2
    	//	  	Trigger			: ADC_TRIGGER_EPWM1_SOCA
    	//	  	Channel			: ADC_CH_ADCIN2
    	//	 	Sample Window	: 9 SYSCLK cycles
    	//		Interrupt Trigger: ADC_INT_SOC_TRIGGER_NONE
    	//
    	ADC_setupSOC(myADC0_BASE, ADC_SOC_NUMBER2, ADC_TRIGGER_EPWM1_SOCA, ADC_CH_ADCIN2, 9U);
    	ADC_setInterruptSOCTrigger(myADC0_BASE, ADC_SOC_NUMBER2, ADC_INT_SOC_TRIGGER_NONE);
    	//
    	// Start of Conversion 3 Configuration
    	//
    	//
    	// Configures a start-of-conversion (SOC) in the ADC and its interrupt SOC trigger.
    	// 	  	SOC number		: 3
    	//	  	Trigger			: ADC_TRIGGER_EPWM1_SOCA
    	//	  	Channel			: ADC_CH_ADCIN3
    	//	 	Sample Window	: 9 SYSCLK cycles
    	//		Interrupt Trigger: ADC_INT_SOC_TRIGGER_NONE
    	//
    	ADC_setupSOC(myADC0_BASE, ADC_SOC_NUMBER3, ADC_TRIGGER_EPWM1_SOCA, ADC_CH_ADCIN3, 9U);
    	ADC_setInterruptSOCTrigger(myADC0_BASE, ADC_SOC_NUMBER3, ADC_INT_SOC_TRIGGER_NONE);
    	//
    	// ADC Interrupt 1 Configuration
    	// 		Source	: ADC_SOC_NUMBER3
    	// 		Interrupt Source: enabled
    	// 		Continuous Mode	: disabled
    	//
    	//
    	ADC_setInterruptSource(myADC0_BASE, ADC_INT_NUMBER1, ADC_SOC_NUMBER3);
    	ADC_clearInterruptStatus(myADC0_BASE, ADC_INT_NUMBER1);
    	ADC_disableContinuousMode(myADC0_BASE, ADC_INT_NUMBER1);
    	ADC_enableInterrupt(myADC0_BASE, ADC_INT_NUMBER1);
    }
    void myADC1_init(){
    	//
    	// ADC Initialization: Write ADC configurations and power up the ADC
    	//
    	// Set the analog voltage reference selection and ADC module's offset trims.
    	// This function sets the analog voltage reference to internal (with the reference voltage of 1.65V or 2.5V) or external for ADC
    	// which is same as ASysCtl APIs.
    	//
    	ADC_setVREF(myADC1_BASE, ADC_REFERENCE_INTERNAL, ADC_REFERENCE_3_3V);
    	//
    	// Configures the analog-to-digital converter module prescaler.
    	//
    	ADC_setPrescaler(myADC1_BASE, ADC_CLK_DIV_2_0);
    	//
    	// Sets the timing of the end-of-conversion pulse
    	//
    	ADC_setInterruptPulseMode(myADC1_BASE, ADC_PULSE_END_OF_ACQ_WIN);
    	//
    	// Sets the timing of early interrupt generation.
    	//
    	ADC_setInterruptCycleOffset(myADC1_BASE, 0U);
    	//
    	// Powers up the analog-to-digital converter core.
    	//
    	ADC_enableConverter(myADC1_BASE);
    	//
    	// Delay for 1ms to allow ADC time to power up
    	//
    	DEVICE_DELAY_US(5000);
    	//
    	// SOC Configuration: Setup ADC EPWM channel and trigger settings
    	//
    	// Disables SOC burst mode.
    	//
    	ADC_disableBurstMode(myADC1_BASE);
    	//
    	// Sets the priority mode of the SOCs.
    	//
    	ADC_setSOCPriority(myADC1_BASE, ADC_PRI_ALL_ROUND_ROBIN);
    	//
    	// Start of Conversion 0 Configuration
    	//
    	//
    	// Configures a start-of-conversion (SOC) in the ADC and its interrupt SOC trigger.
    	// 	  	SOC number		: 0
    	//	  	Trigger			: ADC_TRIGGER_EPWM1_SOCA
    	//	  	Channel			: ADC_CH_ADCIN0
    	//	 	Sample Window	: 9 SYSCLK cycles
    	//		Interrupt Trigger: ADC_INT_SOC_TRIGGER_NONE
    	//
    	ADC_setupSOC(myADC1_BASE, ADC_SOC_NUMBER0, ADC_TRIGGER_EPWM1_SOCA, ADC_CH_ADCIN0, 9U);
    	ADC_setInterruptSOCTrigger(myADC1_BASE, ADC_SOC_NUMBER0, ADC_INT_SOC_TRIGGER_NONE);
    }
    
    //*****************************************************************************
    //
    // EPWM Configurations
    //
    //*****************************************************************************
    void EPWM_init(){
        EPWM_setEmulationMode(myEPWM0_BASE, EPWM_EMULATION_FREE_RUN);	
        EPWM_setClockPrescaler(myEPWM0_BASE, EPWM_CLOCK_DIVIDER_1, EPWM_HSCLOCK_DIVIDER_1);	
        EPWM_setTimeBasePeriod(myEPWM0_BASE, 1999);	
        EPWM_setTimeBaseCounter(myEPWM0_BASE, 0);	
        EPWM_setTimeBaseCounterMode(myEPWM0_BASE, EPWM_COUNTER_MODE_UP);	
        EPWM_disablePhaseShiftLoad(myEPWM0_BASE);	
        EPWM_setPhaseShift(myEPWM0_BASE, 0);	
        EPWM_setCounterCompareValue(myEPWM0_BASE, EPWM_COUNTER_COMPARE_A, 1000);	
        EPWM_setCounterCompareShadowLoadMode(myEPWM0_BASE, EPWM_COUNTER_COMPARE_A, EPWM_COMP_LOAD_ON_CNTR_ZERO);	
        EPWM_setCounterCompareValue(myEPWM0_BASE, EPWM_COUNTER_COMPARE_B, 0);	
        EPWM_setCounterCompareShadowLoadMode(myEPWM0_BASE, EPWM_COUNTER_COMPARE_B, EPWM_COMP_LOAD_ON_CNTR_ZERO);	
        EPWM_setActionQualifierAction(myEPWM0_BASE, EPWM_AQ_OUTPUT_A, EPWM_AQ_OUTPUT_HIGH, EPWM_AQ_OUTPUT_ON_TIMEBASE_ZERO);	
        EPWM_setActionQualifierAction(myEPWM0_BASE, EPWM_AQ_OUTPUT_A, EPWM_AQ_OUTPUT_NO_CHANGE, EPWM_AQ_OUTPUT_ON_TIMEBASE_PERIOD);	
        EPWM_setActionQualifierAction(myEPWM0_BASE, EPWM_AQ_OUTPUT_A, EPWM_AQ_OUTPUT_LOW, EPWM_AQ_OUTPUT_ON_TIMEBASE_UP_CMPA);	
        EPWM_setActionQualifierAction(myEPWM0_BASE, EPWM_AQ_OUTPUT_A, EPWM_AQ_OUTPUT_NO_CHANGE, EPWM_AQ_OUTPUT_ON_TIMEBASE_DOWN_CMPA);	
        EPWM_setActionQualifierAction(myEPWM0_BASE, EPWM_AQ_OUTPUT_A, EPWM_AQ_OUTPUT_NO_CHANGE, EPWM_AQ_OUTPUT_ON_TIMEBASE_UP_CMPB);	
        EPWM_setActionQualifierAction(myEPWM0_BASE, EPWM_AQ_OUTPUT_A, EPWM_AQ_OUTPUT_NO_CHANGE, EPWM_AQ_OUTPUT_ON_TIMEBASE_DOWN_CMPB);	
        EPWM_setActionQualifierAction(myEPWM0_BASE, EPWM_AQ_OUTPUT_B, EPWM_AQ_OUTPUT_LOW, EPWM_AQ_OUTPUT_ON_TIMEBASE_ZERO);	
        EPWM_setActionQualifierAction(myEPWM0_BASE, EPWM_AQ_OUTPUT_B, EPWM_AQ_OUTPUT_NO_CHANGE, EPWM_AQ_OUTPUT_ON_TIMEBASE_PERIOD);	
        EPWM_setActionQualifierAction(myEPWM0_BASE, EPWM_AQ_OUTPUT_B, EPWM_AQ_OUTPUT_HIGH, EPWM_AQ_OUTPUT_ON_TIMEBASE_UP_CMPA);	
        EPWM_setActionQualifierAction(myEPWM0_BASE, EPWM_AQ_OUTPUT_B, EPWM_AQ_OUTPUT_NO_CHANGE, EPWM_AQ_OUTPUT_ON_TIMEBASE_DOWN_CMPA);	
        EPWM_setActionQualifierAction(myEPWM0_BASE, EPWM_AQ_OUTPUT_B, EPWM_AQ_OUTPUT_NO_CHANGE, EPWM_AQ_OUTPUT_ON_TIMEBASE_UP_CMPB);	
        EPWM_setActionQualifierAction(myEPWM0_BASE, EPWM_AQ_OUTPUT_B, EPWM_AQ_OUTPUT_NO_CHANGE, EPWM_AQ_OUTPUT_ON_TIMEBASE_DOWN_CMPB);	
        EPWM_setRisingEdgeDelayCountShadowLoadMode(myEPWM0_BASE, EPWM_RED_LOAD_ON_CNTR_ZERO);	
        EPWM_disableRisingEdgeDelayCountShadowLoadMode(myEPWM0_BASE);	
        EPWM_setFallingEdgeDelayCountShadowLoadMode(myEPWM0_BASE, EPWM_FED_LOAD_ON_CNTR_ZERO);	
        EPWM_disableFallingEdgeDelayCountShadowLoadMode(myEPWM0_BASE);	
        EPWM_enableADCTrigger(myEPWM0_BASE, EPWM_SOC_A);	
        EPWM_setADCTriggerSource(myEPWM0_BASE, EPWM_SOC_A, EPWM_SOC_TBCTR_ZERO);	
        EPWM_setADCTriggerEventPrescale(myEPWM0_BASE, EPWM_SOC_A, 1);	
    }

    Hi Nilesh,

    Thank you for your reply.

    I am attaching the code, and I keep ACQPS value equal to '9'.

    BRs,

    Prathamesh.

  • Hi Prathamesh,

    Let me go throught the code trying to see if i can replicate the issue on my side will update over the weekend. 

    BR,

    Nilesh

  • Hi Prathamesh,

    I looked at the code. 

    So the ADC conversion is triggered by "EPWM_enableADCTrigger(myEPWM0_BASE, EPWM_SOC_A); "  & EPWM_setTimeBaseCounterMode(EPWM1_BASE, EPWM_COUNTER_MODE_UP); at this EPWM counter will  start counting . 

    It looks like that these are part of the EPWM configuration fucnction itself which will start the counter before the ADC is configured to start the conversion. 

    This would depend on the sequence of the configuration. Which maybe caousing this delay. 

    can you try configuring the above mentioned lines as per the original example to see if the delay is reduced or removed. (Need to remove this lines from EPWM configuration function as well at the same time)

    Let me know if this works.

    BR,

    Nilesh