123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555 |
- /**************************************************************************************************
- Filename: hal_sleep.c
- Revised: $Date: 2012-03-07 11:55:12 -0800 (Wed, 07 Mar 2012) $
- Revision: $Revision: 29664 $
- Description: This module contains the HAL power management procedures for the CC2530.
- Copyright 2006-2012 Texas Instruments Incorporated. All rights reserved.
- IMPORTANT: Your use of this Software is limited to those specific rights
- granted under the terms of a software license agreement between the user
- who downloaded the software, his/her employer (which must be your employer)
- and Texas Instruments Incorporated (the "License"). You may not use this
- Software unless you agree to abide by the terms of the License. The License
- limits your use, and you acknowledge, that the Software may not be modified,
- copied or distributed unless embedded on a Texas Instruments microcontroller
- or used solely and exclusively in conjunction with a Texas Instruments radio
- frequency transceiver, which is integrated into your product. Other than for
- the foregoing purpose, you may not use, reproduce, copy, prepare derivative
- works of, modify, distribute, perform, display or sell this Software and/or
- its documentation for any purpose.
- YOU FURTHER ACKNOWLEDGE AND AGREE THAT THE SOFTWARE AND DOCUMENTATION ARE
- PROVIDED “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED,
- INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY, TITLE,
- NON-INFRINGEMENT AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL
- TEXAS INSTRUMENTS OR ITS LICENSORS BE LIABLE OR OBLIGATED UNDER CONTRACT,
- NEGLIGENCE, STRICT LIABILITY, CONTRIBUTION, BREACH OF WARRANTY, OR OTHER
- LEGAL EQUITABLE THEORY ANY DIRECT OR INDIRECT DAMAGES OR EXPENSES
- INCLUDING BUT NOT LIMITED TO ANY INCIDENTAL, SPECIAL, INDIRECT, PUNITIVE
- OR CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA, COST OF PROCUREMENT
- OF SUBSTITUTE GOODS, TECHNOLOGY, SERVICES, OR ANY CLAIMS BY THIRD PARTIES
- (INCLUDING BUT NOT LIMITED TO ANY DEFENSE THEREOF), OR OTHER SIMILAR COSTS.
- Should you have any questions regarding your right to use this Software,
- contact Texas Instruments Incorporated at www.TI.com.
- **************************************************************************************************/
- /* ------------------------------------------------------------------------------------------------
- * Includes
- * ------------------------------------------------------------------------------------------------
- */
- #include "hal_types.h"
- #include "hal_mcu.h"
- #include "hal_board.h"
- #include "hal_sleep.h"
- #include "hal_led.h"
- #include "hal_key.h"
- #include "mac_api.h"
- #include "OSAL.h"
- #include "OSAL_Timers.h"
- #include "OSAL_Tasks.h"
- #include "OSAL_PwrMgr.h"
- #include "OnBoard.h"
- #include "hal_drivers.h"
- #include "hal_assert.h"
- #include "mac_mcu.h"
- #ifndef ZG_BUILD_ENDDEVICE_TYPE
- # define ZG_BUILD_ENDDEVICE_TYPE FALSE
- #endif
- #if ZG_BUILD_ENDDEVICE_TYPE && defined (NWK_AUTO_POLL)
- #include "nwk_globals.h"
- #include "ZGlobals.h"
- #endif
- /* ------------------------------------------------------------------------------------------------
- * Macros
- * ------------------------------------------------------------------------------------------------
- */
- /* POWER CONSERVATION DEFINITIONS
- * Sleep mode H/W definitions (enabled with POWER_SAVING compile option)
- */
- #define CC2530_PM0 0 /* PM0, Clock oscillators on, voltage regulator on */
- #define CC2530_PM1 1 /* PM1, 32.768 kHz oscillators on, voltage regulator on */
- #define CC2530_PM2 2 /* PM2, 32.768 kHz oscillators on, voltage regulator off */
- #define CC2530_PM3 3 /* PM3, All clock oscillators off, voltage regulator off */
- /* HAL power management mode is set according to the power management state. The default
- * setting is HAL_SLEEP_OFF. The actual value is tailored to different HW platform. Both
- * HAL_SLEEP_TIMER and HAL_SLEEP_DEEP selections will:
- * 1. turn off the system clock, and
- * 2. halt the MCU.
- * HAL_SLEEP_TIMER can be woken up by sleep timer interrupt, I/O interrupt and reset.
- * HAL_SLEEP_DEEP can be woken up by I/O interrupt and reset.
- */
- #define HAL_SLEEP_OFF CC2530_PM0
- #define HAL_SLEEP_TIMER CC2530_PM2
- #define HAL_SLEEP_DEEP CC2530_PM3
- /* MAX_SLEEP_TIME calculation:
- * Sleep timer maximum duration = 0xFFFF7F / 32768 Hz = 511.996 seconds
- * Round it to 510 seconds or 510000 ms
- */
- #define MAX_SLEEP_TIME 510000 /* maximum time to sleep allowed by ST */
- #define TICKS_SUBTRACTED 2
- /* minimum time to sleep, this macro is to:
- * 1. avoid thrashing in-and-out of sleep with short OSAL timer (~2ms)
- * 2. define minimum safe sleep period
- */
- #if !defined (PM_MIN_SLEEP_TIME)
- #define PM_MIN_SLEEP_TIME 14 /* default to minimum safe sleep time minimum CAP */
- #endif
- /* The PCON instruction must be 4-byte aligned. The following code may cause excessive power
- * consumption if not aligned. See linker file ".xcl" for actual placement.
- */
- #pragma location = "SLEEP_CODE"
- void halSetSleepMode(void);
- /* This value is used to adjust the sleep timer compare value such that the sleep timer
- * compare takes into account the amount of processing time spent in function halSleep().
- * The first value is determined by measuring the number of sleep timer ticks it from
- * the beginning of the function to entering sleep mode or more precisely, when
- * MAC_PwrNextTimeout() is called. The second value is determined by measuring the number
- * of sleep timer ticks from exit of sleep mode to the call to MAC_PwrOnReq() where the
- * MAC timer is restarted.
- */
- #define HAL_SLEEP_ADJ_TICKS (11 + 12)
- #ifndef HAL_SLEEP_DEBUG_POWER_MODE
- /* set CC2530 power mode; always use PM2 */
- #define HAL_SLEEP_PREP_POWER_MODE(mode) st( SLEEPCMD &= ~PMODE; /* clear mode bits */ \
- SLEEPCMD |= mode; /* set mode bits */ \
- while (!(STLOAD & LDRDY)); \
- halSleepPconValue = PCON_IDLE; \
- )
- #define HAL_SLEEP_SET_POWER_MODE() halSetSleepMode()
- #else
- /* Debug: don't set power mode, just block until sleep timer interrupt */
- #define HAL_SLEEP_PREP_POWER_MODE(mode) /* nothing */
- #define HAL_SLEEP_SET_POWER_MODE() st( while(halSleepInt == FALSE); \
- halSleepInt = FALSE; \
- HAL_DISABLE_INTERRUPTS(); \
- )
- #endif
- /* sleep and external interrupt port masks */
- #define STIE_BV BV(5)
- #define P0IE_BV BV(5)
- #define P1IE_BV BV(4)
- #define P2IE_BV BV(1)
- /* sleep timer interrupt control */
- #define HAL_SLEEP_TIMER_ENABLE_INT() st(IEN0 |= STIE_BV;) /* enable sleep timer interrupt */
- #define HAL_SLEEP_TIMER_DISABLE_INT() st(IEN0 &= ~STIE_BV;) /* disable sleep timer interrupt */
- #define HAL_SLEEP_TIMER_CLEAR_INT() st(STIF = 0;) /* clear sleep interrupt flag */
- /* backup interrupt enable registers before sleep */
- #define HAL_SLEEP_IE_BACKUP_AND_DISABLE(ien0, ien1, ien2) st(ien0 = IEN0; /* backup IEN0 register */ \
- ien1 = IEN1; /* backup IEN1 register */ \
- ien2 = IEN2; /* backup IEN2 register */ \
- IEN0 &= STIE_BV; /* disable IEN0 except STIE */ \
- IEN1 &= P0IE_BV; /* disable IEN1 except P0IE */ \
- IEN2 &= (P1IE_BV|P2IE_BV);) /* disable IEN2 except P1IE, P2IE */
- /* restore interrupt enable registers before sleep */
- #define HAL_SLEEP_IE_RESTORE(ien0, ien1, ien2) st(IEN0 = ien0; /* restore IEN0 register */ \
- IEN1 = ien1; /* restore IEN1 register */ \
- IEN2 = ien2;) /* restore IEN2 register */
- /* convert msec to 320 usec units with round */
- #define HAL_SLEEP_MS_TO_320US(ms) (((((uint32) (ms)) * 100) + 31) / 32)
- /* for optimized indexing of uint32's */
- #if HAL_MCU_LITTLE_ENDIAN()
- #define UINT32_NDX0 0
- #define UINT32_NDX1 1
- #define UINT32_NDX2 2
- #define UINT32_NDX3 3
- #else
- #define UINT32_NDX0 3
- #define UINT32_NDX1 2
- #define UINT32_NDX2 1
- #define UINT32_NDX3 0
- #endif
- static uint32 maxSleepLoopTime = HAL_SLEEP_MS_TO_320US(MAX_SLEEP_TIME);
- /* ------------------------------------------------------------------------------------------------
- * Global Variables
- * ------------------------------------------------------------------------------------------------
- */
- /* PCON register value to program when setting power mode */
- volatile __data uint8 halSleepPconValue = PCON_IDLE;
- /* ------------------------------------------------------------------------------------------------
- * Local Variables
- * ------------------------------------------------------------------------------------------------
- */
- /* HAL power management mode is set according to the power management state.
- */
- static uint8 halPwrMgtMode = HAL_SLEEP_OFF;
- #ifdef HAL_SLEEP_DEBUG_POWER_MODE
- static bool halSleepInt = FALSE;
- #endif
- /* ------------------------------------------------------------------------------------------------
- * Function Prototypes
- * ------------------------------------------------------------------------------------------------
- */
- void halSleepSetTimer(uint32 timeout);
- /**************************************************************************************************
- * @fn halSleep
- *
- * @brief This function put the CC2530 to sleep. The PCON instruction must be 4-byte aligned.
- * The following code may cause excessive power consumption if not aligned. See linker
- * file ".xcl" for actual placement.
- *
- * input parameters
- *
- * @param None.
- *
- * output parameters
- *
- * None.
- *
- * @return None.
- **************************************************************************************************
- */
- void halSetSleepMode(void)
- {
- PCON = halSleepPconValue;
- HAL_DISABLE_INTERRUPTS();
- }
- /**************************************************************************************************
- * @fn halSetMaxSleepLoopTime
- *
- * @brief This function is to used to setup the maximum sleep loop time. This sleep loop time
- * should be lesser than T2 rollover so that a maximum of only one rollover occurs
- * when cc2530 is in sleep. This function should be called whenever rolloverTime is
- * changed using the function macBackoffTimerSetRollover(macTimerRollover);
- *
- * input parameters
- *
- * @param rolloverTime.
- *
- * output parameters
- *
- * None.
- *
- * @return None.
- **************************************************************************************************
- */
- void halSetMaxSleepLoopTime(uint32 rolloverTime)
- {
- if( rolloverTime > HAL_SLEEP_MS_TO_320US(MAX_SLEEP_TIME) )
- {
- maxSleepLoopTime = HAL_SLEEP_MS_TO_320US(MAX_SLEEP_TIME);
- }
- maxSleepLoopTime = (rolloverTime - TICKS_SUBTRACTED);
- }
- /**************************************************************************************************
- * @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’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
- {
- /* 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();
- }
- }
- }
- /**************************************************************************************************
- * @fn halSleepSetTimer
- *
- * @brief This function sets the CC2530 sleep timer compare value. First it reads and
- * stores the value of the sleep timer; this value is used later to update OSAL
- * timers. Then the timeout value is converted from 320 usec units to 32 kHz
- * period units and the compare value is set to the timeout.
- *
- * 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 halSleepSetTimer(uint32 timeout)
- {
- uint32 ticks;
- /* read the sleep timer; ST0 must be read first */
- ((uint8 *) &ticks)[UINT32_NDX0] = ST0;
- ((uint8 *) &ticks)[UINT32_NDX1] = ST1;
- ((uint8 *) &ticks)[UINT32_NDX2] = ST2;
- ((uint8 *) &ticks)[UINT32_NDX3] = 0;
- /* Compute sleep timer compare value. The ratio of 32 kHz ticks to 320 usec ticks
- * is 32768/3125 = 10.48576. This is nearly 671/64 = 10.484375.
- */
- ticks += (timeout * 671) / 64;
- /* subtract the processing time spent in function halSleep() */
- ticks -= HAL_SLEEP_ADJ_TICKS;
- /* set sleep timer compare; ST0 must be written last */
- ST2 = ((uint8 *) &ticks)[UINT32_NDX2];
- ST1 = ((uint8 *) &ticks)[UINT32_NDX1];
- ST0 = ((uint8 *) &ticks)[UINT32_NDX0];
- }
- /**************************************************************************************************
- * @fn TimerElapsed
- *
- * @brief Determine the number of OSAL timer ticks elapsed during sleep.
- * Deprecated for CC2530 and CC2430 SoC.
- *
- * input parameters
- *
- * @param None.
- *
- * output parameters
- *
- * None.
- *
- * @return Number of timer ticks elapsed during sleep.
- **************************************************************************************************
- */
- uint32 TimerElapsed( void )
- {
- /* Stubs */
- return (0);
- }
- /**************************************************************************************************
- * @fn halRestoreSleepLevel
- *
- * @brief Restore the deepest timer sleep level.
- *
- * input parameters
- *
- * @param None
- *
- * output parameters
- *
- * None.
- *
- * @return None.
- **************************************************************************************************
- */
- void halRestoreSleepLevel( void )
- {
- /* Stubs */
- }
- /**************************************************************************************************
- * @fn halSleepTimerIsr
- *
- * @brief Sleep timer ISR.
- *
- * input parameters
- *
- * None.
- *
- * output parameters
- *
- * 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
-
- CLEAR_SLEEP_MODE();
- HAL_EXIT_ISR();
- }
|