EFM32 Zero Gecko Software Documentation  efm32zg-doc-4.2.1
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 /***************************************************************************/
52 /*******************************************************************************
53  ******************************* DEFINES ***********************************
54  ******************************************************************************/
55 
61 #if (LEUART_COUNT == 1)
62 #define LEUART_REF_VALID(ref) ((ref) == LEUART0)
63 #elif (LEUART_COUNT == 2)
64 #define LEUART_REF_VALID(ref) (((ref) == LEUART0) || ((ref) == LEUART1))
65 #else
66 #error "Undefined number of low energy UARTs (LEUART)."
67 #endif
68 
71 /*******************************************************************************
72  ************************** LOCAL FUNCTIONS ********************************
73  ******************************************************************************/
74 
77 /***************************************************************************/
88 __STATIC_INLINE void LEUART_Sync(LEUART_TypeDef *leuart, uint32_t mask)
89 {
90  /* Avoid deadlock if modifying the same register twice when freeze mode is */
91  /* activated. */
92  if (leuart->FREEZE & LEUART_FREEZE_REGFREEZE)
93  {
94  return;
95  }
96 
97  /* Wait for any pending previous write operation to have been completed */
98  /* in low frequency domain */
99  while (leuart->SYNCBUSY & mask)
100  ;
101 }
102 
105 /*******************************************************************************
106  ************************** GLOBAL FUNCTIONS *******************************
107  ******************************************************************************/
108 
109 /***************************************************************************/
129 uint32_t LEUART_BaudrateCalc(uint32_t refFreq, uint32_t clkdiv)
130 {
131  uint32_t divisor;
132  uint32_t remainder;
133  uint32_t quotient;
134  uint32_t br;
135 
136  /* Mask out unused bits */
137  clkdiv &= _LEUART_CLKDIV_MASK;
138 
139  /* We want to use integer division to avoid forcing in float division */
140  /* utils, and yet keep rounding effect errors to a minimum. */
141 
142  /*
143  * Baudrate is given by:
144  *
145  * br = fLEUARTn/(1 + (CLKDIV / 256))
146  *
147  * which can be rewritten to
148  *
149  * br = (256 * fLEUARTn)/(256 + CLKDIV)
150  *
151  * Normally, with fLEUARTn appr 32768Hz, there is no problem with overflow
152  * if using 32 bit arithmetic. However, since fLEUARTn may be derived from
153  * HFCORECLK as well, we must consider overflow when using integer arithmetic.
154  */
155 
156  /*
157  * The basic problem with integer division in the above formula is that
158  * the dividend (256 * fLEUARTn) may become higher than max 32 bit
159  * integer. Yet we want to evaluate dividend first before dividing in
160  * order to get as small rounding effects as possible. We do not want
161  * to make too harsh restrictions on max fLEUARTn value either.
162  *
163  * For division a/b, we can write
164  *
165  * a = qb + r
166  *
167  * where q is the quotient and r is the remainder, both integers.
168  *
169  * The orignal baudrate formula can be rewritten as
170  *
171  * br = 256a / b = 256(qb + r)/b = 256q + 256r/b
172  *
173  * where a is 'refFreq' and b is 'divisor', referring to variable names.
174  */
175 
176  divisor = 256 + clkdiv;
177  quotient = refFreq / divisor;
178  remainder = refFreq % divisor;
179 
180  /* Since divisor >= 256, the below cannot exceed max 32 bit value. */
181  br = 256 * quotient;
182 
183  /*
184  * Remainder < (256 + clkdiv), which means dividend (256 * remainder) worst case is
185  * 256*(256 + 0x7ff8) = 0x80F800.
186  */
187  br += (256 * remainder) / divisor;
188 
189  return br;
190 }
191 
192 
193 /***************************************************************************/
208 {
209  uint32_t freq;
210  CMU_Clock_TypeDef clock;
211 
212  /* Get current frequency */
213  if (leuart == LEUART0)
214  {
215  clock = cmuClock_LEUART0;
216  }
217 #if (LEUART_COUNT > 1)
218  else if (leuart == LEUART1)
219  {
220  clock = cmuClock_LEUART1;
221  }
222 #endif
223  else
224  {
225  EFM_ASSERT(0);
226  return 0;
227  }
228 
229  freq = CMU_ClockFreqGet(clock);
230 
231  return LEUART_BaudrateCalc(freq, leuart->CLKDIV);
232 }
233 
234 
235 /***************************************************************************/
256  uint32_t refFreq,
257  uint32_t baudrate)
258 {
259  uint32_t clkdiv;
260  CMU_Clock_TypeDef clock;
261 
262  /* Inhibit divide by 0 */
263  EFM_ASSERT(baudrate);
264 
265  /*
266  * We want to use integer division to avoid forcing in float division
267  * utils, and yet keep rounding effect errors to a minimum.
268  *
269  * CLKDIV in asynchronous mode is given by:
270  *
271  * CLKDIV = 256*(fLEUARTn/br - 1) = ((256*fLEUARTn)/br) - 256
272  *
273  * Normally, with fLEUARTn appr 32768Hz, there is no problem with overflow
274  * if using 32 bit arithmetic. However, since fLEUARTn may be derived from
275  * HFCORECLK as well, we must consider overflow when using integer arithmetic.
276  *
277  * The basic problem with integer division in the above formula is that
278  * the dividend (256 * fLEUARTn) may become higher than max 32 bit
279  * integer. Yet, we want to evaluate dividend first before dividing in
280  * order to get as small rounding effects as possible. We do not want
281  * to make too harsh restrictions on max fLEUARTn value either.
282  *
283  * Since the last 3 bits of CLKDIV are don't care, we can base our
284  * integer arithmetic on the below formula
285  *
286  * CLKDIV/8 = ((32*fLEUARTn)/br) - 32
287  *
288  * and calculate 1/8 of CLKDIV first. This allows for fLEUARTn
289  * up to 128MHz without overflowing a 32 bit value!
290  */
291 
292  /* Get current frequency? */
293  if (!refFreq)
294  {
295  if (leuart == LEUART0)
296  {
297  clock = cmuClock_LEUART0;
298  }
299 #if (LEUART_COUNT > 1)
300  else if (leuart == LEUART1)
301  {
302  clock = cmuClock_LEUART1;
303  }
304 #endif
305  else
306  {
307  EFM_ASSERT(0);
308  return;
309  }
310 
311  refFreq = CMU_ClockFreqGet(clock);
312  }
313 
314  /* Calculate and set CLKDIV with fractional bits */
315  clkdiv = (32 * refFreq) / baudrate;
316  clkdiv -= 32;
317  clkdiv *= 8;
318 
319  /* Verify that resulting clock divider is within limits */
320  EFM_ASSERT(clkdiv <= _LEUART_CLKDIV_MASK);
321 
322  /* If EFM_ASSERT is not enabled, make sure we don't write to reserved bits */
323  clkdiv &= _LEUART_CLKDIV_MASK;
324 
325  /* LF register about to be modified require sync. busy check */
326  LEUART_Sync(leuart, LEUART_SYNCBUSY_CLKDIV);
327 
328  leuart->CLKDIV = clkdiv;
329 }
330 
331 
332 /***************************************************************************/
353 {
354  uint32_t tmp;
355 
356  /* Make sure the module exists on the selected chip */
357  EFM_ASSERT(LEUART_REF_VALID(leuart));
358 
359  /* Disable as specified */
360  tmp = ~((uint32_t)(enable));
362  tmp <<= 1;
363  /* Enable as specified */
364  tmp |= (uint32_t)(enable);
365 
366  /* LF register about to be modified require sync. busy check */
367  LEUART_Sync(leuart, LEUART_SYNCBUSY_CMD);
368 
369  leuart->CMD = tmp;
370 }
371 
372 
373 /***************************************************************************/
400 void LEUART_FreezeEnable(LEUART_TypeDef *leuart, bool enable)
401 {
402  if (enable)
403  {
404  /*
405  * Wait for any ongoing LF synchronization to complete. This is just to
406  * protect against the rare case when a user
407  * - modifies a register requiring LF sync
408  * - then enables freeze before LF sync completed
409  * - then modifies the same register again
410  * since modifying a register while it is in sync progress should be
411  * avoided.
412  */
413  while (leuart->SYNCBUSY)
414  ;
415 
417  }
418  else
419  {
420  leuart->FREEZE = 0;
421  }
422 }
423 
424 
425 /***************************************************************************/
457 {
458  /* Make sure the module exists on the selected chip */
459  EFM_ASSERT(LEUART_REF_VALID(leuart));
460 
461  /* LF register about to be modified require sync. busy check */
462  LEUART_Sync(leuart, LEUART_SYNCBUSY_CMD);
463 
464  /* Ensure disabled while doing config */
466 
467  /* Freeze registers to avoid stalling for LF synchronization */
468  LEUART_FreezeEnable(leuart, true);
469 
470  /* Configure databits and stopbits */
471  leuart->CTRL = (leuart->CTRL & ~(_LEUART_CTRL_PARITY_MASK
473  | (uint32_t)(init->databits)
474  | (uint32_t)(init->parity)
475  | (uint32_t)(init->stopbits);
476 
477  /* Configure baudrate */
478  LEUART_BaudrateSet(leuart, init->refFreq, init->baudrate);
479 
480  /* Finally enable (as specified) */
481  leuart->CMD = (uint32_t)init->enable;
482 
483  /* Unfreeze registers, pass new settings on to LEUART */
484  LEUART_FreezeEnable(leuart, false);
485 }
486 
487 
488 /***************************************************************************/
496 {
497  /* Make sure the module exists on the selected chip */
498  EFM_ASSERT(LEUART_REF_VALID(leuart));
499 
500  /* Freeze registers to avoid stalling for LF synchronization */
501  LEUART_FreezeEnable(leuart, true);
502 
503  /* Make sure disabled first, before resetting other registers */
506  leuart->CTRL = _LEUART_CTRL_RESETVALUE;
510  leuart->IEN = _LEUART_IEN_RESETVALUE;
511  leuart->IFC = _LEUART_IFC_MASK;
513 #if defined(_LEUART_ROUTEPEN_MASK)
514  leuart->ROUTEPEN = _LEUART_ROUTEPEN_RESETVALUE;
515  leuart->ROUTELOC0 = _LEUART_ROUTELOC0_RESETVALUE;
516 #else
518 #endif
519 
520  /* Unfreeze registers, pass new settings on to LEUART */
521  LEUART_FreezeEnable(leuart, false);
522 }
523 
524 
525 /***************************************************************************/
546 uint8_t LEUART_Rx(LEUART_TypeDef *leuart)
547 {
548  while (!(leuart->STATUS & LEUART_STATUS_RXDATAV))
549  ;
550 
551  return (uint8_t)leuart->RXDATA;
552 }
553 
554 
555 /***************************************************************************/
572 uint16_t LEUART_RxExt(LEUART_TypeDef *leuart)
573 {
574  while (!(leuart->STATUS & LEUART_STATUS_RXDATAV))
575  ;
576 
577  return (uint16_t)leuart->RXDATAX;
578 }
579 
580 
581 /***************************************************************************/
604 void LEUART_Tx(LEUART_TypeDef *leuart, uint8_t data)
605 {
606  /* Check that transmit buffer is empty */
607  while (!(leuart->STATUS & LEUART_STATUS_TXBL))
608  ;
609 
610  /* LF register about to be modified require sync. busy check */
611  LEUART_Sync(leuart, LEUART_SYNCBUSY_TXDATA);
612 
613  leuart->TXDATA = (uint32_t)data;
614 }
615 
616 
617 /***************************************************************************/
636 void LEUART_TxExt(LEUART_TypeDef *leuart, uint16_t data)
637 {
638  /* Check that transmit buffer is empty */
639  while (!(leuart->STATUS & LEUART_STATUS_TXBL))
640  ;
641 
642  /* LF register about to be modified require sync. busy check */
643  LEUART_Sync(leuart, LEUART_SYNCBUSY_TXDATAX);
644 
645  leuart->TXDATAX = (uint32_t)data;
646 }
647 
648 /***************************************************************************/
660 void LEUART_TxDmaInEM2Enable(LEUART_TypeDef *leuart, bool enable)
661 {
662  /* LF register about to be modified require sync. busy check */
663  LEUART_Sync(leuart, LEUART_SYNCBUSY_CTRL);
664 
665  if (enable)
666  {
667  leuart->CTRL |= LEUART_CTRL_TXDMAWU;
668  }
669  else
670  {
671  leuart->CTRL &= ~LEUART_CTRL_TXDMAWU;
672  }
673 }
674 
675 /***************************************************************************/
687 void LEUART_RxDmaInEM2Enable(LEUART_TypeDef *leuart, bool enable)
688 {
689  /* LF register about to be modified require sync. busy check */
690  LEUART_Sync(leuart, LEUART_SYNCBUSY_CTRL);
691 
692  if (enable)
693  {
694  leuart->CTRL |= LEUART_CTRL_RXDMAWU;
695  }
696  else
697  {
698  leuart->CTRL &= ~LEUART_CTRL_RXDMAWU;
699  }
700 }
701 
702 
705 #endif /* defined(LEUART_COUNT) && (LEUART_COUNT > 0) */
Clock management unit (CMU) API.
void LEUART_Tx(LEUART_TypeDef *leuart, uint8_t data)
Transmit one frame.
Definition: em_leuart.c:604
#define LEUART_CMD_TXDIS
#define LEUART_SYNCBUSY_CLKDIV
LEUART_Stopbits_TypeDef stopbits
Definition: em_leuart.h:128
Emlib peripheral API "assert" implementation.
#define LEUART_CTRL_RXDMAWU
__IO uint32_t FREEZE
__I uint32_t RXDATAX
#define _LEUART_SIGFRAME_RESETVALUE
#define LEUART_CMD_RXDIS
#define LEUART_FREEZE_REGFREEZE
#define LEUART_SYNCBUSY_CMD
#define _LEUART_CMD_TXEN_MASK
#define _LEUART_PULSECTRL_RESETVALUE
#define LEUART_SYNCBUSY_CTRL
#define LEUART_CMD_RXBLOCKDIS
#define _LEUART_CTRL_STOPBITS_MASK
__IO uint32_t IFC
#define LEUART0
__IO uint32_t SIGFRAME
void LEUART_Init(LEUART_TypeDef *leuart, LEUART_Init_TypeDef const *init)
Init LEUART.
Definition: em_leuart.c:456
__IO uint32_t ROUTE
#define _LEUART_CTRL_PARITY_MASK
void LEUART_TxExt(LEUART_TypeDef *leuart, uint16_t data)
Transmit one 8-9 bit frame with extended control.
Definition: em_leuart.c:636
#define _LEUART_CLKDIV_MASK
__IO uint32_t STARTFRAME
#define _LEUART_IFC_MASK
__I uint32_t RXDATA
#define _LEUART_CMD_RXEN_MASK
__IO uint32_t CMD
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:255
#define LEUART_STATUS_RXDATAV
CMU_Clock_TypeDef
Definition: em_cmu.h:256
uint32_t LEUART_BaudrateGet(LEUART_TypeDef *leuart)
Get current baudrate for LEUART.
Definition: em_leuart.c:207
#define _LEUART_ROUTE_RESETVALUE
LEUART_Parity_TypeDef parity
Definition: em_leuart.h:125
#define LEUART_SYNCBUSY_TXDATAX
void LEUART_Enable(LEUART_TypeDef *leuart, LEUART_Enable_TypeDef enable)
Enable/disable LEUART receiver and/or transmitter.
Definition: em_leuart.c:352
__IO uint32_t CTRL
#define LEUART_SYNCBUSY_TXDATA
Low Energy Universal Asynchronous Receiver/Transmitter (LEUART) peripheral API.
#define _LEUART_STARTFRAME_RESETVALUE
__IO uint32_t PULSECTRL
__IO uint32_t IEN
void LEUART_Reset(LEUART_TypeDef *leuart)
Reset LEUART to same state as after a HW reset.
Definition: em_leuart.c:495
__IO uint32_t TXDATA
void LEUART_RxDmaInEM2Enable(LEUART_TypeDef *leuart, bool enable)
Enables handling of LEUART RX by DMA in EM2.
Definition: em_leuart.c:687
uint16_t LEUART_RxExt(LEUART_TypeDef *leuart)
Receive one 8-9 bit frame, with extended information.
Definition: em_leuart.c:572
__IO uint32_t TXDATAX
#define _LEUART_IEN_RESETVALUE
#define LEUART_CTRL_TXDMAWU
uint32_t LEUART_BaudrateCalc(uint32_t refFreq, uint32_t clkdiv)
Calculate baudrate for LEUART given reference frequency and clock division.
Definition: em_leuart.c:129
uint8_t LEUART_Rx(LEUART_TypeDef *leuart)
Receive one 8 bit frame, (or part of 9 bit frame).
Definition: em_leuart.c:546
__I uint32_t STATUS
void LEUART_TxDmaInEM2Enable(LEUART_TypeDef *leuart, bool enable)
Enables handling of LEUART TX by DMA in EM2.
Definition: em_leuart.c:660
__IO uint32_t CLKDIV
#define _LEUART_CTRL_RESETVALUE
uint32_t CMU_ClockFreqGet(CMU_Clock_TypeDef clock)
Get clock frequency for a clock point.
Definition: em_cmu.c:1482
__I uint32_t SYNCBUSY
void LEUART_FreezeEnable(LEUART_TypeDef *leuart, bool enable)
LEUART register synchronization freeze control.
Definition: em_leuart.c:400
LEUART_Enable_TypeDef
Definition: em_leuart.h:69
#define LEUART_CMD_CLEARTX
#define LEUART_STATUS_TXBL
LEUART_Databits_TypeDef databits
Definition: em_leuart.h:122
#define _LEUART_CLKDIV_RESETVALUE
#define LEUART_CMD_CLEARRX
LEUART_Enable_TypeDef enable
Definition: em_leuart.h:110