一、NRF51/NRF52系统低功耗表

(浅睡)

型号

电流

nrf51822/nrf51422

2.2uA-3uA

nRF52832

1.9uA-2uA

nRF52840

1.5uA-1.6uA

其他

2uA左右

(深睡,唤醒就复位重启)

型号

电流

nrf51822/nrf51422

<1uA

nRF52832

300nA

nRF52840

400nA

其他

<1uA

二、实测产品没有达到低功耗怎么办?

  1. 测量有100uA,这个时候需要检查[调试器]是否断开,需要掉电一次,再测量。
  2. 测量有几百uA,这一定是外设没有关闭。如下列表:

外设

电流

UART/UARTE

55uA

高频时钟电路

250uA

easyDMA(52810强制打开)

2mA

Timer0/1/2/3/4

70uA(建议裸机用app_timer,os用软定时器)

FPU

>7mA

GPIOTE

1.3mA(IN_EVENT) / <5uA(Port event)

SPI/TWI

mA级别

SAADC

mA级别

J-Link模式

100uA

引用官方手册:

System ON模式,就是CPU可以不工作而外设可以继续工作的一种低功耗模式。
idle模式下,当CPU和所有外设都不工作时,系统电流也就有2uA左右。
(注:除了idle模式,nRF5芯片还支持一种更低功耗的低功耗模式:sleep模式(Nordic芯片手册称为System OFF模式),sleep模式下,CPU和所有外设都强制关闭,所以功耗非常低:只有零点几微安。由于sleep模式下,芯片无法发出广播包或者与手机保持蓝牙连接,所以sleep模式在BLE应用中运用得并不是很多)。
Idle模式可以被任何中断唤醒(sleep模式只能被IO口唤醒),所以idle模式在实际应用中使用得比较多。
在idle模式下,芯片仍然可以正常发出广播或者与手机保持蓝牙连接,所以大部分BLE应用都是工作在idle模式下,这样既保持了BLE功能又可以实现低功耗。

三、如何进入低功耗?

1. 软件部分优化

1.1 广播优化

1.1.1 发射功率
设置发射功率具有 9 个发射等级。系统默认的发射功率是 0dbm,发射功率越大,发射距离就越远,相应的电流消耗就越大。
在0dBm时,打开DC/DC, nRF52832发射电流5.3mA。将TX功率增加到+4dBm只增加2.2mA。减少到- 40dbm只节省2.6mA。

1.1.2 广播间隔
就是广播包发出的频率,广播间隔越长,功耗越低。

1.1.3 广播负载
蓝牙的广播包普通包长度在 31 字节,扫描响应包也有 31 字节。如果蓝牙 5.0 下的第二广播包长度更长,越长的广播负载,会造成越大的电流消耗。

1.2 连接状态优化

1.2.1 连接间隔和从机潜伏周期
连接间隔是保证主从机维持连接,相互发空包的时间间隔。连接间隔可以在 GAP 初始化中进行设置。当设置的连接间隔越长,设备的功耗越低。因此,可以在维持连接状态下,保证数据正常通信的基础下,设置尽可能长的连接间隔。

从机潜伏周期和连接间隔是同时进行配置的,从机潜伏周期允许蓝牙设备一定次数的周期不对蓝牙主机数据进行回复。在这个周期次数范围内,蓝牙主机即使没有收到蓝牙从机设备的回复确认信息包,也会认为设备正常。这种方式也可以降低蓝牙设备的功耗。

连接间隔是保证主从机维持连接,相互发空包的时间间隔。连接间隔可以在 GAP 初始化中进行设置。当设置的连接间隔越长,设备的功耗越低。因此,可以在维持连接状态下,保证数据正常通信的基础下,设置尽可能长的连接间隔。

从机潜伏周期和连接间隔是同时进行配置的,从机潜伏周期允许蓝牙设备一定次数的周期不对蓝牙主机数据进行回复。在这个周期次数范围内,蓝牙主机即使没有收到蓝牙从机设备的回复确认信息包,也会认为设备正常。这种方式也可以降低蓝牙设备的功耗。

确认理论功耗值。BLE功耗跟广播间隔或者连接间隔是成正比关系的,所以20ms连接间隔下的功耗几乎是1s状态下的50倍!,单纯地问“1mA功耗高不高?”是没有意义的,必须结合特定的应用场景才有意义。

