/************************************************************************************************** * @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 /* 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 if (ZNP_CFG1_UART == znpCfg1) { HalUARTSuspend(); } /* 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(); /* Disable interrupt immediately */ HAL_DISABLE_INTERRUPTS(); /* restore interrupt enable registers */ HAL_SLEEP_IE_RESTORE(ien0, ien1, ien2); /* disable sleep timer interrupt */ HAL_SLEEP_TIMER_DISABLE_INT(); if (ZNP_CFG1_UART == znpCfg1) { HalUARTResume(); } #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’t 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 { /* Sleep request is not granted. Check PCON value to see why the sleep is not granted. */ if (halSleepPconValue == 0) { /* 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(); } else { /* PCON value is okay but Radio cannot enter power mode hence just put CPU to idle mode. * Interrupt will be enabled in halSleepEnterIdleMode(). */ halSleepEnterIdleMode(timeout); } } } else if (timeout > PM_MIN_IDLE_TIME) { /* Timeout is too close to enter power mode. Try idle mode. */ HAL_DISABLE_INTERRUPTS(); /* Interrupt will be enabled in halSleepEnterIdleMode(). */ halSleepEnterIdleMode(timeout); } }
/************************************************************************************************** * @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 MCU. * * input parameters * * @param osal_timeout - Next OSAL timer timeout. * * output parameters * * None. * * @return None. ************************************************************************************************** */ void halSleep( uint32 osal_timeout ) { uint32 timeout; uint32 macTimeout; /* Avoid using critical section macros because low power mode macros. * Assert if we enter sleep without interrupt enabled. */ HAL_ASSERT( HAL_INTERRUPTS_ARE_ENABLED() ); /* Don't sleep if time too short or if a key is pressed */ if ( osal_timeout && (osal_timeout < MIN_SLEEP_TIME) ) return; /* initialize the accumulated sleep time */ halAccumulatedSleepTime = 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; } } /* Adjust the wakup time so that the radio is awake in time * for beacon transmission. */ if ( timeout > HAL_BEACON_ENABLE_EARLY_WAKEUP() ) { timeout -= HAL_BEACON_ENABLE_EARLY_WAKEUP(); } /* HAL_SLEEP_PM2 is entered only if the timeout is zero */ halPwrMgtMode = (timeout == 0)? HAL_SLEEP_DEEP : HAL_SLEEP_TIMER; if ( (timeout > HAL_SLEEP_MS_TO_320US(MIN_SLEEP_TIME)) || (timeout == 0) ) { HAL_SLEEP_DISABLE_INTERRUPTS(); /* always use "deep sleep" to turn off radio VREG on MSP430 */ if (MAC_PwrOffReq(MAC_PWR_SLEEP_DEEP) == MAC_SUCCESS) { /* Shut down LED */ HalLedEnterSleep(); while ( (HAL_SLEEP_MS_TO_320US(halAccumulatedSleepTime) < timeout) || (timeout == 0) ) { if (timeout != 0) { /* set sleep timer, timeout is adjusted for the next time */ halSleepSetTimer( timeout ); /* set up sleep timer interrupt */ HAL_SLEEP_TIMER_ENABLE_INT(); } else { /* Halt the timer */ HAL_MAC_SLEEP_TIMER_HALT(); } HalKeyEnterSleep(); /* set MSP430 power mode, global interrupt will be enabled in the macro */ HAL_SLEEP_SET_POWER_MODE(halPwrMgtMode); /* wake up from sleep in ISR */ HAL_SLEEP_DISABLE_INTERRUPTS(); /* disable sleep timer interrupt */ HAL_SLEEP_TIMER_DISABLE_INT(); if (timeout != 0) { /* Calculate timer elapsed only if timer sleep */ halAccumulatedSleepTime += halMacTimerElapsed( &timeout ); } else { /* Restart timer */ HAL_MAC_SLEEP_TIMER_RESTART(); } /* Process keyboard "wake-up" interrupt, exit while loop if key interrupt */ if ( HalKeyExitSleep() || timeout == 0 ) { break; } } /* Restart the LED */ HalLedExitSleep(); /* power on the MAC; blocks until completion. * the MAC timer will be turned on if off. */ MAC_PwrOnReq(); /* Update MAC timer count for OSAL */ macMcuPrecisionCountSleepUpdate( halAccumulatedSleepTime ); HAL_ENABLE_INTERRUPTS(); } else { HAL_ENABLE_INTERRUPTS(); } } }
/************************************************************************************************** * @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; halAccumulatedSleepTime = 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 (MAC_PwrOffReq(MAC_PWR_SLEEP_DEEP) == MAC_SUCCESS) { #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 /* 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 /* 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 */ HAL_SLEEP_SET_POWER_MODE(halPwrMgtMode); /* 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(); /* Calculate timer elasped */ halAccumulatedSleepTime += (HalTimerElapsed() / TICK_COUNT); #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’t 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 { HAL_ENABLE_INTERRUPTS(); } } }