| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639 |
- /***************************************************************************//**
- * @file
- * @brief Low Energy Universal Asynchronous Receiver/Transmitter (LEUART)
- * Peripheral API
- * @author Energy Micro AS
- * @version 3.0.0
- *******************************************************************************
- * @section License
- * <b>(C) Copyright 2012 Energy Micro AS, http://www.energymicro.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: 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 "em_leuart.h"
- #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;
- /* 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 EM_Library) */
|