CC2541 power saving
概述
今天談?wù)凜C2541的低功耗,低功耗也是BLE的一個亮點,據(jù)TI的測試,在ultra power saving的時候,一顆紐扣電池可以做到待機一年。經(jīng)過試驗。在沒有定時任務(wù)的時候,打開POWERSAVING這個宏之后,協(xié)議棧會自動檢測當前的timeout事件,如果沒有的定時任務(wù)話會進入PM3模式,該模式下,系統(tǒng)的所有數(shù)字部分和RF部分都進入了休眠,所有的時鐘包括sleeptimer都處于休眠狀態(tài),這個時候只有復(fù)位操作可以喚醒系統(tǒng),也就是最低的功耗模式。當OSAL中存在定時的事件,在時間間隔內(nèi),系統(tǒng)就會進入PM2,該模式下所有的數(shù)字電路部分、RF電路. 32M晶振都是處于休眠狀態(tài),保留有32K的手表晶振,和一個sleeptimer。PM2的喚醒方式是sleeptimer計時時間到,或者外部中斷,或者是系統(tǒng)復(fù)位。該模式也是用到最多的,如何減少在PM2模式下電流是比較關(guān)心的問題。由于CC2541中sleeptimer的最大可計時時長是41s,這個中間喚醒的次數(shù)會影響功耗的大小。
測試
先對比一下集中不同情況下的波形圖。這里是利用實驗室的DC電源分析儀來測試的,如果沒有的話,可以在整個電路的輸入端串聯(lián)一個10歐姆的電阻,用示波器來測試電阻兩端的電壓,可以獲得更加精確的波形圖。
可以看出在整個系統(tǒng)的平均電流在0.4ma即400uA。
這個是藍牙處于連接狀態(tài)下,這個系統(tǒng)的電流波形圖,可以看出會有很多的脈沖,藍牙出于連接狀態(tài)下,會根據(jù)設(shè)置的連接參數(shù)不同,當主機和從機之間沒有數(shù)據(jù)發(fā)送時,主機會每隔30ms發(fā)送有一個確認幀,可以看到比較矮的脈沖就是確認幀。比較高的脈沖是系統(tǒng)中有事件timeout,喚醒進入active狀態(tài)的波形。
這幅圖是連接情況下沒有發(fā)送數(shù)據(jù)時的波形,在經(jīng)過代碼優(yōu)化的情況下可以看出較上副圖功耗有了明顯的降低。
我根據(jù)經(jīng)驗總結(jié)了幾點可以減少功耗的方法:
(1)IO引腳在不需要的情況下,應(yīng)設(shè)置輸出低電平狀態(tài)
(2)減少周期性事件的頻率,盡量吧一下事件放在一起來處理。
(3)將不需要的功能通過配置宏設(shè)置為DISABLE。
低功耗部分的代碼
程序運行到osal_start_system();后繼而運行到osal_run_system();
在里面有一個 osal_pwrmgr_powerconserve();函數(shù),是系統(tǒng)自動檢測當前是否有任務(wù)在執(zhí)行,沒喲的話將進入低功耗模式。
進入低功耗模式之后,會執(zhí)行halsleep()函數(shù)進行低功耗模式設(shè)置。里面進行了低功耗時間等設(shè)置,進行電源控制的啟動就在這里使能!
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 是否需允許進入低功耗,如果允許,提前進入低功耗狀態(tài) if ( halSleepPconValue && ( LL_PowerOffReq(halPwrMgtMode) == LL_SLEEP_REQUEST_ALLOWED ) ) { /**************************************************************************************************************************/ /* 加入電源控制 此后進入低功耗狀態(tài) 電平拉低 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) // 設(shè)置休眠時間 { // 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 ); // 設(shè)置溢出時間 } 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); // 模式設(shè)置 這里進入的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();// 所有的任務(wù)退出Sleep模式時,調(diào)用CLEAR_SLEEP_MODE(); HAL_EXIT_ISR(); return; }總結(jié)
以上是生活随笔為你收集整理的CC2541 power saving的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 家用电器用户行为分析与事件识别
- 下一篇: 计算机网络周宏博课后题答案,关于计算机硬