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:
- 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) */