em_leuart.c

Go to the documentation of this file.
00001 /***************************************************************************/
00035 #include "em_leuart.h"
00036 #if defined(LEUART_COUNT) && (LEUART_COUNT > 0)
00037 
00038 #include "em_cmu.h"
00039 #include "em_assert.h"
00040 
00041 /***************************************************************************/
00046 /***************************************************************************/
00053 /*******************************************************************************
00054  *******************************   DEFINES   ***********************************
00055  ******************************************************************************/
00056 
00062 #if (LEUART_COUNT == 1)
00063 #define LEUART_REF_VALID(ref)    ((ref) == LEUART0)
00064 #elif (LEUART_COUNT == 2)
00065 #define LEUART_REF_VALID(ref)    (((ref) == LEUART0) || ((ref) == LEUART1))
00066 #else
00067 #error Undefined number of low energy UARTs (LEUART).
00068 #endif
00069 
00072 /*******************************************************************************
00073  **************************   LOCAL FUNCTIONS   ********************************
00074  ******************************************************************************/
00075 
00078 /***************************************************************************/
00089 __STATIC_INLINE void LEUART_Sync(LEUART_TypeDef *leuart, uint32_t mask)
00090 {
00091   /* Avoid deadlock if modifying the same register twice when freeze mode is */
00092   /* activated. */
00093   if (leuart->FREEZE & LEUART_FREEZE_REGFREEZE)
00094   {
00095     return;
00096   }
00097 
00098   /* Wait for any pending previous write operation to have been completed */
00099   /* in low frequency domain */
00100   while (leuart->SYNCBUSY & mask)
00101     ;
00102 }
00103 
00106 /*******************************************************************************
00107  **************************   GLOBAL FUNCTIONS   *******************************
00108  ******************************************************************************/
00109 
00110 /***************************************************************************/
00130 uint32_t LEUART_BaudrateCalc(uint32_t refFreq, uint32_t clkdiv)
00131 {
00132   uint32_t divisor;
00133   uint32_t remainder;
00134   uint32_t quotient;
00135   uint32_t br;
00136 
00137   /* Mask out unused bits */
00138   clkdiv &= _LEUART_CLKDIV_MASK;
00139 
00140   /* We want to use integer division to avoid forcing in float division */
00141   /* utils, and yet keep rounding effect errors to a minimum. */
00142 
00143   /*
00144    * Baudrate is given by:
00145    *
00146    * br = fLEUARTn/(1 + (CLKDIV / 256))
00147    *
00148    * which can be rewritten to
00149    *
00150    * br = (256 * fLEUARTn)/(256 + CLKDIV)
00151    *
00152    * Normally, with fLEUARTn appr 32768Hz, there is no problem with overflow
00153    * if using 32 bit arithmetic. However, since fLEUARTn may be derived from
00154    * HFCORECLK as well, we must consider overflow when using integer arithmetic.
00155    */
00156 
00157   /*
00158    * The basic problem with integer division in the above formula is that
00159    * the dividend (256 * fLEUARTn) may become higher than max 32 bit
00160    * integer. Yet we want to evaluate dividend first before dividing in
00161    * order to get as small rounding effects as possible. We do not want
00162    * to make too harsh restrictions on max fLEUARTn value either.
00163    *
00164    * For division a/b, we can write
00165    *
00166    * a = qb + r
00167    *
00168    * where q is the quotient and r is the remainder, both integers.
00169    *
00170    * The orignal baudrate formula can be rewritten as
00171    *
00172    * br = 256a / b = 256(qb + r)/b = 256q + 256r/b
00173    *
00174    * where a is 'refFreq' and b is 'divisor', referring to variable names.
00175    */
00176 
00177   divisor = 256 + clkdiv;
00178 
00179   quotient  = refFreq / divisor;
00180   remainder = refFreq % divisor;
00181 
00182   /* Since divisor >= 256, the below cannot exceed max 32 bit value. */
00183   br = 256 * quotient;
00184 
00185   /*
00186    * Remainder < (256 + clkdiv), which means dividend (256 * remainder) worst case is
00187    * 256*(256 + 0x7ff8) = 0x80F800.
00188    */
00189   br += (256 * remainder) / divisor;
00190 
00191   return br;
00192 }
00193 
00194 
00195 /***************************************************************************/
00209 uint32_t LEUART_BaudrateGet(LEUART_TypeDef *leuart)
00210 {
00211   uint32_t          freq;
00212   CMU_Clock_TypeDef clock;
00213 
00214   /* Get current frequency */
00215   if (leuart == LEUART0)
00216   {
00217     clock = cmuClock_LEUART0;
00218   }
00219 #if (LEUART_COUNT > 1)
00220   else if (leuart == LEUART1)
00221   {
00222     clock = cmuClock_LEUART1;
00223   }
00224 #endif
00225   else
00226   {
00227     EFM_ASSERT(0);
00228     return 0;
00229   }
00230 
00231   freq = CMU_ClockFreqGet(clock);
00232 
00233   return LEUART_BaudrateCalc(freq, leuart->CLKDIV);
00234 }
00235 
00236 
00237 /***************************************************************************/
00257 void LEUART_BaudrateSet(LEUART_TypeDef *leuart,
00258                         uint32_t refFreq,
00259                         uint32_t baudrate)
00260 {
00261   uint32_t          clkdiv;
00262   CMU_Clock_TypeDef clock;
00263 
00264   /* Inhibit divide by 0 */
00265   EFM_ASSERT(baudrate);
00266 
00267   /*
00268    * We want to use integer division to avoid forcing in float division
00269    * utils, and yet keep rounding effect errors to a minimum.
00270    *
00271    * CLKDIV in asynchronous mode is given by:
00272    *
00273    * CLKDIV = 256*(fLEUARTn/br - 1) = ((256*fLEUARTn)/br) - 256
00274    *
00275    * Normally, with fLEUARTn appr 32768Hz, there is no problem with overflow
00276    * if using 32 bit arithmetic. However, since fLEUARTn may be derived from
00277    * HFCORECLK as well, we must consider overflow when using integer arithmetic.
00278    *
00279    * The basic problem with integer division in the above formula is that
00280    * the dividend (256 * fLEUARTn) may become higher than max 32 bit
00281    * integer. Yet, we want to evaluate dividend first before dividing in
00282    * order to get as small rounding effects as possible. We do not want
00283    * to make too harsh restrictions on max fLEUARTn value either.
00284    *
00285    * Since the last 3 bits of CLKDIV are don't care, we can base our
00286    * integer arithmetic on the below formula
00287    *
00288    * CLKDIV/8 = ((32*fLEUARTn)/br) - 32
00289    *
00290    * and calculate 1/8 of CLKDIV first. This allows for fLEUARTn
00291    * up to 128MHz without overflowing a 32 bit value!
00292    */
00293 
00294   /* Get current frequency? */
00295   if (!refFreq)
00296   {
00297     if (leuart == LEUART0)
00298     {
00299       clock = cmuClock_LEUART0;
00300     }
00301 #if (LEUART_COUNT > 1)
00302     else if (leuart == LEUART1)
00303     {
00304       clock = cmuClock_LEUART1;
00305     }
00306 #endif
00307     else
00308     {
00309       EFM_ASSERT(0);
00310       return;
00311     }
00312 
00313     refFreq = CMU_ClockFreqGet(clock);
00314   }
00315 
00316   /* Calculate and set CLKDIV with fractional bits */
00317   clkdiv  = (32 * refFreq) / baudrate;
00318   clkdiv -= 32;
00319   clkdiv *= 8;
00320 
00321   /* Verify that resulting clock divider is within limits */
00322   EFM_ASSERT(clkdiv <= _LEUART_CLKDIV_MASK);
00323 
00324   /* If EFM_ASSERT is not enabled, make sure we don't write to reserved bits */
00325   clkdiv &= _LEUART_CLKDIV_MASK;
00326 
00327   /* LF register about to be modified require sync. busy check */
00328   LEUART_Sync(leuart, LEUART_SYNCBUSY_CLKDIV);
00329 
00330   leuart->CLKDIV = clkdiv;
00331 }
00332 
00333 
00334 /***************************************************************************/
00354 void LEUART_Enable(LEUART_TypeDef *leuart, LEUART_Enable_TypeDef enable)
00355 {
00356   uint32_t tmp;
00357 
00358   /* Make sure the module exists on the selected chip */
00359   EFM_ASSERT(LEUART_REF_VALID(leuart));
00360 
00361   /* Disable as specified */
00362   tmp   = ~((uint32_t)(enable));
00363   tmp  &= (_LEUART_CMD_RXEN_MASK | _LEUART_CMD_TXEN_MASK);
00364   tmp <<= 1;
00365   /* Enable as specified */
00366   tmp |= (uint32_t)(enable);
00367 
00368   /* LF register about to be modified require sync. busy check */
00369   LEUART_Sync(leuart, LEUART_SYNCBUSY_CMD);
00370 
00371   leuart->CMD = tmp;
00372 }
00373 
00374 
00375 /***************************************************************************/
00402 void LEUART_FreezeEnable(LEUART_TypeDef *leuart, bool enable)
00403 {
00404   if (enable)
00405   {
00406     /*
00407      * Wait for any ongoing LF synchronization to complete. This is just to
00408      * protect against the rare case when a user
00409      * - modifies a register requiring LF sync
00410      * - then enables freeze before LF sync completed
00411      * - then modifies the same register again
00412      * since modifying a register while it is in sync progress should be
00413      * avoided.
00414      */
00415     while (leuart->SYNCBUSY)
00416       ;
00417 
00418     leuart->FREEZE = LEUART_FREEZE_REGFREEZE;
00419   }
00420   else
00421   {
00422     leuart->FREEZE = 0;
00423   }
00424 }
00425 
00426 
00427 /***************************************************************************/
00458 void LEUART_Init(LEUART_TypeDef *leuart, LEUART_Init_TypeDef const *init)
00459 {
00460   /* Make sure the module exists on the selected chip */
00461   EFM_ASSERT(LEUART_REF_VALID(leuart));
00462 
00463   /* LF register about to be modified require sync. busy check */
00464   LEUART_Sync(leuart, LEUART_SYNCBUSY_CMD);
00465 
00466   /* Ensure disabled while doing config */
00467   leuart->CMD = LEUART_CMD_RXDIS | LEUART_CMD_TXDIS;
00468 
00469   /* Freeze registers to avoid stalling for LF synchronization */
00470   LEUART_FreezeEnable(leuart, true);
00471 
00472   /* Configure databits and stopbits */
00473   leuart->CTRL = (leuart->CTRL & ~(_LEUART_CTRL_PARITY_MASK |
00474                                    _LEUART_CTRL_STOPBITS_MASK)) |
00475                  (uint32_t)(init->databits) |
00476                  (uint32_t)(init->parity) |
00477                  (uint32_t)(init->stopbits);
00478 
00479   /* Configure baudrate */
00480   LEUART_BaudrateSet(leuart, init->refFreq, init->baudrate);
00481 
00482   /* Finally enable (as specified) */
00483   leuart->CMD = (uint32_t)(init->enable);
00484 
00485   /* Unfreeze registers, pass new settings on to LEUART */
00486   LEUART_FreezeEnable(leuart, false);
00487 }
00488 
00489 
00490 /***************************************************************************/
00497 void LEUART_Reset(LEUART_TypeDef *leuart)
00498 {
00499   /* Make sure the module exists on the selected chip */
00500   EFM_ASSERT(LEUART_REF_VALID(leuart));
00501 
00502   /* Freeze registers to avoid stalling for LF synchronization */
00503   LEUART_FreezeEnable(leuart, true);
00504 
00505   /* Make sure disabled first, before resetting other registers */
00506   leuart->CMD = LEUART_CMD_RXDIS | LEUART_CMD_TXDIS | LEUART_CMD_RXBLOCKDIS |
00507                 LEUART_CMD_CLEARTX | LEUART_CMD_CLEARRX;
00508   leuart->CTRL       = _LEUART_CTRL_RESETVALUE;
00509   leuart->CLKDIV     = _LEUART_CLKDIV_RESETVALUE;
00510   leuart->STARTFRAME = _LEUART_STARTFRAME_RESETVALUE;
00511   leuart->SIGFRAME   = _LEUART_SIGFRAME_RESETVALUE;
00512   leuart->IEN        = _LEUART_IEN_RESETVALUE;
00513   leuart->IFC        = _LEUART_IFC_MASK;
00514   leuart->PULSECTRL  = _LEUART_PULSECTRL_RESETVALUE;
00515   leuart->ROUTE      = _LEUART_ROUTE_RESETVALUE;
00516 
00517   /* Unfreeze registers, pass new settings on to LEUART */
00518   LEUART_FreezeEnable(leuart, false);
00519 }
00520 
00521 
00522 /***************************************************************************/
00543 uint8_t LEUART_Rx(LEUART_TypeDef *leuart)
00544 {
00545   while (!(leuart->STATUS & LEUART_STATUS_RXDATAV))
00546     ;
00547 
00548   return (uint8_t)(leuart->RXDATA);
00549 }
00550 
00551 
00552 /***************************************************************************/
00569 uint16_t LEUART_RxExt(LEUART_TypeDef *leuart)
00570 {
00571   while (!(leuart->STATUS & LEUART_STATUS_RXDATAV))
00572     ;
00573 
00574   return (uint16_t)(leuart->RXDATAX);
00575 }
00576 
00577 
00578 /***************************************************************************/
00601 void LEUART_Tx(LEUART_TypeDef *leuart, uint8_t data)
00602 {
00603   /* Check that transmit buffer is empty */
00604   while (!(leuart->STATUS & LEUART_STATUS_TXBL))
00605     ;
00606 
00607   /* LF register about to be modified require sync. busy check */
00608   LEUART_Sync(leuart, LEUART_SYNCBUSY_TXDATA);
00609 
00610   leuart->TXDATA = (uint32_t)data;
00611 }
00612 
00613 
00614 /***************************************************************************/
00633 void LEUART_TxExt(LEUART_TypeDef *leuart, uint16_t data)
00634 {
00635   /* Check that transmit buffer is empty */
00636   while (!(leuart->STATUS & LEUART_STATUS_TXBL))
00637     ;
00638 
00639   /* LF register about to be modified require sync. busy check */
00640   LEUART_Sync(leuart, LEUART_SYNCBUSY_TXDATAX);
00641 
00642   leuart->TXDATAX = (uint32_t)data;
00643 }
00644 
00645 /***************************************************************************/
00657 void LEUART_TxDmaInEM2Enable(LEUART_TypeDef *leuart, bool enable)
00658 {
00659   /* LF register about to be modified require sync. busy check */
00660   LEUART_Sync(leuart, LEUART_SYNCBUSY_CTRL);
00661 
00662   if (enable)
00663   {
00664     leuart->CTRL |= LEUART_CTRL_TXDMAWU;
00665   }
00666   else
00667   {
00668     leuart->CTRL &= ~LEUART_CTRL_TXDMAWU;
00669   }
00670 }
00671 
00672 /***************************************************************************/
00684 void LEUART_RxDmaInEM2Enable(LEUART_TypeDef *leuart, bool enable)
00685 {
00686   /* LF register about to be modified require sync. busy check */
00687   LEUART_Sync(leuart, LEUART_SYNCBUSY_CTRL);
00688 
00689   if (enable)
00690   {
00691     leuart->CTRL |= LEUART_CTRL_RXDMAWU;
00692   }
00693   else
00694   {
00695     leuart->CTRL &= ~LEUART_CTRL_RXDMAWU;
00696   }
00697 }
00698 
00699 
00702 #endif /* defined(LEUART_COUNT) && (LEUART_COUNT > 0) */