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

Sleep current is high after using SAADC

Hi,

There is a similar case:

High power consumption in sleep mode after using SAADC

But I am using the SAADC driver version 2.0 because the oversampling feature is only supported in new driver, low power mode is not supported.

My environments:

nRF52840, the sdk version is 17.02, s140 version 7.2.0. Because we need the oversample feature in SAADC, so we are using SAADC driver version 2.0. When there are two channels sampled, the sleep power current is very high, more than 600uA, but if only one channel is sampled ,the sleep current is low, only 4uA. UART is also disabled.

Please help on this. Thanks

Regards

Roy Huang

  • Here is my code to reproduce this issue and I changed it from ble_peripheral ble_app_blinky example in SDK 17.02

    #define NUM_ADC_CHANNELS 1
    // #define NUM_ADC_CHANNELS 2
    
    static nrf_saadc_value_t samples[NUM_ADC_CHANNELS];
    APP_TIMER_DEF(m_sample_timer);
    
    static uint8_t m_event_type;
    
    static void UninitAdc(void * event_data, uint16_t event_size)
    {
    
        ASSERT(*(uint8_t*)event_data == 0x1);
        NRF_LOG_DEBUG("Uninit ADC");
    
        nrfx_saadc_uninit();
        NRF_SAADC->INTENCLR = (SAADC_INTENCLR_END_Clear << SAADC_INTENCLR_END_Pos);
        NVIC_ClearPendingIRQ(SAADC_IRQn);
    }
    
    
    static void SaadcHandler(nrfx_saadc_evt_t const * p_event)
    {
        if (p_event->type == NRFX_SAADC_EVT_DONE)
        {
            ASSERT(p_event->data.done.size == NUM_ADC_CHANNELS);
    
            NRF_LOG_INFO("%d %d", *(uint16_t *)p_event->data.done.p_buffer, *(uint16_t *)(p_event->data.done.p_buffer + 2));
    
            m_event_type = 0x1;
            ret_code_t err_code = app_sched_event_put(&m_event_type, sizeof(m_event_type), UninitAdc);
            APP_ERROR_CHECK(err_code);
        }
    }
    
    static void SampleHandler(void * p_context)
    {
        uint8_t adc_channel_mask;
        ret_code_t err_code;
    
    #if (NUM_ADC_CHANNELS == 1)
        nrfx_saadc_channel_t channels[NUM_ADC_CHANNELS] = {NRFX_SAADC_DEFAULT_CHANNEL_SE(NRF_SAADC_INPUT_AIN3, 0)};
    #else
        nrfx_saadc_channel_t channels[NUM_ADC_CHANNELS] = {NRFX_SAADC_DEFAULT_CHANNEL_SE(NRF_SAADC_INPUT_AIN3, 0),
                                                           NRFX_SAADC_DEFAULT_CHANNEL_SE(NRF_SAADC_INPUT_AIN2, 1)};
    #endif
    
        /* Use gain 1/4 for all conversions */
        for (uint8_t i = 0; i < NUM_ADC_CHANNELS; ++i)
        {
            channels[i].channel_config.gain = NRF_SAADC_GAIN1_4;
        }
    
        err_code = nrfx_saadc_init(NRFX_SAADC_CONFIG_IRQ_PRIORITY);
        APP_ERROR_CHECK(err_code);
    
        err_code = nrfx_saadc_channels_config(channels, NUM_ADC_CHANNELS);
        APP_ERROR_CHECK(err_code);
    
        for (uint8_t i = 0; i < NUM_ADC_CHANNELS; ++i)
        {
            adc_channel_mask |= 1 << i;
        }
    
        err_code = nrfx_saadc_simple_mode_set(adc_channel_mask,
                                              NRF_SAADC_RESOLUTION_12BIT,
                                              NRF_SAADC_OVERSAMPLE_16X,
                                              SaadcHandler);
        APP_ERROR_CHECK(err_code);
    
        err_code = nrfx_saadc_buffer_set(samples, NUM_ADC_CHANNELS);
        APP_ERROR_CHECK(err_code);
    
        err_code = nrfx_saadc_mode_trigger();
        APP_ERROR_CHECK(err_code);
        
        NRF_LOG_INFO("Sample started");
    }
    
    void SampleInit(void)
    {
        ret_code_t err_code;
    
        app_timer_create(&m_sample_timer,
                            APP_TIMER_MODE_REPEATED,
                            SampleHandler);
    
        err_code = app_timer_start(
            m_sample_timer,
            APP_TIMER_TICKS(2000), NULL);
    }
    
    /**@brief Function for application main entry.
     */
    int main(void)
    {
        // Initialize.
        log_init();
        // leds_init();
        timers_init();
        // buttons_init();
        power_management_init();
        APP_SCHED_INIT(1, 10);
        ble_stack_init();
        gap_params_init();
        gatt_init();
        services_init();
        advertising_init();
        conn_params_init();
    
        // Start execution.
        NRF_LOG_INFO("Blinky example started.");
        advertising_start();
    
        SampleInit();
    
        // Enter main loop.
        for (;;)
        {
            idle_state_handle();
            app_sched_execute();
        }
    }
    

    Below is the current curve from PPK2 when only one channel is sampled, advertising interval is about 800ms. Every 2 seconds, SAADC is enabled, after sampling, SAADC is disabled. It works fine.

    Current curve from PPK2 when two channels are sampled.

    I attached the code which is modified from the ble_app_blinky example and can reproduce this phenomenon.

    Unzip it to directory examples\ble_peripheral.

    There is an macro NUM_ADC_CHANNELS at line 564 in file main.c. When it is defined as 1, only one channel is sampled; when defined as 2, two channel are sampled.

    ble_app_saadc_sample.7z

  • Hello Roy Huang,

    Thank you for your patience, and thank you for the very thorough description of your issue!

    This does indeed sound very strange, and I do not immediately see any flaws in your description or included code.
    I have discussed this with some colleagues, and it sounds similar to an issue we've heard of earlier, where the High Frequency CLK is never released and thus is left running. This corresponds well with the 600 µA increase in current you report.

    Could you try to call TASKS_STOP at the end of your NRFX_SAADC_EVT_DONE event handling?
    Alternatively, could you check whether the status of the HFCLK is Running when the increase in current consumption is present?

    I will test your example here on my end shortly, to see if I can see the same behavior. If I am able to reproduce this I will open an internal ticket with our developers, so that they may examine this more closely.

    Best regards,
    Karl

  • Hi Karl,

    For your suggestion:

    1. I tried inserting code "nrf_saadc_task_trigger(NRF_SAADC_TASK_STOP);" in SaadcHandler, but no difference.

    2. I tried to check the state bit in register HFCLKSTAT, but whatever sample one channel or two channels, this bit is always 1, so I guess I have no way to find out whether HFCLK is running when CPU is sleeping.

    Best Regards,

    Roy

  • Hello Roy,

    Thank you for your patience, and for testing this and getting back to me with your findings.
    I find this even more strange, and I will need to confer with our development and test teams, to see if they have any input. 

    I will let you know as soon as I have discussed this issue with them.

    Best regards,
    Karl

  • Hi Roy,

    Please find the attached source code, tested with a nRF52840DK,  it works ok now with low power on multi  ADC channels.

    1: apply the errata 212 which helped. More details on this errata: https://infocenter.nordicsemi.com/topic/errata_nRF52840_Rev2/ERR/nRF52840/Rev2/latest/anomaly_840_212.html

    • As the source code re-configure the ADC on each timeout. So it’s ok to apply this workaround.

    2: change this  to 1 to use the workaround, 0: not to use.

        #define ENABLE_ERRATA_212_WORKAROUND  1

    3: Also noticed the second ADC output always 0,  now it’s ok with buffer index +1 instead +2 “NRF_LOG_INFO("%d %d", *(uint16_t *)p_event->data.done.p_buffer, *(uint16_t *)(p_event->data.done.p_buffer + 1));”5850.ble_app_saadc_sample.7z

Related