added prescaler for 16 bit pwm in LPC1347 target

Fork of mbed-dev by mbed official

Revision:
0:9b334a45a8ff
Child:
50:a417edff4437
diff -r 000000000000 -r 9b334a45a8ff targets/hal/TARGET_Silicon_Labs/TARGET_EFM32/emlib/src/em_leuart.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/targets/hal/TARGET_Silicon_Labs/TARGET_EFM32/emlib/src/em_leuart.c	Thu Oct 01 15:25:22 2015 +0300
@@ -0,0 +1,702 @@
+/***************************************************************************//**
+ * @file em_leuart.c
+ * @brief Low Energy Universal Asynchronous Receiver/Transmitter (LEUART)
+ *   Peripheral API
+ * @version 3.20.12
+ *******************************************************************************
+ * @section License
+ * <b>(C) Copyright 2014 Silicon Labs, http://www.silabs.com</b>
+ *******************************************************************************
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ *    claim that you wrote the original software.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ * DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Silicon Labs has no
+ * obligation to support this Software. Silicon Labs is providing the
+ * Software "AS IS", with no express or implied warranties of any kind,
+ * including, but not limited to, any implied warranties of merchantability
+ * or fitness for any particular purpose or warranties against infringement
+ * of any proprietary rights of a third party.
+ *
+ * Silicon Labs will not be liable for any consequential, incidental, or
+ * special damages, or any other relief, or for any claim by any third party,
+ * arising from your use of this Software.
+ *
+ ******************************************************************************/
+
+
+#include "em_leuart.h"
+#if defined(LEUART_COUNT) && (LEUART_COUNT > 0)
+
+#include "em_cmu.h"
+#include "em_assert.h"
+
+/***************************************************************************//**
+ * @addtogroup EM_Library
+ * @{
+ ******************************************************************************/
+
+/***************************************************************************//**
+ * @addtogroup LEUART
+ * @brief Low Energy Universal Asynchronous Receiver/Transmitter (LEUART)
+ *        Peripheral API
+ * @{
+ ******************************************************************************/
+
+/*******************************************************************************
+ *******************************   DEFINES   ***********************************
+ ******************************************************************************/
+
+/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
+
+
+/** Validation of LEUART register block pointer reference
+ *  for assert statements. */
+#if (LEUART_COUNT == 1)
+#define LEUART_REF_VALID(ref)    ((ref) == LEUART0)
+#elif (LEUART_COUNT == 2)
+#define LEUART_REF_VALID(ref)    (((ref) == LEUART0) || ((ref) == LEUART1))
+#else
+#error Undefined number of low energy UARTs (LEUART).
+#endif
+
+/** @endcond */
+
+/*******************************************************************************
+ **************************   LOCAL FUNCTIONS   ********************************
+ ******************************************************************************/
+
+/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
+
+/***************************************************************************//**
+ * @brief
+ *   Wait for ongoing sync of register(s) to low frequency domain to complete.
+ *
+ * @param[in] leuart
+ *   Pointer to LEUART peripheral register block
+ *
+ * @param[in] mask
+ *   Bitmask corresponding to SYNCBUSY register defined bits, indicating
+ *   registers that must complete any ongoing synchronization.
+ ******************************************************************************/
+__STATIC_INLINE void LEUART_Sync(LEUART_TypeDef *leuart, uint32_t mask)
+{
+  /* Avoid deadlock if modifying the same register twice when freeze mode is */
+  /* activated. */
+  if (leuart->FREEZE & LEUART_FREEZE_REGFREEZE)
+  {
+    return;
+  }
+
+  /* Wait for any pending previous write operation to have been completed */
+  /* in low frequency domain */
+  while (leuart->SYNCBUSY & mask)
+    ;
+}
+
+/** @endcond */
+
+/*******************************************************************************
+ **************************   GLOBAL FUNCTIONS   *******************************
+ ******************************************************************************/
+
+/***************************************************************************//**
+ * @brief
+ *   Calculate baudrate for LEUART given reference frequency and clock division.
+ *
+ * @details
+ *   This function returns the baudrate that a LEUART module will use if
+ *   configured with the given frequency and clock divisor. Notice that
+ *   this function will not use actual HW configuration. It can be used
+ *   to determinate if a given configuration is sufficiently accurate for the
+ *   application.
+ *
+ * @param[in] refFreq
+ *   LEUART peripheral frequency used.
+ *
+ * @param[in] clkdiv
+ *   Clock division factor to be used.
+ *
+ * @return
+ *   Baudrate with given settings.
+ ******************************************************************************/
+uint32_t LEUART_BaudrateCalc(uint32_t refFreq, uint32_t clkdiv)
+{
+  uint32_t divisor;
+  uint32_t remainder;
+  uint32_t quotient;
+  uint32_t br;
+
+  /* Mask out unused bits */
+  clkdiv &= _LEUART_CLKDIV_MASK;
+
+  /* We want to use integer division to avoid forcing in float division */
+  /* utils, and yet keep rounding effect errors to a minimum. */
+
+  /*
+   * Baudrate is given by:
+   *
+   * br = fLEUARTn/(1 + (CLKDIV / 256))
+   *
+   * which can be rewritten to
+   *
+   * br = (256 * fLEUARTn)/(256 + CLKDIV)
+   *
+   * Normally, with fLEUARTn appr 32768Hz, there is no problem with overflow
+   * if using 32 bit arithmetic. However, since fLEUARTn may be derived from
+   * HFCORECLK as well, we must consider overflow when using integer arithmetic.
+   */
+
+  /*
+   * The basic problem with integer division in the above formula is that
+   * the dividend (256 * fLEUARTn) may become higher than max 32 bit
+   * integer. Yet we want to evaluate dividend first before dividing in
+   * order to get as small rounding effects as possible. We do not want
+   * to make too harsh restrictions on max fLEUARTn value either.
+   *
+   * For division a/b, we can write
+   *
+   * a = qb + r
+   *
+   * where q is the quotient and r is the remainder, both integers.
+   *
+   * The orignal baudrate formula can be rewritten as
+   *
+   * br = 256a / b = 256(qb + r)/b = 256q + 256r/b
+   *
+   * where a is 'refFreq' and b is 'divisor', referring to variable names.
+   */
+
+  divisor = 256 + clkdiv;
+
+  quotient  = refFreq / divisor;
+  remainder = refFreq % divisor;
+
+  /* Since divisor >= 256, the below cannot exceed max 32 bit value. */
+  br = 256 * quotient;
+
+  /*
+   * Remainder < (256 + clkdiv), which means dividend (256 * remainder) worst case is
+   * 256*(256 + 0x7ff8) = 0x80F800.
+   */
+  br += (256 * remainder) / divisor;
+
+  return br;
+}
+
+
+/***************************************************************************//**
+ * @brief
+ *   Get current baudrate for LEUART.
+ *
+ * @details
+ *   This function returns the actual baudrate (not considering oscillator
+ *   inaccuracies) used by a LEUART peripheral.
+ *
+ * @param[in] leuart
+ *   Pointer to LEUART peripheral register block.
+ *
+ * @return
+ *   Current baudrate.
+ ******************************************************************************/
+uint32_t LEUART_BaudrateGet(LEUART_TypeDef *leuart)
+{
+  uint32_t          freq;
+  CMU_Clock_TypeDef clock;
+
+  /* Get current frequency */
+  if (leuart == LEUART0)
+  {
+    clock = cmuClock_LEUART0;
+  }
+#if (LEUART_COUNT > 1)
+  else if (leuart == LEUART1)
+  {
+    clock = cmuClock_LEUART1;
+  }
+#endif
+  else
+  {
+    EFM_ASSERT(0);
+    return 0;
+  }
+
+  freq = CMU_ClockFreqGet(clock);
+
+  return LEUART_BaudrateCalc(freq, leuart->CLKDIV);
+}
+
+
+/***************************************************************************//**
+ * @brief
+ *   Configure baudrate (or as close as possible to specified baudrate).
+ *
+ * @note
+ *   The setting of a baudrate requires synchronization into the
+ *   low frequency domain. If the same register is modified before a previous
+ *   update has completed, this function will stall until the previous
+ *   synchronization has completed.
+ *
+ * @param[in] leuart
+ *   Pointer to LEUART peripheral register block.
+ *
+ * @param[in] refFreq
+ *   LEUART reference clock frequency in Hz that will be used. If set to 0,
+ *   the currently configured reference clock is assumed.
+ *
+ * @param[in] baudrate
+ *   Baudrate to try to achieve for LEUART.
+ ******************************************************************************/
+void LEUART_BaudrateSet(LEUART_TypeDef *leuart,
+                        uint32_t refFreq,
+                        uint32_t baudrate)
+{
+  uint32_t          clkdiv;
+  CMU_Clock_TypeDef clock;
+
+  /* Inhibit divide by 0 */
+  EFM_ASSERT(baudrate);
+
+  /*
+   * We want to use integer division to avoid forcing in float division
+   * utils, and yet keep rounding effect errors to a minimum.
+   *
+   * CLKDIV in asynchronous mode is given by:
+   *
+   * CLKDIV = 256*(fLEUARTn/br - 1) = ((256*fLEUARTn)/br) - 256
+   *
+   * Normally, with fLEUARTn appr 32768Hz, there is no problem with overflow
+   * if using 32 bit arithmetic. However, since fLEUARTn may be derived from
+   * HFCORECLK as well, we must consider overflow when using integer arithmetic.
+   *
+   * The basic problem with integer division in the above formula is that
+   * the dividend (256 * fLEUARTn) may become higher than max 32 bit
+   * integer. Yet, we want to evaluate dividend first before dividing in
+   * order to get as small rounding effects as possible. We do not want
+   * to make too harsh restrictions on max fLEUARTn value either.
+   *
+   * Since the last 3 bits of CLKDIV are don't care, we can base our
+   * integer arithmetic on the below formula
+   *
+   * CLKDIV/8 = ((32*fLEUARTn)/br) - 32
+   *
+   * and calculate 1/8 of CLKDIV first. This allows for fLEUARTn
+   * up to 128MHz without overflowing a 32 bit value!
+   */
+
+  /* Get current frequency? */
+  if (!refFreq)
+  {
+    if (leuart == LEUART0)
+    {
+      clock = cmuClock_LEUART0;
+    }
+#if (LEUART_COUNT > 1)
+    else if (leuart == LEUART1)
+    {
+      clock = cmuClock_LEUART1;
+    }
+#endif
+    else
+    {
+      EFM_ASSERT(0);
+      return;
+    }
+
+    refFreq = CMU_ClockFreqGet(clock);
+  }
+
+  /* Calculate and set CLKDIV with fractional bits */
+  clkdiv  = (32 * refFreq) / baudrate;
+  clkdiv -= 32;
+  clkdiv *= 8;
+
+  /* Verify that resulting clock divider is within limits */
+  EFM_ASSERT(clkdiv <= _LEUART_CLKDIV_MASK);
+
+  /* If EFM_ASSERT is not enabled, make sure we don't write to reserved bits */
+  clkdiv &= _LEUART_CLKDIV_MASK;
+
+  /* LF register about to be modified require sync. busy check */
+  LEUART_Sync(leuart, LEUART_SYNCBUSY_CLKDIV);
+
+  leuart->CLKDIV = clkdiv;
+}
+
+
+/***************************************************************************//**
+ * @brief
+ *   Enable/disable LEUART receiver and/or transmitter.
+ *
+ * @details
+ *   Notice that this function does not do any configuration. Enabling should
+ *   normally be done after initialization is done (if not enabled as part
+ *   of init).
+ *
+ * @note
+ *   Enabling/disabling requires synchronization into the low frequency domain.
+ *   If the same register is modified before a previous update has completed,
+ *   this function will stall until the previous synchronization has completed.
+ *
+ * @param[in] leuart
+ *   Pointer to LEUART peripheral register block.
+ *
+ * @param[in] enable
+ *   Select status for receiver/transmitter.
+ ******************************************************************************/
+void LEUART_Enable(LEUART_TypeDef *leuart, LEUART_Enable_TypeDef enable)
+{
+  uint32_t tmp;
+
+  /* Make sure the module exists on the selected chip */
+  EFM_ASSERT(LEUART_REF_VALID(leuart));
+
+  /* Disable as specified */
+  tmp   = ~((uint32_t)(enable));
+  tmp  &= (_LEUART_CMD_RXEN_MASK | _LEUART_CMD_TXEN_MASK);
+  tmp <<= 1;
+  /* Enable as specified */
+  tmp |= (uint32_t)(enable);
+
+  /* LF register about to be modified require sync. busy check */
+  LEUART_Sync(leuart, LEUART_SYNCBUSY_CMD);
+
+  leuart->CMD = tmp;
+}
+
+
+/***************************************************************************//**
+ * @brief
+ *   LEUART register synchronization freeze control.
+ *
+ * @details
+ *   Some LEUART registers require synchronization into the low frequency (LF)
+ *   domain. The freeze feature allows for several such registers to be
+ *   modified before passing them to the LF domain simultaneously (which
+ *   takes place when the freeze mode is disabled).
+ *
+ * @note
+ *   When enabling freeze mode, this function will wait for all current
+ *   ongoing LEUART synchronization to LF domain to complete (Normally
+ *   synchronization will not be in progress.) However for this reason, when
+ *   using freeze mode, modifications of registers requiring LF synchronization
+ *   should be done within one freeze enable/disable block to avoid unecessary
+ *   stalling.
+ *
+ * @param[in] leuart
+ *   Pointer to LEUART peripheral register block.
+ *
+ * @param[in] enable
+ *   @li true - enable freeze, modified registers are not propagated to the
+ *       LF domain
+ *   @li false - disables freeze, modified registers are propagated to LF
+ *       domain
+ ******************************************************************************/
+void LEUART_FreezeEnable(LEUART_TypeDef *leuart, bool enable)
+{
+  if (enable)
+  {
+    /*
+     * Wait for any ongoing LF synchronization to complete. This is just to
+     * protect against the rare case when a user
+     * - modifies a register requiring LF sync
+     * - then enables freeze before LF sync completed
+     * - then modifies the same register again
+     * since modifying a register while it is in sync progress should be
+     * avoided.
+     */
+    while (leuart->SYNCBUSY)
+      ;
+
+    leuart->FREEZE = LEUART_FREEZE_REGFREEZE;
+  }
+  else
+  {
+    leuart->FREEZE = 0;
+  }
+}
+
+
+/***************************************************************************//**
+ * @brief
+ *   Init LEUART.
+ *
+ * @details
+ *   This function will configure basic settings in order to operate in normal
+ *   asynchronous mode. Consider using LEUART_Reset() prior to this function if
+ *   state of configuration is not known, since only configuration settings
+ *   specified by @p init are set.
+ *
+ *   Special control setup not covered by this function may be done either
+ *   before or after using this function (but normally before enabling)
+ *   by direct modification of the CTRL register.
+ *
+ *   Notice that pins used by the LEUART module must be properly configured
+ *   by the user explicitly, in order for the LEUART to work as intended.
+ *   (When configuring pins, one should remember to consider the sequence of
+ *   configuration, in order to avoid unintended pulses/glitches on output
+ *   pins.)
+ *
+ * @note
+ *   Initializing requires synchronization into the low frequency domain.
+ *   If the same register is modified before a previous update has completed,
+ *   this function will stall until the previous synchronization has completed.
+ *
+ * @param[in] leuart
+ *   Pointer to LEUART peripheral register block.
+ *
+ * @param[in] init
+ *   Pointer to initialization structure used to configure basic async setup.
+ ******************************************************************************/
+void LEUART_Init(LEUART_TypeDef *leuart, LEUART_Init_TypeDef const *init)
+{
+  /* Make sure the module exists on the selected chip */
+  EFM_ASSERT(LEUART_REF_VALID(leuart));
+
+  /* LF register about to be modified require sync. busy check */
+  LEUART_Sync(leuart, LEUART_SYNCBUSY_CMD);
+
+  /* Ensure disabled while doing config */
+  leuart->CMD = LEUART_CMD_RXDIS | LEUART_CMD_TXDIS;
+
+  /* Freeze registers to avoid stalling for LF synchronization */
+  LEUART_FreezeEnable(leuart, true);
+
+  /* Configure databits and stopbits */
+  leuart->CTRL = (leuart->CTRL & ~(_LEUART_CTRL_PARITY_MASK |
+                                   _LEUART_CTRL_STOPBITS_MASK)) |
+                 (uint32_t)(init->databits) |
+                 (uint32_t)(init->parity) |
+                 (uint32_t)(init->stopbits);
+
+  /* Configure baudrate */
+  LEUART_BaudrateSet(leuart, init->refFreq, init->baudrate);
+
+  /* Finally enable (as specified) */
+  leuart->CMD = (uint32_t)(init->enable);
+
+  /* Unfreeze registers, pass new settings on to LEUART */
+  LEUART_FreezeEnable(leuart, false);
+}
+
+
+/***************************************************************************//**
+ * @brief
+ *   Reset LEUART to same state as after a HW reset.
+ *
+ * @param[in] leuart
+ *   Pointer to LEUART peripheral register block.
+ ******************************************************************************/
+void LEUART_Reset(LEUART_TypeDef *leuart)
+{
+  /* Make sure the module exists on the selected chip */
+  EFM_ASSERT(LEUART_REF_VALID(leuart));
+
+  /* Freeze registers to avoid stalling for LF synchronization */
+  LEUART_FreezeEnable(leuart, true);
+
+  /* Make sure disabled first, before resetting other registers */
+  leuart->CMD = LEUART_CMD_RXDIS | LEUART_CMD_TXDIS | LEUART_CMD_RXBLOCKDIS |
+                LEUART_CMD_CLEARTX | LEUART_CMD_CLEARRX;
+  leuart->CTRL       = _LEUART_CTRL_RESETVALUE;
+  leuart->CLKDIV     = _LEUART_CLKDIV_RESETVALUE;
+  leuart->STARTFRAME = _LEUART_STARTFRAME_RESETVALUE;
+  leuart->SIGFRAME   = _LEUART_SIGFRAME_RESETVALUE;
+  leuart->IEN        = _LEUART_IEN_RESETVALUE;
+  leuart->IFC        = _LEUART_IFC_MASK;
+  leuart->PULSECTRL  = _LEUART_PULSECTRL_RESETVALUE;
+  leuart->ROUTE      = _LEUART_ROUTE_RESETVALUE;
+
+  /* Unfreeze registers, pass new settings on to LEUART */
+  LEUART_FreezeEnable(leuart, false);
+}
+
+
+/***************************************************************************//**
+ * @brief
+ *   Receive one 8 bit frame, (or part of 9 bit frame).
+ *
+ * @details
+ *   This function is normally used to receive one frame when operating with
+ *   frame length 8 bits. Please refer to LEUART_RxExt() for reception of
+ *   9 bit frames.
+ *
+ *   Notice that possible parity/stop bits are not considered part of specified
+ *   frame bit length.
+ *
+ * @note
+ *   This function will stall if buffer is empty, until data is received.
+ *
+ * @param[in] leuart
+ *   Pointer to LEUART peripheral register block.
+ *
+ * @return
+ *   Data received.
+ ******************************************************************************/
+uint8_t LEUART_Rx(LEUART_TypeDef *leuart)
+{
+  while (!(leuart->STATUS & LEUART_STATUS_RXDATAV))
+    ;
+
+  return (uint8_t)(leuart->RXDATA);
+}
+
+
+/***************************************************************************//**
+ * @brief
+ *   Receive one 8-9 bit frame, with extended information.
+ *
+ * @details
+ *   This function is normally used to receive one frame and additional RX
+ *   status information is required.
+ *
+ * @note
+ *   This function will stall if buffer is empty, until data is received.
+ *
+ * @param[in] leuart
+ *   Pointer to LEUART peripheral register block.
+ *
+ * @return
+ *   Data received.
+ ******************************************************************************/
+uint16_t LEUART_RxExt(LEUART_TypeDef *leuart)
+{
+  while (!(leuart->STATUS & LEUART_STATUS_RXDATAV))
+    ;
+
+  return (uint16_t)(leuart->RXDATAX);
+}
+
+
+/***************************************************************************//**
+ * @brief
+ *   Transmit one frame.
+ *
+ * @details
+ *   Depending on frame length configuration, 8 (least significant) bits from
+ *   @p data are transmitted. If frame length is 9, 8 bits are transmitted from
+ *   @p data and one bit as specified by CTRL register, BIT8DV field. Please
+ *   refer to LEUART_TxExt() for transmitting 9 bit frame with full control of
+ *   all 9 bits.
+ *
+ *   Notice that possible parity/stop bits in asynchronous mode are not
+ *   considered part of specified frame bit length.
+ *
+ * @note
+ *   This function will stall if buffer is full, until buffer becomes available.
+ *
+ * @param[in] leuart
+ *   Pointer to LEUART peripheral register block.
+ *
+ * @param[in] data
+ *   Data to transmit. See details above for further info.
+ ******************************************************************************/
+void LEUART_Tx(LEUART_TypeDef *leuart, uint8_t data)
+{
+  /* Check that transmit buffer is empty */
+  while (!(leuart->STATUS & LEUART_STATUS_TXBL))
+    ;
+
+  /* LF register about to be modified require sync. busy check */
+  LEUART_Sync(leuart, LEUART_SYNCBUSY_TXDATA);
+
+  leuart->TXDATA = (uint32_t)data;
+}
+
+
+/***************************************************************************//**
+ * @brief
+ *   Transmit one 8-9 bit frame with extended control.
+ *
+ * @details
+ *   Notice that possible parity/stop bits in asynchronous mode are not
+ *   considered part of specified frame bit length.
+ *
+ * @note
+ *   This function will stall if buffer is full, until buffer becomes available.
+ *
+ * @param[in] leuart
+ *   Pointer to LEUART peripheral register block.
+ *
+ * @param[in] data
+ *   Data to transmit with extended control. Least significant bits contains
+ *   frame bits, and additional control bits are available as documented in
+ *   the EFM32 reference manual (set to 0 if not used).
+ ******************************************************************************/
+void LEUART_TxExt(LEUART_TypeDef *leuart, uint16_t data)
+{
+  /* Check that transmit buffer is empty */
+  while (!(leuart->STATUS & LEUART_STATUS_TXBL))
+    ;
+
+  /* LF register about to be modified require sync. busy check */
+  LEUART_Sync(leuart, LEUART_SYNCBUSY_TXDATAX);
+
+  leuart->TXDATAX = (uint32_t)data;
+}
+
+/***************************************************************************//**
+ * @brief
+ *   Enables handling of LEUART TX by DMA in EM2
+ *
+ * @param[in] leuart
+ *   Pointer to LEUART peripheral register block.
+ *
+ * @param[in] enable
+ *   true - enables functionality
+ *   false - disables functionality
+ *
+ ******************************************************************************/
+void LEUART_TxDmaInEM2Enable(LEUART_TypeDef *leuart, bool enable)
+{
+  /* LF register about to be modified require sync. busy check */
+  LEUART_Sync(leuart, LEUART_SYNCBUSY_CTRL);
+
+  if (enable)
+  {
+    leuart->CTRL |= LEUART_CTRL_TXDMAWU;
+  }
+  else
+  {
+    leuart->CTRL &= ~LEUART_CTRL_TXDMAWU;
+  }
+}
+
+/***************************************************************************//**
+ * @brief
+ *   Enables handling of LEUART RX by DMA in EM2
+ *
+ * @param[in] leuart
+ *   Pointer to LEUART peripheral register block.
+ *
+ * @param[in] enable
+ *   true - enables functionality
+ *   false - disables functionality
+ *
+ ******************************************************************************/
+void LEUART_RxDmaInEM2Enable(LEUART_TypeDef *leuart, bool enable)
+{
+  /* LF register about to be modified require sync. busy check */
+  LEUART_Sync(leuart, LEUART_SYNCBUSY_CTRL);
+
+  if (enable)
+  {
+    leuart->CTRL |= LEUART_CTRL_RXDMAWU;
+  }
+  else
+  {
+    leuart->CTRL &= ~LEUART_CTRL_RXDMAWU;
+  }
+}
+
+
+/** @} (end addtogroup LEUART) */
+/** @} (end addtogroup EM_Library) */
+#endif /* defined(LEUART_COUNT) && (LEUART_COUNT > 0) */