em_letimer.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533
  1. /***************************************************************************//**
  2. * @file
  3. * @brief Low Energy Timer (LETIMER) Peripheral API
  4. * @author Energy Micro AS
  5. * @version 3.0.0
  6. *******************************************************************************
  7. * @section License
  8. * <b>(C) Copyright 2012 Energy Micro AS, http://www.energymicro.com</b>
  9. *******************************************************************************
  10. *
  11. * Permission is granted to anyone to use this software for any purpose,
  12. * including commercial applications, and to alter it and redistribute it
  13. * freely, subject to the following restrictions:
  14. *
  15. * 1. The origin of this software must not be misrepresented; you must not
  16. * claim that you wrote the original software.
  17. * 2. Altered source versions must be plainly marked as such, and must not be
  18. * misrepresented as being the original software.
  19. * 3. This notice may not be removed or altered from any source distribution.
  20. *
  21. * DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Energy Micro AS has no
  22. * obligation to support this Software. Energy Micro AS is providing the
  23. * Software "AS IS", with no express or implied warranties of any kind,
  24. * including, but not limited to, any implied warranties of merchantability
  25. * or fitness for any particular purpose or warranties against infringement
  26. * of any proprietary rights of a third party.
  27. *
  28. * Energy Micro AS will not be liable for any consequential, incidental, or
  29. * special damages, or any other relief, or for any claim by any third party,
  30. * arising from your use of this Software.
  31. *
  32. ******************************************************************************/
  33. #include "em_letimer.h"
  34. #include "em_cmu.h"
  35. #include "em_assert.h"
  36. /***************************************************************************//**
  37. * @addtogroup EM_Library
  38. * @{
  39. ******************************************************************************/
  40. /***************************************************************************//**
  41. * @addtogroup LETIMER
  42. * @brief Low Energy Timer (LETIMER) Peripheral API
  43. * @{
  44. ******************************************************************************/
  45. /*******************************************************************************
  46. ******************************* DEFINES ***********************************
  47. ******************************************************************************/
  48. /** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
  49. /** Validation of valid comparator register for assert statements. */
  50. #define LETIMER_COMP_REG_VALID(reg) (((reg) <= 1))
  51. /** Validation of LETIMER register block pointer reference for assert statements. */
  52. #define LETIMER_REF_VALID(ref) ((ref) == LETIMER0)
  53. /** Validation of valid repeat counter register for assert statements. */
  54. #define LETIMER_REP_REG_VALID(reg) (((reg) <= 1))
  55. /** @endcond */
  56. /*******************************************************************************
  57. ************************** LOCAL FUNCTIONS ********************************
  58. ******************************************************************************/
  59. /** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
  60. #if defined(_EFM32_GECKO_FAMILY)
  61. /***************************************************************************//**
  62. * @brief
  63. * Wait for ongoing sync of register(s) to low frequency domain to complete.
  64. *
  65. * @note
  66. * This only applies to the Gecko Family, see the reference manual
  67. * chapter about Access to Low Energy Peripherals (Asynchronos Registers)
  68. * for details.
  69. *
  70. * @param[in] letimer
  71. * Pointer to LETIMER peripheral register block
  72. *
  73. * @param[in] mask
  74. * Bitmask corresponding to SYNCBUSY register defined bits, indicating
  75. * registers that must complete any ongoing synchronization.
  76. ******************************************************************************/
  77. __STATIC_INLINE void LETIMER_Sync(LETIMER_TypeDef *letimer, uint32_t mask)
  78. {
  79. /* Avoid deadlock if modifying the same register twice when freeze mode is */
  80. /* activated. */
  81. if (letimer->FREEZE & LETIMER_FREEZE_REGFREEZE)
  82. return;
  83. /* Wait for any pending previous write operation to have been completed */
  84. /* in low frequency domain, only required for Gecko Family of devices */
  85. while (letimer->SYNCBUSY & mask)
  86. ;
  87. }
  88. #endif
  89. /** @endcond */
  90. /*******************************************************************************
  91. ************************** GLOBAL FUNCTIONS *******************************
  92. ******************************************************************************/
  93. /***************************************************************************//**
  94. * @brief
  95. * Get LETIMER compare register value.
  96. *
  97. * @param[in] letimer
  98. * Pointer to LETIMER peripheral register block
  99. *
  100. * @param[in] comp
  101. * Compare register to get, either 0 or 1
  102. *
  103. * @return
  104. * Compare register value, 0 if invalid register selected.
  105. ******************************************************************************/
  106. uint32_t LETIMER_CompareGet(LETIMER_TypeDef *letimer, unsigned int comp)
  107. {
  108. uint32_t ret;
  109. EFM_ASSERT(LETIMER_REF_VALID(letimer) && LETIMER_COMP_REG_VALID(comp));
  110. /* Initialize selected compare value */
  111. switch (comp)
  112. {
  113. case 0:
  114. ret = letimer->COMP0;
  115. break;
  116. case 1:
  117. ret = letimer->COMP1;
  118. break;
  119. default:
  120. /* Unknown compare register selected */
  121. ret = 0;
  122. break;
  123. }
  124. return(ret);
  125. }
  126. /***************************************************************************//**
  127. * @brief
  128. * Set LETIMER compare register value.
  129. *
  130. * @note
  131. * The setting of a compare register requires synchronization into the
  132. * low frequency domain. If the same register is modified before a previous
  133. * update has completed, this function will stall until the previous
  134. * synchronization has completed. This only applies to the Gecko Family, see
  135. * comment in the LETIMER_Sync() internal function call.
  136. *
  137. * @param[in] letimer
  138. * Pointer to LETIMER peripheral register block
  139. *
  140. * @param[in] comp
  141. * Compare register to set, either 0 or 1
  142. *
  143. * @param[in] value
  144. * Initialization value (<= 0x0000ffff)
  145. ******************************************************************************/
  146. void LETIMER_CompareSet(LETIMER_TypeDef *letimer,
  147. unsigned int comp,
  148. uint32_t value)
  149. {
  150. volatile uint32_t *compReg;
  151. uint32_t syncbusy;
  152. EFM_ASSERT(LETIMER_REF_VALID(letimer) &&
  153. LETIMER_COMP_REG_VALID(comp) &&
  154. ((value & ~(_LETIMER_COMP0_COMP0_MASK >> _LETIMER_COMP0_COMP0_SHIFT)) == 0));
  155. /* Initialize selected compare value */
  156. switch (comp)
  157. {
  158. case 0:
  159. compReg = &(letimer->COMP0);
  160. syncbusy = LETIMER_SYNCBUSY_COMP0;
  161. break;
  162. case 1:
  163. compReg = &(letimer->COMP1);
  164. syncbusy = LETIMER_SYNCBUSY_COMP1;
  165. break;
  166. default:
  167. /* Unknown compare register selected, abort */
  168. return;
  169. }
  170. #if defined(_EFM32_GECKO_FAMILY)
  171. /* LF register about to be modified require sync. busy check */
  172. LETIMER_Sync(letimer, syncbusy);
  173. #endif
  174. *compReg = value;
  175. }
  176. /***************************************************************************//**
  177. * @brief
  178. * Start/stop LETIMER.
  179. *
  180. * @note
  181. * The enabling/disabling of the LETIMER modifies the LETIMER CMD register
  182. * which requires synchronization into the low frequency domain. If this
  183. * register is modified before a previous update to the same register has
  184. * completed, this function will stall until the previous synchronization has
  185. * completed. This only applies to the Gecko Family, see comment in the
  186. * LETIMER_Sync() internal function call.
  187. *
  188. * @param[in] letimer
  189. * Pointer to LETIMER peripheral register block.
  190. *
  191. * @param[in] enable
  192. * true to enable counting, false to disable.
  193. ******************************************************************************/
  194. void LETIMER_Enable(LETIMER_TypeDef *letimer, bool enable)
  195. {
  196. EFM_ASSERT(LETIMER_REF_VALID(letimer));
  197. #if defined(_EFM32_GECKO_FAMILY)
  198. /* LF register about to be modified require sync. busy check */
  199. LETIMER_Sync(letimer, LETIMER_SYNCBUSY_CMD);
  200. #endif
  201. if (enable)
  202. {
  203. letimer->CMD = LETIMER_CMD_START;
  204. }
  205. else
  206. {
  207. letimer->CMD = LETIMER_CMD_STOP;
  208. }
  209. }
  210. /***************************************************************************//**
  211. * @brief
  212. * LETIMER register synchronization freeze control.
  213. *
  214. * @details
  215. * Some LETIMER registers require synchronization into the low frequency (LF)
  216. * domain. The freeze feature allows for several such registers to be
  217. * modified before passing them to the LF domain simultaneously (which
  218. * takes place when the freeze mode is disabled).
  219. *
  220. * @note
  221. * When enabling freeze mode, this function will wait for all current
  222. * ongoing LETIMER synchronization to LF domain to complete (Normally
  223. * synchronization will not be in progress.) However for this reason, when
  224. * using freeze mode, modifications of registers requiring LF synchronization
  225. * should be done within one freeze enable/disable block to avoid unecessary
  226. * stalling.
  227. *
  228. * @param[in] letimer
  229. * Pointer to LETIMER peripheral register block.
  230. *
  231. * @param[in] enable
  232. * @li true - enable freeze, modified registers are not propagated to the
  233. * LF domain
  234. * @li false - disables freeze, modified registers are propagated to LF
  235. * domain
  236. ******************************************************************************/
  237. void LETIMER_FreezeEnable(LETIMER_TypeDef *letimer, bool enable)
  238. {
  239. if (enable)
  240. {
  241. /*
  242. * Wait for any ongoing LF synchronization to complete. This is just to
  243. * protect against the rare case when a user
  244. * - modifies a register requiring LF sync
  245. * - then enables freeze before LF sync completed
  246. * - then modifies the same register again
  247. * since modifying a register while it is in sync progress should be
  248. * avoided.
  249. */
  250. while (letimer->SYNCBUSY)
  251. ;
  252. letimer->FREEZE = LETIMER_FREEZE_REGFREEZE;
  253. }
  254. else
  255. {
  256. letimer->FREEZE = 0;
  257. }
  258. }
  259. /***************************************************************************//**
  260. * @brief
  261. * Initialize LETIMER.
  262. *
  263. * @details
  264. * Note that the compare/repeat values must be set separately with
  265. * LETIMER_CompareSet() and LETIMER_RepeatSet(). That should probably be done
  266. * prior to the use of this function if configuring the LETIMER to start when
  267. * initialization is completed.
  268. *
  269. * @note
  270. * The initialization of the LETIMER modifies the LETIMER CTRL/CMD registers
  271. * which require synchronization into the low frequency domain. If any of those
  272. * registers are modified before a previous update to the same register has
  273. * completed, this function will stall until the previous synchronization has
  274. * completed. This only applies to the Gecko Family, see comment in the
  275. * LETIMER_Sync() internal function call.
  276. *
  277. * @param[in] letimer
  278. * Pointer to LETIMER peripheral register block.
  279. *
  280. * @param[in] init
  281. * Pointer to LETIMER initialization structure.
  282. ******************************************************************************/
  283. void LETIMER_Init(LETIMER_TypeDef *letimer, const LETIMER_Init_TypeDef *init)
  284. {
  285. uint32_t tmp = 0;
  286. EFM_ASSERT(LETIMER_REF_VALID(letimer));
  287. /* Stop timer if specified to be disabled and running */
  288. if (!(init->enable) && (letimer->STATUS & LETIMER_STATUS_RUNNING))
  289. {
  290. #if defined(_EFM32_GECKO_FAMILY)
  291. /* LF register about to be modified require sync. busy check */
  292. LETIMER_Sync(letimer, LETIMER_SYNCBUSY_CMD);
  293. #endif
  294. letimer->CMD = LETIMER_CMD_STOP;
  295. }
  296. /* Configure DEBUGRUN flag, sets whether or not counter should be
  297. * updated when debugger is active */
  298. if (init->debugRun)
  299. {
  300. tmp |= LETIMER_CTRL_DEBUGRUN;
  301. }
  302. if (init->rtcComp0Enable)
  303. {
  304. tmp |= LETIMER_CTRL_RTCC0TEN;
  305. }
  306. if (init->rtcComp1Enable)
  307. {
  308. tmp |= LETIMER_CTRL_RTCC1TEN;
  309. }
  310. if (init->comp0Top)
  311. {
  312. tmp |= LETIMER_CTRL_COMP0TOP;
  313. }
  314. if (init->bufTop)
  315. {
  316. tmp |= LETIMER_CTRL_BUFTOP;
  317. }
  318. if (init->out0Pol)
  319. {
  320. tmp |= LETIMER_CTRL_OPOL0;
  321. }
  322. if (init->out1Pol)
  323. {
  324. tmp |= LETIMER_CTRL_OPOL1;
  325. }
  326. tmp |= init->ufoa0 << _LETIMER_CTRL_UFOA0_SHIFT;
  327. tmp |= init->ufoa1 << _LETIMER_CTRL_UFOA1_SHIFT;
  328. tmp |= init->repMode << _LETIMER_CTRL_REPMODE_SHIFT;
  329. #if defined(_EFM32_GECKO_FAMILY)
  330. /* LF register about to be modified require sync. busy check */
  331. LETIMER_Sync(letimer, LETIMER_SYNCBUSY_CTRL);
  332. #endif
  333. letimer->CTRL = tmp;
  334. /* Start timer if specified to be enabled and not already running */
  335. if (init->enable && !(letimer->STATUS & LETIMER_STATUS_RUNNING))
  336. {
  337. #if defined(_EFM32_GECKO_FAMILY)
  338. /* LF register about to be modified require sync. busy check */
  339. LETIMER_Sync(letimer, LETIMER_SYNCBUSY_CMD);
  340. #endif
  341. letimer->CMD = LETIMER_CMD_START;
  342. }
  343. }
  344. /***************************************************************************//**
  345. * @brief
  346. * Get LETIMER repeat register value.
  347. *
  348. * @param[in] letimer
  349. * Pointer to LETIMER peripheral register block
  350. *
  351. * @param[in] rep
  352. * Repeat register to get, either 0 or 1
  353. *
  354. * @return
  355. * Repeat register value, 0 if invalid register selected.
  356. ******************************************************************************/
  357. uint32_t LETIMER_RepeatGet(LETIMER_TypeDef *letimer, unsigned int rep)
  358. {
  359. uint32_t ret;
  360. EFM_ASSERT(LETIMER_REF_VALID(letimer) && LETIMER_REP_REG_VALID(rep));
  361. /* Initialize selected compare value */
  362. switch (rep)
  363. {
  364. case 0:
  365. ret = letimer->REP0;
  366. break;
  367. case 1:
  368. ret = letimer->REP1;
  369. break;
  370. default:
  371. /* Unknown compare register selected */
  372. ret = 0;
  373. break;
  374. }
  375. return(ret);
  376. }
  377. /***************************************************************************//**
  378. * @brief
  379. * Set LETIMER repeat counter register value.
  380. *
  381. * @note
  382. * The setting of a repeat counter register requires synchronization into the
  383. * low frequency domain. If the same register is modified before a previous
  384. * update has completed, this function will stall until the previous
  385. * synchronization has completed. This only applies to the Gecko Family, see
  386. * comment in the LETIMER_Sync() internal function call.
  387. *
  388. * @param[in] letimer
  389. * Pointer to LETIMER peripheral register block
  390. *
  391. * @param[in] rep
  392. * Repeat counter register to set, either 0 or 1
  393. *
  394. * @param[in] value
  395. * Initialization value (<= 0x0000ffff)
  396. ******************************************************************************/
  397. void LETIMER_RepeatSet(LETIMER_TypeDef *letimer,
  398. unsigned int rep,
  399. uint32_t value)
  400. {
  401. volatile uint32_t *repReg;
  402. #if defined(_EFM32_GECKO_FAMILY)
  403. uint32_t syncbusy;
  404. #endif
  405. EFM_ASSERT(LETIMER_REF_VALID(letimer) &&
  406. LETIMER_REP_REG_VALID(rep) &&
  407. ((value & ~(_LETIMER_REP0_REP0_MASK >> _LETIMER_REP0_REP0_SHIFT)) == 0));
  408. /* Initialize selected compare value */
  409. switch (rep)
  410. {
  411. case 0:
  412. repReg = &(letimer->REP0);
  413. #if defined(_EFM32_GECKO_FAMILY)
  414. syncbusy = LETIMER_SYNCBUSY_REP0;
  415. #endif
  416. break;
  417. case 1:
  418. repReg = &(letimer->REP1);
  419. #if defined(_EFM32_GECKO_FAMILY)
  420. syncbusy = LETIMER_SYNCBUSY_REP1;
  421. #endif
  422. break;
  423. default:
  424. /* Unknown compare register selected, abort */
  425. return;
  426. }
  427. #if defined(_EFM32_GECKO_FAMILY)
  428. /* LF register about to be modified require sync. busy check */
  429. LETIMER_Sync(letimer, syncbusy);
  430. #endif
  431. *repReg = value;
  432. }
  433. /***************************************************************************//**
  434. * @brief
  435. * Reset LETIMER to same state as after a HW reset.
  436. *
  437. * @note
  438. * The ROUTE register is NOT reset by this function, in order to allow for
  439. * centralized setup of this feature.
  440. *
  441. * @param[in] letimer
  442. * Pointer to LETIMER peripheral register block.
  443. ******************************************************************************/
  444. void LETIMER_Reset(LETIMER_TypeDef *letimer)
  445. {
  446. /* Freeze registers to avoid stalling for LF synchronization */
  447. LETIMER_FreezeEnable(letimer, true);
  448. /* Make sure disabled first, before resetting other registers */
  449. letimer->CMD = LETIMER_CMD_STOP | LETIMER_CMD_CLEAR |
  450. LETIMER_CMD_CTO0 | LETIMER_CMD_CTO1;
  451. letimer->CTRL = _LETIMER_CTRL_RESETVALUE;
  452. letimer->COMP0 = _LETIMER_COMP0_RESETVALUE;
  453. letimer->COMP1 = _LETIMER_COMP1_RESETVALUE;
  454. letimer->REP0 = _LETIMER_REP0_RESETVALUE;
  455. letimer->REP1 = _LETIMER_REP1_RESETVALUE;
  456. letimer->IEN = _LETIMER_IEN_RESETVALUE;
  457. letimer->IFC = _LETIMER_IFC_MASK;
  458. /* Do not reset route register, setting should be done independently */
  459. /* Unfreeze registers, pass new settings on to LETIMER */
  460. LETIMER_FreezeEnable(letimer, false);
  461. }
  462. /** @} (end addtogroup LETIMER) */
  463. /** @} (end addtogroup EM_Library) */