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

How to uninit SAADC on nRF52 Devkit Using Nordic's saadc Example

I have searched Nordic and the web and have seen several instances of "can't uninit saadc successfully". Neither Nordic support nor I am aware of a single example of saadc uninit working. My private thread with Nordic (in which I share more proprietary code details) has reached a dead end so I am looking for a solution from the community based only on Nordic's saadc example.

I see about ten different library calls that may be relevant to adding an uninit sequence to the SAADC example in SDK 14.2.0. My goal is, using Nordic's example on their DK board, to:

  1. measure DK power consumption before SAADC comes up
  2. measure DK power consumption while SAADC is up
  3. measure DK power consumption after SAADC has been uninit'd
  4. verify that #1 and #3 current measurements are the same

All I am asking for is a recommended uninit sequence for the Nordic SDK 14.2.0 saadc example. Then I will expand on that solution and apply it to my dual ADC channel code below.

Except for the fact that current consumption is (usually) too high after the uninit completes the following code sequence is working as intended. This code is a two adc channel extension of the Nordic example. The #3 current measurement is always 5,10,15, or 20 mA higher than the #1 current measurement which leads me to think that the random timing of the uninit relative to the sampling and clock may be the problem.

// Nordic SDK 14.2.0 example saadc - modified for TWO ADC channels
nrf_drv_saadc_init(NULL, saadc_bemf_callback);

// Two adc channels
nrf_drv_saadc_channel_init(FR_ADC_ISEN_CHANNEL, &m_adc_channel_config0);
nrf_drv_saadc_channel_init(FR_ADC_BEMF_CHANNEL, &m_adc_channel_config1);

nrf_drv_saadc_buffer_convert(l_buffer_pool[0], FR_ADC_BUFFER_SIZE);
nrf_drv_saadc_buffer_convert(l_buffer_pool[1], FR_ADC_BUFFER_SIZE);

nrf_drv_ppi_init();
nrf_drv_timer_init(&l_timer, &timer_cfg, timer_handler);
nrf_drv_timer_extended_compare(
    &l_timer,
    NRF_TIMER_CC_CHANNEL0,
    ticks,
    NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK,
    false
);

