/************************************************************************************************** * @fn halDriverBegPM * * @brief This function is called before entering PM so that drivers can be put into their * lowest power states. * * input parameters * * None. * * output parameters * * None. * * @return None. ************************************************************************************************** */ void halDriverBegPM(void) { #if ((defined HAL_LED) && (HAL_LED == TRUE)) HalLedEnterSleep(); #endif #if ((defined HAL_KEY) && (HAL_KEY == TRUE)) HalKeyEnterSleep(); #endif #if ((defined HAL_I2C) && (HAL_I2C == TRUE)) HalI2CEnterSleep(); #endif }
/************************************************************************************************** * @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; /* 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 LL * and the CC2540. * * input parameters * * @param osal_timeout - Next OSAL timer timeout, in msec. * * output parameters * * @param None. * * @return None. */ void halSleep( uint32 osal_timeout ) { uint32 timeout; uint32 llTimeout; uint32 sleepTimer; halDriverBegPM(); #ifdef DEBUG_GPIO // TEMP P1_0 = 1; #endif // DEBUG_GPIO if (osal_timeout > MAX_16BIT_TIMEOUT) { osal_timeout = MAX_16BIT_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; #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; #endif // DEBUG_GPIO HAL_ASSERT( HAL_INTERRUPTS_ARE_ENABLED() ); HAL_DISABLE_INTERRUPTS(); // check if radio allows sleep, and if so, preps system for shutdown if ( LL_PowerOffReq(halPwrMgtMode) == LL_SLEEP_REQUEST_ALLOWED ) { #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); // 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; #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(); } halDriverEndPM(); #ifdef DEBUG_GPIO // TEMP P1_0 = 0; #endif // DEBUG_GPIO return; }
/************************************************************************************************** * @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(); } } }