hal_sleep.c 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568
  1. /**************************************************************************************************
  2. Filename: hal_sleep.c
  3. Revised: $Date: 2012-03-07 11:55:12 -0800 (Wed, 07 Mar 2012) $
  4. Revision: $Revision: 29664 $
  5. Description: This module contains the HAL power management procedures for the CC2530.
  6. Copyright 2006-2012 Texas Instruments Incorporated. All rights reserved.
  7. IMPORTANT: Your use of this Software is limited to those specific rights
  8. granted under the terms of a software license agreement between the user
  9. who downloaded the software, his/her employer (which must be your employer)
  10. and Texas Instruments Incorporated (the "License"). You may not use this
  11. Software unless you agree to abide by the terms of the License. The License
  12. limits your use, and you acknowledge, that the Software may not be modified,
  13. copied or distributed unless embedded on a Texas Instruments microcontroller
  14. or used solely and exclusively in conjunction with a Texas Instruments radio
  15. frequency transceiver, which is integrated into your product. Other than for
  16. the foregoing purpose, you may not use, reproduce, copy, prepare derivative
  17. works of, modify, distribute, perform, display or sell this Software and/or
  18. its documentation for any purpose.
  19. YOU FURTHER ACKNOWLEDGE AND AGREE THAT THE SOFTWARE AND DOCUMENTATION ARE
  20. PROVIDED “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED,
  21. INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY, TITLE,
  22. NON-INFRINGEMENT AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL
  23. TEXAS INSTRUMENTS OR ITS LICENSORS BE LIABLE OR OBLIGATED UNDER CONTRACT,
  24. NEGLIGENCE, STRICT LIABILITY, CONTRIBUTION, BREACH OF WARRANTY, OR OTHER
  25. LEGAL EQUITABLE THEORY ANY DIRECT OR INDIRECT DAMAGES OR EXPENSES
  26. INCLUDING BUT NOT LIMITED TO ANY INCIDENTAL, SPECIAL, INDIRECT, PUNITIVE
  27. OR CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA, COST OF PROCUREMENT
  28. OF SUBSTITUTE GOODS, TECHNOLOGY, SERVICES, OR ANY CLAIMS BY THIRD PARTIES
  29. (INCLUDING BUT NOT LIMITED TO ANY DEFENSE THEREOF), OR OTHER SIMILAR COSTS.
  30. Should you have any questions regarding your right to use this Software,
  31. contact Texas Instruments Incorporated at www.TI.com.
  32. **************************************************************************************************/
  33. /* ------------------------------------------------------------------------------------------------
  34. * Includes
  35. * ------------------------------------------------------------------------------------------------
  36. */
  37. #include "hal_types.h"
  38. #include "hal_mcu.h"
  39. #include "hal_board.h"
  40. #include "hal_sleep.h"
  41. #include "hal_led.h"
  42. #include "hal_key.h"
  43. #include "mac_api.h"
  44. #include "OSAL.h"
  45. #include "OSAL_Timers.h"
  46. #include "OSAL_Tasks.h"
  47. #include "OSAL_PwrMgr.h"
  48. #include "OnBoard.h"
  49. #include "hal_drivers.h"
  50. #include "hal_assert.h"
  51. #include "mac_mcu.h"
  52. #include "ZGlobals.h"
  53. #include "hal_uart.h"
  54. #if !defined ZG_BUILD_ENDDEVICE_TYPE
  55. #define ZG_BUILD_ENDDEVICE_TYPE FALSE
  56. #endif
  57. #if ZG_BUILD_ENDDEVICE_TYPE && defined (NWK_AUTO_POLL)
  58. #include "nwk_globals.h"
  59. #include "ZGlobals.h"
  60. #endif
  61. /* ------------------------------------------------------------------------------------------------
  62. * Macros
  63. * ------------------------------------------------------------------------------------------------
  64. */
  65. /* POWER CONSERVATION DEFINITIONS
  66. * Sleep mode H/W definitions (enabled with POWER_SAVING compile option)
  67. */
  68. #define CC2530_PM0 0 /* PM0, Clock oscillators on, voltage regulator on */
  69. #define CC2530_PM1 1 /* PM1, 32.768 kHz oscillators on, voltage regulator on */
  70. #define CC2530_PM2 2 /* PM2, 32.768 kHz oscillators on, voltage regulator off */
  71. #define CC2530_PM3 3 /* PM3, All clock oscillators off, voltage regulator off */
  72. /* HAL power management mode is set according to the power management state. The default
  73. * setting is HAL_SLEEP_OFF. The actual value is tailored to different HW platform. Both
  74. * HAL_SLEEP_TIMER and HAL_SLEEP_DEEP selections will:
  75. * 1. turn off the system clock, and
  76. * 2. halt the MCU.
  77. * HAL_SLEEP_TIMER can be woken up by sleep timer interrupt, I/O interrupt and reset.
  78. * HAL_SLEEP_DEEP can be woken up by I/O interrupt and reset.
  79. */
  80. #define HAL_SLEEP_OFF CC2530_PM0
  81. #define HAL_SLEEP_TIMER CC2530_PM2
  82. #define HAL_SLEEP_DEEP CC2530_PM3
  83. /* MAX_SLEEP_TIME calculation:
  84. * Sleep timer maximum duration = 0xFFFF7F / 32768 Hz = 511.996 seconds
  85. * Round it to 510 seconds or 510000 ms
  86. */
  87. #define MAX_SLEEP_TIME 510000 /* maximum time to sleep allowed by ST */
  88. #define TICKS_SUBTRACTED 2
  89. /* minimum time to sleep, this macro is to:
  90. * 1. avoid thrashing in-and-out of sleep with short OSAL timer (~2ms)
  91. * 2. define minimum safe sleep period
  92. */
  93. #if !defined (PM_MIN_SLEEP_TIME)
  94. #define PM_MIN_SLEEP_TIME 14 /* default to minimum safe sleep time minimum CAP */
  95. #endif
  96. /* The PCON instruction must be 4-byte aligned. The following code may cause excessive power
  97. * consumption if not aligned. See linker file ".xcl" for actual placement.
  98. */
  99. #pragma location = "SLEEP_CODE"
  100. void halSetSleepMode(void);
  101. /* This value is used to adjust the sleep timer compare value such that the sleep timer
  102. * compare takes into account the amount of processing time spent in function halSleep().
  103. * The first value is determined by measuring the number of sleep timer ticks it from
  104. * the beginning of the function to entering sleep mode or more precisely, when
  105. * MAC_PwrNextTimeout() is called. The second value is determined by measuring the number
  106. * of sleep timer ticks from exit of sleep mode to the call to MAC_PwrOnReq() where the
  107. * MAC timer is restarted.
  108. */
  109. #define HAL_SLEEP_ADJ_TICKS (11 + 12)
  110. #ifndef HAL_SLEEP_DEBUG_POWER_MODE
  111. /* set CC2530 power mode; always use PM2 */
  112. #define HAL_SLEEP_PREP_POWER_MODE(mode) st( SLEEPCMD &= ~PMODE; /* clear mode bits */ \
  113. SLEEPCMD |= mode; /* set mode bits */ \
  114. while (!(STLOAD & LDRDY)); \
  115. halSleepPconValue = PCON_IDLE; \
  116. )
  117. #define HAL_SLEEP_SET_POWER_MODE() halSetSleepMode()
  118. #else
  119. /* Debug: don't set power mode, just block until sleep timer interrupt */
  120. #define HAL_SLEEP_PREP_POWER_MODE(mode) /* nothing */
  121. #define HAL_SLEEP_SET_POWER_MODE() st( while(halSleepInt == FALSE); \
  122. halSleepInt = FALSE; \
  123. HAL_DISABLE_INTERRUPTS(); \
  124. )
  125. #endif
  126. /* sleep and external interrupt port masks */
  127. #define STIE_BV BV(5)
  128. #define P0IE_BV BV(5)
  129. #define P1IE_BV BV(4)
  130. #define P2IE_BV BV(1)
  131. /* sleep timer interrupt control */
  132. #define HAL_SLEEP_TIMER_ENABLE_INT() st(IEN0 |= STIE_BV;) /* enable sleep timer interrupt */
  133. #define HAL_SLEEP_TIMER_DISABLE_INT() st(IEN0 &= ~STIE_BV;) /* disable sleep timer interrupt */
  134. #define HAL_SLEEP_TIMER_CLEAR_INT() st(STIF = 0;) /* clear sleep interrupt flag */
  135. /* backup interrupt enable registers before sleep */
  136. #define HAL_SLEEP_IE_BACKUP_AND_DISABLE(ien0, ien1, ien2) st(ien0 = IEN0; /* backup IEN0 register */ \
  137. ien1 = IEN1; /* backup IEN1 register */ \
  138. ien2 = IEN2; /* backup IEN2 register */ \
  139. IEN0 &= STIE_BV; /* disable IEN0 except STIE */ \
  140. IEN1 &= P0IE_BV; /* disable IEN1 except P0IE */ \
  141. IEN2 &= (P1IE_BV|P2IE_BV);) /* disable IEN2 except P1IE, P2IE */
  142. /* restore interrupt enable registers before sleep */
  143. #define HAL_SLEEP_IE_RESTORE(ien0, ien1, ien2) st(IEN0 = ien0; /* restore IEN0 register */ \
  144. IEN1 = ien1; /* restore IEN1 register */ \
  145. IEN2 = ien2;) /* restore IEN2 register */
  146. /* convert msec to 320 usec units with round */
  147. #define HAL_SLEEP_MS_TO_320US(ms) (((((uint32) (ms)) * 100) + 31) / 32)
  148. /* for optimized indexing of uint32's */
  149. #if HAL_MCU_LITTLE_ENDIAN()
  150. #define UINT32_NDX0 0
  151. #define UINT32_NDX1 1
  152. #define UINT32_NDX2 2
  153. #define UINT32_NDX3 3
  154. #else
  155. #define UINT32_NDX0 3
  156. #define UINT32_NDX1 2
  157. #define UINT32_NDX2 1
  158. #define UINT32_NDX3 0
  159. #endif
  160. static uint32 maxSleepLoopTime = HAL_SLEEP_MS_TO_320US(MAX_SLEEP_TIME);
  161. /* ------------------------------------------------------------------------------------------------
  162. * Global Variables
  163. * ------------------------------------------------------------------------------------------------
  164. */
  165. /* PCON register value to program when setting power mode */
  166. volatile __data uint8 halSleepPconValue = PCON_IDLE;
  167. /* ------------------------------------------------------------------------------------------------
  168. * Local Variables
  169. * ------------------------------------------------------------------------------------------------
  170. */
  171. /* HAL power management mode is set according to the power management state.
  172. */
  173. static uint8 halPwrMgtMode = HAL_SLEEP_OFF;
  174. #ifdef HAL_SLEEP_DEBUG_POWER_MODE
  175. static bool halSleepInt = FALSE;
  176. #endif
  177. /* ------------------------------------------------------------------------------------------------
  178. * Function Prototypes
  179. * ------------------------------------------------------------------------------------------------
  180. */
  181. void halSleepSetTimer(uint32 timeout);
  182. /**************************************************************************************************
  183. * @fn halSleep
  184. *
  185. * @brief This function put the CC2530 to sleep. The PCON instruction must be 4-byte aligned.
  186. * The following code may cause excessive power consumption if not aligned. See linker
  187. * file ".xcl" for actual placement.
  188. *
  189. * input parameters
  190. *
  191. * @param None.
  192. *
  193. * output parameters
  194. *
  195. * None.
  196. *
  197. * @return None.
  198. **************************************************************************************************
  199. */
  200. void halSetSleepMode(void)
  201. {
  202. PCON = halSleepPconValue;
  203. asm("NOP");
  204. }
  205. /**************************************************************************************************
  206. * @fn halSetMaxSleepLoopTime
  207. *
  208. * @brief This function is to used to setup the maximum sleep loop time. This sleep loop time
  209. * should be lesser than T2 rollover so that a maximum of only one rollover occurs
  210. * when cc2530 is in sleep. This function should be called whenever rolloverTime is
  211. * changed using the function macBackoffTimerSetRollover(macTimerRollover);
  212. *
  213. * input parameters
  214. *
  215. * @param rolloverTime.
  216. *
  217. * output parameters
  218. *
  219. * None.
  220. *
  221. * @return None.
  222. **************************************************************************************************
  223. */
  224. void halSetMaxSleepLoopTime(uint32 rolloverTime)
  225. {
  226. if( rolloverTime > HAL_SLEEP_MS_TO_320US(MAX_SLEEP_TIME) )
  227. {
  228. maxSleepLoopTime = HAL_SLEEP_MS_TO_320US(MAX_SLEEP_TIME);
  229. }
  230. maxSleepLoopTime = (rolloverTime - TICKS_SUBTRACTED);
  231. }
  232. /**************************************************************************************************
  233. * @fn halSleep
  234. *
  235. * @brief This function is called from the OSAL task loop using and existing OSAL
  236. * interface. It sets the low power mode of the MAC and the CC2530.
  237. *
  238. * input parameters
  239. *
  240. * @param osal_timeout - Next OSAL timer timeout.
  241. *
  242. * output parameters
  243. *
  244. * None.
  245. *
  246. * @return None.
  247. **************************************************************************************************
  248. */
  249. void halSleep( uint16 osal_timeout )
  250. {
  251. uint32 timeout;
  252. uint32 macTimeout = 0;
  253. /* get next OSAL timer expiration converted to 320 usec units */
  254. timeout = HAL_SLEEP_MS_TO_320US(osal_timeout);
  255. if (timeout == 0)
  256. {
  257. timeout = MAC_PwrNextTimeout();
  258. }
  259. else
  260. {
  261. /* get next MAC timer expiration */
  262. macTimeout = MAC_PwrNextTimeout();
  263. /* get lesser of two timeouts */
  264. if ((macTimeout != 0) && (macTimeout < timeout))
  265. {
  266. timeout = macTimeout;
  267. }
  268. }
  269. /* HAL_SLEEP_PM2 is entered only if the timeout is zero and
  270. * the device is a stimulated device.
  271. */
  272. halPwrMgtMode = (timeout == 0) ? HAL_SLEEP_DEEP : HAL_SLEEP_TIMER;
  273. /* DEEP sleep can only be entered when zgPollRate == 0.
  274. * This is to eliminate any possibility of entering PM3 between
  275. * two network timers.
  276. */
  277. #if ZG_BUILD_ENDDEVICE_TYPE && defined (NWK_AUTO_POLL)
  278. if ((timeout > HAL_SLEEP_MS_TO_320US(PM_MIN_SLEEP_TIME)) ||
  279. (timeout == 0 && zgPollRate == 0))
  280. #else
  281. if ((timeout > HAL_SLEEP_MS_TO_320US(PM_MIN_SLEEP_TIME)) ||
  282. (timeout == 0))
  283. #endif
  284. {
  285. halIntState_t ien0, ien1, ien2;
  286. HAL_ASSERT(HAL_INTERRUPTS_ARE_ENABLED());
  287. HAL_DISABLE_INTERRUPTS();
  288. /* always use "deep sleep" to turn off radio VREG on CC2530 */
  289. if (halSleepPconValue != 0 && MAC_PwrOffReq(MAC_PWR_SLEEP_DEEP) == MAC_SUCCESS)
  290. {
  291. /* The PCON value is not zero. There is no interrupt overriding the
  292. * sleep decision. Also, the radio granted the sleep request.
  293. */
  294. #if ((defined HAL_KEY) && (HAL_KEY == TRUE))
  295. /* get peripherals ready for sleep */
  296. HalKeyEnterSleep();
  297. #endif
  298. #ifdef HAL_SLEEP_DEBUG_LED
  299. HAL_TURN_OFF_LED3();
  300. #else
  301. /* use this to turn LEDs off during sleep */
  302. HalLedEnterSleep();
  303. #endif
  304. if(timeout > maxSleepLoopTime)
  305. {
  306. timeout = maxSleepLoopTime;
  307. }
  308. /* enable sleep timer interrupt */
  309. if (timeout != 0)
  310. {
  311. if (timeout > HAL_SLEEP_MS_TO_320US( MAX_SLEEP_TIME ))
  312. {
  313. timeout -= HAL_SLEEP_MS_TO_320US( MAX_SLEEP_TIME );
  314. halSleepSetTimer(HAL_SLEEP_MS_TO_320US( MAX_SLEEP_TIME ));
  315. }
  316. else
  317. {
  318. /* set sleep timer */
  319. halSleepSetTimer(timeout);
  320. }
  321. /* set up sleep timer interrupt */
  322. HAL_SLEEP_TIMER_CLEAR_INT();
  323. HAL_SLEEP_TIMER_ENABLE_INT();
  324. }
  325. #ifdef HAL_SLEEP_DEBUG_LED
  326. if (halPwrMgtMode == CC2530_PM1)
  327. {
  328. HAL_TURN_ON_LED1();
  329. }
  330. else
  331. {
  332. HAL_TURN_OFF_LED1();
  333. }
  334. #endif
  335. if (ZNP_CFG1_UART == znpCfg1)
  336. {
  337. HalUARTSuspend();
  338. }
  339. /* Prep CC2530 power mode */
  340. HAL_SLEEP_PREP_POWER_MODE(halPwrMgtMode);
  341. /* save interrupt enable registers and disable all interrupts */
  342. HAL_SLEEP_IE_BACKUP_AND_DISABLE(ien0, ien1, ien2);
  343. HAL_ENABLE_INTERRUPTS();
  344. /* set CC2530 power mode, interrupt is disabled after this function
  345. * Note that an ISR (that could wake up from power mode) which runs
  346. * between the previous instruction enabling interrupts and before
  347. * power mode is set would switch the halSleepPconValue so that
  348. * power mode shall not be entered in such a case.
  349. */
  350. HAL_SLEEP_SET_POWER_MODE();
  351. /* Disable interrupt immediately */
  352. HAL_DISABLE_INTERRUPTS();
  353. /* restore interrupt enable registers */
  354. HAL_SLEEP_IE_RESTORE(ien0, ien1, ien2);
  355. /* disable sleep timer interrupt */
  356. HAL_SLEEP_TIMER_DISABLE_INT();
  357. if (ZNP_CFG1_UART == znpCfg1)
  358. {
  359. HalUARTResume();
  360. }
  361. #ifdef HAL_SLEEP_DEBUG_LED
  362. HAL_TURN_ON_LED3();
  363. #else
  364. /* use this to turn LEDs back on after sleep */
  365. HalLedExitSleep();
  366. #endif
  367. #if ((defined HAL_KEY) && (HAL_KEY == TRUE))
  368. /* handle peripherals */
  369. (void)HalKeyExitSleep();
  370. #endif
  371. /* power on the MAC; blocks until completion */
  372. MAC_PwrOnReq();
  373. HAL_ENABLE_INTERRUPTS();
  374. /* For CC2530, T2 interrupt won’t be generated when the current count is greater than
  375. * the comparator. The interrupt is only generated when the current count is equal to
  376. * the comparator. When the CC2530 is waking up from sleep, there is a small window
  377. * that the count may be grater than the comparator, therefore, missing the interrupt.
  378. * This workaround will call the T2 ISR when the current T2 count is greater than the
  379. * comparator. The problem only occurs when POWER_SAVING is turned on, i.e. the 32KHz
  380. * drives the chip in sleep and SYNC start is used.
  381. */
  382. macMcuTimer2OverflowWorkaround();
  383. }
  384. else
  385. {
  386. /* An interrupt may have changed the sleep decision. Do not sleep at all. Turn on
  387. * the interrupt, exit normally, and the next sleep will be allowed.
  388. */
  389. HAL_ENABLE_INTERRUPTS();
  390. }
  391. }
  392. }
  393. /**************************************************************************************************
  394. * @fn halSleepSetTimer
  395. *
  396. * @brief This function sets the CC2530 sleep timer compare value. First it reads and
  397. * stores the value of the sleep timer; this value is used later to update OSAL
  398. * timers. Then the timeout value is converted from 320 usec units to 32 kHz
  399. * period units and the compare value is set to the timeout.
  400. *
  401. * input parameters
  402. *
  403. * @param timeout - Timeout value in 320 usec units. The sleep timer compare is set to
  404. * this value.
  405. *
  406. * output parameters
  407. *
  408. * None.
  409. *
  410. * @return None.
  411. **************************************************************************************************
  412. */
  413. void halSleepSetTimer(uint32 timeout)
  414. {
  415. uint32 ticks;
  416. /* read the sleep timer; ST0 must be read first */
  417. ((uint8 *) &ticks)[UINT32_NDX0] = ST0;
  418. ((uint8 *) &ticks)[UINT32_NDX1] = ST1;
  419. ((uint8 *) &ticks)[UINT32_NDX2] = ST2;
  420. ((uint8 *) &ticks)[UINT32_NDX3] = 0;
  421. /* Compute sleep timer compare value. The ratio of 32 kHz ticks to 320 usec ticks
  422. * is 32768/3125 = 10.48576. This is nearly 671/64 = 10.484375.
  423. */
  424. ticks += (timeout * 671) / 64;
  425. /* subtract the processing time spent in function halSleep() */
  426. ticks -= HAL_SLEEP_ADJ_TICKS;
  427. /* set sleep timer compare; ST0 must be written last */
  428. ST2 = ((uint8 *) &ticks)[UINT32_NDX2];
  429. ST1 = ((uint8 *) &ticks)[UINT32_NDX1];
  430. ST0 = ((uint8 *) &ticks)[UINT32_NDX0];
  431. }
  432. /**************************************************************************************************
  433. * @fn TimerElapsed
  434. *
  435. * @brief Determine the number of OSAL timer ticks elapsed during sleep.
  436. * Deprecated for CC2530 and CC2430 SoC.
  437. *
  438. * input parameters
  439. *
  440. * @param None.
  441. *
  442. * output parameters
  443. *
  444. * None.
  445. *
  446. * @return Number of timer ticks elapsed during sleep.
  447. **************************************************************************************************
  448. */
  449. uint32 TimerElapsed( void )
  450. {
  451. /* Stubs */
  452. return (0);
  453. }
  454. /**************************************************************************************************
  455. * @fn halRestoreSleepLevel
  456. *
  457. * @brief Restore the deepest timer sleep level.
  458. *
  459. * input parameters
  460. *
  461. * @param None
  462. *
  463. * output parameters
  464. *
  465. * None.
  466. *
  467. * @return None.
  468. **************************************************************************************************
  469. */
  470. void halRestoreSleepLevel( void )
  471. {
  472. /* Stubs */
  473. }
  474. /**************************************************************************************************
  475. * @fn halSleepTimerIsr
  476. *
  477. * @brief Sleep timer ISR.
  478. *
  479. * input parameters
  480. *
  481. * None.
  482. *
  483. * output parameters
  484. *
  485. * None.
  486. *
  487. * @return None.
  488. **************************************************************************************************
  489. */
  490. HAL_ISR_FUNCTION(halSleepTimerIsr, ST_VECTOR)
  491. {
  492. HAL_ENTER_ISR();
  493. HAL_SLEEP_TIMER_CLEAR_INT();
  494. #ifdef HAL_SLEEP_DEBUG_POWER_MODE
  495. halSleepInt = TRUE;
  496. #endif
  497. CLEAR_SLEEP_MODE();
  498. HAL_EXIT_ISR();
  499. }