em_leuart.c 21 KB

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