Help Reducing Sleep Current While Keeping RTC Active

I’m working on a device using a Nordic chip and the following setup:

  • nRF Connect SDK: v2.5.2
  • Toolchains: v2.7.0

We need to minimize current consumption during sleep while keeping the RTC active so it can wake the device at the appropriate time. Currently, we’re measuring around 446 µA in sleep mode, which is significantly higher than expected.

Here are some key points regarding our setup and what we’ve tried so far:

  1. We disable all peripherals (UART, SPI, sensor, etc.) before entering sleep.
  2. We only need the RTC to remain powered to wake the device.
  3. We do not need to retain anything in RAM during sleep.

Despite these measures, the sleep current remains higher than our target. Could you please advise on how to further reduce the sleep current? Are there additional configurations or hardware considerations we should address to achieve lower power consumption levels, ideally closer to typical values for this chip?

Thank you very much for your assistance.

Best regards,


void deep_sleep_state()
{
	LOG_INF("Deep Sleep State");

	uint64_t seconds_to_deep_sleep = 600;

	LOG_INF("Fail count: %u", fail_count);
	LOG_INF("Sleeping for: %llu seconds", seconds_to_deep_sleep);

	// Check if necessary
	//k_sleep(K_MSEC(3000));

	bluetooth_disable();

	if( set_rtc_alarm(seconds_to_deep_sleep) != 0 ) {
		// TODO: Handle nap error
	}

	int rc;
	const struct device *const console_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_console));
	const struct device *const uart1_dev = DEVICE_DT_GET(DT_NODELABEL(uart1));
	const struct device *const sensor_dev = DEVICE_DT_GET_ANY(st_lis2dh);
	const struct device *const spi3_dev = DEVICE_DT_GET(DT_NODELABEL(spi3));

	rc = pm_device_action_run(console_dev, PM_DEVICE_ACTION_SUSPEND);
	rc = pm_device_action_run(uart1_dev, PM_DEVICE_ACTION_SUSPEND);
	rc = pm_device_action_run(sensor_dev, PM_DEVICE_ACTION_SUSPEND);
	flash_suspend();
	rc = pm_device_action_run(spi3_dev, PM_DEVICE_ACTION_SUSPEND);

	// disable pins
	const struct device* gpio0_dev = device_get_binding("gpio@50000000"); // gpio 0

	gpio_pin_configure(gpio0_dev, 12, (GPIO_INPUT | GPIO_PULL_DOWN)); // gpio 0.12 - DS18EXT
	gpio_pin_configure(gpio0_dev, 13, (GPIO_INPUT | GPIO_PULL_DOWN)); // gpio 0.13 - LED
	gpio_pin_configure(gpio0_dev, 26, (GPIO_INPUT | GPIO_PULL_DOWN)); // gpio 0.26 - DS18INT
	gpio_pin_configure(gpio0_dev, 28, (GPIO_INPUT | GPIO_PULL_DOWN)); // gpio 0.28 - CS sensor
	gpio_pin_configure(gpio0_dev, 29, (GPIO_INPUT | GPIO_PULL_DOWN)); // gpio 0.29 - Miso sensor

	k_sem_reset(&rtc_alarm_sem);
	k_sem_take(&rtc_alarm_sem, K_FOREVER);

	rc = pm_device_action_run(console_dev, PM_DEVICE_ACTION_RESUME);

	LOG_WRN("Wokend up - Rebooting");

	// Check if necessary
	//k_sleep(K_MSEC(5000));

	sys_reboot(SYS_REBOOT_COLD);
}

Edit:
Additionally, we want to mention that we are using a custom board with our own hardware design. For your reference, we have attached the relevant .dts and .dtsi files below. If you spot any issues or necessary changes in the device tree configuration that could help lower power consumption, please let us know.

// Copyright (c) 2024 Nordic Semiconductor ASA
// SPDX-License-Identifier: Apache-2.0

/dts-v1/;
#include <nordic/nrf52840_qiaa.dtsi>
#include "imachine_nrf52840-pinctrl.dtsi"
#include <zephyr/dt-bindings/input/input-event-codes.h>

