/************************************************************************************************** * @fn halSleepTimerIsr * * @brief Sleep timer ISR. * * input parameters * * None. * * output parameters * * None. * * @return None. ************************************************************************************************** */ HAL_ISR_FUNCTION(halSleepTimerIsr, ST_VECTOR) { HAL_SLEEP_TIMER_CLEAR_INT(); CLEAR_SLEEP_MODE(); #ifdef HAL_SLEEP_DEBUG_POWER_MODE halSleepInt = TRUE; #endif }
/******************************************************************************* * @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(); HAL_EXIT_ISR(); return; }
/******************************************************************************* * @fn halSleepSetTimer * * @brief This function sets the CC2540 sleep timer compare value based * on a given snapshot of the sleep timer, and a timeout that is * relative to that snapshot. The snapshot is provided as it may * need to be taken as close to the snapshot of Timer 2 (the radio * timer) as possible so that the time to the next radio event, * when converted to 32kHz ticks, is as accurate as possible in * terms of sleep time. In addition, the offset is adjusted based * on a configurable adjustment to take the sleep handler's * execution time into account. The sleep timer interrupt is then * setup for wake. * * input parameters * * @param sleepTimer - Sleep timer value timeout is relative to. * @param timeout - Timeout value in 32kHz units. * * output parameters * * @param None. * * @return None. */ void halSleepSetTimer( uint32 sleepTimer, uint32 timeout ) { HAL_SLEEP_TIMER_DISABLE_INT(); // compute sleep timer compare value sleepTimer += timeout; // subtract the processing time spent in function halSleep() sleepTimer -= HAL_SLEEP_ADJ_TICKS; // set sleep timer compare; ST0 must be written last ST2 = ((uint8 *)&sleepTimer)[UINT32_NDX2]; ST1 = ((uint8 *)&sleepTimer)[UINT32_NDX1]; ST0 = ((uint8 *)&sleepTimer)[UINT32_NDX0]; HAL_SLEEP_TIMER_CLEAR_INT(); HAL_SLEEP_TIMER_ENABLE_INT(); return; }
/************************************************************************************************** * @fn halSleepEnterIdleMode * * @brief This function puts the device into idle mode. * Note that interrupts must have been disabled before this function call. * * input parameters * * @param timeout - Timeout value in 320 usec units. The sleep timer compare is set to * this value. * * output parameters * * None. * * @return None. ************************************************************************************************** */ void halSleepEnterIdleMode(uint32 timeout) { /* 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 ); } /* set sleep timer */ halSleepSetTimer(timeout); /* set up sleep timer interrupt */ HAL_SLEEP_TIMER_CLEAR_INT(); HAL_SLEEP_TIMER_ENABLE_INT(); } HAL_SLEEP_PREP_IDLE_MODE(); HAL_ENABLE_INTERRUPTS(); HAL_SLEEP_SET_POWER_MODE(); }
/************************************************************************************************** * @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 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(); } } }