不管是广播还是连接,特定的使用场景会有一个理论功耗值,NRF功耗网址,以获得你的使用场景下理论功耗多少,比如连接模式下,每1秒钟发20个字节的数据包,这种模式下理论功耗为:7.6uA

1.2.2 发射和接收的数据量
蓝牙数据发送和接收的数据量大小,直观的影响到了功耗。数据吞吐量越大,功耗越高。

1.3 系统及外设功耗优化

1.3.1 协议栈时钟选择
协议栈时钟可以选择外部低速时钟和内部低速时钟。选取外部低速时钟具有更低的功耗,使能外部 32kHz 晶振,通常可以节省 1-2% 的电能。默认使用外部低速晶振。在 main.c 文件,ble_stack_init() 函数中 nrf_sdh_enable_request() 找到

nrf_clock_lf_cfg_t const clock_lf_cfg =
    {
        .source       = NRF_SDH_CLOCK_LF_SRC, // <0=> NRF_CLOCK_LF_SRC_RC      // 内部时钟源
        									  // <1=> NRF_CLOCK_LF_SRC_XTAL    // 外部晶振源
        									  // <2=> NRF_CLOCK_LF_SRC_SYNTH   // 合成时钟源
        .rc_ctiv      = NRF_SDH_CLOCK_LF_RC_CTIV,
        .rc_temp_ctiv = NRF_SDH_CLOCK_LF_RC_TEMP_CTIV,
        .accuracy     = NRF_SDH_CLOCK_LF_ACCURACY
    };

sdk_config.h

#ifndef NRF_SDH_CLOCK_LF_SRC
#define NRF_SDH_CLOCK_LF_SRC 1
#endif

[nrf51][nrf52][S130][SDK12.3] 修改协议栈时钟

1.3.2 关闭日志打印

Android STR待机 低功耗 手机低功耗模式_协议栈


裸机情况下,除非jlink调试器已连接,否则 RTT 不会使用电流。

os(freertos)情况:

  1. 单独开一个线程刷log,功能大约在2.2uA和40uA跳动。
  2. 将log放在空闲线程中刷新,功耗大约在11uA。
  3. 空闲线程每1000个滴答,刷新一次,功耗大约在3uA。(失去实时性)
void sys_sleep(void)
{
#ifdef SOFTDEVICE_PRESENT
#if NRF51
    sd_app_evt_wait();
#else
	nrf_pwr_mgmt_run();
#endif
#else
    // Wait for an event.
    __WFE();
    // Clear the internal event register.
    __SEV();
    __WFE();
#endif
}

/**
 * @brief  线程空闲处理函数
 */
void idle_state_handle(void)
{
	static uint16_t tick = 0;
	tick ++;
    if (get_ble_conn_state())
    {
        ble_data_send_dispose();
        tick = 0;
    }

   if (get_ble_is_bus())
   {
       return;
   }
   if (tick > 1000)
   {
	   // 刷新RTT LOG缓存区
	   if (NRF_LOG_PROCESS() == false)
	   {
	   		sys_sleep();
	   }
   }
}

/**
 * @brief  [弱函数] 线程空闲
 * @param  *argument: NULL
 */
void vApplicationIdleHook(void)
{
    idle_state_handle();
}

1.3.3 动态使用UART

首先 UART 模块本身只需要 55uA 的工作电流,同时会自动打开高频时钟电路,也需要消耗 250uA 左右电流。如果使能了 UARTE 的 EasyDMA,那么 DMA 还需要消耗额外的 2mA 电流。这样 UARTE 工作消耗的电流会很高。因此在 UART 没有数据传输的时候建议将 UART 关掉,以节省功耗。

注:为了达到低功耗和实时性双重目的,在设计 UART 通信的时候,我们经常会额外再加 2 个 GPIO 口用来通知对方 UART要传送数据了。

关闭 UART 的 API 为:nrf_drv_uart_uninit() 或者 app_uart_close()。

如果只是单向发送,无需接收,可以采用队列的形式,当队列空闲1000滴答,则关闭串口。队列不为空,则初始化串口,开始发送。
[单片机框架] [queue] 实现一个简易的消息队列

