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.

CCS/TMS320F280049: Offset sync failure between EPWM and ADC0

Part Number: TMS320F280049

Tool/software: Code Composer Studio

Hello everyone,

i'm just programming a simple EPWM which triggers the ADC when CMPA is reached. 

The code bases on the driverlib ADC example.

The sample and hold time is only one zyklus for testing.

When the interrupt occures the register TBPRD has the number 5050 instead of 5000 or 300 instead of 250. The SYSCLK is 100MHz.

I hope you will understand my problem. Thanks.

#include "driverlib.h"
#include "device.h"
#include "main.h"

//
// Defines
//

#define EPWM1_TIMER_TBPRD  10000U
#define EPWM1_CMPA       5000U
#define EPWM_CMP_UP           1U
#define EPWM_CMP_DOWN         0U

//Define Outputs
#define DEVICE_GPIO_PIN_DRIVER1         10U
#define DEVICE_GPIO_PIN_DRIVER2         11U
#define DEVICE_GPIO_PIN_ANALOGCONT      12U
#define DEVICE_GPIO_PIN_COMPROGDIS      13U
#define DEVICE_GPIO_PIN_COMPSHUNTDIS    17U

//Define Inputs
#define DEVICE_GPIO_PIN_PWMCONTSHUNT    16U
#define DEVICE_GPIO_PIN_PWMCONTROG      6U

//
// Defines
//
#define RESULTS_BUFFER_SIZE     256

//
// Globals
//
uint16_t adcAResults[RESULTS_BUFFER_SIZE];   // Buffer for results
uint16_t index;                              // Index into result buffer
volatile uint16_t bufferFull;                // Flag to indicate buffer is full



void initEPWM1(void);
void initADC(void);
void initADCSOC(void);
__interrupt void adcA1ISR(void);


void main(void)
 {



    // Initialize device clock and peripherals
    Device_init();

    // Initialize GPIO and configure the GPIO pin as a push-pull output
    Device_initGPIO();

	GPIO_setPadConfig(24U, GPIO_PIN_TYPE_STD);
    GPIO_setDirectionMode(24U, GPIO_DIR_MODE_OUT);
	
    //Initialize PWM
    GPIO_setPadConfig(0U, GPIO_PIN_TYPE_STD);
    GPIO_setPinConfig(GPIO_0_EPWM1A);

    //
    // 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();
    // Enable Global Interrupt (INTM) and realtime interrupt (DBGM)

    //
    Interrupt_register(INT_ADCA1, &adcA1ISR);

    //init ADC and power it up; init EPWM
    initADC();

    //
    // Disable sync(Freeze clock to PWM as well)
    //
    SysCtl_disablePeripheral(SYSCTL_PERIPH_CLK_TBCLKSYNC);


	initEPWM1();

    //
    // Enable sync and clock to PWM
    //

    SysCtl_enablePeripheral(SYSCTL_PERIPH_CLK_TBCLKSYNC);


    initADCSOC();

    for(index = 0; index < RESULTS_BUFFER_SIZE; index++)
    {
        adcAResults[index] = 0;
    }

    index = 0;
    bufferFull = 0;

    //
    // Enable ADC interrupt
    //
    Interrupt_enable(INT_ADCA1);

    EINT;
    ERTM;
   

    // Loop Forever
    for(;;)
    {
        EPWM_enableADCTrigger(EPWM1_BASE, EPWM_SOC_A);
        //EPWM_enableADCTrigger(EPWM1_BASE, EPWM_SOC_B);
        EPWM_setTimeBaseCounterMode(EPWM1_BASE, EPWM_COUNTER_MODE_UP);
  
    }
}

void initEPWM1()
{
    //
    // Disable SOCA
    //
    EPWM_disableADCTrigger(EPWM1_BASE, EPWM_SOC_A);
    //
    // Configure the SOC to occur on the first up-count event
    //
    EPWM_setADCTriggerSource(EPWM1_BASE, EPWM_SOC_A, EPWM_SOC_TBCTR_U_CMPA);
    EPWM_setADCTriggerEventPrescale(EPWM1_BASE, EPWM_SOC_A, 1);


    //
    // Set-up TBCLK
    //
    EPWM_setTimeBasePeriod(EPWM1_BASE, EPWM1_TIMER_TBPRD);
    EPWM_setTimeBaseCounter(EPWM1_BASE, 0U);

    //
    // Set Compare values
    //
    EPWM_setCounterCompareValue(EPWM1_BASE,
                                EPWM_COUNTER_COMPARE_A,
                                EPWM1_CMPA);

    //
    // Set up counter mode
    //

    EPWM_setClockPrescaler(EPWM1_BASE,
                           EPWM_CLOCK_DIVIDER_1,
                           EPWM_HSCLOCK_DIVIDER_1);

    //
    // Set up shadowing
    //

    EPWM_setCounterCompareShadowLoadMode(EPWM1_BASE,
                                         EPWM_COUNTER_COMPARE_A,
                                         EPWM_COMP_LOAD_ON_CNTR_ZERO);

    //
    // Set actions
    //
    EPWM_setActionQualifierAction(EPWM1_BASE,
                                  EPWM_AQ_OUTPUT_A,
                                  EPWM_AQ_OUTPUT_HIGH,
                                  EPWM_AQ_OUTPUT_ON_TIMEBASE_UP_CMPA);
    EPWM_setActionQualifierAction(EPWM1_BASE,
                                  EPWM_AQ_OUTPUT_A,
                                  EPWM_AQ_OUTPUT_LOW,
                                  EPWM_AQ_OUTPUT_ON_TIMEBASE_ZERO);

    EPWM_setTimeBaseCounterMode(EPWM1_BASE, EPWM_COUNTER_MODE_STOP_FREEZE);

}

