Unexpected consumption using System OFF Sample & nRF52840DK

Greetings,

I have been measuring the current consumption of our custom FW in our custom board using PPK2 and found some unexpected spikes in the current consumption curve.

I decided to look deeper into this so programmed our custom FW to the nRF52840 DK and the behavior remained the same.

Finally I decide to test some samples with absolutely no changes on the nRF52840DK to confirm the normal behavior of the current curves I should see when the system is Active, in Sleep (system ON Idle) & finally System OFF(which is irrelevant for my application).

It seems that flashing the nRF System OFF sample (unchanged, as it was in the NCS) to the nRF52840 DK produces the same current curve. As mentioned in other tickets ( I have searched and read all of the relevant ones in existence) the current curve in System Sleep (with UART off) should be a few tens of microamps but I am consistently getting the current curve shown below.

Connection diagram (PPK2 in Ampere mode) 
I have also measured with the SW6 to the DEFAULT and nRF ONLY positions but the results were the same.

!!!Below you can see the current curve for the nRF52840DK flashed with the nRF System OFF sample and focus on the duration that corresponds to the System Sleep (System ON Idle) part of the duration ( the sample enters the SYstem Sleep state for two seconds, this what is shown below)

System Sleep (System ON Idle)

System Sleep (System ON Idle) the curve shown above ZOOMED IN ( to be able to distinguish the spikes)

The whole current curve for the System OFF sample

I have tried all different methods of measurement using the PPK2 (Ampere mode & Source mode by providing the external supply from the PPK2) but in all instances, I am getting the same current curve(shown above).

I have also tried the beacon sample (with no changes/modification, as is) and gotten the same curve in Idle mode (apart from when the device wakes up to advertise), between the advertising events I get the same spikes up to 3mA with the current never falling bellow 100's of microamps.

Beacon sample

Beacon sample ZOOMED IN

Even more ZOOMED IN

According to other tickets as well as this one that contains current curves for the Beacon sample this is not at all the Normal curve I should see for this sample

(this is what the people in the ticket mentioned report as the normal current curves for this sample)

which is completely different from my curves.

What could be the cause for the behavior and current curves I am observing when loading the nRF System OFF & Beacon samples to the nRF52840DK and measuring using the PPK2?

Please get back to me for any further information needed, I can also provide the data for the curves shown above.

I look forward to hearing from you.

Best regards,

Stavros

