Problem getting power consumption below 2.2mA even in PM_STATE_SOFT_OFF

I am using a custom board with a nRF52832 (inside a BMD-300 from ublox) that I can't seem to get to a lower power state. The lowest I've been able to get is 2.2mA even using bare-bones firmware (see below for code). Can anyone shine some light on why this is? From what I can tell, I've turned all of the important settings off in my prj.config and my code is doing barely anything.

My custom board is connected to the "Debug Out" connector on the nRF52 DK. I'm using this to power the custom board with the DK 5V out. My multimeter is connected between the 5V out and the input pin on my custom board.

I have tried power cycling the device in case it's stuck in debug mode.

In addition to the code, I've attached both my prj.conf and my .dts file.

I am using NCS 2.1.2 and Zephyr 3.1.99.

Please let me know if I can provide more information that may be helpful in tracking this down.

#include <zephyr.h>
#include <logging/log.h>
#include <stdio.h>
#include <zephyr/drivers/uart.h>
#include <zephyr/pm/pm.h>
#include <zephyr/pm/policy.h>
#include <zephyr/pm/state.h>
#include <zephyr/pm/device.h>
#include <device.h>
#include <drivers/gpio.h>
#include <hal/nrf_gpio.h>
#include <hal/nrf_clock.h>
#include <hal/nrf_power.h>

#define LOG_MODULE_NAME stylus_main

LOG_MODULE_REGISTER(LOG_MODULE_NAME);

// Is it necessary to disable this in order to manually go to sleep? Examples vary.
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)
{
	k_usleep(1);
	k_sleep(K_MSEC(10));

	nrf_clock_lf_src_set(NRF_CLOCK, NRF_CLOCK_LFCLK_RC);
	nrf_clock_task_trigger(NRF_CLOCK, NRF_CLOCK_TASK_LFCLKSTART);

	// This seems to have the same effect as pm_state_force below on the current.
	// nrf_power_system_off(NRF_POWER);

	bool success = pm_state_force(0, &(struct pm_state_info){PM_STATE_SOFT_OFF, 0, 0});
	k_sleep(K_SECONDS(2U));
	if (success)
	{
		// We successfully went to sleep
		// This is never called because we're asleep - that's expected.
		// blink_with_colors(1000, 0, 0, 0, 250, 5);
	}
	else
	{
		printf("Failed to go to sleep.");
	}

	// Should never be reached
	while (1)
	{
	}
}

Here is my prj.conf:

# Bluetooth
CONFIG_BT=n
# CONFIG_BT_PERIPHERAL=n
# CONFIG_BT_DEVICE_NAME="Stylus"
# CONFIG_BT_DEVICE_APPEARANCE=0
# CONFIG_BT_MAX_CONN=1
# CONFIG_BT_LL_SOFTDEVICE=n

CONFIG_GPIO=n
# CONFIG_DK_LIBRARY=n
CONFIG_ASSERT=n

# DFU
CONFIG_MCUMGR=n
# CONFIG_MCUMGR_CMD_IMG_MGMT=n
# CONFIG_MCUMGR_CMD_OS_MGMT=n
# CONFIG_BT_L2CAP_TX_MTU=253
# CONFIG_BT_BUF_ACL_RX_SIZE=256
# CONFIG_MCUMGR_SMP_BT=n
# CONFIG_MCUMGR_SMP_BT_AUTHEN=n

CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=1024
CONFIG_INIT_STACKS=n
CONFIG_THREAD_STACK_INFO=n
CONFIG_MAIN_STACK_SIZE=1024
# CONFIG_BT_RX_STACK_SIZE=2048

CONFIG_ADC=n
# CONFIG_ADC_NRFX_SAADC=n

# CONFIG_BT_BUF_ACL_TX_COUNT=74
# CONFIG_BT_PERIPHERAL_PREF_MIN_INT=6
# CONFIG_BT_PERIPHERAL_PREF_MAX_INT=20

# CONFIG_STDOUT_CONSOLE=n
CONFIG_I2C=n
CONFIG_SENSOR=n
CONFIG_BMI270=n
CONFIG_BMM150=n
CONFIG_BMM150_PRESET_LOW_POWER=y
# CONFIG_BMM150_PRESET_HIGH_ACCURACY=n

# Storage
CONFIG_FLASH=n
CONFIG_FLASH_PAGE_LAYOUT=n
CONFIG_NVS=n
# CONFIG_NVS_LOG_LEVEL_DBG=n
CONFIG_REBOOT=n
CONFIG_MPU_ALLOW_FLASH_WRITE=n

# Comment this out if you need to print floats
# CONFIG_NEWLIB_LIBC=n
CONFIG_CBPRINTF_FP_SUPPORT=n
CONFIG_PRINTK=n

CONFIG_CLOCK_CONTROL_NRF_K32SRC_RC=y
CONFIG_CLOCK_CONTROL_NRF_K32SRC_XTAL=n

# MCUBoot
CONFIG_BOOTLOADER_MCUBOOT=n

# Debugging
CONFIG_UART_CONSOLE=n
# CONFIG_RTT_CONSOLE=n
CONFIG_USE_SEGGER_RTT=n
CONFIG_LOG=n
CONFIG_LOG_DEFAULT_LEVEL=0
# CONFIG_DEBUG_OPTIMIZATIONS=n
# CONFIG_DEBUG_THREAD_INFO=n
CONFIG_SENSOR_LOG_LEVEL_DBG=n

CONFIG_SERIAL=n
CONFIG_RTT_CONSOLE=n

# Power Management
CONFIG_PM=y
CONFIG_PM_DEVICE=y
CONFIG_PM_DEVICE_RUNTIME=y
CONFIG_PM_DEVICE_POWER_DOMAIN=y