void initADC()
{
    //
       // Setup VREF as internal
       //
       ADC_setVREF(ADCA_BASE, ADC_REFERENCE_EXTERNAL, ADC_REFERENCE_3_3V);

       //
       // Set ADCCLK divider to /4
       //
       ADC_setPrescaler(ADCA_BASE, ADC_CLK_DIV_2_0); //50Mhz Clock (100Mhz/2)

       //
       // Set pulse positions to late
       //
       ADC_setInterruptPulseMode(ADCA_BASE, ADC_PULSE_END_OF_CONV);

       //
       // Power up the ADC and then delay for 1 ms
       //
       ADC_enableConverter(ADCA_BASE);
       DEVICE_DELAY_US(1000);
}


void initADCSOC(void)
{
    //
    // Configure SOC0 of ADCA to convert pin A0 with a sample window of 11
    // SYSCLK cycles. The EPWM1SOCA signal will be the trigger.
    //
    ADC_setupSOC(ADCA_BASE, ADC_SOC_NUMBER0, ADC_TRIGGER_EPWM1_SOCA,
                 ADC_CH_ADCIN0, 1);
    //
    // Set SOC0 to set the interrupt 1 flag. Enable the interrupt and make
    // sure its flag is cleared.
    //
    ADC_setInterruptSource(ADCA_BASE, ADC_INT_NUMBER1, ADC_SOC_NUMBER0);
    ADC_enableInterrupt(ADCA_BASE, ADC_INT_NUMBER1);
    ADC_clearInterruptStatus(ADCA_BASE, ADC_INT_NUMBER1);
}


__interrupt void adcA1ISR(void)
{
    //
    // Add the latest result to the buffer
    //
    GPIO_togglePin(24U);
   // adcAResults[index++] = ADC_readResult(ADCARESULT_BASE, ADC_SOC_NUMBER0);


    //
    // Set the bufferFull flag if the buffer is full
    //
    if(RESULTS_BUFFER_SIZE <= index)
    {
        index = 0;
        bufferFull = 1;
    }

    //
    // Clear the interrupt flag
    //
    ADC_clearInterruptStatus(ADCA_BASE, ADC_INT_NUMBER1);

    //
    // Check if overflow has occurred
    //

    if(true == ADC_getInterruptOverflowStatus(ADCA_BASE, ADC_INT_NUMBER1))
    {
        ADC_clearInterruptOverflowStatus(ADCA_BASE, ADC_INT_NUMBER1);
        ADC_clearInterruptStatus(ADCA_BASE, ADC_INT_NUMBER1);
    }

    //
    // Acknowledge the interrupt
    //
    Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP1);
}

Green PWM

BLUE TOGGLE PIN inside of ISR

  • You're not going to see exactly a TBCTR that is exactly equal to CMPA in your ADC ISR. You have to take into account the interrupt latency, the ADC sample and hold time, and the time it takes from the end of the S+H window to when the ADCINT flag is actually set. There are some timing diagrams in the ADC chapter that show this. You can do the calculations to see if this makes sense for the size of the delay you're seeing.

    Some of that can't be avoided, but one adjustment you could try is to set your interrupt pulse generation to occur at the end of the acquisition window instead of at the end of the conversion. See the description of the INTPULSEPOS bit in the TRM. The code above that would change is the pulseMode parameter in ADC_setInterruptPulseMode().

    Whitney

  • Thanks for your answer. I tried to set the pulse generation to "ADC_PULSE_END_OF_ACQ_WIN"  and it is little bit faster. 

    I cannot believe that this is the maximum speed of the ADC (running with 50Mhz). If yes, I cannot measure the current exactly.The PWM is only running with 100kHz.  

    I rechecked the timings in the datasheet and my adc would need in the Early Interrupt Mode 1(tint=0 tsh=1) and in Late Interrupt Mode 23 (tint=22 tsh=1) SYSCLK Cycles . If i compare this with my oscilloscope pictures, my delay is much larger.

  • I did some additonal test. I summarize everything in the following table:

    TBPRD = 1000U

    CMPA = 500U

    Trigger when TBCTR == zero

    Sample and Hold Cycle

    End of acquisition (Number in TBCTR Register when calling adcA1ISR())

    End of conversion (Number in TBCTR Register when calling adcA1ISR())
     1 13 (HEX) -> 19 (DEC) 27 (HEX) -> 39 (DEC)
    11 1D (HEX) -> 29 (DEC) 31 (HEX) -> 49 (DEC)

    Trigger when TBCTR == CMPA

    Sample and Hold Cycle

    End of acquisition (Number in TBCTR Register when calling adcA1ISR())

    End of conversion (Number in TBCTR Register when calling adcA1ISR())
     1 208 (HEX) -> 520 (DEC) 21C (HEX) -> 540 (DEC)
    11 212 (HEX) -> 530 (DEC) 226 (HEX) -> 550 (DEC)

    The values in the table shows, that the ADC timings are okay. Only the interrupt is ~ 18-19 SYSCLK cycles to late. Why is this happening?

  • Are you counting the interrupt latency? There's a minimum of 14 cycles.

    http://processors.wiki.ti.com/index.php/Interrupt_FAQ_for_C2000#ISR_Latency

    You can look at the disassembly to see how many instructions are being executed between the start of the ISR and the toggling of the pin (or the point at which you're pointing a breakpoint to look at TBCTR).

    Also, if you don't have optimization turned on, GPIO_togglePin() is going to do a function call and some calculations before the actual write to the toggle register, so that could be contributing. Turning on optimization should inline it and make it faster.

    Whitney

  • Thanks Whitney,

    after I turned on the optimizer, the speed of the internal ISR is as described in the datasheet.

    Patrick