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.

AM2434: A problem waiting for ADC conversion forever

Part Number: AM2434
Other Parts Discussed in Thread: SYSCONFIG

Hi Expert,

I found a problem waiting for ADC conversion forever in our system. We are using the internal ADC of AM2434 to check the system temperature every 1 sec. This logic was implemented by referring to the ADC one-shot example in SDK 08.02.00.31. we also implemented it using the polling method and tested it. However, this problem happened in both the polling method and the interrupt method. The problem happened sometimes within 2 hours and sometimes didn't happen over 3 days.

Can I ask if a similar situation was reported and under discussion or fixed in the latest SDK?

I would appreciate it if you share your experience, checkpoints, and any history on this issue.

Thanks,

Moonil

  • Hi Moonil,

    I'm unaware of any issues with the ADC like you've described.

    Can you please further describe the problem?

    • What is the configuration of the ADC?
    • What is triggering the ADC?
    • Has the ADC triggered an interrupt when the hang occurs?
    • Do you have a unit test demonstrating the problem?
    • If you have a unit test, have you tried using a later SDK version to see if the problem is resolved?

    Regards,
    Frank

  • Hi Moonil,

    One other question: Do you see incorrect ADC o/p or the ADC conversion does not happen? 

    Regards,
    Frank

  • Hi Frank, 

    Do you see incorrect ADC o/p or the ADC conversion does not happen? 

    In my test, ADC conversion has not been done since ISR seemed not to be triggered from ADC in the problematic situation.

    Could you please review our implemented code below first?

    1. ADC_DRV_AM243X_fnCtor() - initialize ADC during system boots up.
    2. ADC_DRV_AM243X_fnStartConversion() - called by the application to start ADC conversion.
    3. ADC_DRV_AM243X_fnIsConversionDone() - called by the application to wait for ADC conversion done.
      • this has also the code to restart ADC if there is no response for 1 sec. (under testing)
    4. ADC_DRV_AM243X_fnRead() - called by the application to read ADC data.
    5. adc_drv_am243x_fnIsr()  - called by ISR from ADC.

    Application is calling with the order of 2->3->4.

    tuiSTATUS ADC_DRV_AM243X_fnCtor( void )
    {
        HwiP_Params hwiPrms;
        tuiSTATUS   status = eSTATUS_ERR;
    
        if( SystemP_SUCCESS == SemaphoreP_constructBinary( &gAdcSyncSemObject, 0 ) )
        {
            // Register & enable interrupt.
            HwiP_Params_init( &hwiPrms );
            hwiPrms.intNum   = CONFIG_ADC0_INTR;
            hwiPrms.callback = &adc_drv_am243x_fnIsr;
            hwiPrms.priority = 1U;
            if( SystemP_SUCCESS == HwiP_construct( &gAdcHwiObject, &hwiPrms ) )
            {
                // Clear All interrupt status.
                ADCClearIntrStatus( CONFIG_ADC0_BASE_ADDR, ADC_INTR_STATUS_ALL );
    
                // Power up the ADC.
                ADCPowerUp( CONFIG_ADC0_BASE_ADDR, TRUE );
    
                // Wait for 4us at least
                ClockP_usleep( 5u );
    
                // Do the internal calibration.
                ADCInit( CONFIG_ADC0_BASE_ADDR, FALSE, 0u, 0u );
    
                status = eSTATUS_OK;
            }
        }
        return ( status );
    }
    
    tucBOOL ADC_DRV_AM243X_fnStartConversion( void )
    {
        adcSequencerStatus_t status;
        adcStepConfig_t      adcConfig;
        uint32               adcStep;
        uint32               channelCount;
        sint8                configStatus;
    
        // Enable interrupts.
        ADCEnableIntr( CONFIG_ADC0_BASE_ADDR, ( ADC_INTR_SRC_END_OF_SEQUENCE |
                                                ADC_INTR_SRC_FIFO0_THRESHOLD |
                                                ADC_INTR_SRC_FIFO0_OVERRUN |
                                                ADC_INTR_SRC_FIFO0_UNDERFLOW |
                                                ADC_INTR_SRC_FIFO1_THRESHOLD |
                                                ADC_INTR_SRC_FIFO1_OVERRUN |
                                                ADC_INTR_SRC_FIFO1_UNDERFLOW |
                                                ADC_INTR_SRC_OUT_OF_RANGE ) );
    
        // Initialize ADC configuration params.
        adcConfig.mode             = ADC_OPERATION_MODE_SINGLE_SHOT;
        adcConfig.openDelay        = 0x1u;
        adcConfig.sampleDelay      = 0u;
        adcConfig.rangeCheckEnable = 0u;
        adcConfig.averaging        = ADC_AVERAGING_16_SAMPLES;
        adcConfig.fifoNum          = ADC_FIFO_NUM_0;
    
        // Configure all required steps - Step 0 to 7 mapped to channel 0 to 7
        for( channelCount = 0U; channelCount < ADC_DRV_AM243X_NUM_CHANNELS; channelCount++ )
        {
            adcConfig.channel = ADC_CHANNEL_1 + channelCount;
            adcStep = ADC_STEP_1 + channelCount;
            configStatus = ADCSetStepParams( CONFIG_ADC0_BASE_ADDR, adcStep, &adcConfig );
            if( CSL_EFAIL == configStatus )
            {
                return ( eSTATUS_ERR );
            }
        }
    
        // Enable Step IDs, to identify which measurement came from which channels.
        ADCStepIdTagEnable( CONFIG_ADC0_BASE_ADDR, TRUE );
    
        configStatus = ADCSetCPUFIFOThresholdLevel( CONFIG_ADC0_BASE_ADDR, ADC_FIFO_NUM_0, 40u );
        if( CSL_EFAIL == configStatus )
        {
            return ( eSTATUS_ERR );
        }
    
        // Step enable.
        for( channelCount = 0U; channelCount < ADC_DRV_AM243X_NUM_CHANNELS; channelCount++ )
        {
            adcStep = ADC_STEP_1 + channelCount;   // Step -> Channel one to one mapped.
            ADCStepEnable( CONFIG_ADC0_BASE_ADDR, adcStep, TRUE );
        }
    
        // Start ADC conversion.
        ADCStart( CONFIG_ADC0_BASE_ADDR, TRUE);    // Start ADC conversion.
    
        return ( eSTATUS_OK );
    }
    
    tucBOOL ADC_DRV_AM243X_fnIsConversionDone( void )
    {
        sint32 status;
    
        // Wait for the interrupt to occur.
        status = SemaphoreP_pend( &gAdcSyncSemObject, pdMS_TO_TICKS( ADC_DRV_TIMEOUT_MS ) );
    
        // Restart ADC.
        if( SystemP_SUCCESS != status )
        {
            KDBG( DBG_ERR, "Internal ADC timeout and restart!\r\n" );
    
            // Power down the ADC.
            ADCPowerUp( CONFIG_ADC0_BASE_ADDR, FALSE );
    
            // Wait for 4us at least
            ClockP_usleep( 5u );
    
            // Clear All interrupt status.
            ADCClearIntrStatus( CONFIG_ADC0_BASE_ADDR, ADC_INTR_STATUS_ALL );
    
            // Power up the ADC.
            ADCPowerUp( CONFIG_ADC0_BASE_ADDR, TRUE );
    
            // Wait for 4us at least
            ClockP_usleep( 5u );
    
            // Do the internal calibration.
            ADCInit( CONFIG_ADC0_BASE_ADDR, FALSE, 0u, 0u );
    
            // Start conversion.
            ADC_DRV_AM243X_fnStartConversion();
    
            // Wait for the interrupt to occur.
            status = SemaphoreP_pend( &gAdcSyncSemObject, pdMS_TO_TICKS( ADC_DRV_TIMEOUT_MS ) );
            if( SystemP_SUCCESS != status )
            {
                KDBG( DBG_ERR, "Internal ADC timeout again!!!\r\n" );
                return( FALSE );
            }
        }
    
        return( TRUE );
    }
    
    tucBOOL ADC_DRV_AM243X_fnRead( ADC_DRV_AM243X_tzVALUES *pValues )
    {
        uint8  fifoWordCount;
        uint8  index;
        uint8  stepId;
        uint16 rawData;
        uint32 fifoData;
    
        fifoWordCount = ADCGetFIFOWordCount( CONFIG_ADC0_BASE_ADDR, ADC_FIFO_NUM_0 );
    
        for( index = 0U; index < fifoWordCount; index++ )
        {
            // Retrieve the next value from the FIFO.
            fifoData = ADCGetFIFOData( CONFIG_ADC0_BASE_ADDR, ADC_FIFO_NUM_0 );
            stepId   = ( ( fifoData & ADC_FIFODATA_ADCCHNLID_MASK ) >>
                         ADC_FIFODATA_ADCCHNLID_SHIFT);
            rawData  = ( ( fifoData & ADC_FIFODATA_ADCDATA_MASK ) >>
                         ADC_FIFODATA_ADCDATA_SHIFT);
    
            // Insert the value into the appropriate place in the structure
            *( ( uint16 * )pValues + stepId ) = rawData;
        }
    
        return( TRUE );
    }
    
    void adc_drv_am243x_fnIsr( void *handle )
    {
        uint32_t status;
    
        /* Get interrupt status and clear */
        status = ADCGetIntrStatus( CONFIG_ADC0_BASE_ADDR );
        ADCClearIntrStatus( CONFIG_ADC0_BASE_ADDR, status );
    
        /* Process ISR */
        SemaphoreP_post( &gAdcSyncSemObject );
    
        /* Set EOI to generate next interrupt if any */
        ADCWriteEOI( CONFIG_ADC0_BASE_ADDR );
    }

    This was implemented by referring to TI example in SDK.

    fyi, I was not able to find any different codes in "adc.c" between SDK 08.02.00.31 and SDK 08.05.00.24.

    Regards,

    Moonil

  • Hi Moonil,

    I'll take a look at your code to see if I can spot any problems.

    I still have the questions:

    • What is the configuration of the ADC?
    • What is triggering the ADC?
    • Has the ADC triggered an interrupt when the hang occurs?

    What is contents of the ADC_STATUS register (address 0x28001028) when the hang occurs?

    Regards,
    Frank

  • Hi Frank,

    Thank you for the prompt response on this issue,

    What is the configuration of the ADC?

    do you want to know the configuration of ADC syscfg? could you let me know what kind of configuration I have to inform you?

    What is triggering the ADC?

    as I mentioned, the application task triggers ADC to read our system temperature.

    Has the ADC triggered an interrupt when the hang occurs?

    no, isr was not called when the hang occurs.

    What is contents of the ADC_STATUS register (address 0x28001028) when the hang occurs?

    this issue very randomly happens. ADC status was checked in the polling method implementation but not in the interrupt method yet.

    ADC_STATUS was busy during the testing of the polling method. please see the capture below,

    Regards,

    Moonil

  • Hi Moonil,

    do you want to know the configuration of ADC syscfg? could you let me know what kind of configuration I have to inform you?

    Yes, I'm asking for the configuration in Sysconfig along with any configurations you've made via API functions.

    as I mentioned, the application task triggers ADC

    Ok, so you call ADCStart() in an application task? I see this in the code snippets you shared.

    no, isr was not called when the hang occurs.

    What I meant is has the ADC signaled an interrupt to VIM/R5F, not whether the VIM/R5F has responded to the interrupt. Whether the ADC has signaled an interrupt should be visible in ADC_STATUS. You can see this in CCS by opening a Memory Window and setting the address to 0x28001028. You can also check status in the code by calling the API function ADCGetIntrStatus().

    I'll review your code snippets and get back with you.

    Regards,
    Frank

  • Hi Moonil,

    Sorry for the delayed response. I reviewed your code and compared it with the ADC Singleshot example: https://software-dl.ti.com/mcu-plus-sdk/esd/AM243X/08_05_00_24/exports/docs/api_guide_am243x/EXAMPLES_DRIVERS_ADC_SINGLESHOT.html

    I see your code follows the single shot example closely. For the most part, I don't see any issues with your code. However, I do have the following observations:

    #1: ADC_DRV_AM243X_fnStartConversion():
    ADCStart() is called immediately after the ADCStepEnable() loop. The the singleshot example, the FSM is check for IDLE before ADCStart() is called.

    #2: ADC_DRV_AM243X_fnIsConversionDone():
    Semaphore pend() has a timeout. In the singleshot example, Semaphore pend() is set to wait forever.

    #3: ADC_DRV_AM243X_fnIsConversionDone():
    In case of semaphore timeout, code is executed to restart the ADC.

    • Does this code ever execute in your system?
    • I think you should "teardown" APIs before calling the ADC powerup and initialization APIs.

    Can you modify the SDK single shot example to reproduce the problem?

    Regards,
    Frank

  • Hi Frank,

    Thank you for your reviewing my code.

    #1: ADC_DRV_AM243X_fnStartConversion():
    ADCStart() is called immediately after the ADCStepEnable() loop. The the singleshot example, the FSM is check for IDLE before ADCStart() is called.

    as you pointed out, the code to check if FSM is idle was missed, but in my test, the same problem happened after adding the code checking IDLE. I was able to capture the ADC_STATUS register (0x28001028) when the problem happened as below[#1]. Its value was 0x00000010.

    According to TRM[#2], this value is a pending event of "FIFO0 under-flow interrupt" and "ADC does not recover from overrun of underflow conditions automatically. therefore, software will need to disable and re-enable the ADC after this occurs"

    Q1) could you please let me know how to disable and re-enable the ADC after this occurs with a clear example? I need to have the recovery code TI confirm very soon due to the urgency of our production schedule. fyi, the recovery code I shared was not working for this case.

    Q2) What reason can cause under-flow in TI ADC? I would appreciate it if you share your knowledge on this. This underflow seems not to be a root cause but the following situation after other root problems happened.

    Q3) I am using the code to check IDLE before calling ADCStart(), which is the same as the one in the TI example as below, Do you think this TI example code is correct? I think "&&" should be "||". but, when I tested with "||", fsmBusy was always 1 after it ran around 10 hours without problem, so, this caused an "infinite while loop" where FSM never enters idle. Please share your opinion on this code. I need to confirm this example code first in order to narrow down the range for investigation.

        /* Check if FSM is idle */
        ADCGetSequencerStatus(baseAddr, &status);
        while ((ADC_ADCSTAT_FSM_BUSY_IDLE != status.fsmBusy) &&
               ADC_ADCSTAT_STEP_ID_IDLE != status.stepId)
        {
            ADCGetSequencerStatus(baseAddr, &status);
        }

    [#1]

    [#2]

    I am looking forward to your reply.

    Regards,

    Moonil

  • Hi Moonil,

    Q1) could you please let me know how to disable and re-enable the ADC after this occurs with a clear example? I need to have the recovery code TI confirm very soon due to the urgency of our production schedule. fyi, the recovery code I shared was not working for this case.

    I think it's more important to figure out what's causing the underflow than how to recover from it.

    Anyway, I'm unaware of any code in the SDK for recovering from underflow/overflow. I'll ask the SW Dev team about it. As a start, I would suggest calling the "tear-down" APIs in the example followed by the "bring-up" APIs.

    For example, I suggest you try something like this:

    if (underflow)
        // de-init ADC
        App_adcStop(baseAddr);
        App_adcDeInit(baseAddr);
        
        // re-init ADC
        App_adcInit(baseAddr);
        App_adcConfig(baseAddr);
        App_adcStart(baseAddr);
    

    Likely not all of the API functions called from these application-level functions need to be called. For example, I doubt the AFE needs to be powered down and back up for the recovery.

    Q2) What reason can cause under-flow in TI ADC? I would appreciate it if you share your knowledge on this. This underflow seems not to be a root cause but the following situation after other root problems happened.

    The only place I see the cause of underflow mentioned in in the TRM, Section 12.1.1.6.23 ADC_FIFO0DATA Register: A read from this register will auto increment the FIFO read pointer. If you read when FIFO is empty, it will trigger an underflow interrupt.

    Q3) I am using the code to check IDLE before calling ADCStart(), which is the same as the one in the TI example as below, Do you think this TI example code is correct? I think "&&" should be "||". but, when I tested with "||", fsmBusy was always 1 after it ran around 10 hours without problem, so, this caused an "infinite while loop" where FSM never enters idle. Please share your opinion on this code. I need to confirm this example code first in order to narrow down the range for investigation.

    I think this is a bug. According to the TRM, 12.1.1.6.11 ADC_SEQUENCER_STAT Register:

    SW can read this register to find out the currently scheduled step id being converted on the ADC port. If you want to turn the controller off and then back on, the step_id bit should be checked and compared to IDLE before enabling the ADC module again. Also, before enabling the controller again, the user should wait for the FSM bit 5 to read idl.

    This means wait until STEP_IDLE==IDLE (0h) AND FSM_BUSY==IDLE (10h) before the ADC is (re-)enabled, i.e. wait while (STEP_IDLE!=IDLE) OR (FSM_BUSY!=IDLE). The way the wait loop is written, either one can't be idle and the code will stop waiting and proceed.

    fsmBusy was always 1

    I'm unable to reproduce this behavior with the example code. I always observe status.fsmBusy==0 after the first call to ADCGetSequencerStatus().

    Do you have multiple threads in your system that might invoke some of your application-level functions out of the intended order?

    Regards,
    Frank

  • Hi Moonil,

    The underflow interrupt is enabled, but there is no handling of underflow interrupts in the ISR. A semaphore is posted in this ISR which is used for signaling EOC and has nothing to do with underflow. This would need to be properly updated to handle underflow in your application.

    I'm not sure how underflow is happening in your application:

    • the CPU only reads the FIFO after receiving an EOC interrupt from the ADC.
    • the CPU get the FIFO word count and uses it as the number of FIFO reads, so the CPU shouldn’t read the FIFO too many times.

    To find when underflow occurs, you could add code to the ISR to check for underflow. Then step through the remainder of the ISR code and return to the code that caused the underflow.

    Regards,
    Frank

  • Hi Frank,

    Thank you for your explanation.

    I tested two cases again with the polling method as below:

    test case 1:

    • added the code to check if FSM is in an idle state before calling ADCStart() using TI example code (&&) to check the IDLE.
    • this is OK - it is still running without any problem for over 20 hours. will continue this test.

    test case 2:

    • added the code to check if FSM is idle before calling ADCStart() using the modified code (||) to check IDLE.
    • this is NOK - the problem happened within around 9 hours.
    • in this test, the condition to check if FSM is in an idle state was "STEP_IDLE==IDLE (10h) AND FSM_BUSY==IDLE (0h)". As we pointed out a bug in the TI example, this modification was applied in this test, but as a result, it happened that FSM was always busy (1h), and so the code was infinitely looped before calling ADCStart().

    From these test results, I wonder if we can say the TI example code is correct as it is but not a bug.

    Q1) Could you check with your team the example code checking if FSM is idle?

    Q2) Could you review my code below using the polling method again? This code is running without problem for over one day now.

    tuiSTATUS ADC_DRV_AM243X_fnCtor( void )
    {
        sint8           configStatus;
        uint32          channelCount;
        uint32          adcStep;
        adcStepConfig_t adcConfig;
    
        // Clear All interrupt status.
        ADCClearIntrStatus( CSL_ADC0_BASE, ADC_INTR_STATUS_ALL );
    
        // Power up the ADC.
        ADCPowerUp( CSL_ADC0_BASE, TRUE );
    
        // Wait for ADC to power up.
        while( AdcIsPoweredUp( CSL_ADC0_BASE ) == FALSE );
    
        // Do the internal calibration.
        ADCInit( CSL_ADC0_BASE, FALSE, 0U, 0U );
    
        // ADC initializes as started - we stop it here, so it doesn't take measurements before
        // we want them.
        ADCStart( CSL_ADC0_BASE, FALSE );
    
        // Configure the ADC steps.
        // Initialize ADC configuration params.
        adcConfig.mode             = ADC_OPERATION_MODE_SINGLE_SHOT;
        adcConfig.openDelay        = 0x1U;
        adcConfig.sampleDelay      = 0U;
        adcConfig.rangeCheckEnable = 0U;
        adcConfig.averaging        = ADC_AVERAGING_16_SAMPLES;
        adcConfig.fifoNum          = ADC_FIFO_NUM_0;
    
        // Configure all required steps - Step 0 to 7 mapped to channel 0 to 7
        for( channelCount = 0U; channelCount < ADC_DRV_AM243X_NUM_CHANNELS; channelCount++ )
        {
            adcConfig.channel = ADC_CHANNEL_1 + channelCount;
            adcStep = ADC_STEP_1 + channelCount;
            configStatus = ADCSetStepParams( CSL_ADC0_BASE, adcStep, &adcConfig );
            if( CSL_EFAIL == configStatus )
            {
                return ( eSTATUS_ERR );
            }
        }
    
        // Enable Step IDs, to identify which measurement came from which channels.
        ADCStepIdTagEnable( CSL_ADC0_BASE, TRUE );
    
        return ( eSTATUS_OK );
    }
    
    tucBOOL ADC_DRV_AM243X_fnStartConversion( void )
    {
        adcSequencerStatus_t status;
        uint32               channelCount;
        uint32               adcStep;
    
        // Enable each step in the ADC for a one-shot measurement.
        for( channelCount = 0U; channelCount < ADC_DRV_AM243X_NUM_CHANNELS; channelCount++ )
        {
            adcStep = ADC_STEP_1 + channelCount;
            ADCStepEnable( CSL_ADC0_BASE, adcStep, TRUE );
        }
    
        // Check if finite state machine is idle.
        ADCGetSequencerStatus( CSL_ADC0_BASE, &status );
        while ( ( ADC_ADCSTAT_FSM_BUSY_IDLE != status.fsmBusy ) &&
                ( ADC_ADCSTAT_STEP_ID_IDLE != status.stepId ) )
        {
            ADCGetSequencerStatus( CSL_ADC0_BASE, &status );
        }
    
        // Start ADC conversion.
        ADCStart( CSL_ADC0_BASE, TRUE );
    
        return ( TRUE );
    }
    
    tucBOOL ADC_DRV_AM243X_fnIsConversionDone( void )
    {
        adcSequencerStatus_t status;
    
        ADCGetSequencerStatus( CSL_ADC0_BASE, &status );
    
        // Check if FSM is idle.
        if( ( ADC_ADCSTAT_FSM_BUSY_IDLE != status.fsmBusy ) &&
            ( ADC_ADCSTAT_STEP_ID_IDLE != status.stepId ) )
        {
            return( FALSE );
        }
    
        return( TRUE );
    }
    
    tucBOOL ADC_DRV_AM243X_fnRead( ADC_DRV_AM243X_tzVALUES *pValues )
    {
        uint8  fifoWordCount;
        uint8  index;
        uint8  stepId;
        uint16 rawData;
        uint32 fifoData;
    
        fifoWordCount = ADCGetFIFOWordCount( CSL_ADC0_BASE, ADC_FIFO_NUM_0 );
    
        for( index = 0U; index < fifoWordCount; index++ )
        {
            // Retrieve the next value from the FIFO.
            fifoData = ADCGetFIFOData( CSL_ADC0_BASE, ADC_FIFO_NUM_0 );
            stepId   = ( ( fifoData & ADC_FIFODATA_ADCCHNLID_MASK ) >>
                         ADC_FIFODATA_ADCCHNLID_SHIFT);
            rawData  = ( ( fifoData & ADC_FIFODATA_ADCDATA_MASK ) >>
                         ADC_FIFODATA_ADCDATA_SHIFT);
    
            // Insert the value into the appropriate place in the structure
            *( ( uint16 * )pValues + stepId ) = rawData;
        }
    
        return( TRUE );
    }

    Regards,

    Moonil

  • Hi Moonil,

    From these test results, I wonder if we can say the TI example code is correct as it is but not a bug.

    Q1) Could you check with your team the example code checking if FSM is idle?

    I currently only have the information in the TRM. The code in the example doesn't match the TRM description of ADC behavior for the FSM. However, there could be a bug in the TRM. I'm following up on this.

    Q2) Could you review my code below using the polling method again? This code is running without problem for over one day now.

    I reviewed the code and it looks OK to me. I don't see you checking for underflow in the code. Is this no longer occurring?

    Regards,
    Frank

  • Hi Frank,

    I currently only have the information in the TRM. The code in the example doesn't match the TRM description of ADC behavior for the FSM. However, there could be a bug in the TRM. I'm following up on this.

    Yes, TRM may need to be updated correctly.

    I reviewed the code and it looks OK to me. I don't see you checking for underflow in the code. Is this no longer occurring?

    My code you reviewed has been working for over 1 week without any problem. So, I think this issue is resolved and the code checking underflow is not required.

    I will post again if any issue is found.

    Thanks!

    Moonil