LowPower: Standby Mode 0 P56 Key Wakeup¶
1 功能概述¶
本例程演示如何使 SoC 进入 Standby Mode 0 状态,然后通过 P56 按键将其唤醒。
2 环境准备¶
硬件设备与线材:
PAN1080 EVB 核心板与底板各一块
JLink 仿真器(用于烧录例程程序)
电流计(本文使用电流可视化测量设备 PPK2 [Nordic Power Profiler Kit II] 进行演示)
USB-TypeC 线一条(用于底板供电和查看串口打印 Log)
杜邦线数根(用于连接各个硬件设备)
硬件接线:
为确保能够准确地测量 SoC 本身的功耗,排除底板外围电路的影响,请勿将 EVB 核心板插到 EVB 底板上
使用 USB-TypeC 线,将 PC USB 插口与 EVB 底板 USB->UART 插口相连
使用杜邦线将(根据核心板芯片的型号不同,以下两种接法二选一):
EVB 底板上的 TX0 引脚与核心板上的 P00 引脚相连, EVB 底板上的 RX0 引脚与核心板上的 P01 引脚相连(若核心板芯片为 QFN32 或 LQFP64 封装)
EVB 底板上的 TX0 引脚与核心板上的 P30 引脚相连, EVB 底板上的 RX0 引脚与核心板上的 P31 引脚相连(若核心板芯片为 QFN48 封装)
使用杜邦线将 JLink 仿真器的:
SWD_CLK 引脚与 EVB 核心板的 P46 引脚相连
SWD_DAT 引脚与 EVB 核心板的 P47 引脚相连
SWD_GND 引脚与 EVB 核心板的 GND 引脚相连
使用杜邦线将 EVB 底板上的:
P56 引脚(对应按键 WKUP)与 EVB 核心板的 P56 引脚相连
将 PPK2 硬件的:
USB DATA/POWER 接口连接至 PC USB 接口
VOUT 引脚连接至 EVB 核心板的 VBAT 引脚
GND 引脚连接至 EVB 核心板的 GND 引脚
PC 软件:
串口调试助手(UartAssist)或终端工具(SecureCRT),波特率 921600(用于接收串口打印 Log)
nRF Connect Desktop(用于配合 PPK2 测量 SoC 电流)
3 编译和烧录¶
例程位置:zephyr\samples_panchip\low_power\standby_m0_p56_key_wakeup
使用 ZAL 工具擦除 Flash 程序。关于 ZAL 工具的详细介绍请参考:Zephyr APP Launcher 工具介绍。
4 例程演示说明¶
PC 上打开 PPK2 Power Profiler 软件,供电电压选择 3300 mV,然后打开供电开关:
测试芯片中目前还没有程序,所以看到此时芯片耗电保持在 3mA 左右。
使用 ZAL 工具将编译后的例程烧录至芯片
烧录成功后,最好断开 JLink 与芯片的连接以防止芯片 P46 和 P47 两个引脚有漏电情况发生
从串口工具中看到如下的打印信息:
Try to load HW calibration data.. DONE. - Chip Type : 0x80 - Chip CP Version : None - Chip FT Version : 5 - Chip MAC Address : D0000C0293CA - Chip Flash UID : 31373237300A29494330FFFFFFFFFFFF - Chip Flash Size : 1024 KB *** Booting Zephyr OS build zephyr-v2.7.0-1346-g181eea9cf7ca *** Reset Reason: NVIC System Reset. Busy wait 100ms to keep SoC in active mode.. Try to enter SoC sleep/deepsleep mode for 1000ms.. Waked up from SoC sleep/deepsleep mode. Try to enter SoC standby mode 0..
由上述 Log 可得知以下信息:
本次芯片 Reset 原因为 NVIC System Reset,这是因为本次芯片是程序烧录后由 JLink 触发的软件 Reset
芯片在 Active 状态下等待 100ms 后,进入 1s 的 DeepSleep 状态,唤醒后再进入 Stnadby Mode 0 状态
此时观察芯片电流波形,发现稳定在 350nA 左右(说明芯片成功进入了 Standby Mode 0 模式):
按下 EVB 底板上的 WKUP (P56) 按键,由串口打印信息可知触发了芯片唤醒:
Try to load HW calibration data.. DONE. - Chip Type : 0x80 - Chip CP Version : None - Chip FT Version : 5 - Chip MAC Address : D0000C0293CA - Chip Flash UID : 31373237300A29494330FFFFFFFFFFFF - Chip Flash Size : 1024 KB *** Booting Zephyr OS build zephyr-v2.7.0-1346-g181eea9cf7ca *** Reset Reason: Standby Mode 0 P56 Wakeup. Busy wait 100ms to keep SoC in active mode.. Try to enter SoC sleep/deepsleep mode for 1000ms.. Waked up from SoC sleep/deepsleep mode. Try to enter SoC standby mode 0..
由上述 Log 可得知以下信息:
本次芯片 Reset 原因为 Standby Mode 0 P56 Wakeup
芯片在 Active 状态下等待 100ms 后,进入 1s 的 DeepSleep 状态,唤醒后再进入 Stnadby Mode 0 状态
此时再观察芯片电流波形,可以看到芯片触发了 3 次唤醒,最后又进入 DeepSleep 状态等待下次按键唤醒:
由上述电流波形可得知以下信息:
芯片在
t1
时刻从上次的 Standby Mode 0 模式下唤醒,系统 Reset 并执行初始化流程在
t1 ~ t2
之间,芯片处于 100ms 的 Active 状态下全速运行,此时间内平均电流约为 4.5mA此阶段对应程序中
k_busy_wait(100000)
及其之前的初始化程序
芯片在
t2
时刻进入 Sleep 模式此时芯片没有立刻进入 DeepSleep 模式,这是因为系统默认在 Boot 阶段的前 500ms 阻止了芯片进入 DeepSleep 模式,而是令其进入 Sleep 模式;此特性是为了方便 SWD 烧录和调试而默认开启的,因为芯片一旦进入 DeepSleep 模式,SWD 接口将会无法正常通信,我们可以通过更改配置
CONFIG_PM_BOOT_DEEPSLEEP_DELAY_MS
来修改此时间或关闭此功能
在
t2 ~ t3
之间,芯片处于约 260ms 的 Sleep 状态,此时间内平均电流约为 2.8mA此阶段对应程序中
k_msleep(1000)
的前 260ms这是因为当系统处于 Active 或 Sleep 状态时,每隔 260ms(即 24 位 Systick 定时器溢出时间 timeout = 0xFFFFFF / 64M * 1000 = 262ms)左右会触发一次 OS 调度中断
芯片在
t3
时刻从 Sleep 模式下唤醒,执行 OS 调度中断程序后重新进入 Sleep 状态此时芯片依旧没有进入 DeepSleep 模式,这是因为此时刻仍在系统 Boot 阶段的前 500ms 内,因此继续阻止了芯片进入 DeepSleep 模式,而是令其再次进入了 Sleep 模式
在
t3 ~ t4
之间,芯片继续处于约 260ms 的 Sleep 状态,此时间内平均电流约为 2.8mA此阶段对应程序中
k_msleep(1000)
的中间 260ms这仍然是因为当系统处于 Sleep 状态时,需要再等待 260ms 的 OS 调度中断将芯片唤醒
芯片在
t4
时刻再次从 Sleep 模式下唤醒,执行 OS 调度中断程序后转而进入 DeepSleep 状态此时系统检测到当前时刻已经不在 Boot 阶段的前 500ms 内,因此本次允许芯片进入 DeepSleep 模式
在
t4 ~ t5
之间,芯片继续处于约 480ms 的 DeepSleep 状态,此时间内平均电流约为 6uA此阶段对应程序中
k_msleep(1000)
的后 480ms
芯片在
t5
从 DeepSleep 模式下唤醒,接着进入 Standby Mode 0 状态,此时平均电流约为 300nA此阶段对应程序中
soc_enter_standby_mode_0()
语句
5 开发者说明¶
5.1 App Config 配置¶
本例程的 App Config(对应 prj.conf 文件)配置如下:
# Low Power
CONFIG_PM=y
CONFIG_BT_CTLR_SLEEP_CLOCK_SOURCE=1
# Enable GPIO Input Sentinel
CONFIG_PM_GPIO_INPUT_SENTINEL=y
# CONFIG_PM_GPIO_INPUT_SENTINEL_INVERT_CTRL=y
# Disable Serial Uart & Log.
# CONFIG_SERIAL=n
# CONFIG_UART_INTERRUPT_DRIVEN=n
# CONFIG_CONSOLE=n
# CONFIG_UART_CONSOLE=n
# CONFIG_PRINTK=n
# Enable DC/DC
CONFIG_SOC_DCDC_PAN1080=y
其中:
CONFIG_PM=y
:使能低功耗流程CONFIG_BT_CTLR_SLEEP_CLOCK_SOURCE=1
:低功耗时钟相关配置(目前必须固定配置为1)CONFIG_PM_GPIO_INPUT_SENTINEL=y
:开启 GPIO 输入电平检测功能,开启后当系统试图进入 Standby M0 模式时,会先检查当前 P56 引脚输入电平是否与中断配置冲突,若冲突则阻止系统进入 Standby M0 模式此处 “输入电平与中断配置冲突” 的含义是:中断配置为下降沿触发而此时 P56 输入电平已经为低电平,或者中断配置为上升沿触发而此时 P56 输入电平已经为高电平; PAN1080 SoC 的低功耗模式不支持这种冲突情况,因此在配置 P56 唤醒的时候为安全起见可将此开关打开。
CONFIG_SOC_DCDC_PAN1080=y
:使能芯片的 DCDC 供电模式,以降低芯片动态功耗
5.2 App DeviceTree 配置¶
本例程的 App DeviceTree(对应 app.overlay 文件)配置如下:
&uart1 {
status = "disabled";
};
其中:
将 uart1 的
status
属性配置为disabled
以禁止系统上电后初始化 uart1 设备,确保不会因为 uart1 的引脚配置产生 IO 漏电
5.3 程序代码¶
5.3.1 主程序¶
主程序 main() 函数内容如下:
void main(void)
{
uint8_t rst_reason;
/* Get the last reset reason */
printk("\nReset Reason: ");
rst_reason = soc_reset_reason_get();
switch (rst_reason) {
case SOC_RST_REASON_PIN_RESET:
printk("nRESET Pin Reset.\n");
break;
case SOC_RST_REASON_SYS_RESET:
printk("NVIC System Reset.\n");
break;
case SOC_RST_REASON_STBM0_P56_WAKEUP:
printk("Standby Mode 0 P56 Wakeup.\n");
break;
default:
printk("Unhandled Reset Reason, refer to more reason define in soc.h!\n");
}
/* Enable P56 low level wakeup for standby mode 0 */
wakeup_p56_key_init(LP_EXT_P56_WK_EDGE_LOW);
printk("\nBusy wait 100ms to keep SoC in active mode..\n");
k_busy_wait(100000);
printk("Try to enter SoC sleep/deepsleep mode for 1000ms..\n");
k_msleep(1000);
printk("Waked up from SoC sleep/deepsleep mode.\n");
printk("Try to enter SoC standby mode 0..\n\n");
k_busy_wait(1000); /* Wait for all log print done */
soc_enter_standby_mode_0();
printk("WARNING: Failed to enter SoC standby mode 0 due to unexpected P56 IO level detected.\n");
printk(" The wakeup level of P56 is configured as low-level-wakeup, however the\n");
printk(" actual P56 level right before entering standby mode is already low. The\n");
printk(" SoC does not support this case, and the API soc_enter_standby_mode_0()\n");
printk(" just returns when this case is detected.\n");
while (1) {
/* Busy wait */
}
}
获取本次系统复位的原因,本例程中仅检测 3 种情况(更多复位情况请参考 soc.h 文件中的相关定义):
芯片 nRESET 引脚复位(nRESET 按键按下)
NVIC System Reset 软件复位(JLink 烧录后自动触发,或者软件调用
sys_reboot(0)
触发)Standby Mode 0 P56 Wakeup 唤醒复位
在
wakeup_p56_key_init()
函数中初始化 P56 配置执行
k_busy_wait()
以等待 100us,此时间内芯片将保持 Active 状态(对应上述电流波形的t1 ~ t2
之间)执行
k_msleep()
以休眠 1s,此时间内芯片将根据当前状态自动选择进入 Sleep/DeepSleep 状态(对应上述电流波形的t2 ~ t5
之间)执行
soc_enter_standby_mode_0()
以使芯片进入 Standby Mode 0 模式正常情况下,此函数不会返回,而是直接等待唤醒后触发芯片复位,程序重新开始执行
但是,当开启
CONFIG_PM_GPIO_INPUT_SENTINEL
后,若在进入 Standby Mode 0 的时候检测到 P56 电平不符合预期,则此函数将会直接返回
当发现
soc_enter_standby_mode_0()
函数返回,打印 warning log 以提示用户当前未成功进入 Standby M0 模式
5.3.2 P56 初始化程序¶
GPIO 初始化程序 wakeup_p56_key_init() 函数内容如下:
void wakeup_p56_key_init(uint8_t wakeup_edge)
{
/* Configure P56 wakeup level due to wakeup_edge */
ANA->LP_FL_CTRL = (ANA->LP_FL_CTRL & ~BIT1) | (wakeup_edge << 1);
/* Set pinmux func of P56 as GPIO */
SYS_SET_MFP(P5, 6, GPIO);
/* Set P56 to input mode */
GPIO_SetMode(P5, BIT6, GPIO_MODE_INPUT);
if (wakeup_edge == LP_EXT_P56_WK_EDGE_LOW) {
/* Enable internal pull-up resistor if P56 is low level wakeup */
GPIO_EnablePullupPath(P5, BIT6);
} else {
/* Enable internal pull-down resistor if P56 is high level wakeup */
GPIO_EnablePulldownPath(P5, BIT6);
}
/* Necessary for P56 to do manual 3v sync */
CLK_Wait3vSyncReady();
/* Wait for a while to ensure the internal pullup is stable before entering low power mode */
SYS_delay_10nop(0x10000);
}
配置 P56 引脚的唤醒电平
使用 Panchip HAL GPIO Driver 对 P56 进行配置:
将 P56 引脚配置为 GPIO 功能
将 P56 IO 配置为数字输入模式
根据唤醒电平决定使能内部上拉或下拉电阻
等待一段时间,确保前面的上拉电阻状态稳定
5.3.3 与低功耗相关的 Hook 函数¶
目前有 3 个与低功耗密切相关的 Hook 函数,本例程重写了其中的 2 个:
__ramfunc void z_power_hw_deep_sleep_enter_hook(void)
{
#ifdef CONFIG_SERIAL
/* Wait until all UART0 data sending done before entering deepsleep mode */
while (!(UART_GetLineStatus(UART0) & UART_LINE_TXSR_EMPTY)) {
/* Busy wait */
}
/*
* Reset UART PINs to GPIO function and disable digital input path of UART Rx PIN
* to avoid possible current leakage.
*/
SYS_SET_MFP(P0, 0, GPIO);
SYS_SET_MFP(P0, 1, GPIO);
GPIO_DisableDigitalPath(P0, BIT1);
#endif
}
__ramfunc void z_power_hw_deep_sleep_exit_hook(void)
{
#ifdef CONFIG_SERIAL
/* Resume UART PIN Configurations to reenable UART function */
SYS_SET_MFP(P0, 0, UART0_TX);
SYS_SET_MFP(P0, 1, UART0_RX);
GPIO_EnableDigitalPath(P0, BIT1);
#endif
}
当程序执行到 idle 线程中低功耗流程的 DeepSleep 子流程中后,会在 SoC 进入 DeepSleep 模式之前执行
z_power_hw_deep_sleep_enter_hook()
函数,在 SoC 从 DeepSleep 模式下唤醒后执行z_power_hw_deep_sleep_exit_hook()
函数。本例程在
z_power_hw_deep_sleep_enter_hook()
函数中,为防止 UART IO 漏电,编写了相关代码以确保在进入 DeepSleep 模式前:串口 Log 数据都打印完毕(即 UART0 Tx FIFO 应为空)
P00 引脚 Pinmux 功能由 UART0 Tx 切换回 GPIO
P01 引脚 Pinmux 功能由 UART0 Rx 切换回 GPIO,并将其数字输入功能关闭
本例程在
z_power_hw_deep_sleep_exit_hook()
函数中,编写了相关代码以恢复串口 Log 打印功能:P00 引脚 Pinmux 功能由 GPIO 重新切换成 UART0 Tx
P01 引脚 Pinmux 功能由 GPIO 重新切换成 UART0 Rx,并将其数字输入功能重新打开
6 RAM/Flash资源使用情况¶
Memory region Used Size Region Size %age Used
FLASH: 19836 B 384 KB 5.04%
SRAM: 6992 B 64 KB 10.67%