1 概述
co_power_sleep_enable函数控制整个睡眠系统是否使能,参数设为true表示使能,否则表示禁止。 睡眠模式的设置主要涉及这些函数:pmu_32k_enable_in_deep_sleep,co_power_ultra_sleep_mode_enable,co_power_hib_sleep_mode_enable。 睡眠模式分为四种:浅睡、深睡、超深睡、冬眠。它们的唤醒源不同,唤醒后代码的运行位置也不同,对比如下: 模式 | 唤醒源 | 代码运行位置 | 浅睡 | 所有GPIO,RTC,Timer,射频 | 从唤醒处运行 | 深睡 | 所有GPIO,RTC | 从唤醒处运行 | 超深睡 | 所有GPIO,RTC | 从main函数开始运行 | 冬眠 | GPIO 0-12,PMU Timer | 从main函数开始运行 |
在co_power.h文件中,可以看到只有两种睡眠状态:POWER_SLEEP和POWER_DEEP_SLEEP。前面说的浅睡就是POWER_SLEEP,深睡、超深睡、冬眠都是POWER_DEEP_SLEEP的一种。 typedef enum { /// All modules are alive, and CPU run with full speed POWER_ACTIVE, /// All modules are alive, but CPU clock is gating POWER_IDLE, /// Power down most of module(CPU, Peripheral, etc), /// but 32K is alive, only gpio and sleep-timer can wake up chip POWER_SLEEP, /// Power down most of module(CPU, Peripheral, etc), /// 32K is not alive, only gpio can wake up chip POWER_DEEP_SLEEP, /// Power down all module, /// only the power pin can wake up chip POWER_SHUTDOWN, } co_power_status_t; |
如果在POWER_SLEEP_ENTRY睡眠事件中加一些日志,如图所示,发现只有浅睡的时候打印“#”,其它睡眠模式都打印“&”。 注意: 此处加日志输出,是为了让睡眠模式的描述更直观,平时调试也可以作为参考,但正常的应用程序中不要加。 2 睡眠模式 前面说到系统有4中睡眠模式,任何一种睡眠模式,进入休眠后CPU、外设都会被关闭,所有外设的中断都无法响应。要使用外设,首先得唤醒系统,然后阻止系统休眠(pmu_lowpower_prevent(PMU_LP_USER);)。比如系统处于休眠,此时想通过UART接收数据,那必须先唤醒,再阻止休眠,然后才能正常接收数据。在需要休眠的时候调用pmu_lowpower_allow(PMU_LP_USER);。 此章节对这些睡眠模式进行详细描述,所有测试使用的硬件是HS6621CF-C EVB开发板,软件是SVN2969,测试的例程是ble_app_simple_server。 使能了睡眠系统,任何情况下,只要开启了定时器,或者开启了射频(广播、扫描),系统就处于浅睡模式。在定时器超时期间,广播、扫描的间隔期间,都会进入浅睡。浅睡时的平均电流是5uA,常称为“底电流”。 以2秒周期性软件定时器为例,每次定时器超时都会唤醒系统,执行完回调后立即进入浅睡,如图2.1-1日志所示每隔2秒会打印一次“#”。 图2.1-1 仅开启定时器的日志 图2.1-2 仅开启定时器的电流波形 以1秒间隔的广播为例,每次广播后进入浅睡,下一秒射频唤醒系统又发一次广播,如图2.1-3日志所示每隔1秒会打印一次“#”。 图2.1-3 仅开启广播的日志 图2.1-4 仅开启广播的电流波形 深睡模式是常用的模式,因为此时可以快速唤醒系统,功耗也比较低。 软件上应按下面代码配置。 static void ble_stack_config(void) { // Disable WDT wdt_enable(0); // Enable DCDC pmu_dcdc_enable(true); // Power down xtal32k in deep sleep mode pmu_32k_enable_in_deep_sleep(false); // Select 32k clock for stack //pmu_xtal32k_change_param(15, 1); pmu_select_32k(PMU_32K_SEL_RC); // xtal32m param pmu_xtal32m_change_param(15); // ultra sleep mode enable co_power_ultra_sleep_mode_enable(false); // Enable sleep, SWD will be closed. co_power_sleep_enable(true); } int main(void) { ble_stack_config(); hardware_init(); rwip_init(RESET_NO_ERROR); co_power_register_sleep_event(power_sleep_event_handler); log_debug("running %d\n", pmu_reboot_reason()); // dbg_mmi_enable(); appm_init(); // co_timer_set(&simple_timer, 100, TIMER_REPEAT, simple_timer_handler, NULL); while(1) { rwip_schedule(); } } |
上电后,系统会进入深睡模式,按下按键会唤醒系统,唤醒后直接运行唤醒回调里的代码,松开按键时触发GPIO中断然后又进入深睡,如图2.2-1日志所示。 图2.2-1 深睡模式运行日志 如果深睡时禁用32K时钟(pmu_32k_enable_in_deep_sleep(false);),此时平均电流是3uA,如图2.2-2所示。 图2.2-2 禁用32K的深睡电流波形 如果深睡时使能32K时钟(pmu_32k_enable_in_deep_sleep(true);),此时平均电流是4.7uA,如图2.2-3所示。 图2.2-3 使能32K的深睡电流波形 co_power_ultra_sleep_mode_enable函数控制超深睡是否使能。超深睡模式下,可以设置任意GPIO唤醒,也可以设置RTC唤醒。 static void ble_stack_config(void) { // Disable WDT wdt_enable(0); // Enable DCDC pmu_dcdc_enable(true); // Power down xtal32k in deep sleep mode pmu_32k_enable_in_deep_sleep(false); // Select 32k clock for stack //pmu_xtal32k_change_param(15, 1); pmu_select_32k(PMU_32K_SEL_RC); // xtal32m param pmu_xtal32m_change_param(15); // ultra sleep mode enable co_power_ultra_sleep_mode_enable(true); // Enable sleep, SWD will be closed. co_power_sleep_enable(true); } int main(void) { ble_stack_config(); hardware_init(); rwip_init(RESET_NO_ERROR); co_power_register_sleep_event(power_sleep_event_handler); log_debug("running %d\n", pmu_reboot_reason()); // dbg_mmi_enable(); appm_init(); // co_timer_set(&simple_timer, 100, TIMER_REPEAT, simple_timer_handler, NULL); while(1) { rwip_schedule(); } } |
上电后,系统进入休眠,此时是超深睡。按键按下后唤醒系统,代码从main函数开始运行,从日志看复位原因是1,即PMU_REBOOT_FROM_ULTRA_DEEP_SLEEP,如图2.3-1所示。 图2.3-1 超深睡模式运行日志 超深睡模式平均电流是1.6uA,如图2.3-2所示。 图2.3-2 超深睡电流波形 co_power_hib_sleep_mode_enable函数控制冬眠模式是否使能。冬眠模式下,可以设置GPIO 0 – 12来唤醒,也可以设置定时唤醒。 冬眠模式是基于超深睡模式实现的,所以必须使能超深睡模式。 static void ble_stack_config(void) { // Disable WDT wdt_enable(0); // Enable DCDC pmu_dcdc_enable(true); // Power down xtal32k in deep sleep mode pmu_32k_enable_in_deep_sleep(false); // Select 32k clock for stack //pmu_xtal32k_change_param(15, 1); pmu_select_32k(PMU_32K_SEL_RC); // xtal32m param pmu_xtal32m_change_param(15); // ultra sleep mode enable co_power_ultra_sleep_mode_enable(true); // hibernation sleep mode enable co_power_hib_sleep_mode_enable(true); REGW(&HS_PMU->ANA_PD, MASK_1REG(PMU_ANA_PD_CKOPMU32K, 1)); // Enable sleep, SWD will be closed. co_power_sleep_enable(true); } int main(void) { ble_stack_config(); hardware_init(); rwip_init(RESET_NO_ERROR); co_power_register_sleep_event(power_sleep_event_handler); log_debug("running %d\n", pmu_reboot_reason()); // dbg_mmi_enable(); appm_init(); // co_timer_set(&simple_timer, 100, TIMER_REPEAT, simple_timer_handler, NULL); while(1) { rwip_schedule(); } } |
冬眠模式平均电流是0.5 ~ 0.6uA,如图2.4-1所示。 图2.4-1 冬眠模式电流波形 冬眠模式下可以设置定时唤醒,设置函数是hib_timer_start,设置的时间单位是1/32768 S,例如参数设置为327680,就是设置定时唤醒时间为10秒。上电后设置一次即可,此时底电流是1.5uA,如图2.4-2所示。
图2.4-2 冬眠模式定时唤醒电流波形 上电后,系统进入休眠,此时是冬眠。PMU timer超时唤醒系统,系统从main函数开始运行,从日志看复位原因也是1,即PMU_REBOOT_FROM_ULTRA_DEEP_SLEEP,如图2.4-3所示。 图2.4-3 冬眠模式定时唤醒日志 3 功耗调试 产品的功耗调试不是简单的按上面说的模式设置完就结束了。即使打印发现系统已经进入POWER_DEEP_SLEEP,但是产品硬件还是可能漏电。此时,可以开启一个定时器,周期的dump一下系统信息。 static void simple_timer_handler(co_timer_t *timer, void *param) { // sysdump(); pmu_dump(printf); co_timer_dump(printf); } co_timer_set(&simple_timer, 2000, TIMER_REPEAT, simple_timer_handler, NULL); |
例如,ble_app_simple_server例程,设置GPIO2和GPIO30低电平唤醒,dump的信息如图3-1所示, 图3-1 pmu_dump信息 l prevent_status prevent_status每个bit表示一种阻止休眠的原因,具体查看pmu.h文件中的pmu_lowpower_peripheral_t类型定义。 如果发现此值不为0,表示系统没有进入休眠,找到对应的bit即可找到原因。 l wakeup_pin wakeup_pin是当前所有可唤醒系统的GPIO的pin mask值,为1的bit是可唤醒的GPIO。 sleep_level表示当前唤醒IO的状态,此状态应该和唤醒电平相反。如果设置了低电平唤醒,那sleep_level中唤醒IO对应的bit应该为1,否则为0。 cur_level必须和sleep_level相同,否则系统一致处于唤醒状态。 l pull_up和pull_down pull_up是当前所有上拉GPIO的pin mask值,后面括号内cur_level值必须和pull_up值相同,表示当前所有上拉的GPIO状态都是高电平。但凡其中有一个GPIO是低电平,系统就会有漏电。 pull_down是当前所有下拉GPIO的pin mask值,后面括号内cur_level值必须为全0,表示当前所有下拉的GPIO状态是低电平。但凡其中有一个GPIO是高电平,系统就会漏电。 l clocking 关注CPU时钟频率即可,如果想降低功耗,可以在休眠时把CPU时钟频率降低。 4 总结 无论是否使能冬眠模式、或是超深睡模式,系统上电后都可以开启定时器、射频、RTC,开启后系统就处于浅睡模式。 当所有定时器停止工作,射频也停了,RTC也关闭了,系统就会进入当前设定允许的功耗最低的睡眠模式。
|