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