在TMOS系统中手动管理休眠
以CH58x、CH59x为例,使用电池供电的应用,多数会选用Sleep或Shutdown等级的休眠,那么至少要有RTC或是GPIO唤醒中的其中一种手段唤醒MCU(BAT低电压唤醒取决于VIO33引脚上的电压,不是代码能够控制的,暂且排开)。
在BLE代码中,为了让BLE协议栈能够在休眠期间及时唤醒发包,维持BLE的连接,一般是建议在BLE工程中启用HAL_SLEEP,让协议栈管理休眠,协议栈会利用RTC唤醒来安排低功耗;如果用户对逻辑把握准确,BLE使用频次不高,也是可以参考PM代码中的休眠,自行管理休眠的。
比如某个应用中,蓝牙MCU不需要循环工作,再等待外部信号时才需要工作,故不由RTC唤醒,只由外部GPIO信号唤醒;蓝牙MCU醒来后跑BLE,期间MCU全速工作不休眠,通信完后才休眠,等待下一次外部GPIO信号唤醒;要求MCU不能复位,RAM要保持供电。此时可以参考下方代码块安排休眠。
下列代码是测试时偷懒,在MCU.c的HAL_ProcessEvent中新增了事件,注意MCU.c是EVT公用文件,此处修改会影响其他工程。
/*****其他事件*****/ if(events & HAL_TEST_EVENT) //主循环、GPIO中断中的flag控制启用该事件 { tmos_start_task(halTaskID, HAL_SLEEP_EVENT, MS1_TO_SYSTEM_TIME(2000)); // 模拟一定时间后sleep休眠 uint8_t initial_advertising_enable = FALSE; //关广播 GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, sizeof(uint8_t), &initial_advertising_enable); tmos_stop_task(halTaskID, HAL_REG_INIT_EVENT); //默认2分钟一次的校准先暂停 //为了更好掌握代码逻辑,建议所有TMOS事件在此暂停,唤醒后再安排事件。 return events ^ HAL_TEST_EVENT; } if(events & HAL_SLEEP_EVENT) //MCU在此事件中休眠和唤醒,唤醒后立即校准RF,再开蓝牙广播 { sys_safe_access_enable(); R8_CK32K_CONFIG &= ~(RB_CLK_INT32K_PON | RB_CLK_XT32K_PON); //关闭32K晶振电源,更省电 sys_safe_access_disable(); LowPower_Sleep(RB_PWR_RAM24K | RB_PWR_RAM2K); //保留24+2K的SRAM的供电 sys_safe_access_enable(); R8_CK32K_CONFIG |= RB_CLK_INT32K_PON; //32K电源打开,供TMOS系统使用 sys_safe_access_disable(); HSECFG_Current(HSE_RCur_100); // 降为额定电流(低功耗函数中提升了HSE偏置电流) DelayMs(5); //这里建议给一定的延时,延时足够到下方的打印能正常就好 PRINT("wake.. \n"); tmos_set_event(halTaskID, HAL_REG_INIT_EVENT); //先校准RF,再开广播 tmos_start_task(halTaskID, HAL_ADV_START_EVENT, 80); return events ^ HAL_SLEEP_EVENT; } if(events & HAL_ADV_START_EVENT) //唤醒且RC校准后开广播 { uint8_t initial_advertising_enable = TRUE; //开广播 GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, sizeof(uint8_t), &initial_advertising_enable); return events ^ HAL_ADV_START_EVENT; } /****其他事件***/
GPIO唤醒的代码可以直接参考PM中的配置,本次测试用了PB4作为唤醒引脚。
/********GPIO唤醒的配置,加在主函数初始化中********/ GPIOB_ModeCfg(GPIO_Pin_4, GPIO_ModeIN_PU); GPIOB_ITModeCfg(GPIO_Pin_4, GPIO_ITMode_FallEdge); // 下降沿唤醒 PFIC_EnableIRQ(GPIO_B_IRQn); PWR_PeriphWakeUpCfg(ENABLE, RB_SLP_GPIO_WAKE, Long_Delay); /******************************************/ /********************************************************************* * @fn Main_Circulation * * @brief 主循环 * * @return none */ __HIGH_CODE __attribute__((noinline)) void Main_Circulation() { while(1) { TMOS_SystemProcess(); if(flag_gpio_int){ //根据GPIO中置的标志变量来安排事件 flag_gpio_int = 0; tmos_start_task(halTaskID, HAL_TEST_EVENT, 160); PRINT("main_flag\n"); } } } /********************************************************************* * @fn GPIOA_IRQHandler * * @brief GPIOA中断函数 * * @return none */ __INTERRUPT __HIGH_CODE void GPIOB_IRQHandler(void) { static uint8_t int_num = 0; //按一下PB4休眠,再按一下唤醒 GPIOB_ClearITFlagBit(GPIO_Pin_4); if(!int_num){ flag_gpio_int = 1; //置标志,主循环中查标志并安排事件 int_num = 1; } else { int_num = 0; } }