/ {
	model = "IMACHINE_NRF52840";
	compatible = "nordic,imachine-nrf52840";

	chosen {
		zephyr,console = &uart0;
		zephyr,shell-uart = &uart0;
		zephyr,uart-mcumgr = &uart0;
		zephyr,bt-mon-uart = &uart0;
		zephyr,bt-c2h-uart = &uart0;
		zephyr,sram = &sram0;
		zephyr,flash = &flash0;
		zephyr,code-partition = &slot0_partition;
		zephyr,settings-partition = &settings_partition;
	};

	buttons {
		compatible = "gpio-keys";
		hall_effect_sensor: hall_effect_sensor {
			gpios = <&gpio0 3 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
			label = "Sensor hall";
			zephyr,code = <INPUT_KEY_0>;
		};
		// TODO: a ser validado com o Andre -sugestao do Mauro P0.18
		reset_button: reset_button {
			gpios = <&gpio1 13 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
			label = "Reset";
			zephyr,code = <INPUT_KEY_1>;
		};
	};

	leds {
		compatible = "gpio-leds";
		reg_control_gpio: reg_control_gpio {
			gpios =
				<&gpio0 27 (GPIO_ACTIVE_LOW | GPIO_PULL_DOWN)>;
		};

		led: led {
			gpios = <&gpio0 13 GPIO_ACTIVE_LOW>;
		};

	};

	aliases {
		hallsensor = &hall_effect_sensor;
		resetbutton = &reset_button;
		regcontrolgpio = &reg_control_gpio;
		watchdog0 = &wdt;
		led = &led;
	};

	w1: w1 {
		compatible = "zephyr,w1-gpio";
		gpios = <&gpio0 26 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN | GPIO_PULL_UP)>;
	};
};

&flash0 {
	partitions {
		compatible = "fixed-partitions";
		#address-cells = <1>;
		#size-cells = <1>;

		boot_partition: partition@0 {
			label = "mcuboot";
			reg = <0x0 0xc000>;
		};
		slot0_partition: partition@c000 {
			label = "image-0";
			reg = <0xc000 0x71000>;
		};
		slot1_partition: partition@7d000 {
			label = "image-1";
			reg = <0x7d000 0x71000>;
		};

		/* Partition used to store Bluetooth config data- 8kB */
		settings_partition: partition@ee000 {
			label = "settings";
			reg = <0xee000 0x2000>;
		};
		scratch_partition: partition@f0000 {
			label = "image-scratch";
			reg = <0xf0000 0xa000>;
		};

		/* Partition used to store accelerometer data - 24kB */
		storage_partition: partition@fa000 {
			label = "storage";
			reg = <0xfa000 0x6000>;
		};
	};
};

&gpio0 {
	status = "okay";
};

&gpio1 {
	status = "okay";
};

&gpiote {
	status = "okay";
};

&uart0 {
	compatible = "nordic,nrf-uarte";
	status = "okay";
	current-speed = <115200>;
	pinctrl-0 = <&uart0_default>;
	pinctrl-1 = <&uart0_sleep>;
	pinctrl-names = "default", "sleep";
};

&spi3 {
	compatible = "nordic,nrf-spim";
	status = "okay";
	pinctrl-0 = <&spi3_default>;
	pinctrl-1 = <&spi3_sleep>;
	pinctrl-names = "default","sleep";
	cs-gpios = <&gpio0 28 GPIO_ACTIVE_LOW>;

	lis2dh12_imachine@0 {
		status = "okay";
		compatible = "lis2_imachine";
		reg = <0x0>;
		spi-max-frequency = <10000000>;
		// INT1 = P0.31, INT2 = P0.05
		irq-gpios = <&gpio0 31 GPIO_ACTIVE_HIGH>, <&gpio0 5 GPIO_ACTIVE_HIGH>;
	};
};

&uart1 {
	status = "okay";
	current-speed = <115200>;
	pinctrl-0 = <&uart1_default>;
	pinctrl-1 = <&uart1_sleep>;
	pinctrl-names = "default", "sleep";
};

