Power Modes
Active mode: The fully functional mode. The voltage regulator to the digital core is on, and either the 16-MHz RC oscillator or the 32-MHz crystal oscillator or both are running. Either the 32-kHz RCOSC or the 32-kHz XOSC is running. Idle mode: Identical to active mode, except that the CPU core stops operating (is idle). PM1: The voltage regulator to the digital part is on. Neither the 32-MHz XOSC nor the 16-MHz RCOSC is running. Either the 32-kHz RCOSC or the 32-kHz XOSC is running. The system goes to active mode on reset, an external interrupt, or when the Sleep Timer expires. PM2: The voltage regulator to the digital core is turned off. Neither the 32-MHz XOSC nor the 16-MHz RCOSC is running. Either the 32-kHz RCOSC or the 32-kHz XOSC is running. The system goes to active mode on reset, an external interrupt, or when the Sleep Timer expires. PM3: The voltage regulator to the digital core is turned off. None of the oscillators is running. The system goes to active mode on reset or an external interrupt.The POR is active in PM2/PM3, but the BOD is powered down, which gives a limited voltage supervision. If the supply voltage is lowered to below 1.4 V during PM2/PM3, at temperatures of 70°C or higher, and then brought back up to good operating voltage before active mode is re-entered, registers and RAM contents that are saved in PM2/PM3 may become altered. Hence, care should be taken in the design of the system power supply to ensure that this does not occur. The voltage can be periodically supervised accurately by entering active mode, as a BOD reset is triggered if the supply voltage is below approximately 1.7 V. The CC2533 and CC2541 have functionality to perform automatically a CRC check of the retained configuration register values in PM2/PM3 to check that the device state was not altered during sleep. The bits in SRCRC.CRC_RESULT indicate whether there were any changes, and by enabling SRCRC.CRC_RESET_EN, the device immediately resets itself with a watchdog reset if SRCRC.CRC_RESULT is not 00 (= CRC of retained registers passed) after wakeup from PM2/PM3. The SRCRC register also contains the SRCRC.FORCE_RESET bit that can be used by software to immediately trigger a watchdog reset to reboot the device. 必須滿足下面所有條件才能進入休眠模式 1. ZDO節點描述符指定“Rx is off when idle,在f8wConfig.cfg文件中將RFD_RCVC_ALWAYS_ON設為false實現 2. 所有zstack 任務”贊同“進入睡眠模式 3. zstack各個任務都沒有預定的活動 4.MAC層沒有預定的活動 cc2531和cc2530 zigbee協議棧工程中調用以下函數使設備進入低功耗狀態。在這里(osal_pwrmgr_powerconserve()函數中),在嘗試進入休眠模式時會做另外兩個檢查。首先,檢查變量pwrmgr_device是否被設置為電池設備。這項設定在設備入網后執行—詳情見例程的ZDApp.c源文件。其次,檢查變量pwrmgr_task_state確認沒有任務的節能狀態是“put a hold”。該機制允許每個Z-Stack任務在臨界區操作時禁止休眠。當這兩個條件都滿足時,預期的休眠時間取決于OSAL定時器的下一次溢出時間。如果下一次溢出時間大于0小于MIN_SLEEP_TIME,選擇SLEEP_LITE模式。在這種模式下,系統定時器被調整為首先到期的定時器事件提供一個“喚醒”中斷。MIN_SLEEP_TIME定義在hal_sleep.c中,為了防止很短時間的休眠。當沒有預定的Z-Stack 事件或者定時器時,選擇SLEEP_DEEP模式,因此下一次溢出時間為0,允許最大限度的節能: void osal_pwrmgr_powerconserve( void ) { uint16 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(); // Re-enable interrupts. HAL_EXIT_CRITICAL_SECTION( intState ); // Put the processor into sleep mode OSAL_SET_CPU_INTO_SLEEP( next ); } } } 以上函數實際調用的是halSleep(),該函數通過對休眠時間,設定條件和堆棧分配情況的判斷來選擇節點進入PM1,PM2,PM3。halSleep()函數被OSAL_SET_CPU_INTO_SLEEP調用,該函數執行一系列有序的操作:關閉MAC層,關斷外設,使MCU進入休眠模式,休眠后喚醒MCU,開啟外設,最后重啟MAC層。Z-Stack OSAL主循環獨立運行于MAC層,因此Z-Stack不知道MAC層的運行狀態。調用MAC_PwrOffReq()函數可以關閉MAC層。需要注意的是,當設置空閑時接收器使能會導致MAC層休眠時不關閉,這會阻止設備進入休眠模式 /************************************************************************************************** * @fn halSleep * * @brief This function is called from the OSAL task loop using and existing OSAL * interface. It sets the low power mode of the MAC and the CC2530. * * input parameters * * @param osal_timeout - Next OSAL timer timeout. * * output parameters * * None. * * @return None. ************************************************************************************************** */ void halSleep( uint16 osal_timeout ) { uint32 timeout; uint32 macTimeout = 0; /* get next OSAL timer expiration converted to 320 usec units */ timeout = HAL_SLEEP_MS_TO_320US(osal_timeout); if (timeout == 0) { timeout = MAC_PwrNextTimeout(); } else { /* get next MAC timer expiration */ macTimeout = MAC_PwrNextTimeout(); /* get lesser of two timeouts */ if ((macTimeout != 0) && (macTimeout < timeout)) { timeout = macTimeout; } } /* HAL_SLEEP_PM2 is entered only if the timeout is zero and * the device is a stimulated device. */ halPwrMgtMode = (timeout == 0) ? HAL_SLEEP_DEEP : HAL_SLEEP_TIMER; /* DEEP sleep can only be entered when zgPollRate == 0. * This is to eliminate any possibility of entering PM3 between * two network timers. */ #if ZG_BUILD_ENDDEVICE_TYPE && defined (NWK_AUTO_POLL) if ((timeout > HAL_SLEEP_MS_TO_320US(PM_MIN_SLEEP_TIME)) || (timeout == 0 && zgPollRate == 0)) #else if ((timeout > HAL_SLEEP_MS_TO_320US(PM_MIN_SLEEP_TIME)) || (timeout == 0)) #endif { halIntState_t ien0, ien1, ien2; HAL_ASSERT(HAL_INTERRUPTS_ARE_ENABLED()); HAL_DISABLE_INTERRUPTS(); /* always use "deep sleep" to turn off radio VREG on CC2530 */ if (halSleepPconValue != 0 && MAC_PwrOffReq(MAC_PWR_SLEEP_DEEP) == MAC_SUCCESS) { /* The PCON value is not zero. There is no interrupt overriding the * sleep decision. Also, the radio granted the sleep request. */ #if ((defined HAL_KEY) && (HAL_KEY == TRUE)) /* get peripherals ready for sleep */ HalKeyEnterSleep(); #endif #ifdef HAL_SLEEP_DEBUG_LED HAL_TURN_OFF_LED3(); #else /* use this to turn LEDs off during sleep */ HalLedEnterSleep(); #endif if(timeout > maxSleepLoopTime) { timeout = maxSleepLoopTime; } /* enable sleep timer interrupt */ if (timeout != 0) { if (timeout > HAL_SLEEP_MS_TO_320US( MAX_SLEEP_TIME )) { timeout -= HAL_SLEEP_MS_TO_320US( MAX_SLEEP_TIME ); halSleepSetTimer(HAL_SLEEP_MS_TO_320US( MAX_SLEEP_TIME )); } else { /* set sleep timer */ halSleepSetTimer(timeout); } /* set up sleep timer interrupt */ HAL_SLEEP_TIMER_CLEAR_INT(); HAL_SLEEP_TIMER_ENABLE_INT(); } #ifdef HAL_SLEEP_DEBUG_LED if (halPwrMgtMode == CC2530_PM1) { HAL_TURN_ON_LED1(); } else { HAL_TURN_OFF_LED1(); } #endif /* Prep CC2530 power mode */ HAL_SLEEP_PREP_POWER_MODE(halPwrMgtMode); /* save interrupt enable registers and disable all interrupts */ HAL_SLEEP_IE_BACKUP_AND_DISABLE(ien0, ien1, ien2); HAL_ENABLE_INTERRUPTS(); /* set CC2530 power mode, interrupt is disabled after this function * Note that an ISR (that could wake up from power mode) which runs * between the previous instruction enabling interrupts and before * power mode is set would switch the halSleepPconValue so that * power mode shall not be entered in such a case. */ HAL_SLEEP_SET_POWER_MODE(); /* the interrupt is disabled - see halSetSleepMode() */ /* restore interrupt enable registers */ HAL_SLEEP_IE_RESTORE(ien0, ien1, ien2); /* disable sleep timer interrupt */ HAL_SLEEP_TIMER_DISABLE_INT(); #ifdef HAL_SLEEP_DEBUG_LED HAL_TURN_ON_LED3(); #else /* use this to turn LEDs back on after sleep */ HalLedExitSleep(); #endif #if ((defined HAL_KEY) && (HAL_KEY == TRUE)) /* handle peripherals */ (void)HalKeyExitSleep(); #endif /* power on the MAC; blocks until completion */ MAC_PwrOnReq(); HAL_ENABLE_INTERRUPTS(); /* For CC2530, T2 interrupt won抰 be generated when the current count is greater than * the comparator. The interrupt is only generated when the current count is equal to * the comparator. When the CC2530 is waking up from sleep, there is a small window * that the count may be grater than the comparator, therefore, missing the interrupt. * This workaround will call the T2 ISR when the current T2 count is greater than the * comparator. The problem only occurs when POWER_SAVING is turned on, i.e. the 32KHz * drives the chip in sleep and SYNC start is used. */ macMcuTimer2OverflowWorkaround(); } else { /* An interrupt may have changed the sleep decision. Do not sleep at all. Turn on * the interrupt, exit normally, and the next sleep will be allowed. */ HAL_ENABLE_INTERRUPTS(); } } }
|