nrf_drv_timer_enable(&l_timer);
nrf_drv_timer_compare_event_address_get(
    &l_timer,
    NRF_TIMER_CC_CHANNEL0
);
nrf_drv_saadc_sample_task_get();
nrf_drv_ppi_channel_alloc(&l_ppi_channel();
nrf_drv_ppi_channel_enable(l_ppi_channel);

// The above, similar to Nordic's example but with TWO ADC channels works
// perfectly

// Init done, now try uninit

nrf_drv_ppi_channel_disable(l_ppi_channel);

nrf_drv_ppi_channel_free(l_ppi_channel);
nrf_drv_timer_disable(&l_timer);
nrf_drv_timer_uninit(&l_timer);
nrf_drv_ppi_uninit();

// Again, I'm using Two ADC channels
nrf_drv_saadc_channel_uninit(FR_ADC_BEMF_CHANNEL);
nrf_drv_saadc_channel_uninit(FR_ADC_ISEN_CHANNEL);

nrf_drv_saadc_uninit();

// Current draw is NOT back to initial levels, and the resulting draw varies
// by multiples of 5mA !!! What would cause the 5mA granularity (I'm using a
// microamp capable meter)

Parents Reply
  • The problem I'm asking for help on is how to modify the DevKit SAADC example from 14.2.0, so that the DK will a) bring up SAADC, b) get samples from SAADC, and c) uninit SAADC. The Nordic example code does a and b but does not do c.

    I would love to be proven wrong but no one has been able to tell me what the uninit call sequence needs to be. I believe Nordic does not know how to uninit SAADC

Children
  • Hi,

    I have tried to recreate what you describe using the attached example running on an nRF52-DK, but have not been able to make it fail yet.

    Can you modify the attached example (place it in \nRF5_SDK_14.2.0_17b948a\examples\peripheral\saadc) and make it fail so I can recreate it here?

    I did also change in sdk_config.h the following:
    #define NRF_LOG_DEFERRED 0

    /**
     * Copyright (c) 2014 - 2017, Nordic Semiconductor ASA
     * 
     * All rights reserved.
     * 
     * Redistribution and use in source and binary forms, with or without modification,
     * are permitted provided that the following conditions are met:
     * 
     * 1. Redistributions of source code must retain the above copyright notice, this
     *    list of conditions and the following disclaimer.
     * 
     * 2. Redistributions in binary form, except as embedded into a Nordic
     *    Semiconductor ASA integrated circuit in a product or a software update for
     *    such product, must reproduce the above copyright notice, this list of
     *    conditions and the following disclaimer in the documentation and/or other
     *    materials provided with the distribution.
     * 
     * 3. Neither the name of Nordic Semiconductor ASA nor the names of its
     *    contributors may be used to endorse or promote products derived from this
     *    software without specific prior written permission.
     * 
     * 4. This software, with or without modification, must only be used with a
     *    Nordic Semiconductor ASA integrated circuit.
     * 
     * 5. Any software provided in binary form under this license must not be reverse
     *    engineered, decompiled, modified and/or disassembled.
     * 
     * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
     * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
     * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
     * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
     * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
     * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     * 
     */
    /** @file
     * @defgroup nrf_adc_example main.c
     * @{
     * @ingroup nrf_adc_example
     * @brief ADC Example Application main file.
     *
     * This file contains the source code for a sample application using ADC.
     *
     * @image html example_board_setup_a.jpg "Use board setup A for this example."
     */
    
    #include <stdbool.h>
    #include <stdint.h>
    #include <stdio.h>
    #include <string.h>
    #include "nrf.h"
    #include "nrf_drv_saadc.h"
    #include "nrf_drv_ppi.h"
    #include "nrf_drv_timer.h"
    #include "boards.h"
    #include "app_error.h"
    #include "nrf_delay.h"
    #include "app_util_platform.h"
    #include "nrf_pwr_mgmt.h"
    #include "nrf_drv_power.h"
    
    #include "nrf_log.h"
    #include "nrf_log_ctrl.h"
    #include "nrf_log_default_backends.h"
    
    #define SAMPLES_IN_BUFFER 1
    
    static nrf_saadc_value_t     m_buffer_pool[2][SAMPLES_IN_BUFFER];
    static volatile uint32_t     m_adc_evt_counter;
    
    static volatile bool adc_done;
    static volatile uint32_t uart_conf;
    
    void saadc_callback(nrf_drv_saadc_evt_t const * p_event)
    {
        if (p_event->type == NRF_DRV_SAADC_EVT_DONE)
        {
            int i;
            NRF_LOG_INFO("ADC event number: %x", m_adc_evt_counter);
    
            for (i = 0; i < SAMPLES_IN_BUFFER; i++)
            {
                NRF_LOG_INFO("%d", p_event->data.done.p_buffer[i]);
            }
            m_adc_evt_counter++;				
    				
    				adc_done = true;
        }		
    }
    
    
    void saadc_init(void)
    {
        ret_code_t err_code;
        nrf_saadc_channel_config_t channel_config =
            NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN0);
    	
        err_code = nrf_drv_saadc_init(NULL, 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], 1);
        APP_ERROR_CHECK(err_code);
    	
    		err_code = nrf_drv_saadc_sample();
    		APP_ERROR_CHECK(err_code);	
    }
    
    
    /**
     * @brief Function for main application entry.
     */
    int main(void)
    {
        uint32_t err_code = NRF_LOG_INIT(NULL);
        APP_ERROR_CHECK(err_code);
    
        NRF_LOG_DEFAULT_BACKENDS_INIT();
    
        err_code = nrf_drv_power_init(NULL);
        APP_ERROR_CHECK(err_code);
    
        ret_code_t ret_code = nrf_pwr_mgmt_init();
        APP_ERROR_CHECK(ret_code);
    
        NRF_LOG_INFO("SAADC HAL simple example.");
        saadc_init();
    	
    		while(!adc_done)
    			;
    		
    		NRF_LOG_INFO("Uninit SAADC driver");
    		nrf_drv_saadc_uninit();
    		
        while (1)
        {
    			NRF_LOG_INFO("Sleep");
    			
    			// To keep the UART logging low power while in sleep.			
    			uart_conf = NRF_UART0->ENABLE;
    			NRF_UART0->ENABLE = 0;			
    			nrf_pwr_mgmt_run();    			
    			NRF_UART0->ENABLE = uart_conf;	
        }
    }
    
    
    /** @} */
    

    Thanks,
    Kenneth

  • Your example is not the 14.2.0 saadc example. The 14.2.0 example uses PPI. Has Nordic stopped supporting PPI for SAADC? That would explain why PPI was removed from the example.

  • I simplified the example for purpose of finding out if uninit of the saadc works. It seems to work as intended.

    Best regards,

    Kenneth

  • The reason you simplified the code is because you don't know how to uinit the 14.2.0 Saadc example code written by Nordic. The libraries are broken why can't you just admit that instead of refusing to answer my question I DEMAND ESCALATION TO YOUR SUPERVISOR.

  • There is no operation mode (possible beside the radio) that can cause an current consumption of 10-20mA. If you don't uninit a peripheral then the peripheral is going to claim the internal regulators and clocks powered ON while enabled, however this means a total of 1-2mA sleep current. The only way you can achieve 10-20mA is through a GPIO (or other external circuitry is causing excessive current). Based on this it will not help you if I update the entire SAADC example to uninit (stop) timer, ppi and saadc (that is the order I would do it in).

    If I were you, I would look at the GPIO configuration for all pins, and be aware that when you uninit a peripheral, the pin that was claimed by the peripheral will fall-back to the GPIO configuration you may have configured. So for instance if you are using the SAADC to measure a pin, and you have (unintentionally) configured this pin as a output GPIO, then this pin will become an output when you uninit the SAADC. Something like this can explain your 10-20mA current consumption. 

    In general you may find this useful:
    https://devzone.nordicsemi.com/nordic/nordic-blog/b/blog/posts/optimizing-power-on-nrf52-designs

Related