BLE 协议栈中电源低功耗的管理与CC2530的模式一样,都有PM1,PM2,PM3三种低功耗状态,其中PM2在可以用定时器唤醒,在PM3中只能使用外部中断的方式才能唤醒。
下面的程序实现的是在CC2541片子上使用低功耗电源管理芯片TPS62730,控制引脚在P1_0位置。
程序运行到osal_start_system();后继而运行到osal_run_system();
在里面有一个 osal_pwrmgr_powerconserve();函数,是系统自动检测当前是否有任务在执行,没哟的话将进入低功耗模式。
void osal_pwrmgr_powerconserve( void ) // 系统自动调用 如果当前没有任务,将进入低功耗模式 { uint32 next; halIntState_t intState; // Should we even look into power conservation if ( pwrmgr_attribute.pwrmgr_device != PWRMGR_ALWAYS_ON ) { // Are all tasks in agreement to conserve if ( pwrmgr_attribute.pwrmgr_task_state == 0 ) { // Hold off interrupts. HAL_ENTER_CRITICAL_SECTION( intState ); // Get next time-out next = osal_next_timeout(); //选取最近事件的Timeout 作为实际休眠时间 // Re-enable interrupts. HAL_EXIT_CRITICAL_SECTION( intState ); // Put the processor into sleep mode OSAL_SET_CPU_INTO_SLEEP( next ); // 进入低功耗模式 } } }
进入低功耗模式之后,会执行halsleep()函数进行低功耗模式设置。
里面进行了低功耗时间等设置,进行电源控制的启动就在这里使能!
void halSleep( uint32 osal_timeout ) { uint32 timeout; uint32 llTimeout; uint32 sleepTimer; #ifdef DEBUG_GPIO // TEMP P1_0 = 1; #endif // DEBUG_GPIO // max allowed sleep time in ms if (osal_timeout > MAX_SLEEP_TIMEOUT) { osal_timeout = MAX_SLEEP_TIMEOUT; } // get LL timeout value already converted to 32kHz ticks LL_TimeToNextRfEvent( &sleepTimer, &llTimeout ); // check if no OSAL timeout // Note: If the next wake event is due to an OSAL timeout, then wakeForRF // will already be FALSE, and the call to LL_TimeToNExtRfEvent will // already have taken a snapshot of the Sleep Timer. if (osal_timeout == 0) { // use common variable timeout = llTimeout; // check if there's time before the next radio event // Note: Since the OSAL timeout is zero, then if the radio timeout is // not zero, the next wake (if one) will be due to the radio event. wakeForRF = (timeout != 0) ? TRUE : FALSE; } else // OSAL timeout is non-zero { // convet OSAL timeout to sleep time // Note: Could be early by one 32kHz timer tick due to rounding. timeout = HAL_SLEEP_MS_TO_32KHZ( osal_timeout ); // so check time to radio event is non-zero, and if so, use shorter value if ((llTimeout != 0) && (llTimeout < timeout)) { // use common variable timeout = llTimeout; // the next ST wake time is due to radio wakeForRF = TRUE; } else // OSAL timeout will be used to wake { // so take a snapshot of the sleep timer for sleep based on OSAL timeout sleepTimer = halSleepReadTimer(); // the next ST wake time is not due to radio wakeForRF = FALSE; } } // HAL_SLEEP_PM3 is entered only if the timeout is zero halPwrMgtMode = (timeout == 0) ? HAL_SLEEP_DEEP : HAL_SLEEP_TIMER; // 选择进入PM 模式 #ifdef DEBUG_GPIO // TEMP P1_0 = 0; #endif // DEBUG_GPIO // check if sleep should be entered if ( (timeout > PM_MIN_SLEEP_TIME) || (timeout == 0) ) { halIntState_t ien0, ien1, ien2; #ifdef DEBUG_GPIO // TEMP P1_0 = 1; /************************************************************************BLE 自带 与电源控制功能冲突*******/ #endif // DEBUG_GPIO /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// HAL_ASSERT( HAL_INTERRUPTS_ARE_ENABLED()); HAL_DISABLE_INTERRUPTS(); // check if radio allows sleep, and if so, preps system for shutdown // 检查radio 是否需允许进入低功耗,如果允许,提前进入低功耗状态 if ( halSleepPconValue && ( LL_PowerOffReq(halPwrMgtMode) == LL_SLEEP_REQUEST_ALLOWED ) ) { /**************************************************************************************************************************/ /* 加入电源控制 此后进入低功耗状态 电平拉低 P1_0 =1 */ /**************************************************************************************************************************/ #if ((defined HAL_KEY) && (HAL_KEY == TRUE)) // get peripherals ready for sleep HalKeyEnterSleep(); #endif // ((defined HAL_KEY) && (HAL_KEY == TRUE)) #ifdef HAL_SLEEP_DEBUG_LED HAL_TURN_OFF_LED3(); #else // use this to turn LEDs off during sleep HalLedEnterSleep(); #endif // HAL_SLEEP_DEBUG_LED // enable sleep timer interrupt if (timeout != 0) // 设置休眠时间 { // check if the time to next wake event is greater than max sleep time if (timeout > MAX_SLEEP_TIME ) { // it is, so limit to max allowed sleep time (~510s) halSleepSetTimer( sleepTimer, MAX_SLEEP_TIME ); // 设置溢出时间 } else // not more than allowed sleep time { // so set sleep time to actual amount halSleepSetTimer( sleepTimer, timeout ); } } // prep CC254x power mode HAL_SLEEP_PREP_POWER_MODE(halPwrMgtMode); // 模式设置 这里进入的PM2 // save interrupt enable registers and disable all interrupts HAL_SLEEP_IE_BACKUP_AND_DISABLE(ien0, ien1, ien2); HAL_ENABLE_INTERRUPTS(); ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #ifdef DEBUG_GPIO // TEMP P1_0 = 0; #endif // DEBUG_GPIO // set CC254x power mode; interrupts are disabled after this function // Note: Any ISR that could wake the device from sleep needs to use // CLEAR_SLEEP_MODE(), which will clear the halSleepPconValue flag // used to enter sleep mode, thereby preventing the device from // missing this interrupt. HAL_SLEEP_SET_POWER_MODE(); #ifdef DEBUG_GPIO // TEMP P1_0 = 1; /************************************************************************BLE 自带 与电源控制功能冲突*******/ #endif // DEBUG_GPIO // check if ST interrupt pending, and if not, clear wakeForRF flag // Note: This is needed in case we are not woken by the sleep timer but // by for example a key press. In this case, the flag has to be // cleared as we are not just before a radio event. // Note: There is the possiblity that we may wake from an interrupt just // before the sleep timer would have woken us just before a radio // event, in which case power will be wasted as we will probably // enter this routine one or more times before the radio event. // However, this is presumably unusual, and isn't expected to have // much impact on average power consumption. if ( (wakeForRF == TRUE) && !(IRCON & 0x80) ) { wakeForRF = FALSE; } // restore interrupt enable registers HAL_SLEEP_IE_RESTORE(ien0, ien1, ien2); // power on the LL; blocks until completion // Note: This is done here to ensure the 32MHz XOSC has stablized, in // case it is needed (e.g. the ADC is used by the joystick). LL_PowerOnReq( (halPwrMgtMode == CC2540_PM3), wakeForRF ); #ifdef HAL_SLEEP_DEBUG_LED HAL_TURN_ON_LED3(); #else //!HAL_SLEEP_DEBUG_LED // use this to turn LEDs back on after sleep HalLedExitSleep(); #endif // HAL_SLEEP_DEBUG_LED #if ((defined HAL_KEY) && (HAL_KEY == TRUE)) // handle peripherals (void)HalKeyExitSleep(); #endif // ((defined HAL_KEY) && (HAL_KEY == TRUE)) } HAL_ENABLE_INTERRUPTS(); } #ifdef DEBUG_GPIO // TEMP P1_0 = 0; #endif // DEBUG_GPIO return; }
跳出低功耗模式在此程序里是sleep timer 时间溢出,也就是在中断里退出低功耗模式
/******************************************************************************* * @fn halSleepTimerIsr * * @brief Sleep timer ISR. * * input parameters * * None. * * output parameters * * @param None. * * @return None. */ HAL_ISR_FUNCTION(halSleepTimerIsr, ST_VECTOR) { HAL_ENTER_ISR(); HAL_SLEEP_TIMER_CLEAR_INT(); #ifdef HAL_SLEEP_DEBUG_POWER_MODE halSleepInt = TRUE; #endif // HAL_SLEEP_DEBUG_POWER_MODE ///////////////////////////////////////////////////////////////////////////////////////////// 拉高 /////////////////////////////////////////////////////////////////////////////////////////////////// CLEAR_SLEEP_MODE();// 所有的任务退出Sleep模式时,调用CLEAR_SLEEP_MODE(); HAL_EXIT_ISR(); return; }