efm32_leuart.c 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635
  1. /***************************************************************************//**
  2. * @file
  3. * @brief Low Energy Universal Asynchronous Receiver/Transmitter (LEUART)
  4. * peripheral module peripheral API for EFM32.
  5. * @author Energy Micro AS
  6. * @version 2.3.2
  7. *******************************************************************************
  8. * @section License
  9. * <b>(C) Copyright 2010 Energy Micro AS, http://www.energymicro.com</b>
  10. *******************************************************************************
  11. *
  12. * This source code is the property of Energy Micro AS. The source and compiled
  13. * code may only be used on Energy Micro "EFM32" microcontrollers.
  14. *
  15. * This copyright notice may not be removed from the source code nor changed.
  16. *
  17. * DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Energy Micro AS has no
  18. * obligation to support this Software. Energy Micro AS is providing the
  19. * Software "AS IS", with no express or implied warranties of any kind,
  20. * including, but not limited to, any implied warranties of merchantability
  21. * or fitness for any particular purpose or warranties against infringement
  22. * of any proprietary rights of a third party.
  23. *
  24. * Energy Micro AS will not be liable for any consequential, incidental, or
  25. * special damages, or any other relief, or for any claim by any third party,
  26. * arising from your use of this Software.
  27. *
  28. ******************************************************************************/
  29. #include "efm32_leuart.h"
  30. #include "efm32_cmu.h"
  31. #include "efm32_assert.h"
  32. /***************************************************************************//**
  33. * @addtogroup EFM32_Library
  34. * @{
  35. ******************************************************************************/
  36. /***************************************************************************//**
  37. * @addtogroup LEUART
  38. * @brief Low Energy Universal Asynchronous Receiver/Transmitter (LEUART)
  39. * Peripheral API for EFM32
  40. * @{
  41. ******************************************************************************/
  42. /*******************************************************************************
  43. ******************************* DEFINES ***********************************
  44. ******************************************************************************/
  45. /** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
  46. /** Validation of LEUART register block pointer reference
  47. * for assert statements. */
  48. #if (LEUART_COUNT == 1)
  49. #define LEUART_REF_VALID(ref) ((ref) == LEUART0)
  50. #elif (LEUART_COUNT == 2)
  51. #define LEUART_REF_VALID(ref) (((ref) == LEUART0) || ((ref) == LEUART1))
  52. #else
  53. #error Undefined number of low energy UARTs (LEUART).
  54. #endif
  55. /** @endcond */
  56. /*******************************************************************************
  57. ************************** LOCAL FUNCTIONS ********************************
  58. ******************************************************************************/
  59. /** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
  60. /***************************************************************************//**
  61. * @brief
  62. * Wait for ongoing sync of register(s) to low frequency domain to complete.
  63. *
  64. * @param[in] leuart
  65. * Pointer to LEUART peripheral register block
  66. *
  67. * @param[in] mask
  68. * Bitmask corresponding to SYNCBUSY register defined bits, indicating
  69. * registers that must complete any ongoing synchronization.
  70. ******************************************************************************/
  71. static __INLINE void LEUART_Sync(LEUART_TypeDef *leuart, uint32_t mask)
  72. {
  73. /* Avoid deadlock if modifying the same register twice when freeze mode is */
  74. /* activated. */
  75. if (leuart->FREEZE & LEUART_FREEZE_REGFREEZE)
  76. {
  77. return;
  78. }
  79. /* Wait for any pending previous write operation to have been completed */
  80. /* in low frequency domain */
  81. while (leuart->SYNCBUSY & mask)
  82. ;
  83. }
  84. /** @endcond */
  85. /*******************************************************************************
  86. ************************** GLOBAL FUNCTIONS *******************************
  87. ******************************************************************************/
  88. /***************************************************************************//**
  89. * @brief
  90. * Calculate baudrate for LEUART given reference frequency and clock division.
  91. *
  92. * @details
  93. * This function returns the baudrate that a LEUART module will use if
  94. * configured with the given frequency and clock divisor. Notice that
  95. * this function will not use actual HW configuration. It can be used
  96. * to determinate if a given configuration is sufficiently accurate for the
  97. * application.
  98. *
  99. * @param[in] refFreq
  100. * LEUART peripheral frequency used.
  101. *
  102. * @param[in] clkdiv
  103. * Clock division factor to be used.
  104. *
  105. * @return
  106. * Baudrate with given settings.
  107. ******************************************************************************/
  108. uint32_t LEUART_BaudrateCalc(uint32_t refFreq, uint32_t clkdiv)
  109. {
  110. uint32_t divisor;
  111. uint32_t remainder;
  112. uint32_t quotient;
  113. uint32_t br;
  114. /* Mask out unused bits */
  115. clkdiv &= _LEUART_CLKDIV_MASK;
  116. /* We want to use integer division to avoid forcing in float division */
  117. /* utils, and yet keep rounding effect errors to a minimum. */
  118. /*
  119. * Baudrate is given by:
  120. *
  121. * br = fLEUARTn/(1 + (CLKDIV / 256))
  122. *
  123. * which can be rewritten to
  124. *
  125. * br = (256 * fLEUARTn)/(256 + CLKDIV)
  126. *
  127. * Normally, with fLEUARTn appr 32768Hz, there is no problem with overflow
  128. * if using 32 bit arithmetic. However, since fLEUARTn may be derived from
  129. * HFCORECLK as well, we must consider overflow when using integer arithmetic.
  130. */
  131. /*
  132. * The basic problem with integer division in the above formula is that
  133. * the dividend (256 * fLEUARTn) may become higher than max 32 bit
  134. * integer. Yet we want to evaluate dividend first before dividing in
  135. * order to get as small rounding effects as possible. We do not want
  136. * to make too harsh restrictions on max fLEUARTn value either.
  137. *
  138. * For division a/b, we can write
  139. *
  140. * a = qb + r
  141. *
  142. * where q is the quotient and r is the remainder, both integers.
  143. *
  144. * The orignal baudrate formula can be rewritten as
  145. *
  146. * br = 256a / b = 256(qb + r)/b = 256q + 256r/b
  147. *
  148. * where a is 'refFreq' and b is 'divisor', referring to variable names.
  149. */
  150. divisor = 256 + clkdiv;
  151. quotient = refFreq / divisor;
  152. remainder = refFreq % divisor;
  153. /* Since divisor >= 256, the below cannot exceed max 32 bit value. */
  154. br = 256 * quotient;
  155. /*
  156. * Remainder < (256 + clkdiv), which means dividend (256 * remainder) worst case is
  157. * 256*(256 + 0x7ff8) = 0x80F800.
  158. */
  159. br += (256 * remainder) / divisor;
  160. return br;
  161. }
  162. /***************************************************************************//**
  163. * @brief
  164. * Get current baudrate for LEUART.
  165. *
  166. * @details
  167. * This function returns the actual baudrate (not considering oscillator
  168. * inaccuracies) used by a LEUART peripheral.
  169. *
  170. * @param[in] leuart
  171. * Pointer to LEUART peripheral register block.
  172. *
  173. * @return
  174. * Current baudrate.
  175. ******************************************************************************/
  176. uint32_t LEUART_BaudrateGet(LEUART_TypeDef *leuart)
  177. {
  178. uint32_t freq;
  179. CMU_Clock_TypeDef clock;
  180. /* Get current frequency */
  181. if (leuart == LEUART0)
  182. {
  183. clock = cmuClock_LEUART0;
  184. }
  185. #if (LEUART_COUNT > 1)
  186. else if (leuart == LEUART1)
  187. {
  188. clock = cmuClock_LEUART1;
  189. }
  190. #endif
  191. else
  192. {
  193. EFM_ASSERT(0);
  194. return 0;
  195. }
  196. freq = CMU_ClockFreqGet(clock);
  197. return LEUART_BaudrateCalc(freq, leuart->CLKDIV);
  198. }
  199. /***************************************************************************//**
  200. * @brief
  201. * Configure baudrate (or as close as possible to specified baudrate).
  202. *
  203. * @note
  204. * The setting of a baudrate requires synchronization into the
  205. * low frequency domain. If the same register is modified before a previous
  206. * update has completed, this function will stall until the previous
  207. * synchronization has completed.
  208. *
  209. * @param[in] leuart
  210. * Pointer to LEUART peripheral register block.
  211. *
  212. * @param[in] refFreq
  213. * LEUART reference clock frequency in Hz that will be used. If set to 0,
  214. * the currently configured reference clock is assumed.
  215. *
  216. * @param[in] baudrate
  217. * Baudrate to try to achieve for LEUART.
  218. ******************************************************************************/
  219. void LEUART_BaudrateSet(LEUART_TypeDef *leuart,
  220. uint32_t refFreq,
  221. uint32_t baudrate)
  222. {
  223. uint32_t clkdiv;
  224. CMU_Clock_TypeDef clock;
  225. /* Inhibit divide by 0 */
  226. EFM_ASSERT(baudrate);
  227. /*
  228. * We want to use integer division to avoid forcing in float division
  229. * utils, and yet keep rounding effect errors to a minimum.
  230. *
  231. * CLKDIV in asynchronous mode is given by:
  232. *
  233. * CLKDIV = 256*(fLEUARTn/br - 1) = ((256*fLEUARTn)/br) - 256
  234. *
  235. * Normally, with fLEUARTn appr 32768Hz, there is no problem with overflow
  236. * if using 32 bit arithmetic. However, since fLEUARTn may be derived from
  237. * HFCORECLK as well, we must consider overflow when using integer arithmetic.
  238. *
  239. * The basic problem with integer division in the above formula is that
  240. * the dividend (256 * fLEUARTn) may become higher than max 32 bit
  241. * integer. Yet, we want to evaluate dividend first before dividing in
  242. * order to get as small rounding effects as possible. We do not want
  243. * to make too harsh restrictions on max fLEUARTn value either.
  244. *
  245. * Since the last 3 bits of CLKDIV are don't care, we can base our
  246. * integer arithmetic on the below formula
  247. *
  248. * CLKDIV/8 = ((32*fLEUARTn)/br) - 32
  249. *
  250. * and calculate 1/8 of CLKDIV first. This allows for fLEUARTn
  251. * up to 128MHz without overflowing a 32 bit value!
  252. */
  253. /* Get current frequency? */
  254. if (!refFreq)
  255. {
  256. if (leuart == LEUART0)
  257. {
  258. clock = cmuClock_LEUART0;
  259. }
  260. #if (LEUART_COUNT > 1)
  261. else if (leuart == LEUART1)
  262. {
  263. clock = cmuClock_LEUART1;
  264. }
  265. #endif
  266. else
  267. {
  268. EFM_ASSERT(0);
  269. return;
  270. }
  271. refFreq = CMU_ClockFreqGet(clock);
  272. }
  273. /* Calculate and set CLKDIV with fractional bits */
  274. clkdiv = (32 * refFreq) / baudrate;
  275. clkdiv -= 32;
  276. clkdiv *= 8;
  277. /* LF register about to be modified require sync. busy check */
  278. LEUART_Sync(leuart, LEUART_SYNCBUSY_CLKDIV);
  279. leuart->CLKDIV = clkdiv;
  280. }
  281. /***************************************************************************//**
  282. * @brief
  283. * Enable/disable LEUART receiver and/or transmitter.
  284. *
  285. * @details
  286. * Notice that this function does not do any configuration. Enabling should
  287. * normally be done after initialization is done (if not enabled as part
  288. * of init).
  289. *
  290. * @note
  291. * Enabling/disabling requires synchronization into the low frequency domain.
  292. * If the same register is modified before a previous update has completed,
  293. * this function will stall until the previous synchronization has completed.
  294. *
  295. * @param[in] leuart
  296. * Pointer to LEUART peripheral register block.
  297. *
  298. * @param[in] enable
  299. * Select status for receiver/transmitter.
  300. ******************************************************************************/
  301. void LEUART_Enable(LEUART_TypeDef *leuart, LEUART_Enable_TypeDef enable)
  302. {
  303. uint32_t tmp;
  304. /* Make sure the module exists on the selected chip */
  305. EFM_ASSERT(LEUART_REF_VALID(leuart));
  306. /* Disable as specified */
  307. tmp = ~((uint32_t)(enable));
  308. tmp &= (_LEUART_CMD_RXEN_MASK | _LEUART_CMD_TXEN_MASK);
  309. tmp <<= 1;
  310. /* Enable as specified */
  311. tmp |= (uint32_t)(enable);
  312. /* LF register about to be modified require sync. busy check */
  313. LEUART_Sync(leuart, LEUART_SYNCBUSY_CMD);
  314. leuart->CMD = tmp;
  315. }
  316. /***************************************************************************//**
  317. * @brief
  318. * LEUART register synchronization freeze control.
  319. *
  320. * @details
  321. * Some LEUART registers require synchronization into the low frequency (LF)
  322. * domain. The freeze feature allows for several such registers to be
  323. * modified before passing them to the LF domain simultaneously (which
  324. * takes place when the freeze mode is disabled).
  325. *
  326. * @note
  327. * When enabling freeze mode, this function will wait for all current
  328. * ongoing LEUART synchronization to LF domain to complete (Normally
  329. * synchronization will not be in progress.) However for this reason, when
  330. * using freeze mode, modifications of registers requiring LF synchronization
  331. * should be done within one freeze enable/disable block to avoid unecessary
  332. * stalling.
  333. *
  334. * @param[in] leuart
  335. * Pointer to LEUART peripheral register block.
  336. *
  337. * @param[in] enable
  338. * @li true - enable freeze, modified registers are not propagated to the
  339. * LF domain
  340. * @li false - disables freeze, modified registers are propagated to LF
  341. * domain
  342. ******************************************************************************/
  343. void LEUART_FreezeEnable(LEUART_TypeDef *leuart, bool enable)
  344. {
  345. if (enable)
  346. {
  347. /*
  348. * Wait for any ongoing LF synchronization to complete. This is just to
  349. * protect against the rare case when a user
  350. * - modifies a register requiring LF sync
  351. * - then enables freeze before LF sync completed
  352. * - then modifies the same register again
  353. * since modifying a register while it is in sync progress should be
  354. * avoided.
  355. */
  356. while (leuart->SYNCBUSY)
  357. ;
  358. leuart->FREEZE = LEUART_FREEZE_REGFREEZE;
  359. }
  360. else
  361. {
  362. leuart->FREEZE = 0;
  363. }
  364. }
  365. /***************************************************************************//**
  366. * @brief
  367. * Init LEUART.
  368. *
  369. * @details
  370. * This function will configure basic settings in order to operate in normal
  371. * asynchronous mode. Consider using LEUART_Reset() prior to this function if
  372. * state of configuration is not known, since only configuration settings
  373. * specified by @p init are set.
  374. *
  375. * Special control setup not covered by this function may be done either
  376. * before or after using this function (but normally before enabling)
  377. * by direct modification of the CTRL register.
  378. *
  379. * Notice that pins used by the LEUART module must be properly configured
  380. * by the user explicitly, in order for the LEUART to work as intended.
  381. * (When configuring pins, one should remember to consider the sequence of
  382. * configuration, in order to avoid unintended pulses/glitches on output
  383. * pins.)
  384. *
  385. * @note
  386. * Initializing requires synchronization into the low frequency domain.
  387. * If the same register is modified before a previous update has completed,
  388. * this function will stall until the previous synchronization has completed.
  389. *
  390. * @param[in] leuart
  391. * Pointer to LEUART peripheral register block.
  392. *
  393. * @param[in] init
  394. * Pointer to initialization structure used to configure basic async setup.
  395. ******************************************************************************/
  396. void LEUART_Init(LEUART_TypeDef *leuart, LEUART_Init_TypeDef *init)
  397. {
  398. /* Make sure the module exists on the selected chip */
  399. EFM_ASSERT(LEUART_REF_VALID(leuart));
  400. /* LF register about to be modified require sync. busy check */
  401. LEUART_Sync(leuart, LEUART_SYNCBUSY_CMD);
  402. /* Ensure disabled while doing config */
  403. leuart->CMD = LEUART_CMD_RXDIS | LEUART_CMD_TXDIS;
  404. /* Freeze registers to avoid stalling for LF synchronization */
  405. LEUART_FreezeEnable(leuart, true);
  406. /* Configure databits and stopbits */
  407. leuart->CTRL = (leuart->CTRL & ~(_LEUART_CTRL_PARITY_MASK |
  408. _LEUART_CTRL_STOPBITS_MASK)) |
  409. (uint32_t)(init->databits) |
  410. (uint32_t)(init->parity) |
  411. (uint32_t)(init->stopbits);
  412. /* Configure baudrate */
  413. LEUART_BaudrateSet(leuart, init->refFreq, init->baudrate);
  414. /* Finally enable (as specified) */
  415. leuart->CMD = (uint32_t)(init->enable);
  416. /* Unfreeze registers, pass new settings on to LEUART */
  417. LEUART_FreezeEnable(leuart, false);
  418. }
  419. /***************************************************************************//**
  420. * @brief
  421. * Reset LEUART to same state as after a HW reset.
  422. *
  423. * @param[in] leuart
  424. * Pointer to LEUART peripheral register block.
  425. ******************************************************************************/
  426. void LEUART_Reset(LEUART_TypeDef *leuart)
  427. {
  428. /* Make sure the module exists on the selected chip */
  429. EFM_ASSERT(LEUART_REF_VALID(leuart));
  430. /* Freeze registers to avoid stalling for LF synchronization */
  431. LEUART_FreezeEnable(leuart, true);
  432. /* Make sure disabled first, before resetting other registers */
  433. leuart->CMD = LEUART_CMD_RXDIS | LEUART_CMD_TXDIS | LEUART_CMD_RXBLOCKDIS |
  434. LEUART_CMD_CLEARTX | LEUART_CMD_CLEARRX;
  435. leuart->CTRL = _LEUART_CTRL_RESETVALUE;
  436. leuart->CLKDIV = _LEUART_CLKDIV_RESETVALUE;
  437. leuart->STARTFRAME = _LEUART_STARTFRAME_RESETVALUE;
  438. leuart->SIGFRAME = _LEUART_SIGFRAME_RESETVALUE;
  439. leuart->IEN = _LEUART_IEN_RESETVALUE;
  440. leuart->IFC = _LEUART_IFC_MASK;
  441. leuart->PULSECTRL = _LEUART_PULSECTRL_RESETVALUE;
  442. leuart->ROUTE = _LEUART_ROUTE_RESETVALUE;
  443. /* Do not reset route register, setting should be done independently */
  444. /* Unfreeze registers, pass new settings on to LEUART */
  445. LEUART_FreezeEnable(leuart, false);
  446. }
  447. /***************************************************************************//**
  448. * @brief
  449. * Receive one 8 bit frame, (or part of 9 bit frame).
  450. *
  451. * @details
  452. * This function is normally used to receive one frame when operating with
  453. * frame length 8 bits. Please refer to LEUART_RxExt() for reception of
  454. * 9 bit frames.
  455. *
  456. * Notice that possible parity/stop bits are not considered part of specified
  457. * frame bit length.
  458. *
  459. * @note
  460. * This function will stall if buffer is empty, until data is received.
  461. *
  462. * @param[in] leuart
  463. * Pointer to LEUART peripheral register block.
  464. *
  465. * @return
  466. * Data received.
  467. ******************************************************************************/
  468. uint8_t LEUART_Rx(LEUART_TypeDef *leuart)
  469. {
  470. while (!(leuart->STATUS & LEUART_STATUS_RXDATAV))
  471. ;
  472. return (uint8_t)(leuart->RXDATA);
  473. }
  474. /***************************************************************************//**
  475. * @brief
  476. * Receive one 8-9 bit frame, with extended information.
  477. *
  478. * @details
  479. * This function is normally used to receive one frame and additional RX
  480. * status information is required.
  481. *
  482. * @note
  483. * This function will stall if buffer is empty, until data is received.
  484. *
  485. * @param[in] leuart
  486. * Pointer to LEUART peripheral register block.
  487. *
  488. * @return
  489. * Data received.
  490. ******************************************************************************/
  491. uint16_t LEUART_RxExt(LEUART_TypeDef *leuart)
  492. {
  493. while (!(leuart->STATUS & LEUART_STATUS_RXDATAV))
  494. ;
  495. return (uint16_t)(leuart->RXDATAX);
  496. }
  497. /***************************************************************************//**
  498. * @brief
  499. * Transmit one frame.
  500. *
  501. * @details
  502. * Depending on frame length configuration, 8 (least significant) bits from
  503. * @p data are transmitted. If frame length is 9, 8 bits are transmitted from
  504. * @p data and one bit as specified by CTRL register, BIT8DV field. Please
  505. * refer to LEUART_TxExt() for transmitting 9 bit frame with full control of
  506. * all 9 bits.
  507. *
  508. * Notice that possible parity/stop bits in asynchronous mode are not
  509. * considered part of specified frame bit length.
  510. *
  511. * @note
  512. * This function will stall if buffer is full, until buffer becomes available.
  513. *
  514. * @param[in] leuart
  515. * Pointer to LEUART peripheral register block.
  516. *
  517. * @param[in] data
  518. * Data to transmit. See details above for further info.
  519. ******************************************************************************/
  520. void LEUART_Tx(LEUART_TypeDef *leuart, uint8_t data)
  521. {
  522. /* Check that transmit buffer is empty */
  523. while (!(leuart->STATUS & LEUART_STATUS_TXBL))
  524. ;
  525. /* LF register about to be modified require sync. busy check */
  526. LEUART_Sync(leuart, LEUART_SYNCBUSY_TXDATA);
  527. leuart->TXDATA = (uint32_t)data;
  528. }
  529. /***************************************************************************//**
  530. * @brief
  531. * Transmit one 8-9 bit frame with extended control.
  532. *
  533. * @details
  534. * Notice that possible parity/stop bits in asynchronous mode are not
  535. * considered part of specified frame bit length.
  536. *
  537. * @note
  538. * This function will stall if buffer is full, until buffer becomes available.
  539. *
  540. * @param[in] leuart
  541. * Pointer to LEUART peripheral register block.
  542. *
  543. * @param[in] data
  544. * Data to transmit with extended control. Least significant bits contains
  545. * frame bits, and additional control bits are available as documented in
  546. * the EFM32 reference manual (set to 0 if not used).
  547. ******************************************************************************/
  548. void LEUART_TxExt(LEUART_TypeDef *leuart, uint16_t data)
  549. {
  550. /* Check that transmit buffer is empty */
  551. while (!(leuart->STATUS & LEUART_STATUS_TXBL))
  552. ;
  553. /* LF register about to be modified require sync. busy check */
  554. LEUART_Sync(leuart, LEUART_SYNCBUSY_TXDATAX);
  555. leuart->TXDATAX = (uint32_t)data;
  556. }
  557. /** @} (end addtogroup LEUART) */
  558. /** @} (end addtogroup EFM32_Library) */