Low power SAADC

Hi,

I have a very high consumption when using the ADC of the nRF9160, I am using the NCS 2.0, the firmware used is inspired by the sample provided on github below:

https://github.com/Rallare/fw-nrfconnect-nrf/blob/nrf9160_samples/samples/nrf9160/adc/src/main.c

Attached is the developed firmware, it is as simple as possible, reading only one channel from the ADC!

adc_nh3.7z

The consumption of the reading of 1 channel from the ADC every 2 seconds can be observed below, I find a floor current of 2.81 mA, something totally unexpected.

This consumption refers to firmware with log and serial disabled, in addition to the problem with consumption, I found a problem related to the use of timer, when I use a timer (it is commented on in the code) to perform the reading of the ADC, I have the first 2 readings totally wrong, regardless of the interval between readings, always the first reading is 0 and the second is a totally inconsistent value, but this disappears when I read through a while inside main.

I looked in several topics related to SAADC how to reduce consumption, but I didn't find anything related to NCS, zephyr. There are many samples for nRF5, but for NCS the information and samples regarding SAADC are limited to the basics or theory.

In the github below, you can see that through nRF5, it is possible to reach an average consumption of 5uA using SAADC, how could you get close to that using NCS 2.0?

https://github.com/NordicPlayground/nRF52-ADC-examples/tree/master/saadc_low_power

Could you give me some example or tips to reduce consumption when using SAADC?

Thank you.

Parents
  • Hi. 

    You can achieve similar average current in NCS, by combining the ADC example with the system_off example. 

    In system_off, they use PM_STATE_SOFT_OFF with the power manager, which you shouldn't be using. Instead use PM_STATE_SUSPEND_TO_RAM, and put the system to sleep between ADC reads, using k_msleep() or k_sleep().

    Attached is an image of the current profile I was able to achieve on nRF52832 using NCS. The sleep current is 2.8 uA. 

  • Hi  ,

    Thanks for the answer!

    Should this work on the nRF9160? I changed my code forcing to use the recommended power management mode "PM_STATE_SUSPEND_TO_RAM", during idle state between ADC reads, but there was no change in consumption... did I miss any details?

    while (1) {
    		err = adc_sample();
    		if (err) {
    			printk("Error in adc sampling: %d\n", err);
    		}
    		pm_state_force ( 0u , &( struct pm_state_info){PM_STATE_SUSPEND_TO_RAM, 0 , 0 });
    		k_sleep(K_MSEC(2000));
    	}
  • It took me a while to get the code, personally I have it in form of an easy to read function

    bool force_low_power_state()
    {
        struct pm_state_info info =
        {
            .state            = PM_STATE_SUSPEND_TO_RAM,
            .substate_id      = 0,
            .exit_latency_us  = 0,
            .min_residency_us = 0,
        };
        return pm_state_force(CPU_ID, &info);
    }

    But yeah, your code checks out, and it should not be taking THAT much current.

    Are you sure it isn't being consumed by some some other component or peripheral? 

  • Yes, I'm sure it's not being consumed by any other peripheral, this code is exclusive to the ADC, I don't have threads and this while is in the main, I only execute the ADC reads every 2 seconds.

    I added the necessary libs and CONFIG, so I didn't have any errors, but my consumption didn't change.

    I take the opportunity to leave a question, this approach only reduces consumption (or should) when the entire system is idle right? So in a real application, where I will have other threads, will the high consumption of the ADC remain even when I wake up the CPU to run other threads that are not related to the ADC? If that's the case, I don't think it's ideal for my application.

Reply
  • Yes, I'm sure it's not being consumed by any other peripheral, this code is exclusive to the ADC, I don't have threads and this while is in the main, I only execute the ADC reads every 2 seconds.

    I added the necessary libs and CONFIG, so I didn't have any errors, but my consumption didn't change.

    I take the opportunity to leave a question, this approach only reduces consumption (or should) when the entire system is idle right? So in a real application, where I will have other threads, will the high consumption of the ADC remain even when I wake up the CPU to run other threads that are not related to the ADC? If that's the case, I don't think it's ideal for my application.

Children
  • You can choose to turn off the ADC when it is not needed. That being said, I leave it as is, and it doesn't consume any power unless I perform an ADC operation (capture, process etc.)

    I find it very strange that the baseline current for sleep is 2.8 mA, that is extremely high. 

    I would advise that you thoroughly check your measurement process, and make sure nothing else is running in the background. GPIOs, i2c, PWM, could be any of those. 

  • You can choose to turn off the ADC when it is not needed. That being said, I leave it as is, and it doesn't consume any power unless I perform an ADC operation (capture, process etc.)

    How could I do this? Is there any example?

    I find it very strange that the baseline current for sleep is 2.8 mA, that is extremely high. 

    I would advise that you thoroughly check your measurement process, and make sure nothing else is running in the background. GPIOs, i2c, PWM, could be any of those. 

    I agree, it's extremely something, and I'm really not using anything other than ADC, my firmware is attached, if you can check and confirm that, I'd appreciate it! Without any hardware modifications or how to measure, just programming another firmware based on mqtt_simple, I get 2.62 uA in PSM, so it really looks like this is totally related to this ADC sample.

    In this firmware if I disable the ADC settings, the setup, and call the driver, I have an average consumption of 2.93uA.

  • Hi  ,

    Now your suggestion is working!

    I found another approach, developed by @Jared in the topic below, and it worked, so I decided to go back to your approach and it also worked... It was strange to work only after reprogramming and without making any changes, but anyway, both approaches are working and I reach a current of 2 uA floor!

    devzone.nordicsemi.com/.../386791

    But in both approaches I don't have a clean graph, I have a noise between the ADC readings, it seems that these noises appear at fixed time intervals, as can be seen below:

    Do you also observe this behavior? Thanks.

  • One explanation might be that you had debugging on while measuring the current. That itself adds a whole lot of static current, and it doesn't go back to normal even when you stop debugging. It goes back when you reset, or re-flash. 

    As for the fixed interval noises, if you are not running any other task or peripheral in the background, it could be related to hardware (like a capacitor charging/discharging at intervals, or the regulator misbehaving)

    If you can attach the latest code here, I can have a look at it during my free time. 

  • Hi  ,

    It was not due to a debug, but due to a "bug" found in the nRF9160-DK, if the power jumper of the nRF9160 is removed to connect the PPK2 while the DK is with the switch in the ON position, the consumption is with a floor current of 2.8mA.

    After that, even if the DK is restarted, or the device's flash is erased, both by the Programmer and VS Code, none of this solves the problem. The consumption only returns to normal (below 2.8mA) if certain firmware is written. Recording the adc sample firmware did not solve the problem, but if I record at_client, or mqtt_simple (these were the ones I tested, but many others can work) the consumption returns to normal, observing floor currents lower than 3uA. The noises also disappeared!

Related