This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Adding Second SAADC increases sleep current to go greater 380uA

SDK 16.0 and SDK 17.0 (tried both versions)

nrf52832

Segger Embedded Studio

Windows 10

When Adding an additional ADC the sleep current increases to greater than 380uA (from one to two).  When I comment out either of the two ADCs (see below) the sleep current returns to less than 2uA.

I've been working on this for several days and have gone through all of the DevZone examples I could find on the subject (perhaps not), enough where I was coming back to the same ticket multiple times.  

Here's the code:

void saadc_callback(nrf_drv_saadc_evt_t const * p_event)
{
    ret_code_t err_code;
     uint8_t value[SAADC_SAMPLES_IN_BUFFER*2];
     uint16_t adc_value;
     uint32_t        tempVoltage_u32 = 0 ;

if (p_event->type == NRF_DRV_SAADC_EVT_DONE) //Capture offset calibration complete event {
        err_code = nrf_drv_saadc_buffer_convert(p_event->data.done.p_buffer, SAADC_SAMPLES_IN_BUFFER);
        APP_ERROR_CHECK(err_code);
                        
        for (int i = 0; i < SAADC_SAMPLES_IN_BUFFER; i++)
        {
            adc_value = p_event->data.done.p_buffer[i];
            value[i*2] = adc_value;
            value[(i*2)+1] = adc_value >> 8;

            tempVoltage_u32 += p_event->data.done.p_buffer[i] ;
        }
        m_adc_evt_counter++;
        }
}

void saadc_init (void){
ret_code_t err_code; nrf_drv_saadc_config_t saadc_config = NRF_DRV_SAADC_DEFAULT_CONFIG;
saadc_config.resolution = NRF_SAADC_RESOLUTION_12BIT;
saadc_config.low_power_mode = true; nrf_saadc_channel_config_t channel_0_config = NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_VDD); channel_0_config.resistor_p = NRF_SAADC_RESISTOR_DISABLED; //Disable pullup resistor on the input pin channel_0_config.resistor_n = NRF_SAADC_RESISTOR_DISABLED; //Disable pulldown resistor on the input pin channel_0_config.gain = NRF_SAADC_GAIN1_6; //Set input gain to 1/6. The maximum SAADC input voltage is then 0.6V/(1/6)=3.6V. The single ended input range is then 0V-3.6V channel_0_config.reference = NRF_SAADC_REFERENCE_INTERNAL; //Set internal reference of fixed 0.6 volts channel_0_config.acq_time = NRF_SAADC_ACQTIME_10US ; //Set acquisition time. Set low acquisition time to enable maximum sampling frequency of 200kHz. Set high acquisition time to allow maximum source resistance up to 800 kohm, see the SAADC electrical specification in the PS. channel_0_config.mode = NRF_SAADC_MODE_SINGLE_ENDED; //Set SAADC as single ended. This means it will only have the positive pin as input, and the negative pin is shorted to ground (0V) internally. channel_0_config.burst = NRF_SAADC_BURST_ENABLED; channel_0_config.pin_p = NRF_SAADC_INPUT_VDD;//NRF_SAADC_INPUT_AIN0; //Select the input pin for the channel. AIN0 pin maps to physical pin P0.02. channel_0_config.pin_n = NRF_SAADC_INPUT_DISABLED; //Since the SAADC is single ended, the negative pin is disabled. The negative pin is shorted to ground internally. nrf_saadc_channel_config_t channel_1_config = NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN5); channel_1_config.resistor_p = NRF_SAADC_RESISTOR_DISABLED; //Disable pullup resistor on the input pin channel_1_config.resistor_n = NRF_SAADC_RESISTOR_DISABLED; //Disable pulldown resistor on the input pin channel_1_config.gain = NRF_SAADC_GAIN1_6; //Set input gain to 1/6. The maximum SAADC input voltage is then 0.6V/(1/6)=3.6V. The single ended input range is then 0V-3.6V channel_1_config.reference = NRF_SAADC_REFERENCE_VDD4; //Set internal reference of fixed 0.6 volts channel_1_config.acq_time = NRF_SAADC_ACQTIME_10US ; //Set acquisition time. Set low acquisition time to enable maximum sampling frequency of 200kHz. Set high acquisition time to allow maximum source resistance up to 800 kohm, see the SAADC electrical specification in the PS. channel_1_config.mode = NRF_SAADC_MODE_SINGLE_ENDED; //Set SAADC as single ended. This means it will only have the positive pin as input, and the negative pin is shorted to ground (0V) internally. //channel_1_config.burst = NRF_SAADC_BURST_ENABLED; channel_1_config.burst = NRF_SAADC_BURST_DISABLED; // channel_1_config.pin_p = NRF_SAADC_INPUT_AIN1; //Select the input pin for the channel. AIN0 pin maps to physical pin P0.02. channel_1_config.pin_n = NRF_SAADC_INPUT_DISABLED; //Since the SAADC is single ended, the negative pin is disabled. The negative pin is shorted to ground internally. err_code = nrf_drv_saadc_init(&saadc_config, saadc_callback); APP_ERROR_CHECK(err_code);
// ********************************************************************************

