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

power consumption increases 1.1mA after first ADC sample

Hello,

I am experiencing a power issue after the first ADC sample is triggered. Sampling 2 ADC channels using PPI and TIMER as the triggering mechanism. Here is a capture using PPK2.

It is alright to have a spike in power but in this case, once the power increases it never decreases.

Is there a way to fix this?

TIA

Parents
  • Adding a note here for future users because I had the same issue and the posted solutions didn't work, due to a slightly different config.

    I have a very low sample rate application, which is using SDK 17.1 and NRFX_SAADC_API_V2 because I wanted a simple non-blocking mode. Only capturing one channel at a time. And oversampling, though that isn't relevant to the problem.

    Sequence looks like this:

        SDK_ASSERT(nrfx_saadc_simple_mode_set((1u << (unsigned) channel),
                                              NRF_SAADC_RESOLUTION_14BIT,
                                              NRF_SAADC_OVERSAMPLE_256X
    ,                                          NULL));
    
        nrf_saadc_value_t value = (int16_t) 0x8000;  // Invalid ADC value
        SDK_ASSERT(nrfx_saadc_buffer_set(&value, 1));
        SDK_ASSERT(nrfx_saadc_mode_trigger());

    Before that call, idle current is ~5uA. After it, it's almost 500uA.

    As stated in other answers (but not explicitly), the SAADC appears to require a STOP event to allow its EasyDMA to go back to low-power. The proposed solutions say to call nrfx_saadc_uninit(). But in the NRFX_SAADC_API_V2 API:

    void nrfx_saadc_uninit(void)
    {
        nrfx_saadc_abort();
        NRFX_IRQ_DISABLE(SAADC_IRQn);
        nrf_saadc_disable();
        m_cb.saadc_state = NRF_SAADC_STATE_UNINITIALIZED;
    }
    
    void nrfx_saadc_abort(void)
    {
        NRFX_ASSERT(m_cb.saadc_state != NRF_SAADC_STATE_UNINITIALIZED);
    
        if (!m_cb.event_handler)
        {
            m_cb.p_buffer_primary = NULL;
            m_cb.p_buffer_secondary = NULL;
            m_cb.samples_converted = 0;
        }
        else
        {
            nrf_saadc_task_trigger(NRF_SAADC_TASK_STOP);
            if (m_cb.saadc_state == NRF_SAADC_STATE_CALIBRATION)
            {
                // STOPPED event does not appear when the calibration is ongoing
                m_cb.saadc_state = NRF_SAADC_STATE_IDLE;
            }
        }
    }

    So the STOP event gets generated only if there's an event handler. In blocking mode there is not.

    The solution is simple enough, just issue the STOP command before uninit:

        nrf_saadc_task_trigger(NRF_SAADC_TASK_STOP);
        while (!nrf_saadc_event_check(NRF_SAADC_EVENT_STOPPED))
        {
        }
        nrf_saadc_event_clear(NRF_SAADC_EVENT_STOPPED);
    It also works to issue STOP after the nrfx_saadc_mode_trigger(), in which case it isn't necessary to init/deinit every sample.
Reply
  • Adding a note here for future users because I had the same issue and the posted solutions didn't work, due to a slightly different config.

    I have a very low sample rate application, which is using SDK 17.1 and NRFX_SAADC_API_V2 because I wanted a simple non-blocking mode. Only capturing one channel at a time. And oversampling, though that isn't relevant to the problem.

    Sequence looks like this:

        SDK_ASSERT(nrfx_saadc_simple_mode_set((1u << (unsigned) channel),
                                              NRF_SAADC_RESOLUTION_14BIT,
                                              NRF_SAADC_OVERSAMPLE_256X
    ,                                          NULL));
    
        nrf_saadc_value_t value = (int16_t) 0x8000;  // Invalid ADC value
        SDK_ASSERT(nrfx_saadc_buffer_set(&value, 1));
        SDK_ASSERT(nrfx_saadc_mode_trigger());

    Before that call, idle current is ~5uA. After it, it's almost 500uA.

    As stated in other answers (but not explicitly), the SAADC appears to require a STOP event to allow its EasyDMA to go back to low-power. The proposed solutions say to call nrfx_saadc_uninit(). But in the NRFX_SAADC_API_V2 API:

    void nrfx_saadc_uninit(void)
    {
        nrfx_saadc_abort();
        NRFX_IRQ_DISABLE(SAADC_IRQn);
        nrf_saadc_disable();
        m_cb.saadc_state = NRF_SAADC_STATE_UNINITIALIZED;
    }
    
    void nrfx_saadc_abort(void)
    {
        NRFX_ASSERT(m_cb.saadc_state != NRF_SAADC_STATE_UNINITIALIZED);
    
        if (!m_cb.event_handler)
        {
            m_cb.p_buffer_primary = NULL;
            m_cb.p_buffer_secondary = NULL;
            m_cb.samples_converted = 0;
        }
        else
        {
            nrf_saadc_task_trigger(NRF_SAADC_TASK_STOP);
            if (m_cb.saadc_state == NRF_SAADC_STATE_CALIBRATION)
            {
                // STOPPED event does not appear when the calibration is ongoing
                m_cb.saadc_state = NRF_SAADC_STATE_IDLE;
            }
        }
    }

    So the STOP event gets generated only if there's an event handler. In blocking mode there is not.

    The solution is simple enough, just issue the STOP command before uninit:

        nrf_saadc_task_trigger(NRF_SAADC_TASK_STOP);
        while (!nrf_saadc_event_check(NRF_SAADC_EVENT_STOPPED))
        {
        }
        nrf_saadc_event_clear(NRF_SAADC_EVENT_STOPPED);
    It also works to issue STOP after the nrfx_saadc_mode_trigger(), in which case it isn't necessary to init/deinit every sample.
Children
Related