void idle_state_handle(void)
{
	static uint16_t tick = 0;
	tick ++;
    if (get_ble_conn_state())
    {
        ble_data_send_dispose();
        tick = 0;
    }

   if (get_ble_is_bus())
   {
       return;
   }
	if (queue_de(&m_urat_send_q, g_uart_send_data))
	{
		tick = 0;
		bsp_uart_init(BSP_UART_0);
		bsp_uart_send_nbyte(BSP_UART_0, g_uart_send_data);
		return;
	}
	        
   if (tick > 1000)
   {
   	   bsp_uart_deinit(BSP_UART_0); // 内置标志位,不会每次都操作关闭
	   // 刷新RTT LOG缓存区
	   if (NRF_LOG_PROCESS() == false)
	   {
	   		sys_sleep();
	   }
   }
}

1.3.4 动态使用SPI/TWI
在不使用的时候建议采用 uninit 函数进行关闭,这部分的外设也消耗电流。需要使用的时候进行 init 初始化开启。

SPI 开启和关闭:nrf_drv_spi_init 和 nrf_drv_spi_uninit
TWI 开启和关闭:nrf_drv_twi_enable 和 nrf_drv_twi_disable

1.3.5 动态使用SAADC

在不使用的时候建议采用 uninit 函数进行关闭,需要使用的时候进行 init 初始化开启。

ADC 开启和关闭:nrfx_saadc_init 和 nrfx_saadc_uninit

如果你发现 uninit ADC 后,功耗还是很高,建议打开这个宏 NRFX_SAADC_CONFIG_LP_MODE,再试一下,功耗有可能就降下来了。

Android STR待机 低功耗 手机低功耗模式_初始化_02

官方SAADC低功耗例子

1.3.5 建议使用GPIOE低精度模式
GPIOE 事件模式下具有两钟模式:高精度模式(hi_accuracy 为 true)和低精度模式(hi_accuracy 为 false)。高精度模式 IN event 中断比低精度模式 Port event 中断消耗更多的电流 10~20uA。如果只是检测 IO 口电平,建议使用低精度模式,也就是所有的输入信号都使用一个中断申请,库函数调用配置:
GPIOTE_CONFIG_IN_SENSE_HITOLO(false);

1.3.5 Timer–>不建议使用硬件定时器

Timer0/1/2/3/4。Timer 的工作电流大概为 5~50uA 左右(nRF51功耗会更高),对低功耗应用来说,已经非常大了。如果你的定时精度要求不高,而且是毫秒的倍数,那么强烈建议你使用 RTC 来实现定时功能。协议栈下为 app_timer 软件定时器,app_timer 的功耗只有 0.2uA 左右。

1.3.6 FPU

由于 nRF52x 系列处理器不同于 nRF51 系列,其内核为 ARM Cortex M4 处理器。ARM Cortex M4 处理器 带 FPU 浮点运算单元。每当程序要执行浮点运算的时候,内核就会自动把 FPU 打开i。FPU 将消耗 7mA 以上的电流,此种情况下,进入 idle 模式之前必须手动关闭 FPU,手动关闭 FPU 代码如下所示:

/* Clear FPSCR register and clear pending FPU interrupts. This code is base on

         * nRF5x_release_notes.txt in documentation folder. It is necessary part of code when

         * application using power saving mode and after handling FPU errors in polling mode.

*/

__set_FPSCR(__get_FPSCR() & ~(FPU_EXCEPTION_MASK));

(void) __get_FPSCR();

NVIC_ClearPendingIRQ(FPU_IRQn);

在新版本 SDK17 中已经默认添加了

x\nRF5_SDK_17.0.2_d674dde\components\libraries\pwr_mgmt\nrf_pwr_mgmt.c
函数:nrf_pwr_mgmt_run()

Android STR待机 低功耗 手机低功耗模式_Android STR待机 低功耗_03


1.3.7 nrf_pwr_mgmt_run()函数解析

打开 nrf_pwr_mgmt_run() 函数,BLE 状态下,如果 CPU 处于空闲状态就会进入 sd_app_evt_wait() 函数,这个函数是进入低功耗的关键,是协议栈提供的一个等待事件函数。

void nrf_pwr_mgmt_run(void)
{
    PWR_MGMT_FPU_SLEEP_PREPARE();  // 清除FDU异常,避免FDU中断被挂起
    PWR_MGMT_SLEEP_LOCK_ACQUIRE();  // 锁定临界区
    PWR_MGMT_CPU_USAGE_MONITOR_SECTION_ENTER();  // 用户监视段进入,监听进入低功耗的时间
    PWR_MGMT_DEBUG_PIN_SET();  // 置位仿真引脚

    // Wait for an event.
#ifdef SOFTDEVICE_PRESENT  // 带协议栈状态下
    if (nrf_sdh_is_enabled())  // 如果协议栈被使能
    {
        ret_code_t ret_code = sd_app_evt_wait();  //调用协议栈等待函数
        ASSERT((ret_code == NRF_SUCCESS) || (ret_code == NRF_ERROR_SOFTDEVICE_NOT_ENABLED));
        UNUSED_VARIABLE(ret_code);
    }
    else
#endif // SOFTDEVICE_PRESENT  // 否则,不带协议栈状态
    {
        // Wait for an event.
        __WFE();
        // Clear the internal event register.
        __SEV();
        __WFE();
    }

    PWR_MGMT_DEBUG_PIN_CLEAR();  // 清除仿真引脚
    PWR_MGMT_CPU_USAGE_MONITOR_SECTION_EXIT();  // 用户监视段退出
    PWR_MGMT_SLEEP_LOCK_RELEASE();  // 锁定临界区释放
}

1.4 软件进入低功耗需要调用的函数

static void idle_state_handle(void)
{
    if(NRF_LOG_PROCESS() == false)  // 如果调试缓冲区没有更多日志
    {
        nrf_pwr_mgmt_run();
    }
}
int main(void)
{
    ···
    ···
    for(;;)
    {
        idle_state_handle();
    }
}

2. 硬件部分优化

不同的内部稳压器选择,会造成不同的电路消耗。可以通过选择不同的硬件电路配置,来选取下面两种内部稳压器:

  • 内部 LDO 稳压器
  • 内部 DC/DC 稳压器

LDO 是系统默认的稳压器,而 DC/DC 稳压器可用作 LDO 稳压器的替代产品。与使用 LDO 稳压器相比,使用 DC/DC 稳压器具有更低的电流消耗,但 DC/DC 稳压器需要连接外部 LC 滤波器:

Android STR待机 低功耗 手机低功耗模式_Android STR待机 低功耗_04


其中关于 DC/DC 稳压器所连接的 外部 LC 滤波器电路上的电感和电容参数,请参看芯片手册 53 节 Reference circuitry 所提供的参考电路。

Android STR待机 低功耗 手机低功耗模式_初始化_05


Android STR待机 低功耗 手机低功耗模式_低功耗_06

Android STR待机 低功耗 手机低功耗模式_初始化_07

Android STR待机 低功耗 手机低功耗模式_低功耗_08

由于默认选择的是内部 LDO 稳压器,因此如果需要切换到使用内部 DC/DC 稳压器,还需要在软件上进行配置。
首先需要在主函数 main.c 中,初始化 softDevice 协议栈前,执行 NRF_POWER->DCDCEN=1
或者在初始化softDevice 协议栈后,执行 sd_power_dcdc_mode_set(1)

sdk_config.h 配置文件中勾选 NRFX_POWER_ENABLED 使能选项,同时把选项下的 DC/DC 使能选项 NRFX_POWER_CONFIG_DEFAULT_DCDCEN 进行勾选。

Android STR待机 低功耗 手机低功耗模式_Android STR待机 低功耗_09


在选取电源电压为 3.0 V ,广播间隔为 500ms,发射功率为 0dbm 的情况下,选择 DC/DC 稳压方式的总平均功耗电流为 20uA,而选择 LDO 稳压方式的总平均电流在 32uA 左右。因此,选择 DC/DC 稳压方式可以大幅度的降低功耗。

Android STR待机 低功耗 手机低功耗模式_协议栈_10

电池寿命预估

已知设备的平均电流消耗和电池的容量,计算电池寿命。以下是不同能量容量电池的电池寿命计算实例:

例1:
设备的平均电流消耗 :20uA
电池容量:220mAh (标准CR2032纽扣电池 )
电池寿命:0.22Ah/0.00002A=11000hours=458days

例2:
设备的平均电流消耗 :100uA
电池容量:220mAh (标准CR2032纽扣电池 )
电池寿命:0.22Ah/0.0001A=2200hours=91days

例3:
设备的平均电流消耗 :100uA
电池容量:1000mAh (标准CR2477纽扣电池 )
电池寿命:1Ah/0.0001A=10000hours=417days