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.

TMS320F28027: ADCSOCFLG not being cleared during ping-pong sampling

Part Number: TMS320F28027

Hi

I have copied the adcOffsetSelfCal() and its dependent functions from the piccolo example files. I have converted the code to use the driver type HAL instead of the structs and use the code to take a ping-pong sampling of a single pin. This is called directly after the device setup, which includes ADC initialisation and calibration.

There is one problem however; at the lines at which the code waits in an empty while loop for the ADCINTFLG register's ADCINT1 and INT2 bits to be cleared to indicate that the related interrupts has been triggered. It seems that the bits in question are never cleared (i.e. execution sits in the while loop forever), at least as far as the compiled code is concerned.

In the original example these lines are coded as: 

        //Wait for ADCINT1 to trigger, then add ADCRESULT0-7 registers to sum
        while (AdcRegs.ADCINTFLG.bit.ADCINT1 == 0){}

and:

        //Wait for ADCINT2 to trigger, then add ADCRESULT8-15 registers to sum
        while (AdcRegs.ADCINTFLG.bit.ADCINT2 == 0){}

In my converted code, the full function is as follows:

uint_least8_t getValueFromPingPong (void) 
{ uint16_t address = 0U, i = 0U; ADC_Handle adcHandle = ADC_init((void *)ADC_BASE_ADDR, sizeof(ADC_Obj)); ADC_Obj * adc = (ADC_Obj *)adcHandle; ADC_SocNumber_e soc = ADC_SocNumber_0; for (; soc <= ADC_SocNumber_15; ++soc) { /* Select channel for all SOC. */ ADC_setSocChanNumber(adcHandle, soc, SENSE_ADC_CHANNEL); } /* Select sample window size for all SOC. */ for (soc = ADC_SocNumber_0; soc <= ADC_SocNumber_15; ++soc) { ADC_setSocSampleWindow(adcHandle, soc, ADC_SocSampleWindow_7_cycles); } /* Setup for ping-pong sampling. */ ADC_enableInt(adcHandle, ADC_IntNumber_1); ADC_enableInt(adcHandle, ADC_IntNumber_2); ADC_setIntMode(adcHandle, ADC_IntNumber_1, ADC_IntMode_ClearFlag); ADC_setIntMode(adcHandle, ADC_IntNumber_2, ADC_IntMode_ClearFlag); ADC_setIntPulseGenMode(adcHandle, ADC_IntPulseGenMode_Prior); ADC_setIntSrc(adcHandle, ADC_IntNumber_1, ADC_IntSrc_EOC6); ADC_setIntSrc(adcHandle, ADC_IntNumber_2, ADC_IntSrc_EOC14); ENABLE_PROTECTED_REGISTER_WRITE_MODE; adc->ADCINTSOCSEL1 = 0xAAAAU; /* Set ADCINT1 to start SOC8-15. */ adc->ADCINTSOCSEL2 = 0x5555U; /* Set ADCINT2 to start SOC0-7. */ DISABLE_PROTECTED_REGISTER_WRITE_MODE; DELAY_US(ADC_usDELAY); adc->ADCSOCFRC1 = 0x00FFU; /* Force start SOC0-7 to begin ping-pong sampling. */ uint32_t sum = 0U; /* Take samples. */ for (i = 0U; i < SENSE_SAMPLE_SIZE; i += 16U) { while((adc->ADCINTFLG & 0x01U) == 0U) {} // This condition is only ever true on the first iteration thru the for loop adc->ADCINTFLGCLR = 0x01U; sum += adc->ADCRESULT[0]; sum += adc->ADCRESULT[1]; sum += adc->ADCRESULT[2]; sum += adc->ADCRESULT[3]; sum += adc->ADCRESULT[4]; sum += adc->ADCRESULT[5]; sum += adc->ADCRESULT[6]; /* Wait for SOC9 conversion to start, which gives time for SOC7 * conversion result. */ while(adc->ADCSOCFLG1 & 0x200U) {} sum += adc->ADCRESULT[7]; while((adc->ADCINTFLG & 0x02U) == 0U) {} // This condition is never seems to be true adc->ADCINTFLGCLR = 0x02U; sum += adc->ADCRESULT[8]; sum += adc->ADCRESULT[9]; sum += adc->ADCRESULT[10]; sum += adc->ADCRESULT[11]; sum += adc->ADCRESULT[12]; sum += adc->ADCRESULT[13]; sum += adc->ADCRESULT[14]; /* Wait for SOC1 conversion to start, which gives time for SOC15 * conversion result. */ while(adc->ADCSOCFLG1 & 0x02U) {} sum += adc->ADCRESULT[15]; } ADC_disableInt(adcHandle, ADC_IntNumber_1); /* Stop the ping-pong sampling. */ ADC_disableInt(adcHandle, ADC_IntNumber_2); /* Average the sample data. */ uint16_t adcConvMean = (uint16_t)(sum / SENSE_SAMPLE_SIZE); return adcConvMean; }

Why does this happen and what can I do to fix it?

Realised that my adcOffsetSelfCal() function is called immediately before this and uses code that is very similar, yet it works. The main difference being that the SelfCal function uses ADC B5, with B5 connected to the VREFLO.

Thanks, T

  • Toby,

    Do the SOC, INT, or OVF flags have any bits set when code execution is stuck in the while loop?

    Just to sanity check, does this work for one iteration if you software force all 16 SOCs?

    -Tommy
  • Hi Tommy

    When the function starts the flags are clear (in ADCINTFLG, ADCINTOVF, ADCSOCOVF1). I added the following before enabling the interrupts to be sure:

        /* Clear flags. */
        adc->ADCINTFLGCLR = 0x00FF;
        adc->ADCINTOVFCLR = 0x00FF;
        adc->ADCSOCOVFCLR1 = 0xFFFF;
    

    If I pause execution is trapped at the marked line, then ADCINTOVF is 0x0001, although the ADCINTFLG is 0.

    I notice that the for loop does complete one iteration correctly, it is on the second iteration that things get "stuck".  Forcing all 16 SOCs gives the same behaviour.

    T

  • Toby,

    I think the interrupt flags stop working as expected when there is an overflow event. Can you see if the ADCINTOVF flag is set just before the SOCs are software forced?

    Another approach could be to configure the INTSEL1N2 interrupt generation as continuous so that overflows will not affect the code.

    This might be a similar scenario as this thread from a few years ago: e2e.ti.com/.../1343383

    -Tommy
  • Hi Tommy,

    The ADCINTOVF is 0 before the SOCs are forced. Adding adc->ADCINTOVFCLR = 0x01U; and adc->ADCINTOVFCLR = 0x02U; after the respective while()s clears the related overflow bits during the first pass, but execution still gets waits forever for ADCINTFLG & 0x01 on the second iteration.

    I notice that the ADSCOOVF1 register holds 0x00FF, although the reference guide mentions that this has no effect other than to simply indicate that a trigger was missed, could this still have some effect?

    Thanks, T

  • Changed to continuous sampling (INT1CONT = 1; and INT2CONT = 1;) and this allows the measurements to complete.

    One odd thing while doing this is that I had to 'manually' set the registers to the correct values (i.e. INTSELxNy[0] = 0x6E66;) as using the ADC_setIntSrc() and ADC_setIntMode functions somehow always resulted in clearing the CONT bits leaving the value, incorrectly, at 0x2E26.

    The code I tried was:

        /* Set ADC INT1 to be raised by EOC6. */
        ADC_setIntSrc(adcHandle, ADC_IntNumber_1, ADC_IntSrc_EOC6);
        /* Set ADC INT2 to be raised by EOC14. */
        ADC_setIntSrc(adcHandle, ADC_IntNumber_2, ADC_IntSrc_EOC14);
        /* Set mode to trigger on EOC */
        ADC_setIntMode(adcHandle, ADC_IntNumber_1, ADC_IntMode_EOC);
        ADC_setIntMode(adcHandle, ADC_IntNumber_2, ADC_IntMode_EOC);

    Why would this be the case? I took a quick look at the function's implementations but couldn't see anything glaringly wrong.

    Ta, T

  • Toby,

    It's strange that the ADCSOCOVF1 flags are set.  I would not expect this to ever happen in the ping-pong configuration.  It might be contributing to the issue.

    Looking into the HAL drivers, I see that these statements in ADC_setIntMode() are supposed to clear and set the continuous bit:

    uint16_t clearValue = ADC_INTSELxNy_INTCONT_BITS << lShift;
    uint16_t setValue = intMode << lShift;

    However, it seems to me that the ADC_IntMode_e enumeration values might be wrong and the function should work as expected if it is changed to:

    typedef enum
    {
        ADC_IntMode_ClearFlag=(0 << 6),     //!< Denotes that a new interrupt with not be generated until the interrupt flag is cleared
        ADC_IntMode_EOC=(1 << 6)            //!< Denotes that a new interrupt with be generated on the next end of conversion (EOC)
    } ADC_IntMode_e;

    -Tommy