//Comment out to lower sleep current (next two lines) err_code = nrf_drv_saadc_channel_init(0, &channel_0_config); APP_ERROR_CHECK(err_code);

// Or
// comment out to lower sleep current (next two lines) err_code = nrf_drv_saadc_channel_init(1, &channel_1_config); APP_ERROR_CHECK(err_code);

// ******************************************************************************** err_code = nrf_drv_saadc_buffer_convert(m_buffer_pool[0],SAADC_SAMPLES_IN_BUFFER); APP_ERROR_CHECK(err_code); err_code = nrf_drv_saadc_buffer_convert(m_buffer_pool[1],SAADC_SAMPLES_IN_BUFFER); APP_ERROR_CHECK(err_code);
}

END

Also - Placing the following code in the call back function the micro resets (so it's not being used):
        nrf_drv_saadc_uninit();
NRF_SAADC->INTENCLR = (SAADC_INTENCLR_END_Clear << SAADC_INTENCLR_END_Pos); NVIC_ClearPendingIRQ(SAADC_IRQn);

I had hopes for this solution but nrf_drv_saadc_unit() causes a reset.
https://devzone.nordicsemi.com/f/nordic-q-a/65477/multi-channel-saadc-disable-in-nrf52832

Also, even though running:
volatile uint32_t temp1;
    volatile uint32_t temp2;
    volatile uint32_t temp3;

    ret_code_t err_code;

    temp1 = *(volatile uint32_t *)0x40007640ul;
    temp2 = *(volatile uint32_t *)0x40007644ul;
    temp3 = *(volatile uint32_t *)0x40007648ul;

    *(volatile uint32_t *)0x40007FFCul = 0ul; 
    *(volatile uint32_t *)0x40007FFCul; 
    *(volatile uint32_t *)0x40007FFCul = 1ul;

    *(volatile uint32_t *)0x40007640ul = temp1;
    *(volatile uint32_t *)0x40007644ul = temp2;
    *(volatile uint32_t *)0x40007648ul = temp3;


Will drop the current back to ~2uA, I'm only able to re-init one channel ( nrf_drv_saadc_channel_init(1, &channel_1_config) ).

attempting to stop (code directly above) and starting the second ADC causes a reset.

I have no float variables or functions.

In conclusion, all I'm attempting to do is use two ADCs and have low sleep current.

Thank you in advance for your assistance.

Peter





Parents
  • One minor issue is the internal reference consumes power when enabled, but there is no spec for the SAADC. The COMP  spec for that internal bandgap is only 13uA, so may be the same, don't know.

    INT_REF Current used by the internal bandgap reference when selected as source for VREF = 13 μA

    This is the code I use, you may already have tried this:

        while (0 == nrf_saadc_event_check(NRF_SAADC_EVENT_END) && timeout > 0)
        {
            timeout--;
        }
        nrf_saadc_task_trigger(NRF_SAADC_TASK_STOP);
        nrf_saadc_event_clear(NRF_SAADC_EVENT_STARTED);
        nrf_saadc_event_clear(NRF_SAADC_EVENT_END);
        // Disable command
        nrf_saadc_disable();

    Errata 178 could be an issue if trying to disable before the SAADC has finished

  • Thank you for the fast response.  No, I have not yet tried your suggestion, nor have I run across it. 

    Two obvious questions:

    Is this code placed in the saadc_callback? (I assume so) and what is the timeout value initiated to?

    I'll give this a shot tomorrow.

    Thanks again!

  • Ah, no; in fact as a general guide there should be as little as possible in the callbacks as the callbacks are run from within the interrupt handlers.

    This code would just be used after ADC sampling was completed before going to sleep. It can be used will polled sampling or interrupt-driven sampling. The timeout depends on sampling details, just make it longer than the normal completion time; it isn't (shouldn't be) needed, I use it as a fail-safe.

Reply
  • Ah, no; in fact as a general guide there should be as little as possible in the callbacks as the callbacks are run from within the interrupt handlers.

    This code would just be used after ADC sampling was completed before going to sleep. It can be used will polled sampling or interrupt-driven sampling. The timeout depends on sampling details, just make it longer than the normal completion time; it isn't (shouldn't be) needed, I use it as a fail-safe.

