Zephyr - 2 NRF52840 Low Power Mode Test
Hardware
Electrical specification

Hardware connection


Signal Chip Module (All GPIOs are floating)
Condition:
supply voltage: 3V
temperature: 25
System Off Mode: 0.7uA
Typical: 0.4uA

System on Mode: 3.5uA (Full RAM with RTC wakeup)
Typical: 3.16uA

NRF52840DK-NRF52840
Cut
- SB16
- SB10 - SB12
- SB13 - SB15
- SB40 (VDD_nRF)
Remove
- R16 - R19
- U5 - U8
- SW9
Fly wire
- Debug in pin4 -> P26 pin4 (SWDCLK)
- Debug in pin2 -> P26 pin5 (SWDIO)
System-off Mode: 0.82uA

System on idle Mode: 3.86uA

Cactes BLB200
System Off Mode with LoRa in Sleep mode 1.15uA

System on Mode with LoRa in sleep mode 3.71uA

System On Mode with LoRa in standby mode (716uA)

System Off Mode with LoRa in standby mode (739uA)

Source Code for BLB200 module
prj.conf
# Power Control
CONFIG_PM_DEVICE=y
CONFIG_POWEROFF=y
# GPIO
CONFIG_GPIO=y
# UART
CONFIG_SERIAL=y
CONFIG_CONSOLE=y
CONFIG_UART_CONSOLE=y
# SPI
CONFIG_SPI=y
# LoRa
CONFIG_LORA=y
CONFIG_USE_SEGGER_RTT=n
main.c
/*
* Copyright (c) 2023 Cactes Technology
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/init.h>
#include <zephyr/pm/pm.h>
#include <zephyr/pm/device.h>
#include <zephyr/pm/policy.h>
#include <zephyr/sys/poweroff.h>
#include <soc.h>
#include <hal/nrf_gpio.h>
#include <zephyr/dt-bindings/gpio/nordic-nrf-gpio.h>
const struct device *gpio0 = DEVICE_DT_GET(DT_NODELABEL(gpio0));
const struct device *gpio1 = DEVICE_DT_GET(DT_NODELABEL(gpio1));
void gpio_disconnect(const struct device *gpio_dev, int pin)
{
int ret;
if (!device_is_ready(gpio_dev)) {
printk("%s: device not ready.\n", gpio_dev->name);
return;
} else {
printk("%s: device ready.\n", gpio_dev->name);
}
ret = gpio_pin_configure(gpio_dev, pin, GPIO_DISCONNECTED);
if (!ret) {
printk("set gpio ok %d\n", ret);
}
}
void gpio_low(const struct device *gpio_dev, int pin)
{
int ret;
if (!device_is_ready(gpio_dev)) {
printk("%s: device not ready.\n", gpio_dev->name);
return;
} else {
printk("%s: device ready.\n", gpio_dev->name);
}
ret = gpio_pin_configure(gpio_dev, pin, GPIO_OUTPUT_LOW);
if (!ret) {
printk("set gpio ok %d\n", ret);
}
}
void gpio_high(const struct device *gpio_dev, int pin)
{
int ret;
if (!device_is_ready(gpio_dev)) {
printk("%s: device not ready.\n", gpio_dev->name);
return;
} else {
printk("%s: device ready.\n", gpio_dev->name);
}
ret = gpio_pin_configure(gpio_dev, pin, GPIO_OUTPUT_HIGH);
if (!ret) {
printk("set gpio ok %d\n", ret);
}
}
struct gpios_map {
const struct device *gpio;
int pin;
int action;
};
/* -2 skip, -1 disconnect, 0 low, 1 high */
struct gpios_map gpio_map_disconnect [] = {
#ifdef CONFIG_APP_SYSTEMOFF
{ .gpio = DEVICE_DT_GET(DT_NODELABEL(gpio0)), .pin = 0, .action = -1 },
{ .gpio = DEVICE_DT_GET(DT_NODELABEL(gpio0)), .pin = 1, .action = -1 },
#else
{ .gpio = DEVICE_DT_GET(DT_NODELABEL(gpio0)), .pin = 0, .action = -2 },
{ .gpio = DEVICE_DT_GET(DT_NODELABEL(gpio0)), .pin = 1, .action = -2 },
#endif
{ .gpio = DEVICE_DT_GET(DT_NODELABEL(gpio0)), .pin = 2, .action = -1 },
{ .gpio = DEVICE_DT_GET(DT_NODELABEL(gpio0)), .pin = 3, .action = -1 },
{ .gpio = DEVICE_DT_GET(DT_NODELABEL(gpio0)), .pin = 4, .action = -1 },
{ .gpio = DEVICE_DT_GET(DT_NODELABEL(gpio0)), .pin = 5, .action = -1 },
{ .gpio = DEVICE_DT_GET(DT_NODELABEL(gpio0)), .pin = 6, .action = -1 },
{ .gpio = DEVICE_DT_GET(DT_NODELABEL(gpio0)), .pin = 7, .action = -1 },
{ .gpio = DEVICE_DT_GET(DT_NODELABEL(gpio0)), .pin = 8, .action = -1 },
{ .gpio = DEVICE_DT_GET(DT_NODELABEL(gpio0)), .pin = 9, .action = -1 },
{ .gpio = DEVICE_DT_GET(DT_NODELABEL(gpio0)), .pin = 10, .action = -1 },
{ .gpio = DEVICE_DT_GET(DT_NODELABEL(gpio0)), .pin = 11, .action = -1 },
{ .gpio = DEVICE_DT_GET(DT_NODELABEL(gpio0)), .pin = 12, .action = -1 },
{ .gpio = DEVICE_DT_GET(DT_NODELABEL(gpio0)), .pin = 13, .action = -2 },
{ .gpio = DEVICE_DT_GET(DT_NODELABEL(gpio0)), .pin = 14, .action = -2 },
{ .gpio = DEVICE_DT_GET(DT_NODELABEL(gpio0)), .pin = 15, .action = -2 },
{ .gpio = DEVICE_DT_GET(DT_NODELABEL(gpio0)), .pin = 16, .action = -2 },
{ .gpio = DEVICE_DT_GET(DT_NODELABEL(gpio0)), .pin = 17, .action = -2 },
{ .gpio = DEVICE_DT_GET(DT_NODELABEL(gpio0)), .pin = 18, .action = -1 },
{ .gpio = DEVICE_DT_GET(DT_NODELABEL(gpio0)), .pin = 19, .action = -2 },
{ .gpio = DEVICE_DT_GET(DT_NODELABEL(gpio0)), .pin = 20, .action = -2 },
{ .gpio = DEVICE_DT_GET(DT_NODELABEL(gpio0)), .pin = 21, .action = -1 },
{ .gpio = DEVICE_DT_GET(DT_NODELABEL(gpio0)), .pin = 22, .action = -1 },
{ .gpio = DEVICE_DT_GET(DT_NODELABEL(gpio0)), .pin = 23, .action = -1 },
{ .gpio = DEVICE_DT_GET(DT_NODELABEL(gpio0)), .pin = 24, .action = -1 },
{ .gpio = DEVICE_DT_GET(DT_NODELABEL(gpio0)), .pin = 25, .action = -1 },
{ .gpio = DEVICE_DT_GET(DT_NODELABEL(gpio0)), .pin = 26, .action = -1 },
{ .gpio = DEVICE_DT_GET(DT_NODELABEL(gpio0)), .pin = 27, .action = -1 },
{ .gpio = DEVICE_DT_GET(DT_NODELABEL(gpio0)), .pin = 28, .action = -1 },
{ .gpio = DEVICE_DT_GET(DT_NODELABEL(gpio0)), .pin = 29, .action = -1 },
{ .gpio = DEVICE_DT_GET(DT_NODELABEL(gpio0)), .pin = 30, .action = -1 },
{ .gpio = DEVICE_DT_GET(DT_NODELABEL(gpio0)), .pin = 31, .action = -1 },
{ .gpio = DEVICE_DT_GET(DT_NODELABEL(gpio1)), .pin = 0, .action = -1 },
{ .gpio = DEVICE_DT_GET(DT_NODELABEL(gpio1)), .pin = 1, .action = -1 },
{ .gpio = DEVICE_DT_GET(DT_NODELABEL(gpio1)), .pin = 2, .action = -1 },
{ .gpio = DEVICE_DT_GET(DT_NODELABEL(gpio1)), .pin = 3, .action = -1 },
{ .gpio = DEVICE_DT_GET(DT_NODELABEL(gpio1)), .pin = 4, .action = -1 },
{ .gpio = DEVICE_DT_GET(DT_NODELABEL(gpio1)), .pin = 5, .action = -1 },
{ .gpio = DEVICE_DT_GET(DT_NODELABEL(gpio1)), .pin = 6, .action = -1 },
{ .gpio = DEVICE_DT_GET(DT_NODELABEL(gpio1)), .pin = 7, .action = -1 },
{ .gpio = DEVICE_DT_GET(DT_NODELABEL(gpio1)), .pin = 8, .action = -1 },
{ .gpio = DEVICE_DT_GET(DT_NODELABEL(gpio1)), .pin = 9, .action = -1 },
{ .gpio = DEVICE_DT_GET(DT_NODELABEL(gpio1)), .pin = 10, .action = -1 },
{ .gpio = DEVICE_DT_GET(DT_NODELABEL(gpio1)), .pin = 11, .action = -1 },
{ .gpio = DEVICE_DT_GET(DT_NODELABEL(gpio1)), .pin = 12, .action = -1 },
{ .gpio = DEVICE_DT_GET(DT_NODELABEL(gpio1)), .pin = 13, .action = -1 },
{ .gpio = DEVICE_DT_GET(DT_NODELABEL(gpio1)), .pin = 14, .action = -1 },
{ .gpio = DEVICE_DT_GET(DT_NODELABEL(gpio1)), .pin = 15, .action = -1 },
};
void deinit(void)
{
int i;
for (i = 0; i < ARRAY_SIZE(gpio_map_disconnect); i++) {
switch (gpio_map_disconnect[i].action) {
case -2:
break;
case -1:
gpio_disconnect(gpio_map_disconnect[i].gpio, gpio_map_disconnect[i].pin);
break;
case 0:
gpio_low(gpio_map_disconnect[i].gpio, gpio_map_disconnect[i].pin);
break;
case 1:
gpio_high(gpio_map_disconnect[i].gpio, gpio_map_disconnect[i].pin);
break;
}
}
}
#if DT_NODE_HAS_STATUS(DT_ALIAS(lora0), okay)
#include <zephyr/drivers/lora.h>
static const struct device *const lora_dev = DEVICE_DT_GET(DT_ALIAS(lora0));
static const struct device *const lora_spi = DEVICE_DT_GET(DT_PARENT(DT_ALIAS(lora0)));
#endif
#ifdef CONFIG_SERIAL
static const struct device *const cons = DEVICE_DT_GET(DT_CHOSEN(zephyr_console));
#endif
#define BUSY_WAIT_S 2U
#define SLEEP_S 2U
void main(void)
{
int rc;
/* lora init and set to sleep */
#if DT_NODE_HAS_STATUS(DT_ALIAS(lora0), okay)
struct lora_modem_config config;
if (!device_is_ready(lora_dev)) {
printk("%s Device not ready\n", lora_dev->name);
}
config.frequency = 865100000;
config.bandwidth = BW_125_KHZ;
config.datarate = SF_10;
config.preamble_len = 8;
config.coding_rate = CR_4_5;
config.iq_inverted = false;
config.public_network = false;
config.tx_power = 4;
config.tx = true;
rc = lora_config(lora_dev, &config);
if (rc < 0) {
printk("LoRa config failed\n");
}
rc = pm_device_action_run(lora_spi, PM_DEVICE_ACTION_SUSPEND);
#endif
if (!device_is_ready(gpio0)) {
printk("%s: device not ready.\n", gpio0->name);
}
if (!device_is_ready(gpio1)) {
printk("%s: device not ready.\n", gpio1->name);
}
/* uart deinit */
#ifdef CONFIG_SERIAL
rc = pm_device_action_run(cons, PM_DEVICE_ACTION_SUSPEND);
#endif
/* gpio deinit */
deinit();
while (true) {
/* simulate system is busy */
k_busy_wait(BUSY_WAIT_S * USEC_PER_SEC);
/* enter sleep mode */
#ifdef CONFIG_APP_STANDBY
k_sleep(K_SECONDS(SLEEP_S));
#elif defined CONFIG_APP_SYSTEMOFF
sys_poweroff();
k_sleep(K_SECONDS(SLEEP_S));
#endif
}
}
- auto deinit serial and spi
- auto deinit non-lora connected gpios
Reference
- A user confirmed about ~4uA idle sleep mode
- https://devzone.nordicsemi.com/f/nordic-q-a/102588/nrf52840-zephyr-blinky-sleep-power-consumption
- Can only achieve 0.7uA with another one's firmware
- 7587.ble_app_pwr_profiling_pca10056_s140.hex
- https://devzone.nordicsemi.com/f/nordic-q-a/66062/achieving-3-16-a-system-on-sleep-in-zephyr/272756
- Worth to read,
- https://devzone.nordicsemi.com/f/nordic-q-a/53117/high-current-consumption-4ua-even-without-peripherals-enabled-in-idle/214583
