/***************************************************************************//** * @file * @brief Low Energy Universal Asynchronous Receiver/Transmitter (LEUART) * peripheral module peripheral API for EFM32. * @author Energy Micro AS * @version 2.3.2 ******************************************************************************* * @section License * (C) Copyright 2010 Energy Micro AS, http://www.energymicro.com ******************************************************************************* * * This source code is the property of Energy Micro AS. The source and compiled * code may only be used on Energy Micro "EFM32" microcontrollers. * * This copyright notice may not be removed from the source code nor changed. * * DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Energy Micro AS has no * obligation to support this Software. Energy Micro AS 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. * * Energy Micro AS 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 "efm32_leuart.h" #include "efm32_cmu.h" #include "efm32_assert.h" /***************************************************************************//** * @addtogroup EFM32_Library * @{ ******************************************************************************/ /***************************************************************************//** * @addtogroup LEUART * @brief Low Energy Universal Asynchronous Receiver/Transmitter (LEUART) * Peripheral API for EFM32 * @{ ******************************************************************************/ /******************************************************************************* ******************************* 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; /* 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 *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; /* Do not reset route register, setting should be done independently */ /* 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; } /** @} (end addtogroup LEUART) */ /** @} (end addtogroup EFM32_Library) */