Page 1 of 1

【蓝牙 Beacon】PHY6252 Beacon Mesh SDK 低功耗相关

Posted: 2023年 Nov 15日 17:58
by beautifulzzzz

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 的功耗:

power.png

 

:ugeek:



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 后休眠:

lowpower.png

:!: :!: :!:
由于改动较多,我直接将涉及到的几个文件放在附件中:

main.c
M apps/tuyaos_demo_beaconmesh_peripheral_phy6252/src/main.c
(4.41 KiB) Downloaded 110 times
main.c
M vendor/phy6252_adv/sdk/phy6252_adv_sdk/app/main.c
(10.31 KiB) Downloaded 105 times
phy_plus_phy.c
M vendor/phy6252_adv/sdk/phy6252_adv_sdk/app/source/phy_plus_phy.c
(20.38 KiB) Downloaded 123 times
phy_plus_phy.h
M vendor/phy6252_adv/sdk/phy6252_adv_sdk/app/source/phy_plus_phy.h
(3.87 KiB) Downloaded 119 times
pwrmgr.c
M vendor/phy6252_adv/sdk/phy6252_adv_sdk/components/driver/pwrmgr/pwrmgr.c
(14.87 KiB) Downloaded 112 times