低功耗蓝牙应用对功耗要求越低越好,功耗越低电池续航时间就越长,用户体验就越好。当你发现你板子功耗偏高时,建议按照如下步骤进行自检:
1) 确认理论功耗值。BLE功耗跟广播间隔或者连接间隔是成正比关系的,所以20ms连接间隔下的功耗几乎是1s状态下的50倍!,单纯地问“1mA功耗高不高?”是没有意义的,必须结合特定的应用场景才有意义。不管是广播还是连接,特定的使用场景会有一个理论功耗值,大家可以访问网址: https://devzone.nordicsemi.com/power/,以获得你的使用场景下理论功耗多少,比如连接模式下,每1秒钟发20个字节的数据包,这种模式下理论功耗为:7.5uA
2) 确定板子漏电流。如果板子包含的元器件比较多,那么也有可能是其他非nRF5元器件导致的高功耗,比如传感器,codec,或者电路设计本身的问题等。为了确定高功耗是来自nRF5器件还是其他器件,根据自己的情况,有如下三个方法供你参考:一如果你的固件可以直接在Nordic官方DK上运行,那么你可以把你的固件直接下载到DK上,然后通过DK测量nRF5芯片的功耗,如果这个功耗正常,那么大电流应该是由其他非nRF5元器件引起的;如果这个功耗偏高,那么大电流的确是由nRF5芯片固件引起的,此时请参考后续操作步骤说明。二如果你的固件不能在DK上直接运行,那么可以让nRF5芯片进入深度睡眠模式(System OFF模式),此时nRF5芯片功耗只有零点几微安,nRF5芯片所有IO口将处于floating状态,此时再测量板子电流。如果板子电流恢复正常,那么大电流应该是由nRF5芯片固件引起的;如果板子电流还是不正常,那么大电流应该由其他非nRF5元器件引起的。关于如何进入深度睡眠模式,你可以参考工程:SDK安装目录\examples\peripheral\ram_retention\pca10040\blank\arm5_no_packs,或者参考ble_app_hrs工程中函数:sleep_mode_enter。三如果你的板子太复杂,无法按照上面两种方法来确定漏电流,那么只能将板子其他非必需元器件焊下来,只留下一个nRF5最小工作系统,然后再测量此时的板子电流是否正常。
3) 确定板子已经退出J-Link模式。如果板子一直是电池供电,那么在某些情况下,即使程序下载完成而且运行正常,此时板子有可能还处在J-Link模式。J-Link模式下板子会有2mA左右的额外电流。要退出J-Link模式,有2种方式,一是给板子进行上电复位,二是通过nrfjprog发出—reset指令(nRF52系列)或者—pinreset指令(nRF51系列),两种方式都能让板子退出J-Link模式,从而进入应用模式。
4) 如果最终确认大电流的确是由nRF5芯片引起的,那么几乎可以肯定系统在进入idle模式(System ON模式)之前,没有关掉不需要的模块。模块没有关掉,它就一直在耗电,从而导致功耗过大。Idle模式下,如下模块会耗费比较多的电流,若允许建议全部关掉。
- Idle模式。先说明一下什么是idle模式,所谓Idle模式,Nordic芯片手册也称为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功能又可以实现低功耗。有softdevice时进入idle模式的函数是:
sd_app_evt_wait
无softdevice时进入idle模式的代码是:
__WFE(); // Clear the internal event register. __SEV(); __WFE();
这里我们顺便把进入sleep模式的函数也贴出来,供大家对比参考。有softdevice时进入sleep模式的函数是:
sd_power_system_off
无softdevice时进入sleep模式的代码是:
// Enter System OFF and wait for wake up from GPIO detect signal. NRF_POWER->SYSTEMOFF = 0x1;
不管softdevice有没有使能,idle模式下的电流都很低,只有2uA左右
- UART/UARTE。由于UART需要实时检测RX线上有没有下降沿,所以一旦UART初始化成功,高频时钟将一直处于打开状态,从而导致UART模块消耗的电流比较大,虽然UART模块本身只需要55uA的工作电流,但是自动打开的高频时钟电路需要消耗250uA左右电流。如果使能了UARTE的easyDMA,那么DMA还需要消耗额外的2mA电流。这样UARTE工作总共需要消耗:55uA + 250uA + 2mA。因此在进入idle模式之前,强烈建议将UART关掉,以节省系统功耗。注:为了达到低功耗和实时性双重目的,在设计UART通信的时候,我们经常会额外再加2个GPIO口用来通知对方UART要传送数据了。关闭uart的API为:nrf_drv_uart_uninit或者app_uart_close。
- CLI/UART。如果你使用了CLI/UART模块,请使用cli模块自带的uninit函数去关闭本模块。当cli模块和RTOS结合一起使用的时候,经常发现cli模块关闭不彻底,从而导致idle模式下功耗还是很高(比如450uA左右),此时有可能需要多次调用nrf_cli_uninit这个函数,从而确保cli/uart模块真正被关闭了。
- GPIOTE。GPIOTE中断有两种工作模式:高精度模式(hi_accuracy为true)和低精度模式(hi_accuracy为false)。hi_accuracy为true将使能IN event中断;hi_accuracy为false将使能Port event中断。IN event中断功耗比Port event中断功耗高很多,因此,如果是检测一般的沿或者IO口电平,那么建议使用低精度模式,即使用如下初始化语句:
GPIOTE_CONFIG_IN_SENSE_TOGGLE(false) //低功耗低精度IO口中断模式
- DMA。Nordic大部分外设都自带DMA功能,如果DMA可以关闭的话(有些设备DMA是不能关闭的),用完DMA之后,记得把DMA关掉,否则会有2mA左右的功耗。使用ADC的时候尤其要注意这点。
- FPU。每当程序要执行浮点数运算的时候,Cortex M4F会自动把FPU打开,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);
- Timer0/1/2/3/4。Timer的工作电流大概为70uA,对低功耗应用来说,已经非常大了。如果你的定时精度要求不高,而且是毫秒的倍数,那么强烈建议你使用app_timer来实现定时功能,app_timer的功耗只有0.2uA左右。
- SPI/TWI/ADC等。在进入idle模式之前,建议把SPI/TWI/ADC等模块也uninit。大家可能会担心反复init和uninit同一个模块会不会有问题?这个不用担心,目前还没看到任何副作用。
5) 还有一种电流异常情况:大部分芯片功耗是正常的,只有少部分芯片功耗是异常的。这种情况一般都跟IO口状态有关,如果碰到这种情况,建议对芯片每个IO口进行重新初始化,或许问题就解决了。这里面又分两种情况:
- 已使用IO口。不管nRF51还是nRF52,尤其这些IO口被用作为其他外设比如IIC/SPI等,哪怕IO口之前已经是确定状态,在进入idle模式之前,建议对其再次进行初始化,或许问题就解决了。
- 未使用IO口。这个问题好像只有nRF51802才有而且跟板子也有关系,在进入sleep模式或者idle模式之前,对未使用的IO口进行非floating初始化。(其他芯片好像没有发现这个问题)