added prescaler for 16 bit pwm in LPC1347 target
Fork of mbed-dev by
Diff: targets/hal/TARGET_Silicon_Labs/TARGET_EFM32/emlib/src/em_leuart.c
- Revision:
- 144:ef7eb2e8f9f7
- Parent:
- 50:a417edff4437
diff -r 423e1876dc07 -r ef7eb2e8f9f7 targets/hal/TARGET_Silicon_Labs/TARGET_EFM32/emlib/src/em_leuart.c --- a/targets/hal/TARGET_Silicon_Labs/TARGET_EFM32/emlib/src/em_leuart.c Tue Aug 02 14:07:36 2016 +0000 +++ b/targets/hal/TARGET_Silicon_Labs/TARGET_EFM32/emlib/src/em_leuart.c Fri Sep 02 15:07:44 2016 +0100 @@ -1,705 +1,705 @@ -/***************************************************************************//** - * @file em_leuart.c - * @brief Low Energy Universal Asynchronous Receiver/Transmitter (LEUART) - * Peripheral API - * @version 4.2.1 - ******************************************************************************* - * @section License - * <b>(C) Copyright 2015 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; -#if defined(_LEUART_ROUTEPEN_MASK) - leuart->ROUTEPEN = _LEUART_ROUTEPEN_RESETVALUE; - leuart->ROUTELOC0 = _LEUART_ROUTELOC0_RESETVALUE; -#else - leuart->ROUTE = _LEUART_ROUTE_RESETVALUE; -#endif - - /* 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 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) */ +/***************************************************************************//** + * @file em_leuart.c + * @brief Low Energy Universal Asynchronous Receiver/Transmitter (LEUART) + * Peripheral API + * @version 4.2.1 + ******************************************************************************* + * @section License + * <b>(C) Copyright 2015 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; +#if defined(_LEUART_ROUTEPEN_MASK) + leuart->ROUTEPEN = _LEUART_ROUTEPEN_RESETVALUE; + leuart->ROUTELOC0 = _LEUART_ROUTELOC0_RESETVALUE; +#else + leuart->ROUTE = _LEUART_ROUTE_RESETVALUE; +#endif + + /* 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 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) */