Parents
  • Hi Stravos 

    We are about to enter Christmas vacation on support, so the support will be slow/limted until start of January. 
    Just to verify. Is the DK prepared for current measurement as it is described in the Current measurement guide?

    I would also recommend to not power the DK over USB when you do current measurement as it might affect the measurement. 

    Regards

    Runar

  • Hi runsiv,

    A kind reminder, for any updates or information about this would be very helpful as this is a critical issue for us.

    I look forward to hearing from you.

    Best regards,

    Stavros

  • How did you turn off the UART? When UART reception is enabled in sleep mode, the HF clock remains active to allow data reception. This means the sleep current will be around .5 to 1 mA in System ON.

    You can disable the UART before entering sleep using the pm_device_* APIs as shown in the code snippet I posted here:  RE: How to put the UART to sleep (low power mode) 

  • I used the nrf/system_off sample without any modifications. I did not set this up, it is provided with the nRF Connect SDK v2.1.0 in samples folder.

    I am pasting the code of the sample below (only has two files a main.c and a retained.c which is not used as the config option for this is disabled)

    main.c (FYI CONFIG_APP_RETENTION is disabled)

    /*
     * Copyright (c) 2019 Nordic Semiconductor ASA
     *
     * SPDX-License-Identifier: Apache-2.0
     */
    
    #include <stdio.h>
    #include <zephyr/zephyr.h>
    #include <zephyr/device.h>
    #include <zephyr/init.h>
    #include <zephyr/pm/pm.h>
    #include <zephyr/pm/device.h>
    #include <zephyr/pm/policy.h>
    #include <soc.h>
    #include "retained.h"
    #include <hal/nrf_gpio.h>
    
    #define BUSY_WAIT_S 2U
    #define SLEEP_S 2U
    
    /* Prevent deep sleep (system off) from being entered on long timeouts
     * or `K_FOREVER` due to the default residency policy.
     *
     * This has to be done before anything tries to sleep, which means
     * before the threading system starts up between PRE_KERNEL_2 and
     * POST_KERNEL.  Do it at the start of PRE_KERNEL_2.
     */
    static int disable_ds_1(const struct device *dev)
    {
    	ARG_UNUSED(dev);
    
    	pm_policy_state_lock_get(PM_STATE_SOFT_OFF, PM_ALL_SUBSTATES);
    	return 0;
    }
    
    SYS_INIT(disable_ds_1, PRE_KERNEL_2, 0);
    
    void main(void)
    {
    	int rc;
    	const struct device *cons = DEVICE_DT_GET(DT_CHOSEN(zephyr_console));
    
    	if (!device_is_ready(cons)) {
    		printk("%s: device not ready.\n", cons->name);
    		return;
    	}
    
    	printk("\n%s system off demo\n", CONFIG_BOARD);
    
    	if (IS_ENABLED(CONFIG_APP_RETENTION)) {
    		bool retained_ok = retained_validate();
    
    		/* Increment for this boot attempt and update. */
    		retained.boots += 1;
    		retained_update();
    
    		printk("Retained data: %s\n", retained_ok ? "valid" : "INVALID");
    		printk("Boot count: %u\n", retained.boots);
    		printk("Off count: %u\n", retained.off_count);
    		printk("Active Ticks: %" PRIu64 "\n", retained.uptime_sum);
    	} else {
    		printk("Retained data not supported\n");
    	}
    
    	/* Configure to generate PORT event (wakeup) on button 1 press. */
    	nrf_gpio_cfg_input(NRF_DT_GPIOS_TO_PSEL(DT_ALIAS(sw0), gpios),
    			   NRF_GPIO_PIN_PULLUP);
    	nrf_gpio_cfg_sense_set(NRF_DT_GPIOS_TO_PSEL(DT_ALIAS(sw0), gpios),
    			       NRF_GPIO_PIN_SENSE_LOW);
    
    	printk("Busy-wait %u s\n", BUSY_WAIT_S);
    	k_busy_wait(BUSY_WAIT_S * USEC_PER_SEC);
    
    	printk("Busy-wait %u s with UART off\n", BUSY_WAIT_S);
    	rc = pm_device_action_run(cons, PM_DEVICE_ACTION_SUSPEND);
    	k_busy_wait(BUSY_WAIT_S * USEC_PER_SEC);
    	rc = pm_device_action_run(cons, PM_DEVICE_ACTION_RESUME);
    
    	printk("Sleep %u s\n", SLEEP_S);
    	k_sleep(K_SECONDS(SLEEP_S));
    
    	printk("Sleep %u s with UART off\n", SLEEP_S);
    	rc = pm_device_action_run(cons, PM_DEVICE_ACTION_SUSPEND);
    	k_sleep(K_SECONDS(SLEEP_S));
    	rc = pm_device_action_run(cons, PM_DEVICE_ACTION_RESUME);
    
    	printk("Entering system off; press BUTTON1 to restart\n");
    
    	if (IS_ENABLED(CONFIG_APP_RETENTION)) {
    		/* Update the retained state */
    		retained.off_count += 1;
    		retained_update();
    	}
    
    	/* Above we disabled entry to deep sleep based on duration of
    	 * controlled delay.  Here we need to override that, then
    	 * force entry to deep sleep on any delay.
    	 */
    	pm_state_force(0u, &(struct pm_state_info){PM_STATE_SOFT_OFF, 0, 0});
    
    	/* Now we need to go sleep. This will let the idle thread runs and
    	 * the pm subsystem will use the forced state. To confirm that the
    	 * forced state is used, lets set the same timeout used previously.
    	 */
    	k_sleep(K_SECONDS(SLEEP_S));
    
    	printk("ERROR: System off failed\n");
    	while (true) {
    		/* spin to avoid fall-off behavior */
    	}
    }

    This sample can be found in ..\v2.1.0\zephyr\samples\boards\nrf\system_off

  • I tried the 'system OFF' sample just now and got similar results. The problem is that the sample does not suspend the UART before entering 'System OFF', causing the UART pin states to be retained during sleep. This leads to current leakage. I will report this as a bug internally. You can comment the  'rc = pm_device_action_run(cons, PM_DEVICE_ACTION_RESUME);' at line 85 for now.

    Edit: this was fixed by this commit: https://github.com/nrfconnect/sdk-zephyr/commit/2cbc452d1d0dcf9dc6bc9c6081b4bc54f6a04e89 

  • The commit you mentioned removed the previous 4 states (Busy Wait 2 seconds, Busy Wait UART off 2 seconds, System Sleep, System Sleep UART off) and only put the device in System Off mode. The state I am interested in measuring the consumption for is the 4th state (System Sleep aka System ON Idle or Idle current) not the System OFF ( I do not use the System Off mode in my application at all so it totally irrelevant to me).

    Also I tried commenting out line 85 and I get the same current curves as the ones shown above (no change).

    I also tried commenting out lines 85, 83 & 77 but I get the same result again.

    I also tried commenting out lines 85, 83, 77 & 75 and adding the UART suspend in the beginning as shown in the code below but also got the same results as my previous post.

    /*
     * Copyright (c) 2019 Nordic Semiconductor ASA
     *
     * SPDX-License-Identifier: Apache-2.0
     */
    
    #include <stdio.h>
    #include <zephyr/zephyr.h>
    #include <zephyr/device.h>
    #include <zephyr/init.h>
    #include <zephyr/pm/pm.h>
    #include <zephyr/pm/device.h>
    #include <zephyr/pm/policy.h>
    #include <soc.h>
    #include "retained.h"
    #include <hal/nrf_gpio.h>
    
    #define BUSY_WAIT_S 2U
    #define SLEEP_S 2U
    
    /* Prevent deep sleep (system off) from being entered on long timeouts
     * or `K_FOREVER` due to the default residency policy.
     *
     * This has to be done before anything tries to sleep, which means
     * before the threading system starts up between PRE_KERNEL_2 and
     * POST_KERNEL.  Do it at the start of PRE_KERNEL_2.
     */
    static int disable_ds_1(const struct device *dev)
    {
    	ARG_UNUSED(dev);
    
    	pm_policy_state_lock_get(PM_STATE_SOFT_OFF, PM_ALL_SUBSTATES);
    	return 0;
    }
    
    SYS_INIT(disable_ds_1, PRE_KERNEL_2, 0);
    
    void main(void)
    {
    	int rc;
    	const struct device *cons = DEVICE_DT_GET(DT_CHOSEN(zephyr_console));
    
    	if (!device_is_ready(cons)) {
    		printk("%s: device not ready.\n", cons->name);
    		return;
    	}
    
    	rc = pm_device_action_run(cons, PM_DEVICE_ACTION_SUSPEND);
    
    	printk("\n%s system off demo\n", CONFIG_BOARD);
    
    	if (IS_ENABLED(CONFIG_APP_RETENTION)) {
    		bool retained_ok = retained_validate();
    
    		/* Increment for this boot attempt and update. */
    		retained.boots += 1;
    		retained_update();
    
    		printk("Retained data: %s\n", retained_ok ? "valid" : "INVALID");
    		printk("Boot count: %u\n", retained.boots);
    		printk("Off count: %u\n", retained.off_count);
    		printk("Active Ticks: %" PRIu64 "\n", retained.uptime_sum);
    	} else {
    		printk("Retained data not supported\n");
    	}
    
    	/* Configure to generate PORT event (wakeup) on button 1 press. */
    	nrf_gpio_cfg_input(NRF_DT_GPIOS_TO_PSEL(DT_ALIAS(sw0), gpios),
    			   NRF_GPIO_PIN_PULLUP);
    	nrf_gpio_cfg_sense_set(NRF_DT_GPIOS_TO_PSEL(DT_ALIAS(sw0), gpios),
    			       NRF_GPIO_PIN_SENSE_LOW);
    
    	printk("Busy-wait %u s\n", BUSY_WAIT_S);
    	k_busy_wait(BUSY_WAIT_S * USEC_PER_SEC);
    
    	printk("Busy-wait %u s with UART off\n", BUSY_WAIT_S);
    	// rc = pm_device_action_run(cons, PM_DEVICE_ACTION_SUSPEND);
    	k_busy_wait(BUSY_WAIT_S * USEC_PER_SEC);
    	// rc = pm_device_action_run(cons, PM_DEVICE_ACTION_RESUME);
    
    	printk("Sleep %u s\n", SLEEP_S);
    	k_sleep(K_SECONDS(SLEEP_S));
    
    	printk("Sleep %u s with UART off\n", SLEEP_S);
    	// rc = pm_device_action_run(cons, PM_DEVICE_ACTION_SUSPEND);
    	k_sleep(K_SECONDS(SLEEP_S));
    	// rc = pm_device_action_run(cons, PM_DEVICE_ACTION_RESUME);
    
    	printk("Entering system off; press BUTTON1 to restart\n");
    
    	if (IS_ENABLED(CONFIG_APP_RETENTION)) {
    		/* Update the retained state */
    		retained.off_count += 1;
    		retained_update();
    	}
    
    	/* Above we disabled entry to deep sleep based on duration of
    	 * controlled delay.  Here we need to override that, then
    	 * force entry to deep sleep on any delay.
    	 */
    	pm_state_force(0u, &(struct pm_state_info){PM_STATE_SOFT_OFF, 0, 0});
    
    	/* Now we need to go sleep. This will let the idle thread runs and
    	 * the pm subsystem will use the forced state. To confirm that the
    	 * forced state is used, lets set the same timeout used previously.
    	 */
    	k_sleep(K_SECONDS(SLEEP_S));
    
    	printk("ERROR: System off failed\n");
    	while (true) {
    		/* spin to avoid fall-off behavior */
    	}
    }
    

    nrf_system_off_uart_modified.hex

    I have included both the code and the FW image.

    Could you please provide a way to properly suspend the UART and avoid the current leakage so I can properly measure the current consumption for the System ON Idle aka System Sleep (Idle current) (no the System Off current as it is irrelevant for my application). I need to know how to properly suspend the UART this way (during runtime) as this is exactly what I need to do in my custom application ( I cannot disable it during build time using CONFIG_SERIAL=n).

  • Please try to build the sample against a newer SDK version and see if you still experience the high idle current after suspending the UART. For example, v2.5.1.