&i2c0 {
	status = "disabled";
	pinctrl-0 = <&i2c0_default>;
	pinctrl-1 = <&i2c0_sleep>;
	pinctrl-names = "default","sleep";
};

&rtc2 {
	status = "okay";
	prescaler = <256>;
};

&wdt {
	status = "okay";
};

&timer1 {
	status = "okay";
};


/*
 * Copyright (c) 2022 Nordic Semiconductor
 * SPDX-License-Identifier: Apache-2.0
 */

 &pinctrl {
	// Console UART
	uart0_default: uart0_default {
		group1 {
			psels = <NRF_PSEL(UART_TX, 0, 6)>; // Originally P0.8 - changed to use UART instead of RTT
		};
		group2 {
			psels = <NRF_PSEL(UART_RX, 0, 8)>; // Originally P0.11 - changed to use UART instead of RTT
			bias-pull-up;
		};
	};

	uart0_sleep: uart0_sleep {
		group1 {
			psels = <NRF_PSEL(UART_TX, 0, 6)>, // Originally P0.8 - changed to use UART instead of RTT
				<NRF_PSEL(UART_RX, 0, 8)>; // Originally P0.11 - changed to use UART instead of RTT
			low-power-enable;
		};
	};

	uart1_default: uart1_default {
		group1 {
			psels = <NRF_PSEL(UART_TX, 1, 7)>;
		};
		group2 {
			psels = <NRF_PSEL(UART_RX, 1, 6)>;
		};
	};

	uart1_sleep: uart1_sleep {
		group1 {
			psels = <NRF_PSEL(UART_TX, 1, 7)>,
				<NRF_PSEL(UART_RX, 1, 6)>;
			low-power-enable;
		};
	};

	// LIS2DH12
	// SD0 - MISO
	// SDA - MOSI

	spi3_default: spi3_default {
		group1 {
			psels = <NRF_PSEL(SPIM_SCK, 0, 2)>,
				<NRF_PSEL(SPIM_MOSI, 0, 30)>,
				<NRF_PSEL(SPIM_MISO, 0, 29)>;
		};
	};

	spi3_sleep: spi3_sleep {
		group1 {
			psels = <NRF_PSEL(SPIM_SCK, 0, 2)>,
				<NRF_PSEL(SPIM_MOSI, 0, 30)>,
				<NRF_PSEL(SPIM_MISO, 0, 29)>;
			low-power-enable;
		};
	};

	i2c0_default: i2c0_default {
		group1 {
			psels = <NRF_PSEL(TWIM_SDA, 0, 4)>,
					<NRF_PSEL(TWIM_SCL, 0, 7)>; // Originally P0.6 - changed to use UART instead of RTT
		};
	};

	i2c0_sleep: i2c0_sleep {
		group1 {
			psels = <NRF_PSEL(TWIM_SDA, 0, 4)>,
					<NRF_PSEL(TWIM_SCL, 0, 7)>; // Originally P0.6 - changed to use UART instead of RTT
			low-power-enable;
		};
	};

};
Parents
  • Hello,

    If you haven't already I recommend to go through Lesson 7 in our DevAcademy course:
    https://academy.nordicsemi.com/courses/nrf-connect-sdk-fundamentals/lessons/lesson-7-multithreaded-applications/  

    There is also a Power Optimization chapter in the nRF Connect SDK:
    https://docs.nordicsemi.com/bundle/ncs-latest/page/nrf/test_and_optimize/optimizing/power.html  

    Also, in general if you are using uart, the main challenge is the current consumption when in rx mode, so if you just need uart for logging, you can disable rx mode by adding e.g.:

    /* Disabling uart rx pin to get low power */
    &uart0 {

        disable-rx;
    };

    Best regards,
    Kenneth

  • Hello Kenneth,

    Thank you very much for your response and for the references you provided!
    I have a question regarding the UART suspend behavior. When using the following command to suspend the UART:

    const struct device *const uart1_dev = DEVICE_DT_GET(DT_NODELABEL(uart1));
    rc = pm_device_action_run(uart1_dev, PM_DEVICE_ACTION_SUSPEND);
    


    Shouldn’t this command automatically disable everything possible (including the RX pin) to ensure the lowest power consumption? Or do I still need to explicitly add disable-rx in the device tree to ensure the RX pin is turned off?

    Thank you in advance for your help!

    Best regards,

  • I think most examples in the nRF Connect SDK are power somewhat power optimized as long as you disable logging by for instance CONFIG_SERIAL=n. Once you enable any kind of logging, then achieving low power is likely not that easily possible no, though you can try the trick I suggested with disable-rx; for the uart node for logging.

    However you mention deep sleep, in this mode the only wakeup source is nfc or pin toggling, there is no rtc wakeup in this mode. Deep sleep is entered by calling sys_poweroff(). 

    Kenneth

  • Hello Kenneth,

    When I mentioned “deep sleep,” I was referring to a low-power, system-on mode with the RTC active. I’ve tested disabling the RX lines for any unneeded UART interfaces, and I’m also using pm_device_action_run(...) to suspend all the serial peripherals, but I’m still seeing around 450 µA.

    Regarding Bluetooth, I haven’t found any examples in the nRF Connect SDK or Zephyr that specifically use bt_disable(). Could you advise on the proper way to fully turn off Bluetooth to minimize power consumption when it’s not needed?

    Additionally, I use the high-frequency clock (HFCLK) for sampling during normal operation. I’m enabling it with the code snippet below. How should I properly turn off or release the HFCLK once I’m done, to reduce power consumption further?

    
    static int enable_high_frequency_clock(void)
    {
    	int err = 0;
    	int res = 0;
    	struct onoff_manager *clk_mgr;
    	struct onoff_client clk_cli;
    
    	clk_mgr = z_nrf_clock_control_get_onoff(CLOCK_CONTROL_NRF_SUBSYS_HF);
    	if (!clk_mgr)
    	{
    		LOG_ERR("Unable to get the Clock manager");
    		return -ENXIO;
    	}
    
    	sys_notify_init_spinwait(&clk_cli.notify);
    
    	err = onoff_request(clk_mgr, &clk_cli);
    	if (err < 0)
    	{
    		LOG_ERR("Clock request failed: %d", err);
    		return err;
    	}
    
    	do {
    		err = sys_notify_fetch_result(&clk_cli.notify, &res);
    		if (!err && res)
    		{
    			LOG_ERR("Clock could not be started: %d", res);
    			return res;
    		}
    	} while (err);
    
    	LOG_INF("HF clock started");
    	return 0;
    }

  • Arthur J Sary said:
    Regarding Bluetooth, I haven’t found any examples in the nRF Connect SDK or Zephyr that specifically use bt_disable(). Could you advise on the proper way to fully turn off Bluetooth to minimize power consumption when it’s not needed?

    There is no need to disable bt to achieve the 2.7uA idle current. Actually even with BT active in connection or advertisment, you will still get 2.7uA in between BLE events.

    Arthur J Sary said:
    Additionally, I use the high-frequency clock (HFCLK) for sampling during normal operation. I’m enabling it with the code snippet below. How should I properly turn off or release the HFCLK once I’m done, to reduce power consumption further?

    Starting the HFCLK will likely cause the 400-500uA you see. Based on the code snippet you shared, I would expect onoff_request() has an onoff_release() api.

    Kenneth

  • Hello Kenneth,

    Thank you so much for the advice regarding the HFCLK. Disabling it once I finish sampling really resolved the high current issue, and I’m now seeing about 2.9 µA during sleep. However, I’ve encountered another related challenge.

    I’m using a 3.7V LiPo battery (hence High Voltage mode), but when testing on a bench power supply, I’ve noticed that the sleep current varies significantly depending on the supply voltage. Here are the sleep current measurements I observed:

    • 3.6V: ~3 µA
    • 3.3V: ~50 µA
    • 3.0V: ~700 µA

    To gather these measurements, I perform a single cycle test: I remove power from the system, change the power supply voltage, then re-energize at the new voltage.

    Do you have any suggestions on what might be causing these discrepancies at different voltages, and how I can address them? Thank you for any guidance you can provide.

    Best regards,

Reply Children
No Data
Related