EFM32 Pearl Gecko 12 Software Documentation  efm32pg12-doc-5.1.2
em_leuart.c
Go to the documentation of this file.
1 /***************************************************************************/
34 #include "em_leuart.h"
35 #if defined(LEUART_COUNT) && (LEUART_COUNT > 0)
36 
37 #include "em_cmu.h"
38 #include "em_assert.h"
39 
40 /***************************************************************************/
45 /***************************************************************************/
57 /*******************************************************************************
58  ******************************* DEFINES ***********************************
59  ******************************************************************************/
60 
66 #if (LEUART_COUNT == 1)
67 #define LEUART_REF_VALID(ref) ((ref) == LEUART0)
68 #elif (LEUART_COUNT == 2)
69 #define LEUART_REF_VALID(ref) (((ref) == LEUART0) || ((ref) == LEUART1))
70 #else
71 #error "Undefined number of low energy UARTs (LEUART)."
72 #endif
73 
76 /*******************************************************************************
77  ************************** LOCAL FUNCTIONS ********************************
78  ******************************************************************************/
79 
82 /***************************************************************************/
93 __STATIC_INLINE void LEUART_Sync(LEUART_TypeDef *leuart, uint32_t mask)
94 {
95  /* Avoid deadlock if modifying the same register twice when freeze mode is */
96  /* activated. */
97  if (leuart->FREEZE & LEUART_FREEZE_REGFREEZE)
98  {
99  return;
100  }
101 
102  /* Wait for any pending previous write operation to have been completed */
103  /* in low frequency domain */
104  while (leuart->SYNCBUSY & mask)
105  ;
106 }
107 
110 /*******************************************************************************
111  ************************** GLOBAL FUNCTIONS *******************************
112  ******************************************************************************/
113 
114 /***************************************************************************/
134 uint32_t LEUART_BaudrateCalc(uint32_t refFreq, uint32_t clkdiv)
135 {
136  uint32_t divisor;
137  uint32_t remainder;
138  uint32_t quotient;
139  uint32_t br;
140 
141  /* Mask out unused bits */
142  clkdiv &= _LEUART_CLKDIV_MASK;
143 
144  /* We want to use integer division to avoid forcing in float division */
145  /* utils, and yet keep rounding effect errors to a minimum. */
146 
147  /*
148  * Baudrate is given by:
149  *
150  * br = fLEUARTn/(1 + (CLKDIV / 256))
151  *
152  * which can be rewritten to
153  *
154  * br = (256 * fLEUARTn)/(256 + CLKDIV)
155  *
156  * Normally, with fLEUARTn appr 32768Hz, there is no problem with overflow
157  * if using 32 bit arithmetic. However, since fLEUARTn may be derived from
158  * HFCORECLK as well, we must consider overflow when using integer arithmetic.
159  */
160 
161  /*
162  * The basic problem with integer division in the above formula is that
163  * the dividend (256 * fLEUARTn) may become higher than max 32 bit
164  * integer. Yet we want to evaluate dividend first before dividing in
165  * order to get as small rounding effects as possible. We do not want
166  * to make too harsh restrictions on max fLEUARTn value either.
167  *
168  * For division a/b, we can write
169  *
170  * a = qb + r
171  *
172  * where q is the quotient and r is the remainder, both integers.
173  *
174  * The orignal baudrate formula can be rewritten as
175  *
176  * br = 256a / b = 256(qb + r)/b = 256q + 256r/b
177  *
178  * where a is 'refFreq' and b is 'divisor', referring to variable names.
179  */
180 
181  divisor = 256 + clkdiv;
182  quotient = refFreq / divisor;
183  remainder = refFreq % divisor;
184 
185  /* Since divisor >= 256, the below cannot exceed max 32 bit value. */
186  br = 256 * quotient;
187 
188  /*
189  * Remainder < (256 + clkdiv), which means dividend (256 * remainder) worst case is
190  * 256*(256 + 0x7ff8) = 0x80F800.
191  */
192  br += (256 * remainder) / divisor;
193 
194  return br;
195 }
196 
197 
198 /***************************************************************************/
213 {
214  uint32_t freq;
215  CMU_Clock_TypeDef clock;
216 
217  /* Get current frequency */
218  if (leuart == LEUART0)
219  {
220  clock = cmuClock_LEUART0;
221  }
222 #if (LEUART_COUNT > 1)
223  else if (leuart == LEUART1)
224  {
225  clock = cmuClock_LEUART1;
226  }
227 #endif
228  else
229  {
230  EFM_ASSERT(0);
231  return 0;
232  }
233 
234  freq = CMU_ClockFreqGet(clock);
235 
236  return LEUART_BaudrateCalc(freq, leuart->CLKDIV);
237 }
238 
239 
240 /***************************************************************************/
261  uint32_t refFreq,
262  uint32_t baudrate)
263 {
264  uint32_t clkdiv;
265  CMU_Clock_TypeDef clock;
266 
267  /* Inhibit divide by 0 */
268  EFM_ASSERT(baudrate);
269 
270  /*
271  * We want to use integer division to avoid forcing in float division
272  * utils, and yet keep rounding effect errors to a minimum.
273  *
274  * CLKDIV in asynchronous mode is given by:
275  *
276  * CLKDIV = 256*(fLEUARTn/br - 1) = ((256*fLEUARTn)/br) - 256
277  *
278  * Normally, with fLEUARTn appr 32768Hz, there is no problem with overflow
279  * if using 32 bit arithmetic. However, since fLEUARTn may be derived from
280  * HFCORECLK as well, we must consider overflow when using integer arithmetic.
281  *
282  * The basic problem with integer division in the above formula is that
283  * the dividend (256 * fLEUARTn) may become higher than max 32 bit
284  * integer. Yet, we want to evaluate dividend first before dividing in
285  * order to get as small rounding effects as possible. We do not want
286  * to make too harsh restrictions on max fLEUARTn value either.
287  *
288  * Since the last 3 bits of CLKDIV are don't care, we can base our
289  * integer arithmetic on the below formula
290  *
291  * CLKDIV/8 = ((32*fLEUARTn)/br) - 32
292  *
293  * and calculate 1/8 of CLKDIV first. This allows for fLEUARTn
294  * up to 128MHz without overflowing a 32 bit value!
295  */
296 
297  /* Get current frequency? */
298  if (!refFreq)
299  {
300  if (leuart == LEUART0)
301  {
302  clock = cmuClock_LEUART0;
303  }
304 #if (LEUART_COUNT > 1)
305  else if (leuart == LEUART1)
306  {
307  clock = cmuClock_LEUART1;
308  }
309 #endif
310  else
311  {
312  EFM_ASSERT(0);
313  return;
314  }
315 
316  refFreq = CMU_ClockFreqGet(clock);
317  }
318 
319  /* Calculate and set CLKDIV with fractional bits */
320  clkdiv = (32 * refFreq) / baudrate;
321  clkdiv -= 32;
322  clkdiv *= 8;
323 
324  /* Verify that resulting clock divider is within limits */
325  EFM_ASSERT(clkdiv <= _LEUART_CLKDIV_MASK);
326 
327  /* If EFM_ASSERT is not enabled, make sure we don't write to reserved bits */
328  clkdiv &= _LEUART_CLKDIV_MASK;
329 
330  /* LF register about to be modified require sync. busy check */
331  LEUART_Sync(leuart, LEUART_SYNCBUSY_CLKDIV);
332 
333  leuart->CLKDIV = clkdiv;
334 }
335 
336 
337 /***************************************************************************/
358 {
359  uint32_t tmp;
360 
361  /* Make sure the module exists on the selected chip */
362  EFM_ASSERT(LEUART_REF_VALID(leuart));
363 
364  /* Disable as specified */
365  tmp = ~((uint32_t)(enable));
367  tmp <<= 1;
368  /* Enable as specified */
369  tmp |= (uint32_t)(enable);
370 
371  /* LF register about to be modified require sync. busy check */
372  LEUART_Sync(leuart, LEUART_SYNCBUSY_CMD);
373 
374  leuart->CMD = tmp;
375 }
376 
377 
378 /***************************************************************************/
405 void LEUART_FreezeEnable(LEUART_TypeDef *leuart, bool enable)
406 {
407  if (enable)
408  {
409  /*
410  * Wait for any ongoing LF synchronization to complete. This is just to
411  * protect against the rare case when a user
412  * - modifies a register requiring LF sync
413  * - then enables freeze before LF sync completed
414  * - then modifies the same register again
415  * since modifying a register while it is in sync progress should be
416  * avoided.
417  */
418  while (leuart->SYNCBUSY)
419  ;
420 
422  }
423  else
424  {
425  leuart->FREEZE = 0;
426  }
427 }
428 
429 
430 /***************************************************************************/
462 {
463  /* Make sure the module exists on the selected chip */
464  EFM_ASSERT(LEUART_REF_VALID(leuart));
465 
466  /* LF register about to be modified require sync. busy check */
467  LEUART_Sync(leuart, LEUART_SYNCBUSY_CMD);
468 
469  /* Ensure disabled while doing config */
471 
472  /* Freeze registers to avoid stalling for LF synchronization */
473  LEUART_FreezeEnable(leuart, true);
474 
475  /* Configure databits and stopbits */
476  leuart->CTRL = (leuart->CTRL & ~(_LEUART_CTRL_PARITY_MASK
478  | (uint32_t)(init->databits)
479  | (uint32_t)(init->parity)
480  | (uint32_t)(init->stopbits);
481 
482  /* Configure baudrate */
483  LEUART_BaudrateSet(leuart, init->refFreq, init->baudrate);
484 
485  /* Finally enable (as specified) */
486  leuart->CMD = (uint32_t)init->enable;
487 
488  /* Unfreeze registers, pass new settings on to LEUART */
489  LEUART_FreezeEnable(leuart, false);
490 }
491 
492 
493 /***************************************************************************/
501 {
502  /* Make sure the module exists on the selected chip */
503  EFM_ASSERT(LEUART_REF_VALID(leuart));
504 
505  /* Freeze registers to avoid stalling for LF synchronization */
506  LEUART_FreezeEnable(leuart, true);
507 
508  /* Make sure disabled first, before resetting other registers */
511  leuart->CTRL = _LEUART_CTRL_RESETVALUE;
515  leuart->IEN = _LEUART_IEN_RESETVALUE;
516  leuart->IFC = _LEUART_IFC_MASK;
518 #if defined(_LEUART_ROUTEPEN_MASK)
521 #else
522  leuart->ROUTE = _LEUART_ROUTE_RESETVALUE;
523 #endif
524 
525  /* Unfreeze registers, pass new settings on to LEUART */
526  LEUART_FreezeEnable(leuart, false);
527 }
528 
529 
530 /***************************************************************************/
551 uint8_t LEUART_Rx(LEUART_TypeDef *leuart)
552 {
553  while (!(leuart->STATUS & LEUART_STATUS_RXDATAV))
554  ;
555 
556  return (uint8_t)leuart->RXDATA;
557 }
558 
559 
560 /***************************************************************************/
577 uint16_t LEUART_RxExt(LEUART_TypeDef *leuart)
578 {
579  while (!(leuart->STATUS & LEUART_STATUS_RXDATAV))
580  ;
581 
582  return (uint16_t)leuart->RXDATAX;
583 }
584 
585 
586 /***************************************************************************/
609 void LEUART_Tx(LEUART_TypeDef *leuart, uint8_t data)
610 {
611  /* Check that transmit buffer is empty */
612  while (!(leuart->STATUS & LEUART_STATUS_TXBL))
613  ;
614 
615  /* LF register about to be modified require sync. busy check */
616  LEUART_Sync(leuart, LEUART_SYNCBUSY_TXDATA);
617 
618  leuart->TXDATA = (uint32_t)data;
619 }
620 
621 
622 /***************************************************************************/
641 void LEUART_TxExt(LEUART_TypeDef *leuart, uint16_t data)
642 {
643  /* Check that transmit buffer is empty */
644  while (!(leuart->STATUS & LEUART_STATUS_TXBL))
645  ;
646 
647  /* LF register about to be modified require sync. busy check */
648  LEUART_Sync(leuart, LEUART_SYNCBUSY_TXDATAX);
649 
650  leuart->TXDATAX = (uint32_t)data;
651 }
652 
653 /***************************************************************************/
665 void LEUART_TxDmaInEM2Enable(LEUART_TypeDef *leuart, bool enable)
666 {
667  /* LF register about to be modified require sync. busy check */
668  LEUART_Sync(leuart, LEUART_SYNCBUSY_CTRL);
669 
670  if (enable)
671  {
672  leuart->CTRL |= LEUART_CTRL_TXDMAWU;
673  }
674  else
675  {
676  leuart->CTRL &= ~LEUART_CTRL_TXDMAWU;
677  }
678 }
679 
680 /***************************************************************************/
692 void LEUART_RxDmaInEM2Enable(LEUART_TypeDef *leuart, bool enable)
693 {
694  /* LF register about to be modified require sync. busy check */
695  LEUART_Sync(leuart, LEUART_SYNCBUSY_CTRL);
696 
697  if (enable)
698  {
699  leuart->CTRL |= LEUART_CTRL_RXDMAWU;
700  }
701  else
702  {
703  leuart->CTRL &= ~LEUART_CTRL_RXDMAWU;
704  }
705 }
706 
707 
710 #endif /* defined(LEUART_COUNT) && (LEUART_COUNT > 0) */
Clock management unit (CMU) API.
#define _LEUART_CLKDIV_RESETVALUE
__IOM uint32_t CTRL
__IOM uint32_t PULSECTRL
#define LEUART_CMD_RXBLOCKDIS
#define LEUART_CMD_TXDIS
void LEUART_Tx(LEUART_TypeDef *leuart, uint8_t data)
Transmit one frame.
Definition: em_leuart.c:609
__IM uint32_t SYNCBUSY
#define _LEUART_CTRL_STOPBITS_MASK
LEUART_Stopbits_TypeDef stopbits
Definition: em_leuart.h:128
#define LEUART_SYNCBUSY_TXDATAX
Emlib peripheral API "assert" implementation.
#define LEUART_CMD_CLEARTX
__IOM uint32_t TXDATAX
#define _LEUART_CTRL_PARITY_MASK
#define LEUART_FREEZE_REGFREEZE
#define _LEUART_PULSECTRL_RESETVALUE
#define _LEUART_ROUTELOC0_RESETVALUE
__IOM uint32_t IEN
#define LEUART_SYNCBUSY_CMD
#define _LEUART_SIGFRAME_RESETVALUE
#define _LEUART_ROUTEPEN_RESETVALUE
void LEUART_Init(LEUART_TypeDef *leuart, LEUART_Init_TypeDef const *init)
Init LEUART.
Definition: em_leuart.c:461
#define _LEUART_IFC_MASK
void LEUART_TxExt(LEUART_TypeDef *leuart, uint16_t data)
Transmit one 8-9 bit frame with extended control.
Definition: em_leuart.c:641
#define LEUART_STATUS_RXDATAV
#define LEUART_SYNCBUSY_CTRL
__IOM uint32_t ROUTEPEN
#define _LEUART_CMD_RXEN_MASK
#define LEUART_CTRL_TXDMAWU
void LEUART_BaudrateSet(LEUART_TypeDef *leuart, uint32_t refFreq, uint32_t baudrate)
Configure baudrate (or as close as possible to specified baudrate).
Definition: em_leuart.c:260
CMU_Clock_TypeDef
Definition: em_cmu.h:257
uint32_t LEUART_BaudrateGet(LEUART_TypeDef *leuart)
Get current baudrate for LEUART.
Definition: em_leuart.c:212
#define _LEUART_CMD_TXEN_MASK
__IOM uint32_t TXDATA
__IOM uint32_t CLKDIV
__IOM uint32_t FREEZE
__IOM uint32_t STARTFRAME
LEUART_Parity_TypeDef parity
Definition: em_leuart.h:125
__IM uint32_t RXDATAX
void LEUART_Enable(LEUART_TypeDef *leuart, LEUART_Enable_TypeDef enable)
Enable/disable LEUART receiver and/or transmitter.
Definition: em_leuart.c:357
__IM uint32_t RXDATA
__IOM uint32_t ROUTELOC0
Low Energy Universal Asynchronous Receiver/Transmitter (LEUART) peripheral API.
void LEUART_Reset(LEUART_TypeDef *leuart)
Reset LEUART to same state as after a HW reset.
Definition: em_leuart.c:500
#define LEUART_CMD_CLEARRX
#define LEUART_CTRL_RXDMAWU
__IM uint32_t STATUS
void LEUART_RxDmaInEM2Enable(LEUART_TypeDef *leuart, bool enable)
Enables handling of LEUART RX by DMA in EM2.
Definition: em_leuart.c:692
uint16_t LEUART_RxExt(LEUART_TypeDef *leuart)
Receive one 8-9 bit frame, with extended information.
Definition: em_leuart.c:577
#define LEUART_CMD_RXDIS
#define _LEUART_CTRL_RESETVALUE
__IOM uint32_t SIGFRAME
__IOM uint32_t CMD
#define _LEUART_IEN_RESETVALUE
uint32_t LEUART_BaudrateCalc(uint32_t refFreq, uint32_t clkdiv)
Calculate baudrate for LEUART given reference frequency and clock division.
Definition: em_leuart.c:134
uint8_t LEUART_Rx(LEUART_TypeDef *leuart)
Receive one 8 bit frame, (or part of 9 bit frame).
Definition: em_leuart.c:551
void LEUART_TxDmaInEM2Enable(LEUART_TypeDef *leuart, bool enable)
Enables handling of LEUART TX by DMA in EM2.
Definition: em_leuart.c:665
#define _LEUART_STARTFRAME_RESETVALUE
#define _LEUART_CLKDIV_MASK
#define LEUART_SYNCBUSY_CLKDIV
__IOM uint32_t IFC
uint32_t CMU_ClockFreqGet(CMU_Clock_TypeDef clock)
Get clock frequency for a clock point.
Definition: em_cmu.c:1550
void LEUART_FreezeEnable(LEUART_TypeDef *leuart, bool enable)
LEUART register synchronization freeze control.
Definition: em_leuart.c:405
#define LEUART_STATUS_TXBL
LEUART_Enable_TypeDef
Definition: em_leuart.h:69
#define LEUART0
#define LEUART_SYNCBUSY_TXDATA
LEUART_Databits_TypeDef databits
Definition: em_leuart.h:122
LEUART_Enable_TypeDef enable
Definition: em_leuart.h:110