【蓝牙 Beacon】PHY6252 Beacon Mesh SDK 低功耗相关
1. DeepSleep
1.1 6252 deep sleep 的限制:
- 只能 IO 唤醒,不能定时唤醒
- IO 不能保持(这个硬件电路设计需要小心,否则会导致多于的功耗浪费)
- 唤醒后重头跑(重新开始一切,相当于再次上电)
1.2 6252 deep sleep 的代码实施:
在 phy_plus_phy.c 中增加:
Code: Select all
UINT8_T enter_sleep = 0; extern UINT8_T cooling; uint16 PhyPlusPhy_ProcessEvent(uint8 task_id, uint16 events) { if(enter_sleep) return 0; if (events & PPP_5MS){ ...
在业务层,需要进入 deepsleep 的地方调用(需要增加头文件#include "pwrmgr.h"):
Code: Select all
pwroff_cfg_t cfg = { .pin = P15, .type = POL_FALLING, .on_time = 3000 }; hal_pwrmgr_poweroff(&cfg,1); sg_enter_sleep = 1;
经过上述两步,可以实现 beacon mesh 常供电 SDK 的 deep sleep,此时通过 IO15 可以唤醒设备。
下图是 IO15 唤醒后工作 10S 的功耗:
2. 普通 LowPower
2.1 6252 普通 LowPower 的限制:
- 基于 os,没有其他任务都挺的情况下,会自动进入休眠模式
- 10uA 左右低功耗
- 唤醒后不重新跑
- 支持 IO 唤醒和定时唤醒
2.2 6252 LowPower 的代码实施:
phy6252_adv/sdk/phy6252_adv_sdk/app/main.c
注释掉 hal_init 中的 4 行代码:(这4行代码一开,就会影响低功耗逻辑)
Code: Select all
static void hal_init(void) { hal_low_power_io_init(); clk_init(g_system_clk); //system init hal_rtc_clock_config((CLK32K_e)g_clk32K_config); hal_pwrmgr_init(); // hal_pwrmgr_register(MOD_USR2, ty_sleep_handler, ty_wakeup_handler); <--- // if(!hal_pwrmgr_is_lock(MOD_USR2)) { <--- // hal_pwrmgr_lock(MOD_USR2); <--- // } <---
phy6252_adv/sdk/phy6252_adv_sdk/app/source/phy_plus_phy.c
1)在 app_main 中的 osal_pwrmgr_device 的参数从 PWRMGR_ALWAYS_ON 改为 PWRMGR_BATTERY (这个是低功耗的开关)
2)在 PhyPlusPhy_ProcessEvent 中增加 5 行代码:(因为进入低功耗模式需要将所有任务关闭,并且只监听周期性唤醒的定时器,这些逻辑都放在 app_low_power_processEvent 中实现)Code: Select all
uint16 PhyPlusPhy_ProcessEvent(uint8 task_id, uint16 events) { extern int app_low_power_processEvent(uint8 task_id, uint16 events); //<---- int ret = app_low_power_processEvent(task_id,events); //<---- if(ret != 1){ //<---- return ret; //<---- } //<---- if (events & PPP_5MS){
3)在 app_main 后面增加低功耗实现的基础代码:
Code: Select all
int app_main(void){ osal_init_system();//Initialize the operating system osal_pwrmgr_device(PWRMGR_BATTERY);//PWRMGR_ALWAYS_ON osal_start_system(); //Start OSAL, No Return from here return 0; } ///////////////////////////////////////////////////////////////////// // LOW POWR ///////////////////////////////////////////////////////////////////// typedef VOID_T (*TIMEOUT_CB)(VOID_T *args); static uint8_t low_power_flag = 0; static TIMEOUT_CB sg_timeout_cb = NULL; extern void hal_clock_time_run(void); //每次调用,就会进入低功耗 int app_enter_low_power(uint32_t sleepms,void (*timeout_cb)(void *)){ PR_DEBUG("app_enter_low_power\n"); low_power_flag = 1; s_phy.Status=PHYPLUS_RFPHY_IDLE; //wake up by timeout if(sleepms != 0 && timeout_cb != NULL){ sg_timeout_cb = timeout_cb; osal_start_timerEx(PhyPlusPhy_TaskID, PPP_SLEEP_WAKE_UP, sleepms); } } //失能低功耗 int app_disable_low_power(void){ PR_DEBUG("app_disable_low_power\n"); low_power_flag = 0; sg_timeout_cb = NULL; hal_clock_time_run(); osal_stop_timerEx(PhyPlusPhy_TaskID,PPP_SLEEP_WAKE_UP); } int app_get_low_power_state(void){ return low_power_flag; } // 如果判断是低功耗模式,关闭其他定时任务 int app_low_power_processEvent(uint8 task_id, uint16 events){ if(app_get_low_power_state() && task_id == PhyPlusPhy_TaskID){ if(events & PPP_PERIODIC_TX_EVT) osal_stop_timerEx(PhyPlusPhy_TaskID,PPP_PERIODIC_TX_EVT); if(events & PPP_PERIODIC_RX_EVT) osal_stop_timerEx(PhyPlusPhy_TaskID,PPP_PERIODIC_RX_EVT); if(events & PPP_TX_DONE_EVT) osal_stop_timerEx(PhyPlusPhy_TaskID,PPP_TX_DONE_EVT); if(events & PPP_RX_DONE_EVT) osal_stop_timerEx(PhyPlusPhy_TaskID,PPP_RX_DONE_EVT); if(events & PPP_PERIODIC_TX_EVT) osal_stop_timerEx(PPP_RX_DATA_PROCESS_EVT,PPP_RX_DATA_PROCESS_EVT); if(events & PPP_TY_BEACON_EVT) osal_stop_timerEx(PhyPlusPhy_TaskID,PPP_TY_BEACON_EVT); if(events & PPP_TY_AUTHORIZE_EVT) osal_stop_timerEx(PhyPlusPhy_TaskID,PPP_TY_AUTHORIZE_EVT); if(events & PPP_RTC_TEST_EVT) osal_stop_timerEx(PhyPlusPhy_TaskID,PPP_RTC_TEST_EVT); if(events & PPP_5MS) osal_stop_timerEx(PhyPlusPhy_TaskID,PPP_5MS); //PR_DEBUG("%d %d\n",task_id,events); //SLEEP_WAKE_UP{ if(events & PPP_SLEEP_WAKE_UP) { if(sg_timeout_cb != NULL){ sg_timeout_cb(NULL); } } return 0; } //PR_DEBUG("%d %d\n",task_id,events); return 1; }
phy6252_adv/sdk/phy6252_adv_sdk/app/source/phy_plus_phy.h
定义一个定时唤醒任务 ID:
Code: Select all
// PHY PLUS PHY Task Events ... #define PPP_5MS 0x0400 #define PPP_SLEEP_WAKE_UP 0x0800 // <----
phy6252_adv/sdk/phy6252_adv_sdk/components/driver/pwrmgr/pwrmgr.c
将 osal_idle_task 修改为:(当处于低功耗模式时才调用 osal_pwrmgr_powerconserve0 ,同时在低功耗时不运行 tuyaos 上层业务 loop)
Code: Select all
__ATTR_SECTION_SRAM__ void osal_idle_task (void) { AP_WDT_FEED; extern int app_get_low_power_state(void); if(app_get_low_power_state()){ osal_pwrmgr_powerconserve0(); }else{ void hal_clock_time_run(void); hal_clock_time_run(); extern void tal_framework_loop(void); tal_framework_loop(); } }
apps/tuyaos_demo_beaconmesh_peripheral_phy6252/src/main.c
上层应用 DEMO, IO 15上升沿唤醒+定时 10S 唤醒,唤醒后上报一个 DP, 1S 后休眠
tal_framework_loop 中增加的 app_low_power_run 别忘了!!!Code: Select all
VOID_T tal_framework_loop(VOID_T){ ty_beacon2_node_run();//beacon mesh 节点主逻辑维护进程 app_led_run();//led 闪烁维护进程 app_led_reset_run(&beacon_dev);//三次快速上电重置逻辑维护进程 extern void app_low_power_run(void); app_low_power_run(); } // LOW POWRE #include "tkl_gpio.h" #include "ty_stimer_event.h" extern uint8_t tkl_beacon_send_flag; extern int app_enter_low_power(uint32_t sleepms,void (*timeout_cb)(void *)); extern int app_disable_low_power(void); extern void app_low_power_timeout_wake_up(void *args); void app_dp_upload(u8 dpid, u8 dpty, u8 dplen, u8 *dpvalue){ uint8_t payload[16]; memset(payload,0,16); payload[0] = dpid;//dp id = 1 payload[1] = (dpty<<4) | dplen;//dp kind = 1; dp len = 1 (各占4bits) memcpy(&payload[2],dpvalue,dplen);//dp value frame_send(0x08, 0x8000, 0x0A, payload, beacon_dev.beaconkey, 3);//heart upload } int app_low_power_1s_cb(void){ app_enter_low_power(8000,app_low_power_timeout_wake_up); return -1; } void app_low_power_io_wake_up(void *args){ PR_DEBUG("low power io wake up\n"); app_disable_low_power(); uint8_t onoff = 1; app_dp_upload(1,1,1,&onoff); ty_stimer_event_stop(TY_STIMER_TIMER_USER); ty_stimer_event_start(TY_STIMER_TIMER_USER, app_low_power_1s_cb, 1000000); } void app_low_power_timeout_wake_up(void *args){ PR_DEBUG("low power timeout wake up\n"); app_disable_low_power(); uint8_t onoff = 0; app_dp_upload(1,1,1,&onoff); ty_stimer_event_stop(TY_STIMER_TIMER_USER); ty_stimer_event_start(TY_STIMER_TIMER_USER, app_low_power_1s_cb, 1000000); } void app_low_power_run(void){ static int cnt = 0; //AFTER ABOUT 8S -> INIT & ENTER TO LOW POWER if(cnt == 400000){ //set wake up by io (once) TUYA_GPIO_IRQ_T mgpio_irq = { .mode = TUYA_GPIO_IRQ_RISE, .cb = app_low_power_io_wake_up, .arg = NULL, }; tkl_gpio_irq_init(TUYA_GPIO_NUM_15, &mgpio_irq); tkl_gpio_irq_enable(TUYA_GPIO_NUM_15); //enter low power(every call will enter sleep) app_enter_low_power(8000,app_low_power_timeout_wake_up); } cnt ++; }
经过上述两步,可以实现 beacon mesh 常供电 SDK 的普通 low power,此时通过 IO15 可以唤醒设备,同时 10S 也会定时唤醒一次,唤醒后上报一个 DP,然后 1S 后休眠:
由于改动较多,我直接将涉及到的几个文件放在附件中: