当前文档版本为 v1.0.1,您可以访问当前页面的 开发中 版本以获取最近可能的更新。

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 例程演示说明

  1. PC 上打开 PPK2 Power Profiler 软件,供电电压选择 3300 mV,然后打开供电开关:

    image

    PPK2 使能芯片供电

    测试芯片中目前还没有程序,所以看到此时芯片耗电保持在 3mA 左右。

  2. 使用 ZAL 工具将编译后的例程烧录至芯片

    烧录成功后,最好断开 JLink 与芯片的连接以防止芯片 P46 和 P47 两个引脚有漏电情况发生

  3. 从串口工具中看到如下的打印信息:

    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 状态

  4. 此时观察芯片电流波形,发现稳定在 350nA 左右(说明芯片成功进入了 Standby Mode 0 模式):

    image

    系统初始化后进入 Standby Mode 0 模式

  5. 按下 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 状态

  6. 此时再观察芯片电流波形,可以看到芯片触发了 3 次唤醒,最后又进入 DeepSleep 状态等待下次按键唤醒:

    image

    使用 P56 WKUP 按键唤醒芯片

    由上述电流波形可得知以下信息:

    • 芯片在 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";
};

其中:

  • uart1status 属性配置为 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 */
	}
}
  1. 获取本次系统复位的原因,本例程中仅检测 3 种情况(更多复位情况请参考 soc.h 文件中的相关定义):

    • 芯片 nRESET 引脚复位(nRESET 按键按下)

    • NVIC System Reset 软件复位(JLink 烧录后自动触发,或者软件调用 sys_reboot(0) 触发)

    • Standby Mode 0 P56 Wakeup 唤醒复位

  2. wakeup_p56_key_init() 函数中初始化 P56 配置

  3. 执行 k_busy_wait() 以等待 100us,此时间内芯片将保持 Active 状态(对应上述电流波形的 t1 ~ t2 之间)

  4. 执行 k_msleep() 以休眠 1s,此时间内芯片将根据当前状态自动选择进入 Sleep/DeepSleep 状态(对应上述电流波形的 t2 ~ t5 之间)

  5. 执行 soc_enter_standby_mode_0() 以使芯片进入 Standby Mode 0 模式

    • 正常情况下,此函数不会返回,而是直接等待唤醒后触发芯片复位,程序重新开始执行

    • 但是,当开启 CONFIG_PM_GPIO_INPUT_SENTINEL 后,若在进入 Standby Mode 0 的时候检测到 P56 电平不符合预期,则此函数将会直接返回

  6. 当发现 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);
}
  1. 配置 P56 引脚的唤醒电平

  2. 使用 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
}
  1. 当程序执行到 idle 线程中低功耗流程的 DeepSleep 子流程中后,会在 SoC 进入 DeepSleep 模式之前执行 z_power_hw_deep_sleep_enter_hook() 函数,在 SoC 从 DeepSleep 模式下唤醒后执行 z_power_hw_deep_sleep_exit_hook() 函数。

  2. 本例程在 z_power_hw_deep_sleep_enter_hook() 函数中,为防止 UART IO 漏电,编写了相关代码以确保在进入 DeepSleep 模式前:

    • 串口 Log 数据都打印完毕(即 UART0 Tx FIFO 应为空)

    • P00 引脚 Pinmux 功能由 UART0 Tx 切换回 GPIO

    • P01 引脚 Pinmux 功能由 UART0 Rx 切换回 GPIO,并将其数字输入功能关闭

  3. 本例程在 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%