Reply Children
  • Hi Vidar,

    I have run the System Off & Beacon samples without any changes as I did previously and also by disabling the UART during build (CONFIG_UART=n). It is clear that the issue is the UART not being suspended, as you can see the power consumption drops to the expected values when disabled.

    System Off sample - unmodified

    Zoomed in the System Sleep (System ON Idle) state

    System Off sample - built with UART disabled (CONFIG_SERIAL=n)

    immediately you can see the difference in the SYstem ON Idle portions of the execution.

    Zoomed in the System Sleep (System ON Idle) state

    As you can see the System ON Idle (system Sleep) portion o f the sample has the expected values (same current curve and values as the system_on_demo.hex you provided) and significantly less power consumption. This is also observed for the beacon sample shown below.

    Beacon sample - unmodified

    Zoomed in the System Sleep (System ON Idle) state between the advertising events

    Beacon sample - built with UART disabled (CONFIG_SERIAL=n)

    Zoomed in the System Sleep (System ON Idle) state between the advertising events

    Again the power consumption is significantly less and aligns with the current values reported by the datasheet

    Now I want to find the way to disable/suspend the UART module during runtime correctly as the 

    pm_device_action_run(cons, PM_DEVICE_ACTION_SUSPEND);
    function does not seem to work properly.
    I will need to be able to do this in the current SDK version (v2.1.0) because migrating to the v2.5.1 only because I want to suspend the UART during runtime will be very time-consuming.
    So my question is how is it possible to suspend the UART correctly in the v2.1.0 version of the SDK?
    I will try of course your suggestion but I should be able to properly suspend the UART in v2.1.0 as well unless that is a bug in the NCS which will require extensive modifications to patch. I would like to keep working with v2.1.0 unless it's absolutely necessary and no other way is possible but to migrate to a newer version. Also if there is a fix I can apply to my version of NCS that would be great or if you have any alternatives o suggest.
    Thank you for your support and I look forward to hearing from you!
    Best regards,
    Stavros

  • Hi Stavros, 

    The main goal of attempting to build the project with a newer SDK version was to help narrow down the issue. I've just done that, and it think I've found the culprit; until SDK v2.4.0, the nRF52840 DK board file had the UARTE1 instance enabled by default. The PM action only disables the UARTE0 instance, which is assigned to the Zephyr console.

    Solution:

    Create a DT overlay (nrf52840dk_nrf52840.overlay) that disabled the UARTE1 instance:

     

    &uart1 {
        status = "disabled";
    };

  • Hi Vidar,

    I tested the System Off modified sample with an nrf52840dk_nrf52840.overlay file with the lines you suggested and it worked!

    The consumption is now on the correct/expected levels! Thank you!

    Unfortunately, I am still trying to fix this issue on my custom fw application, I have gone through all the steps of making sure to suspend the UART and disable uart1 but I am still getting this increased consumption (exactly the same current curve as when a UART peripheral is enabled) which is clearly a UART peripheral still enabled.

    Is there a way to check (e.g. from the output files like .conf or the Compiled DeviceTree Output) if a UART module is still enabled?

    Thank you and I look forward to hearing from you

    Best regards,

    Stavros

  • Hi Stavros,

    Glad to hear that it worked now.

    Is there a way to check (e.g. from the output files like .conf or the Compiled DeviceTree Output) if a UART module is still enabled?

    Yes, you can check the generated zephyr.dts in build/zephyr to see whether the uart1 node is enabled or not.

    To be sure the ~500 uA is from the UART, do you get the expected idle current if you build with CONFIG_SERIAL=n

    Best regards,

    Vidar

  • To be sure the ~500 uA is from the UART, do you get the expected idle current if you build with CONFIG_SERIAL=n

    I do not. But it has exactly the same current curve and current levels that I see with the samples (UART enabled)

    Beacon System Sleep between advertising events

    My custom FW (essentially just initializes some sensors which are not present in the DK and at this point its just a connectable BLE device that just advertises every 100 msec, not much different in terms of execution than the beacon. There is no other execution that is performed at this point only BLE advertise)

    These are identical, for me it is for sure the UART.

    What else could cause this current curve/consumption between advertising events ( where the device does nothing else but BLE advertise)? Could it be some other peripheral that is enabled, like SPI or I2C? Any input or hint is very helpful on this, or how I could go about debugging this.

    Thank you very much

    BR

    Stavros

Related