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