# Test
CONFIG_CONSOLE=n
CONFIG_SPI_NOR_IDLE_IN_DPD=y
CONFIG_SPI=n
CONFIG_TICKLESS_KERNEL=y
CONFIG_ADC_NRFX_SAADC=n

# Minimize boot time
CONFIG_BOOT_DELAY=0

# Disable unused hardware
CONFIG_NFCT_PINS_AS_GPIOS=y

CONFIG_PWM=n

And here is my .dts file:

/dts-v1/;
#include <nordic/nrf52832_qfaa.dtsi>
#include "mydevice-pinctrl.dtsi"

/ {
	model = "MyDevice";
	compatible = "nordic,mydevice";

	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;
	};

	buttons {
		compatible = "gpio-keys";
		
		capbutton: cap_button {
			//gpios = <&gpio0 12 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>; // Incorrect, but used for testing charger
			gpios = <&gpio0 24 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>; // Correct
			label = "Button on the top of the stylus cap";
		};

		battcharge: batt_charge {
			//gpios = <&gpio0 24 (GPIO_ACTIVE_LOW | GPIO_PULL_DOWN)>; // Incorrect, but used for testing charger
			gpios = <&gpio0 12 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; // Correct
			label = "Charge pin connected to the battery";
		};

		battfault: batt_fault {
			gpios = <&gpio0 1 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>;
			label = "Fault pin connected to the battery";
		};

		dockstatus: dock_status {
			gpios = <&gpio0 30 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>;
			label = "Hall effect sensor output that determines if the device is docked on the base station";
		};
	};

	// These pins are not used but can't be left in a floating electrical state.
	// If they are, the device will leak current.
	extras {
		compatible = "gpio-keys";

		// BMI270
		imu_int1: imu_int1 {
			gpios = <&gpio0 3 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
			label = "IMU Interrupt 1";
		};
		imu_int2: imu_int2 {
			gpios = <&gpio0 9 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
			label = "IMU Interrupt 2";
		};
		// End BMI 270

		// BMM150
		mag_drdy: mag_drdy {
			gpios = <&gpio0 5 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
			label = "Magnetometer Data Ready";
		};
		mag_int: mag_int {
			gpios = <&gpio0 11 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
			label = "Magnetometer Interrupt";
		};
	};

	aliases {
		capbutton = &capbutton;
		battcharge = &battcharge;
		battfault = &battfault;
		dockstatus = &dockstatus;
		backupstorage = &backupstorage;
		imu-int1 = &imu_int1;
		imu-int2 = &imu_int2;
		mag-drdy = &mag_drdy;
		mag-int = &mag_int;
	};

};

&adc {
	status = "disabled";

	#address-cells = <1>;
   	#size-cells = <0>;

	// Force Sensor
	// NOTE: If any issues are encountered with the force sensor, note this changed from AIN5 (P0.29) to AIN0 (P0.02)
	channel@0 {
		reg = <0>;
		zephyr,gain = "ADC_GAIN_1_5";
		zephyr,reference = "ADC_REF_INTERNAL";
		zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
		zephyr,input-positive = <NRF_SAADC_AIN0>;
		status = "disabled";
	};
 
	// Battery VBatt Divided
	channel@1 {
		reg = <1>;
		zephyr,gain = "ADC_GAIN_1_5";
		zephyr,reference = "ADC_REF_INTERNAL";
		zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
		zephyr,input-positive = <NRF_SAADC_AIN2>;
		status = "disabled";
	};
};

&gpiote {
	status = "disabled";
};

&gpio0 {
	status = "disabled";
};

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

&i2c0 {
	compatible = "nordic,nrf-twi";
	status = "disabled";

	pinctrl-0 = <&i2c0_default>;
	pinctrl-1 = <&i2c0_sleep>;
	pinctrl-names = "default", "sleep";
	clock-frequency = <400000>;

	bmi270: bmi270@68 {
		compatible = "bosch,bmi270";
		reg = <0x68>;
		label = "BMI270";
		status = "disabled";
	};

	bmm150: bmm150@10 {
		compatible = "bosch,bmm150";
		reg = <0x10>;
		label = "BMM150";
		status = "disabled";
	};

	ledctrl: ledctrl@58 {
		compatible = "i2c-device";
		reg = <0x58>;
		label = "ledctrl";
		status = "disabled";
	};
};

&spi1 {
	status = "disabled";
	compatible = "nordic,nrf-spi";
	cs-gpios = <&gpio0 13 (GPIO_ACTIVE_LOW | GPIO_PULL_DOWN)>;
	pinctrl-0 = <&spi1_default>;
	pinctrl-1 = <&spi1_sleep>;
	pinctrl-names = "default", "sleep";

	backupstorage: backupstorage@0 {
		compatible = "issi,is66wvs4m8bll";
		reg = <0>;
		spi-max-frequency = <1600000>;
		label = "backupstorage";
	};
};

&flash0 {

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

		boot_partition: partition@0 {
			label = "mcuboot";
			reg = <0x00000000 0xc000>;
		};
		slot0_partition: partition@c000 {
			label = "image-0";
			reg = <0x0000C000 0x32000>;
		};
		slot1_partition: partition@3e000 {
			label = "image-1";
			reg = <0x0003E000 0x32000>;
		};
		scratch_partition: partition@70000 {
			label = "image-scratch";
			reg = <0x00070000 0xa000>;
		};
		// 0x0007a000 0x00006000 = 475136
		// 475136 = 464KB
		storage_partition: partition@7a000 {
			label = "storage";
			reg = <0x0007a000 0x00006000>;
		};
	};
};


Related