Children
  • I suspected as such - but asked as several of the examples I've seen have put nrf_drv_saadc_uninit(); in the call back.

  • I was able to lower the current draw by using one ADC  (two ADC's):

    // ***********************************************************************************

    void UnintSAADC(void){
    
        uint32_t timeout = 10000;
    
        while (0 == nrf_saadc_event_check(NRF_SAADC_EVENT_END) && timeout > 0)
        {
            timeout--;
        }
        nrf_saadc_task_trigger(NRF_SAADC_TASK_STOP);
        nrf_saadc_event_clear(NRF_SAADC_EVENT_STARTED);
        nrf_saadc_event_clear(NRF_SAADC_EVENT_END);
        // Disable command
        nrf_saadc_disable();
    
        nrf_drv_saadc_uninit();
    
        NRF_SAADC->INTENCLR = (SAADC_INTENCLR_END_Clear << SAADC_INTENCLR_END_Pos);
        NVIC_ClearPendingIRQ(SAADC_IRQn);

    }

    // **************************************************************************************

    void ConfigADCToVDD(void){
    
    nrf_saadc_channel_config_t channel_config = NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_VDD);
        ret_code_t err_code;
    
        if (s_ssadcActive_b) {
            UnintSAADC();
        }
        channel_config.resistor_p = NRF_SAADC_RESISTOR_DISABLED;   //Disable pullup resistor on the input pin
        channel_config.resistor_n = NRF_SAADC_RESISTOR_DISABLED;   //Disable pulldown resistor on the input pin
        channel_config.gain       = NRF_SAADC_GAIN1_6;             //Set input gain to 1/6. The maximum SAADC input voltage is then 0.6V/(1/6)=3.6V. The single ended input range is then 0V-3.6V
        channel_config.reference  = NRF_SAADC_REFERENCE_INTERNAL;  //Set internal reference of fixed 0.6 volts
        channel_config.acq_time   = NRF_SAADC_ACQTIME_10US ;        //Set acquisition time. Set low acquisition time to enable maximum sampling frequency of 200kHz. Set high acquisition time to allow maximum source resistance up to 800 kohm, see the SAADC electrical specification in the PS.
        channel_config.mode       = NRF_SAADC_MODE_SINGLE_ENDED;   //Set SAADC as single ended. This means it will only have the positive pin as input, and the negative pin is shorted to ground (0V) internally.
        channel_config.burst      = NRF_SAADC_BURST_ENABLED;
        //channel_config.burst      = NRF_SAADC_BURST_DISABLED;
        channel_config.pin_p      = NRF_SAADC_INPUT_VDD;//NRF_SAADC_INPUT_AIN0;          //Select the input pin for the channel. AIN0 pin maps to physical pin P0.02.
        channel_config.pin_n      = NRF_SAADC_INPUT_DISABLED;      //Since the SAADC is single ended, the negative pin is disabled. The negative pin is shorted to ground internally.
    
        saadc_config.resolution = NRF_SAADC_RESOLUTION_12BIT;
        saadc_config.low_power_mode         = true;                                                   //Enable low power mode.
    
        err_code = nrf_drv_saadc_init(&saadc_config, saadc_callback);
        APP_ERROR_CHECK(err_code);
    
        err_code = nrf_drv_saadc_channel_init(0, &channel_config);
        APP_ERROR_CHECK(err_code);
    
        err_code = nrf_drv_saadc_buffer_convert(m_buffer_pool[0],SAADC_SAMPLES_IN_BUFFER);
        APP_ERROR_CHECK(err_code);   
        err_code = nrf_drv_saadc_buffer_convert(m_buffer_pool[1],SAADC_SAMPLES_IN_BUFFER);
        APP_ERROR_CHECK(err_code);   
    
        s_ssadcActive_b = true;     // to disable SSADC next time around
    
    }
    
    // *******************************************************************
    void ConfigADCToAIN1(void){ nrf_saadc_channel_config_t channel_config = NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN5); ret_code_t err_code; if (s_ssadcActive_b) { UnintSAADC(); } channel_config.resistor_p = NRF_SAADC_RESISTOR_DISABLED; //Disable pullup resistor on the input pin channel_config.resistor_n = NRF_SAADC_RESISTOR_DISABLED; //Disable pulldown resistor on the input pin channel_config.gain = NRF_SAADC_GAIN1_6; //Set input gain to 1/6. The maximum SAADC input voltage is then 0.6V/(1/6)=3.6V. The single ended input range is then 0V-3.6V channel_config.reference = NRF_SAADC_REFERENCE_VDD4; //Set internal reference of fixed 0.6 volts channel_config.acq_time = NRF_SAADC_ACQTIME_10US ; //Set acquisition time. Set low acquisition time to enable maximum sampling frequency of 200kHz. Set high acquisition time to allow maximum source resistance up to 800 kohm, see the SAADC electrical specification in the PS. channel_config.mode = NRF_SAADC_MODE_SINGLE_ENDED; //Set SAADC as single ended. This means it will only have the positive pin as input, and the negative pin is shorted to ground (0V) internally. //channel_config.burst = NRF_SAADC_BURST_ENABLED; channel_config.burst = NRF_SAADC_BURST_DISABLED; // channel_config.pin_p = NRF_SAADC_INPUT_AIN1; //Select the input pin for the channel. AIN0 pin maps to physical pin P0.02. channel_config.pin_n = NRF_SAADC_INPUT_DISABLED; //Since the SAADC is single ended, the negative pin is disabled. The negative pin is shorted to ground internally. saadc_config.resolution = NRF_SAADC_RESOLUTION_12BIT; saadc_config.low_power_mode = true; //Enable low power mode. err_code = nrf_drv_saadc_init(&saadc_config, saadc_callback); APP_ERROR_CHECK(err_code); err_code = nrf_drv_saadc_channel_init(0, &channel_config); APP_ERROR_CHECK(err_code); err_code = nrf_drv_saadc_buffer_convert(m_buffer_pool[0],SAADC_SAMPLES_IN_BUFFER); APP_ERROR_CHECK(err_code); err_code = nrf_drv_saadc_buffer_convert(m_buffer_pool[1],SAADC_SAMPLES_IN_BUFFER); APP_ERROR_CHECK(err_code); s_ssadcActive_b = true; // to disable SSADC next time around } // ************************************************************* void nrf_config_adc(void){ ConfigADCToVDD(); }




    1) Set
    s_ssadcActive_b = false
    2) Call nrf_config_adc
    3) Call either ConfigADCToVDD or ConfigADCToVDD as needed.

    I have not yet tested if the ADC is working but suspect it is. I will update
    after testing ADC operation. For now I'm happy with the low current.

    Thanks Again.

  • Well... Turns out the power drops as desired but I'm not able to get the SAADC back up and running. 

    No more interrupts / callbacks

     - Would you be able to provide code to re-enable the ADC after it has been disabled by:

    while (0 == nrf_saadc_event_check(NRF_SAADC_EVENT_END) && timeout > 0)
    {
    timeout--;
    }
    nrf_saadc_task_trigger(NRF_SAADC_TASK_STOP);
    nrf_saadc_event_clear(NRF_SAADC_EVENT_STARTED);
    nrf_saadc_event_clear(NRF_SAADC_EVENT_END);
    // Disable command
    nrf_saadc_disable();

    Again, Thank you in advance!

    Peter

  • This is simple polled code, maybe it will help; this function is called a few dozen times a second and monitors an external voltage divider

    #define ADC12_COUNTS_PER_VOLT 4829
    
    uint16_t GetBatteryVoltage(void)
    {
        uint16_t result = 9999; // 9999 means no reading
        uint32_t timeout = 10000;
        volatile int16_t buffer[8];
        // Enable command
        nrf_saadc_enable();
        NRF_SAADC->RESULT.PTR = (uint32_t)buffer;
        NRF_SAADC->RESULT.MAXCNT = 1;
        //NRF_SAADC->RESULT.AMOUNT
        nrf_saadc_event_clear(NRF_SAADC_EVENT_END);
        nrf_saadc_task_trigger(NRF_SAADC_TASK_START);
        nrf_saadc_task_trigger(NRF_SAADC_TASK_SAMPLE);
        while (0 == nrf_saadc_event_check(NRF_SAADC_EVENT_END) && timeout > 0)
        {
            timeout--;
        }
        nrf_saadc_task_trigger(NRF_SAADC_TASK_STOP);
        nrf_saadc_event_clear(NRF_SAADC_EVENT_STARTED);
        nrf_saadc_event_clear(NRF_SAADC_EVENT_END);
        // Disable command
        nrf_saadc_disable();
        if (timeout != 0)
        {
            result = ((buffer[0] * 1000L)+(ADC12_COUNTS_PER_VOLT/2)) / ADC12_COUNTS_PER_VOLT;
        }
        return result;
    }
    

  • Thank you - I may have solved it. 

    Now that I effectively have only one ADC I had forgotten to change the SAADC_SAMPLES_IN_BUFFER from 2 to 1. 

    It now appears to be working (fingers are crossed and I want to let it run awhile before I claim victory)

    I'll keep you posted.

    Peter

Related