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_usart.c
- Revision:
- 144:ef7eb2e8f9f7
- Parent:
- 50:a417edff4437
diff -r 423e1876dc07 -r ef7eb2e8f9f7 targets/hal/TARGET_Silicon_Labs/TARGET_EFM32/emlib/src/em_usart.c --- a/targets/hal/TARGET_Silicon_Labs/TARGET_EFM32/emlib/src/em_usart.c Tue Aug 02 14:07:36 2016 +0000 +++ b/targets/hal/TARGET_Silicon_Labs/TARGET_EFM32/emlib/src/em_usart.c Fri Sep 02 15:07:44 2016 +0100 @@ -1,1215 +1,1215 @@ -/***************************************************************************//** - * @file em_usart.c - * @brief Universal synchronous/asynchronous receiver/transmitter (USART/UART) - * 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_usart.h" -#if defined(USART_COUNT) && (USART_COUNT > 0) - -#include "em_cmu.h" -#include "em_bus.h" -#include "em_assert.h" - -/***************************************************************************//** - * @addtogroup EM_Library - * @{ - ******************************************************************************/ - -/***************************************************************************//** - * @addtogroup USART - * @brief Universal Synchronous/Asynchronous Receiver/Transmitter - * Peripheral API - * @{ - ******************************************************************************/ - -/******************************************************************************* - ******************************* DEFINES *********************************** - ******************************************************************************/ - -/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */ - - -/** Validation of USART register block pointer reference for assert statements. */ -#if (USART_COUNT == 1) && defined(USART0) -#define USART_REF_VALID(ref) ((ref) == USART0) - -#elif (USART_COUNT == 1) && defined(USART1) -#define USART_REF_VALID(ref) ((ref) == USART1) - -#elif (USART_COUNT == 2) && defined(USART2) -#define USART_REF_VALID(ref) (((ref) == USART1) || ((ref) == USART2)) - -#elif (USART_COUNT == 2) -#define USART_REF_VALID(ref) (((ref) == USART0) || ((ref) == USART1)) - -#elif (USART_COUNT == 3) -#define USART_REF_VALID(ref) (((ref) == USART0) || ((ref) == USART1) || \ - ((ref) == USART2)) -#elif (USART_COUNT == 4) -#define USART_REF_VALID(ref) (((ref) == USART0) || ((ref) == USART1) || \ - ((ref) == USART2) || ((ref) == USART3)) -#elif (USART_COUNT == 5) -#define USART_REF_VALID(ref) (((ref) == USART0) || ((ref) == USART1) || \ - ((ref) == USART2) || ((ref) == USART3) || \ - ((ref) == USART4)) -#elif (USART_COUNT == 6) -#define USART_REF_VALID(ref) (((ref) == USART0) || ((ref) == USART1) || \ - ((ref) == USART2) || ((ref) == USART3) || \ - ((ref) == USART4) || ((ref) == USART5)) -#else -#error "Undefined number of USARTs." -#endif - -#if defined(USARTRF_COUNT) && (USARTRF_COUNT > 0) -#if (USARTRF_COUNT == 1) && defined(USARTRF0) -#define USARTRF_REF_VALID(ref) ((ref) == USARTRF0) -#elif (USARTRF_COUNT == 1) && defined(USARTRF1) -#define USARTRF_REF_VALID(ref) ((ref) == USARTRF1) -#else -#define USARTRF_REF_VALID(ref) (0) -#endif -#else -#define USARTRF_REF_VALID(ref) (0) -#endif - -#if defined(_EZR32_HAPPY_FAMILY) -#define USART_IRDA_VALID(ref) ((ref) == USART0) -#elif defined(_EFM32_HAPPY_FAMILY) -#define USART_IRDA_VALID(ref) (((ref) == USART0) || ((ref) == USART1)) -#elif defined(USART0) -#define USART_IRDA_VALID(ref) ((ref) == USART0) -#elif (USART_COUNT == 1) && defined(USART1) -#define USART_IRDA_VALID(ref) ((ref) == USART1) -#else -#define USART_IRDA_VALID(ref) (0) -#endif - -#if defined(_EZR32_HAPPY_FAMILY) -#define USART_I2S_VALID(ref) ((ref) == USART0) -#elif defined(_EFM32_HAPPY_FAMILY) -#define USART_I2S_VALID(ref) (((ref) == USART0) || ((ref) == USART1)) -#elif defined(_EFM32_TINY_FAMILY) || defined(_EFM32_ZERO_FAMILY) || defined(_SILICON_LABS_32B_PLATFORM_2) -#define USART_I2S_VALID(ref) ((ref) == USART1) -#elif defined(_EFM32_GIANT_FAMILY) || defined(_EFM32_WONDER_FAMILY) -#define USART_I2S_VALID(ref) (((ref) == USART1) || ((ref) == USART2)) -#endif - -#if (UART_COUNT == 1) -#define UART_REF_VALID(ref) ((ref) == UART0) -#elif (UART_COUNT == 2) -#define UART_REF_VALID(ref) (((ref) == UART0) || ((ref) == UART1)) -#else -#define UART_REF_VALID(ref) (0) -#endif - -/** @endcond */ - - -/******************************************************************************* - ************************** GLOBAL FUNCTIONS ******************************* - ******************************************************************************/ - -/***************************************************************************//** - * @brief - * Configure USART/UART operating in asynchronous mode to use a given - * baudrate (or as close as possible to specified baudrate). - * - * @param[in] usart - * Pointer to USART/UART peripheral register block. - * - * @param[in] refFreq - * USART/UART 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 USART/UART. - * - * @param[in] ovs - * Oversampling to be used. Normal is 16x oversampling, but lower oversampling - * may be used to achieve higher rates or better baudrate accuracy in some - * cases. Notice that lower oversampling frequency makes channel more - * vulnerable to bit faults during reception due to clock inaccuracies - * compared to link partner. - ******************************************************************************/ -void USART_BaudrateAsyncSet(USART_TypeDef *usart, - uint32_t refFreq, - uint32_t baudrate, - USART_OVS_TypeDef ovs) -{ - uint32_t clkdiv; - uint32_t oversample; - - /* 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 * (fHFPERCLK/(oversample * br) - 1) - * or - * CLKDIV = (256 * fHFPERCLK)/(oversample * br) - 256 - * - * The basic problem with integer division in the above formula is that - * the dividend (256 * fHFPERCLK) 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 fHFPERCLK value either. - * - * One can possibly factorize 256 and oversample/br. However, - * since the last 6 or 3 bits of CLKDIV are don't care, we can base our - * integer arithmetic on the below formula - * - * CLKDIV / 64 = (4 * fHFPERCLK)/(oversample * br) - 4 (3 bits dont care) - * or - * CLKDIV / 8 = (32 * fHFPERCLK)/(oversample * br) - 32 (6 bits dont care) - * - * and calculate 1/64 of CLKDIV first. This allows for fHFPERCLK - * up to 1GHz without overflowing a 32 bit value! - */ - - /* HFPERCLK used to clock all USART/UART peripheral modules */ - if (!refFreq) - { - refFreq = CMU_ClockFreqGet(cmuClock_HFPER); - } - - /* Map oversampling */ - switch (ovs) - { - case USART_CTRL_OVS_X16: - EFM_ASSERT(baudrate <= (refFreq / 16)); - oversample = 16; - break; - - case USART_CTRL_OVS_X8: - EFM_ASSERT(baudrate <= (refFreq / 8)); - oversample = 8; - break; - - case USART_CTRL_OVS_X6: - EFM_ASSERT(baudrate <= (refFreq / 6)); - oversample = 6; - break; - - case USART_CTRL_OVS_X4: - EFM_ASSERT(baudrate <= (refFreq / 4)); - oversample = 4; - break; - - default: - /* Invalid input */ - EFM_ASSERT(0); - return; - } - - /* Calculate and set CLKDIV with fractional bits. - * The addend (oversample*baudrate)/2 in the first line is to round the - * divisor up by half the divisor before the division in order to reduce the - * integer division error, which consequently results in a higher baudrate - * than desired. */ -#if defined(_USART_CLKDIV_DIV_MASK) && (_USART_CLKDIV_DIV_MASK >= 0x7FFFF8UL) - clkdiv = 32 * refFreq + (oversample * baudrate) / 2; - clkdiv /= (oversample * baudrate); - clkdiv -= 32; - clkdiv *= 8; -#else - clkdiv = 4 * refFreq + (oversample * baudrate) / 2; - clkdiv /= (oversample * baudrate); - clkdiv -= 4; - clkdiv *= 64; -#endif - - /* Verify that resulting clock divider is within limits */ - EFM_ASSERT(clkdiv <= _USART_CLKDIV_DIV_MASK); - - /* If EFM_ASSERT is not enabled, make sure we don't write to reserved bits */ - clkdiv &= _USART_CLKDIV_DIV_MASK; - - usart->CTRL &= ~_USART_CTRL_OVS_MASK; - usart->CTRL |= ovs; - usart->CLKDIV = clkdiv; -} - - -/***************************************************************************//** - * @brief - * Calculate baudrate for USART/UART given reference frequency, clock division - * and oversampling rate (if async mode). - * - * @details - * This function returns the baudrate that a USART/UART module will use if - * configured with the given frequency, clock divisor and mode. 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 - * USART/UART HF peripheral frequency used. - * - * @param[in] clkdiv - * Clock division factor to be used. - * - * @param[in] syncmode - * @li true - synchronous mode operation. - * @li false - asynchronous mode operation. - * - * @param[in] ovs - * Oversampling used if asynchronous mode. Not used if @p syncmode is true. - * - * @return - * Baudrate with given settings. - ******************************************************************************/ -uint32_t USART_BaudrateCalc(uint32_t refFreq, - uint32_t clkdiv, - bool syncmode, - USART_OVS_TypeDef ovs) -{ - uint32_t oversample; - uint64_t divisor; - uint64_t factor; - uint64_t remainder; - uint64_t quotient; - uint32_t br; - - /* Mask out unused bits */ - clkdiv &= _USART_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 calculation depends on if synchronous or asynchronous mode */ - if (syncmode) - { - /* - * Baudrate is given by: - * - * br = fHFPERCLK/(2 * (1 + (CLKDIV / 256))) - * - * which can be rewritten to - * - * br = (128 * fHFPERCLK)/(256 + CLKDIV) - */ - oversample = 1; /* Not used in sync mode, ie 1 */ - factor = 128; - } - else - { - /* - * Baudrate in asynchronous mode is given by: - * - * br = fHFPERCLK/(oversample * (1 + (CLKDIV / 256))) - * - * which can be rewritten to - * - * br = (256 * fHFPERCLK)/(oversample * (256 + CLKDIV)) - * - * First of all we can reduce the 256 factor of the dividend with - * (part of) oversample part of the divisor. - */ - - switch (ovs) - { - case USART_CTRL_OVS_X16: - oversample = 1; - factor = 256 / 16; - break; - - case USART_CTRL_OVS_X8: - oversample = 1; - factor = 256 / 8; - break; - - case USART_CTRL_OVS_X6: - oversample = 3; - factor = 256 / 2; - break; - - default: - oversample = 1; - factor = 256 / 4; - break; - } - } - - /* - * The basic problem with integer division in the above formula is that - * the dividend (factor * fHFPERCLK) may become larger than a 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 fHFPERCLK 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 = xa / b = x(qb + r)/b = xq + xr/b - * - * where x is 'factor', a is 'refFreq' and b is 'divisor', referring to - * variable names. - */ - - /* Divisor will never exceed max 32 bit value since clkdiv <= 0xFFFFF8 */ - /* and 'oversample' has been reduced to <= 3. */ - divisor = oversample * (256 + clkdiv); - - quotient = refFreq / divisor; - remainder = refFreq % divisor; - - /* factor <= 128 and since divisor >= 256, the below cannot exceed max */ - /* 32 bit value. However, factor * remainder can become larger than 32-bit */ - /* because of the size of _USART_CLKDIV_DIV_MASK on some families. */ - br = (uint32_t)(factor * quotient); - - /* - * factor <= 128 and remainder < (oversample*(256 + clkdiv)), which - * means dividend (factor * remainder) worst case is - * 128 * (3 * (256 + _USART_CLKDIV_DIV_MASK)) = 0x1_8001_7400. - */ - br += (uint32_t)((factor * remainder) / divisor); - - return br; -} - - -/***************************************************************************//** - * @brief - * Get current baudrate for USART/UART. - * - * @details - * This function returns the actual baudrate (not considering oscillator - * inaccuracies) used by a USART/UART peripheral. - * - * @param[in] usart - * Pointer to USART/UART peripheral register block. - * - * @return - * Current baudrate. - ******************************************************************************/ -uint32_t USART_BaudrateGet(USART_TypeDef *usart) -{ - uint32_t freq; - USART_OVS_TypeDef ovs; - bool syncmode; - - if (usart->CTRL & USART_CTRL_SYNC) - { - syncmode = true; - } - else - { - syncmode = false; - } - - /* HFPERCLK used to clock all USART/UART peripheral modules */ - freq = CMU_ClockFreqGet(cmuClock_HFPER); - ovs = (USART_OVS_TypeDef)(usart->CTRL & _USART_CTRL_OVS_MASK); - return USART_BaudrateCalc(freq, usart->CLKDIV, syncmode, ovs); -} - - -/***************************************************************************//** - * @brief - * Configure USART operating in synchronous mode to use a given baudrate - * (or as close as possible to specified baudrate). - * - * @details - * The configuration will be set to use a baudrate <= the specified baudrate - * in order to ensure that the baudrate does not exceed the specified value. - * - * Fractional clock division is suppressed, although the HW design allows it. - * It could cause half clock cycles to exceed specified limit, and thus - * potentially violate specifications for the slave device. In some special - * situations fractional clock division may be useful even in synchronous - * mode, but in those cases it must be directly adjusted, possibly assisted - * by USART_BaudrateCalc(): - * - * @param[in] usart - * Pointer to USART peripheral register block. (Cannot be used on UART - * modules.) - * - * @param[in] refFreq - * USART 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 USART. - ******************************************************************************/ -void USART_BaudrateSyncSet(USART_TypeDef *usart, uint32_t refFreq, uint32_t baudrate) -{ -#if defined(_USART_CLKDIV_DIV_MASK) && (_USART_CLKDIV_DIV_MASK >= 0x7FFFF8UL) - uint64_t clkdiv; -#else - uint32_t clkdiv; -#endif - - /* Inhibit divide by 0 */ - EFM_ASSERT(baudrate); - - /* - * CLKDIV in synchronous mode is given by: - * - * CLKDIV = 256 * (fHFPERCLK/(2 * br) - 1) - */ - - /* HFPERCLK used to clock all USART/UART peripheral modules */ - if (!refFreq) - { - refFreq = CMU_ClockFreqGet(cmuClock_HFPER); - } - -#if defined(_USART_CLKDIV_DIV_MASK) && (_USART_CLKDIV_DIV_MASK >= 0x7FFFF8UL) - /* Calculate and set CLKDIV without fractional bits */ - clkdiv = 2 * baudrate; - clkdiv = (0x100ULL * (uint64_t)refFreq) / clkdiv; - - /* Round up by not subtracting 256 and mask off fractional part */ - clkdiv &= ~0xFF; -#else - /* Calculate and set CLKDIV with fractional bits */ - clkdiv = 2 * refFreq; - clkdiv += baudrate - 1; - clkdiv /= baudrate; - clkdiv -= 4; - clkdiv *= 64; - /* Make sure we don't use fractional bits by rounding CLKDIV */ - /* up (and thus reducing baudrate, not increasing baudrate above */ - /* specified value). */ - clkdiv += 0xc0; - clkdiv &= 0xffffff00; -#endif - - /* Verify that resulting clock divider is within limits */ - EFM_ASSERT(!(clkdiv & ~_USART_CLKDIV_DIV_MASK)); - - /* If EFM_ASSERT is not enabled, make sure we don't write to reserved bits */ - clkdiv &= _USART_CLKDIV_DIV_MASK; - - BUS_RegMaskedWrite(&usart->CLKDIV, _USART_CLKDIV_DIV_MASK, clkdiv); -} - - -/***************************************************************************//** - * @brief - * Enable/disable USART/UART 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). - * - * @param[in] usart - * Pointer to USART/UART peripheral register block. - * - * @param[in] enable - * Select status for receiver/transmitter. - ******************************************************************************/ -void USART_Enable(USART_TypeDef *usart, USART_Enable_TypeDef enable) -{ - uint32_t tmp; - - /* Make sure the module exists on the selected chip */ - EFM_ASSERT( USART_REF_VALID(usart) - || USARTRF_REF_VALID(usart) - || UART_REF_VALID(usart) ); - - /* Disable as specified */ - tmp = ~((uint32_t) (enable)); - tmp &= _USART_CMD_RXEN_MASK | _USART_CMD_TXEN_MASK; - usart->CMD = tmp << 1; - - /* Enable as specified */ - usart->CMD = (uint32_t) (enable); -} - - -/***************************************************************************//** - * @brief - * Init USART/UART for normal asynchronous mode. - * - * @details - * This function will configure basic settings in order to operate in normal - * asynchronous mode. - * - * Special control setup not covered by this function must be done after - * using this function by direct modification of the CTRL register. - * - * Notice that pins used by the USART/UART module must be properly configured - * by the user explicitly, in order for the USART/UART 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.) - * - * @param[in] usart - * Pointer to USART/UART peripheral register block. - * - * @param[in] init - * Pointer to initialization structure used to configure basic async setup. - ******************************************************************************/ -void USART_InitAsync(USART_TypeDef *usart, const USART_InitAsync_TypeDef *init) -{ - /* Make sure the module exists on the selected chip */ - EFM_ASSERT( USART_REF_VALID(usart) - || USARTRF_REF_VALID(usart) - || UART_REF_VALID(usart) ); - - /* Init USART registers to HW reset state. */ - USART_Reset(usart); - -#if defined(USART_INPUT_RXPRS) && defined(USART_CTRL_MVDIS) - /* Disable majority vote if specified. */ - if (init->mvdis) - { - usart->CTRL |= USART_CTRL_MVDIS; - } - - /* Configure PRS input mode. */ - if (init->prsRxEnable) - { - usart->INPUT = (uint32_t) init->prsRxCh | USART_INPUT_RXPRS; - } -#endif - - /* Configure databits, stopbits and parity */ - usart->FRAME = (uint32_t)init->databits - | (uint32_t)init->stopbits - | (uint32_t)init->parity; - - /* Configure baudrate */ - USART_BaudrateAsyncSet(usart, init->refFreq, init->baudrate, init->oversampling); - -#if defined(_USART_TIMING_CSHOLD_MASK) - usart->TIMING = ((init->autoCsHold << _USART_TIMING_CSHOLD_SHIFT) - & _USART_TIMING_CSHOLD_MASK) - | ((init->autoCsSetup << _USART_TIMING_CSSETUP_SHIFT) - & _USART_TIMING_CSSETUP_MASK); - if (init->autoCsEnable) - { - usart->CTRL |= USART_CTRL_AUTOCS; - } -#endif - /* Finally enable (as specified) */ - usart->CMD = (uint32_t)init->enable; -} - - -/***************************************************************************//** - * @brief - * Init USART for synchronous mode. - * - * @details - * This function will configure basic settings in order to operate in - * synchronous mode. - * - * Special control setup not covered by this function must be done after - * using this function by direct modification of the CTRL register. - * - * Notice that pins used by the USART module must be properly configured - * by the user explicitly, in order for the USART 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.) - * - * @param[in] usart - * Pointer to USART peripheral register block. (UART does not support this - * mode.) - * - * @param[in] init - * Pointer to initialization structure used to configure basic async setup. - ******************************************************************************/ -void USART_InitSync(USART_TypeDef *usart, const USART_InitSync_TypeDef *init) -{ - /* Make sure the module exists on the selected chip */ - EFM_ASSERT( USART_REF_VALID(usart) || USARTRF_REF_VALID(usart) ); - - /* Init USART registers to HW reset state. */ - USART_Reset(usart); - - /* Set bits for synchronous mode */ - usart->CTRL |= (USART_CTRL_SYNC) - | (uint32_t)init->clockMode - | (init->msbf ? USART_CTRL_MSBF : 0); - -#if defined(_USART_CTRL_AUTOTX_MASK) - usart->CTRL |= init->autoTx ? USART_CTRL_AUTOTX : 0; -#endif - -#if defined(_USART_INPUT_RXPRS_MASK) - /* Configure PRS input mode. */ - if (init->prsRxEnable) - { - usart->INPUT = (uint32_t)init->prsRxCh | USART_INPUT_RXPRS; - } -#endif - - /* Configure databits, leave stopbits and parity at reset default (not used) */ - usart->FRAME = (uint32_t)init->databits - | USART_FRAME_STOPBITS_DEFAULT - | USART_FRAME_PARITY_DEFAULT; - - /* Configure baudrate */ - USART_BaudrateSyncSet(usart, init->refFreq, init->baudrate); - - /* Finally enable (as specified) */ - if (init->master) - { - usart->CMD = USART_CMD_MASTEREN; - } - -#if defined(_USART_TIMING_CSHOLD_MASK) - usart->TIMING = ((init->autoCsHold << _USART_TIMING_CSHOLD_SHIFT) - & _USART_TIMING_CSHOLD_MASK) - | ((init->autoCsSetup << _USART_TIMING_CSSETUP_SHIFT) - & _USART_TIMING_CSSETUP_MASK); - if (init->autoCsEnable) - { - usart->CTRL |= USART_CTRL_AUTOCS; - } -#endif - - usart->CMD = (uint32_t)init->enable; -} - - -#if defined(USART0) || ((USART_COUNT == 1) && defined(USART1)) -/***************************************************************************//** - * @brief - * Init USART0 for asynchronous IrDA mode. - * - * @details - * This function will configure basic settings in order to operate in - * asynchronous IrDA mode. - * - * Special control setup not covered by this function must be done after - * using this function by direct modification of the CTRL and IRCTRL - * registers. - * - * Notice that pins used by the USART/UART module must be properly configured - * by the user explicitly, in order for the USART/UART 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.) - * - * @param[in] init - * Pointer to initialization structure used to configure async IrDA setup. - * - * @note - * This function only applies to USART0 as IrDA is not supported on the other - * USART modules. - * - ******************************************************************************/ -void USART_InitIrDA(const USART_InitIrDA_TypeDef *init) -{ - #if (USART_COUNT == 1) && defined(USART1) - USART_TypeDef *usart = USART1; - #else - USART_TypeDef *usart = USART0; - #endif - - /* Init USART as async device */ - USART_InitAsync(usart, &(init->async)); - - /* Set IrDA modulation to RZI (return-to-zero-inverted) */ - usart->CTRL |= USART_CTRL_TXINV; - - /* Invert Rx signal before demodulator if enabled */ - if (init->irRxInv) - { - usart->CTRL |= USART_CTRL_RXINV; - } - - /* Configure IrDA */ - usart->IRCTRL |= (uint32_t)init->irPw - | (uint32_t)init->irPrsSel - | ((uint32_t)init->irFilt << _USART_IRCTRL_IRFILT_SHIFT) - | ((uint32_t)init->irPrsEn << _USART_IRCTRL_IRPRSEN_SHIFT); - - /* Enable IrDA */ - usart->IRCTRL |= USART_IRCTRL_IREN; -} -#endif - - -#if defined(_USART_I2SCTRL_MASK) -/***************************************************************************//** - * @brief - * Init USART for I2S mode. - * - * @details - * This function will configure basic settings in order to operate in I2S - * mode. - * - * Special control setup not covered by this function must be done after - * using this function by direct modification of the CTRL and I2SCTRL - * registers. - * - * Notice that pins used by the USART module must be properly configured - * by the user explicitly, in order for the USART 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.) - * - * @param[in] usart - * Pointer to USART peripheral register block. (UART does not support this - * mode.) - * - * @param[in] init - * Pointer to initialization structure used to configure basic I2S setup. - * - * @note - * This function does not apply to all USART's. Refer to chip manuals. - * - ******************************************************************************/ -void USART_InitI2s(USART_TypeDef *usart, USART_InitI2s_TypeDef *init) -{ - USART_Enable_TypeDef enable; - - /* Make sure the module exists on the selected chip */ - EFM_ASSERT(USART_I2S_VALID(usart)); - - /* Override the enable setting. */ - enable = init->sync.enable; - init->sync.enable = usartDisable; - - /* Init USART as a sync device. */ - USART_InitSync(usart, &init->sync); - - /* Configure and enable I2CCTRL register acording to selected mode. */ - usart->I2SCTRL = (uint32_t)init->format - | (uint32_t)init->justify - | (init->delay ? USART_I2SCTRL_DELAY : 0) - | (init->dmaSplit ? USART_I2SCTRL_DMASPLIT : 0) - | (init->mono ? USART_I2SCTRL_MONO : 0) - | USART_I2SCTRL_EN; - - if (enable != usartDisable) - { - USART_Enable(usart, enable); - } -} -#endif - - -/***************************************************************************//** - * @brief - * Initialize automatic transmissions using PRS channel as trigger - * @note - * Initialize USART with USART_Init() before setting up PRS configuration - * - * @param[in] usart Pointer to USART to configure - * @param[in] init Pointer to initialization structure - ******************************************************************************/ -void USART_InitPrsTrigger(USART_TypeDef *usart, const USART_PrsTriggerInit_TypeDef *init) -{ - uint32_t trigctrl; - - /* Clear values that will be reconfigured */ - trigctrl = usart->TRIGCTRL & ~(_USART_TRIGCTRL_RXTEN_MASK - | _USART_TRIGCTRL_TXTEN_MASK -#if defined(USART_TRIGCTRL_AUTOTXTEN) - | _USART_TRIGCTRL_AUTOTXTEN_MASK -#endif - | _USART_TRIGCTRL_TSEL_MASK); - -#if defined(USART_TRIGCTRL_AUTOTXTEN) - if (init->autoTxTriggerEnable) - { - trigctrl |= USART_TRIGCTRL_AUTOTXTEN; - } -#endif - if (init->txTriggerEnable) - { - trigctrl |= USART_TRIGCTRL_TXTEN; - } - if (init->rxTriggerEnable) - { - trigctrl |= USART_TRIGCTRL_RXTEN; - } - trigctrl |= init->prsTriggerChannel; - - /* Enable new configuration */ - usart->TRIGCTRL = trigctrl; -} - - -/***************************************************************************//** - * @brief - * Reset USART/UART to same state as after a HW reset. - * - * @param[in] usart - * Pointer to USART/UART peripheral register block. - ******************************************************************************/ -void USART_Reset(USART_TypeDef *usart) -{ - /* Make sure the module exists on the selected chip */ - EFM_ASSERT( USART_REF_VALID(usart) - || USARTRF_REF_VALID(usart) - || UART_REF_VALID(usart) ); - - /* Make sure disabled first, before resetting other registers */ - usart->CMD = USART_CMD_RXDIS | USART_CMD_TXDIS | USART_CMD_MASTERDIS - | USART_CMD_RXBLOCKDIS | USART_CMD_TXTRIDIS | USART_CMD_CLEARTX - | USART_CMD_CLEARRX; - usart->CTRL = _USART_CTRL_RESETVALUE; - usart->FRAME = _USART_FRAME_RESETVALUE; - usart->TRIGCTRL = _USART_TRIGCTRL_RESETVALUE; - usart->CLKDIV = _USART_CLKDIV_RESETVALUE; - usart->IEN = _USART_IEN_RESETVALUE; - usart->IFC = _USART_IFC_MASK; -#if defined(_USART_ROUTEPEN_MASK) || defined(_UART_ROUTEPEN_MASK) - usart->ROUTEPEN = _USART_ROUTEPEN_RESETVALUE; - usart->ROUTELOC0 = _USART_ROUTELOC0_RESETVALUE; - usart->ROUTELOC1 = _USART_ROUTELOC1_RESETVALUE; -#else - usart->ROUTE = _USART_ROUTE_RESETVALUE; -#endif - - if (USART_IRDA_VALID(usart)) - { - usart->IRCTRL = _USART_IRCTRL_RESETVALUE; - } - -#if defined(_USART_INPUT_RESETVALUE) - usart->INPUT = _USART_INPUT_RESETVALUE; -#endif - -#if defined(_USART_I2SCTRL_RESETVALUE) - if (USART_I2S_VALID(usart)) - { - usart->I2SCTRL = _USART_I2SCTRL_RESETVALUE; - } -#endif -} - - -/***************************************************************************//** - * @brief - * Receive one 4-8 bit frame, (or part of 10-16 bit frame). - * - * @details - * This function is normally used to receive one frame when operating with - * frame length 4-8 bits. Please refer to @ref USART_RxExt() for reception of - * 9 bit frames. - * - * Notice that possible parity/stop bits in asynchronous mode are not - * considered part of specified frame bit length. - * - * @note - * This function will stall if the buffer is empty, until data is received. - * Alternatively the user can explicitly check whether data is available, and - * if data is avaliable, call @ref USART_RxDataGet() to read the RXDATA - * register directly. - * - * @param[in] usart - * Pointer to USART/UART peripheral register block. - * - * @return - * Data received. - ******************************************************************************/ -uint8_t USART_Rx(USART_TypeDef *usart) -{ - while (!(usart->STATUS & USART_STATUS_RXDATAV)) - ; - - return (uint8_t)usart->RXDATA; -} - - -/***************************************************************************//** - * @brief - * Receive two 4-8 bit frames, or one 10-16 bit frame. - * - * @details - * This function is normally used to receive one frame when operating with - * frame length 10-16 bits. Please refer to @ref USART_RxDoubleExt() for - * reception of two 9 bit frames. - * - * 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 empty, until data is received. - * Alternatively the user can explicitly check whether data is available, and - * if data is avaliable, call @ref USART_RxDoubleGet() to read the RXDOUBLE - * register directly. - * - * @param[in] usart - * Pointer to USART/UART peripheral register block. - * - * @return - * Data received. - ******************************************************************************/ -uint16_t USART_RxDouble(USART_TypeDef *usart) -{ - while (!(usart->STATUS & USART_STATUS_RXFULL)) - ; - - return (uint16_t)usart->RXDOUBLE; -} - - -/***************************************************************************//** - * @brief - * Receive two 4-9 bit frames, or one 10-16 bit frame with extended - * information. - * - * @details - * This function is normally used to receive one frame when operating with - * frame length 10-16 bits and additional RX status information is required. - * - * 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 empty, until data is received. - * Alternatively the user can explicitly check whether data is available, and - * if data is avaliable, call @ref USART_RxDoubleXGet() to read the RXDOUBLEX - * register directly. - * - * @param[in] usart - * Pointer to USART/UART peripheral register block. - * - * @return - * Data received. - ******************************************************************************/ -uint32_t USART_RxDoubleExt(USART_TypeDef *usart) -{ - while (!(usart->STATUS & USART_STATUS_RXFULL)) - ; - - return usart->RXDOUBLEX; -} - - -/***************************************************************************//** - * @brief - * Receive one 4-9 bit frame, (or part of 10-16 bit frame) with extended - * information. - * - * @details - * This function is normally used to receive one frame when operating with - * frame length 4-9 bits and additional RX status information is required. - * - * 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 empty, until data is received. - * Alternatively the user can explicitly check whether data is available, and - * if data is avaliable, call @ref USART_RxDataXGet() to read the RXDATAX - * register directly. - * - * @param[in] usart - * Pointer to USART/UART peripheral register block. - * - * @return - * Data received. - ******************************************************************************/ -uint16_t USART_RxExt(USART_TypeDef *usart) -{ - while (!(usart->STATUS & USART_STATUS_RXDATAV)) - ; - - return (uint16_t)usart->RXDATAX; -} - - -/***************************************************************************//** - * @brief - * Perform one 8 bit frame SPI transfer. - * - * @note - * This function will stall if the transmit buffer is full. When a transmit - * buffer becomes available, data is written and the function will wait until - * the data is fully transmitted. The SPI return value is then read out and - * returned. - * - * @param[in] usart - * Pointer to USART peripheral register block. - * - * @param[in] data - * Data to transmit. - * - * @return - * Data received. - ******************************************************************************/ -uint8_t USART_SpiTransfer(USART_TypeDef *usart, uint8_t data) -{ - while (!(usart->STATUS & USART_STATUS_TXBL)) - ; - usart->TXDATA = (uint32_t)data; - while (!(usart->STATUS & USART_STATUS_TXC)) - ; - return (uint8_t)usart->RXDATA; -} - - -/***************************************************************************//** - * @brief - * Transmit one 4-9 bit frame. - * - * @details - * Depending on frame length configuration, 4-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 USART_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] usart - * Pointer to USART/UART peripheral register block. - * - * @param[in] data - * Data to transmit. See details above for further info. - ******************************************************************************/ -void USART_Tx(USART_TypeDef *usart, uint8_t data) -{ - /* Check that transmit buffer is empty */ - while (!(usart->STATUS & USART_STATUS_TXBL)) - ; - usart->TXDATA = (uint32_t)data; -} - - -/***************************************************************************//** - * @brief - * Transmit two 4-9 bit frames, or one 10-16 bit frame. - * - * @details - * Depending on frame length configuration, 4-8 (least significant) bits from - * each byte in @p data are transmitted. If frame length is 9, 8 bits are - * transmitted from each byte in @p data adding one bit as specified by CTRL - * register, BIT8DV field, to each byte. Please refer to USART_TxDoubleExt() - * for transmitting two 9 bit frames with full control of all 9 bits. - * - * If frame length is 10-16, 10-16 (least significant) bits from @p data - * are transmitted. - * - * 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] usart - * Pointer to USART/UART peripheral register block. - * - * @param[in] data - * Data to transmit, the least significant byte holds the frame transmitted - * first. See details above for further info. - ******************************************************************************/ -void USART_TxDouble(USART_TypeDef *usart, uint16_t data) -{ - /* Check that transmit buffer is empty */ - while (!(usart->STATUS & USART_STATUS_TXBL)) - ; - usart->TXDOUBLE = (uint32_t)data; -} - - -/***************************************************************************//** - * @brief - * Transmit two 4-9 bit frames, or one 10-16 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] usart - * Pointer to USART/UART peripheral register block. - * - * @param[in] data - * Data to transmit with extended control. Contains two 16 bit words - * concatenated. Least significant word holds frame transitted first. If frame - * length is 4-9, two frames with 4-9 least significant bits from each 16 bit - * word are transmitted. - * @par - * If frame length is 10-16 bits, 8 data bits are taken from the least - * significant 16 bit word, and the remaining bits from the other 16 bit word. - * @par - * Additional control bits are available as documented in the reference - * manual (set to 0 if not used). For 10-16 bit frame length, these control - * bits are taken from the most significant 16 bit word. - ******************************************************************************/ -void USART_TxDoubleExt(USART_TypeDef *usart, uint32_t data) -{ - /* Check that transmit buffer is empty */ - while (!(usart->STATUS & USART_STATUS_TXBL)) - ; - usart->TXDOUBLEX = data; -} - - -/***************************************************************************//** - * @brief - * Transmit one 4-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] usart - * Pointer to USART/UART 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 USART_TxExt(USART_TypeDef *usart, uint16_t data) -{ - /* Check that transmit buffer is empty */ - while (!(usart->STATUS & USART_STATUS_TXBL)) - ; - usart->TXDATAX = (uint32_t)data; -} - - -/** @} (end addtogroup USART) */ -/** @} (end addtogroup EM_Library) */ -#endif /* defined(USART_COUNT) && (USART_COUNT > 0) */ +/***************************************************************************//** + * @file em_usart.c + * @brief Universal synchronous/asynchronous receiver/transmitter (USART/UART) + * 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_usart.h" +#if defined(USART_COUNT) && (USART_COUNT > 0) + +#include "em_cmu.h" +#include "em_bus.h" +#include "em_assert.h" + +/***************************************************************************//** + * @addtogroup EM_Library + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup USART + * @brief Universal Synchronous/Asynchronous Receiver/Transmitter + * Peripheral API + * @{ + ******************************************************************************/ + +/******************************************************************************* + ******************************* DEFINES *********************************** + ******************************************************************************/ + +/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */ + + +/** Validation of USART register block pointer reference for assert statements. */ +#if (USART_COUNT == 1) && defined(USART0) +#define USART_REF_VALID(ref) ((ref) == USART0) + +#elif (USART_COUNT == 1) && defined(USART1) +#define USART_REF_VALID(ref) ((ref) == USART1) + +#elif (USART_COUNT == 2) && defined(USART2) +#define USART_REF_VALID(ref) (((ref) == USART1) || ((ref) == USART2)) + +#elif (USART_COUNT == 2) +#define USART_REF_VALID(ref) (((ref) == USART0) || ((ref) == USART1)) + +#elif (USART_COUNT == 3) +#define USART_REF_VALID(ref) (((ref) == USART0) || ((ref) == USART1) || \ + ((ref) == USART2)) +#elif (USART_COUNT == 4) +#define USART_REF_VALID(ref) (((ref) == USART0) || ((ref) == USART1) || \ + ((ref) == USART2) || ((ref) == USART3)) +#elif (USART_COUNT == 5) +#define USART_REF_VALID(ref) (((ref) == USART0) || ((ref) == USART1) || \ + ((ref) == USART2) || ((ref) == USART3) || \ + ((ref) == USART4)) +#elif (USART_COUNT == 6) +#define USART_REF_VALID(ref) (((ref) == USART0) || ((ref) == USART1) || \ + ((ref) == USART2) || ((ref) == USART3) || \ + ((ref) == USART4) || ((ref) == USART5)) +#else +#error "Undefined number of USARTs." +#endif + +#if defined(USARTRF_COUNT) && (USARTRF_COUNT > 0) +#if (USARTRF_COUNT == 1) && defined(USARTRF0) +#define USARTRF_REF_VALID(ref) ((ref) == USARTRF0) +#elif (USARTRF_COUNT == 1) && defined(USARTRF1) +#define USARTRF_REF_VALID(ref) ((ref) == USARTRF1) +#else +#define USARTRF_REF_VALID(ref) (0) +#endif +#else +#define USARTRF_REF_VALID(ref) (0) +#endif + +#if defined(_EZR32_HAPPY_FAMILY) +#define USART_IRDA_VALID(ref) ((ref) == USART0) +#elif defined(_EFM32_HAPPY_FAMILY) +#define USART_IRDA_VALID(ref) (((ref) == USART0) || ((ref) == USART1)) +#elif defined(USART0) +#define USART_IRDA_VALID(ref) ((ref) == USART0) +#elif (USART_COUNT == 1) && defined(USART1) +#define USART_IRDA_VALID(ref) ((ref) == USART1) +#else +#define USART_IRDA_VALID(ref) (0) +#endif + +#if defined(_EZR32_HAPPY_FAMILY) +#define USART_I2S_VALID(ref) ((ref) == USART0) +#elif defined(_EFM32_HAPPY_FAMILY) +#define USART_I2S_VALID(ref) (((ref) == USART0) || ((ref) == USART1)) +#elif defined(_EFM32_TINY_FAMILY) || defined(_EFM32_ZERO_FAMILY) || defined(_SILICON_LABS_32B_PLATFORM_2) +#define USART_I2S_VALID(ref) ((ref) == USART1) +#elif defined(_EFM32_GIANT_FAMILY) || defined(_EFM32_WONDER_FAMILY) +#define USART_I2S_VALID(ref) (((ref) == USART1) || ((ref) == USART2)) +#endif + +#if (UART_COUNT == 1) +#define UART_REF_VALID(ref) ((ref) == UART0) +#elif (UART_COUNT == 2) +#define UART_REF_VALID(ref) (((ref) == UART0) || ((ref) == UART1)) +#else +#define UART_REF_VALID(ref) (0) +#endif + +/** @endcond */ + + +/******************************************************************************* + ************************** GLOBAL FUNCTIONS ******************************* + ******************************************************************************/ + +/***************************************************************************//** + * @brief + * Configure USART/UART operating in asynchronous mode to use a given + * baudrate (or as close as possible to specified baudrate). + * + * @param[in] usart + * Pointer to USART/UART peripheral register block. + * + * @param[in] refFreq + * USART/UART 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 USART/UART. + * + * @param[in] ovs + * Oversampling to be used. Normal is 16x oversampling, but lower oversampling + * may be used to achieve higher rates or better baudrate accuracy in some + * cases. Notice that lower oversampling frequency makes channel more + * vulnerable to bit faults during reception due to clock inaccuracies + * compared to link partner. + ******************************************************************************/ +void USART_BaudrateAsyncSet(USART_TypeDef *usart, + uint32_t refFreq, + uint32_t baudrate, + USART_OVS_TypeDef ovs) +{ + uint32_t clkdiv; + uint32_t oversample; + + /* 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 * (fHFPERCLK/(oversample * br) - 1) + * or + * CLKDIV = (256 * fHFPERCLK)/(oversample * br) - 256 + * + * The basic problem with integer division in the above formula is that + * the dividend (256 * fHFPERCLK) 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 fHFPERCLK value either. + * + * One can possibly factorize 256 and oversample/br. However, + * since the last 6 or 3 bits of CLKDIV are don't care, we can base our + * integer arithmetic on the below formula + * + * CLKDIV / 64 = (4 * fHFPERCLK)/(oversample * br) - 4 (3 bits dont care) + * or + * CLKDIV / 8 = (32 * fHFPERCLK)/(oversample * br) - 32 (6 bits dont care) + * + * and calculate 1/64 of CLKDIV first. This allows for fHFPERCLK + * up to 1GHz without overflowing a 32 bit value! + */ + + /* HFPERCLK used to clock all USART/UART peripheral modules */ + if (!refFreq) + { + refFreq = CMU_ClockFreqGet(cmuClock_HFPER); + } + + /* Map oversampling */ + switch (ovs) + { + case USART_CTRL_OVS_X16: + EFM_ASSERT(baudrate <= (refFreq / 16)); + oversample = 16; + break; + + case USART_CTRL_OVS_X8: + EFM_ASSERT(baudrate <= (refFreq / 8)); + oversample = 8; + break; + + case USART_CTRL_OVS_X6: + EFM_ASSERT(baudrate <= (refFreq / 6)); + oversample = 6; + break; + + case USART_CTRL_OVS_X4: + EFM_ASSERT(baudrate <= (refFreq / 4)); + oversample = 4; + break; + + default: + /* Invalid input */ + EFM_ASSERT(0); + return; + } + + /* Calculate and set CLKDIV with fractional bits. + * The addend (oversample*baudrate)/2 in the first line is to round the + * divisor up by half the divisor before the division in order to reduce the + * integer division error, which consequently results in a higher baudrate + * than desired. */ +#if defined(_USART_CLKDIV_DIV_MASK) && (_USART_CLKDIV_DIV_MASK >= 0x7FFFF8UL) + clkdiv = 32 * refFreq + (oversample * baudrate) / 2; + clkdiv /= (oversample * baudrate); + clkdiv -= 32; + clkdiv *= 8; +#else + clkdiv = 4 * refFreq + (oversample * baudrate) / 2; + clkdiv /= (oversample * baudrate); + clkdiv -= 4; + clkdiv *= 64; +#endif + + /* Verify that resulting clock divider is within limits */ + EFM_ASSERT(clkdiv <= _USART_CLKDIV_DIV_MASK); + + /* If EFM_ASSERT is not enabled, make sure we don't write to reserved bits */ + clkdiv &= _USART_CLKDIV_DIV_MASK; + + usart->CTRL &= ~_USART_CTRL_OVS_MASK; + usart->CTRL |= ovs; + usart->CLKDIV = clkdiv; +} + + +/***************************************************************************//** + * @brief + * Calculate baudrate for USART/UART given reference frequency, clock division + * and oversampling rate (if async mode). + * + * @details + * This function returns the baudrate that a USART/UART module will use if + * configured with the given frequency, clock divisor and mode. 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 + * USART/UART HF peripheral frequency used. + * + * @param[in] clkdiv + * Clock division factor to be used. + * + * @param[in] syncmode + * @li true - synchronous mode operation. + * @li false - asynchronous mode operation. + * + * @param[in] ovs + * Oversampling used if asynchronous mode. Not used if @p syncmode is true. + * + * @return + * Baudrate with given settings. + ******************************************************************************/ +uint32_t USART_BaudrateCalc(uint32_t refFreq, + uint32_t clkdiv, + bool syncmode, + USART_OVS_TypeDef ovs) +{ + uint32_t oversample; + uint64_t divisor; + uint64_t factor; + uint64_t remainder; + uint64_t quotient; + uint32_t br; + + /* Mask out unused bits */ + clkdiv &= _USART_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 calculation depends on if synchronous or asynchronous mode */ + if (syncmode) + { + /* + * Baudrate is given by: + * + * br = fHFPERCLK/(2 * (1 + (CLKDIV / 256))) + * + * which can be rewritten to + * + * br = (128 * fHFPERCLK)/(256 + CLKDIV) + */ + oversample = 1; /* Not used in sync mode, ie 1 */ + factor = 128; + } + else + { + /* + * Baudrate in asynchronous mode is given by: + * + * br = fHFPERCLK/(oversample * (1 + (CLKDIV / 256))) + * + * which can be rewritten to + * + * br = (256 * fHFPERCLK)/(oversample * (256 + CLKDIV)) + * + * First of all we can reduce the 256 factor of the dividend with + * (part of) oversample part of the divisor. + */ + + switch (ovs) + { + case USART_CTRL_OVS_X16: + oversample = 1; + factor = 256 / 16; + break; + + case USART_CTRL_OVS_X8: + oversample = 1; + factor = 256 / 8; + break; + + case USART_CTRL_OVS_X6: + oversample = 3; + factor = 256 / 2; + break; + + default: + oversample = 1; + factor = 256 / 4; + break; + } + } + + /* + * The basic problem with integer division in the above formula is that + * the dividend (factor * fHFPERCLK) may become larger than a 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 fHFPERCLK 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 = xa / b = x(qb + r)/b = xq + xr/b + * + * where x is 'factor', a is 'refFreq' and b is 'divisor', referring to + * variable names. + */ + + /* Divisor will never exceed max 32 bit value since clkdiv <= 0xFFFFF8 */ + /* and 'oversample' has been reduced to <= 3. */ + divisor = oversample * (256 + clkdiv); + + quotient = refFreq / divisor; + remainder = refFreq % divisor; + + /* factor <= 128 and since divisor >= 256, the below cannot exceed max */ + /* 32 bit value. However, factor * remainder can become larger than 32-bit */ + /* because of the size of _USART_CLKDIV_DIV_MASK on some families. */ + br = (uint32_t)(factor * quotient); + + /* + * factor <= 128 and remainder < (oversample*(256 + clkdiv)), which + * means dividend (factor * remainder) worst case is + * 128 * (3 * (256 + _USART_CLKDIV_DIV_MASK)) = 0x1_8001_7400. + */ + br += (uint32_t)((factor * remainder) / divisor); + + return br; +} + + +/***************************************************************************//** + * @brief + * Get current baudrate for USART/UART. + * + * @details + * This function returns the actual baudrate (not considering oscillator + * inaccuracies) used by a USART/UART peripheral. + * + * @param[in] usart + * Pointer to USART/UART peripheral register block. + * + * @return + * Current baudrate. + ******************************************************************************/ +uint32_t USART_BaudrateGet(USART_TypeDef *usart) +{ + uint32_t freq; + USART_OVS_TypeDef ovs; + bool syncmode; + + if (usart->CTRL & USART_CTRL_SYNC) + { + syncmode = true; + } + else + { + syncmode = false; + } + + /* HFPERCLK used to clock all USART/UART peripheral modules */ + freq = CMU_ClockFreqGet(cmuClock_HFPER); + ovs = (USART_OVS_TypeDef)(usart->CTRL & _USART_CTRL_OVS_MASK); + return USART_BaudrateCalc(freq, usart->CLKDIV, syncmode, ovs); +} + + +/***************************************************************************//** + * @brief + * Configure USART operating in synchronous mode to use a given baudrate + * (or as close as possible to specified baudrate). + * + * @details + * The configuration will be set to use a baudrate <= the specified baudrate + * in order to ensure that the baudrate does not exceed the specified value. + * + * Fractional clock division is suppressed, although the HW design allows it. + * It could cause half clock cycles to exceed specified limit, and thus + * potentially violate specifications for the slave device. In some special + * situations fractional clock division may be useful even in synchronous + * mode, but in those cases it must be directly adjusted, possibly assisted + * by USART_BaudrateCalc(): + * + * @param[in] usart + * Pointer to USART peripheral register block. (Cannot be used on UART + * modules.) + * + * @param[in] refFreq + * USART 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 USART. + ******************************************************************************/ +void USART_BaudrateSyncSet(USART_TypeDef *usart, uint32_t refFreq, uint32_t baudrate) +{ +#if defined(_USART_CLKDIV_DIV_MASK) && (_USART_CLKDIV_DIV_MASK >= 0x7FFFF8UL) + uint64_t clkdiv; +#else + uint32_t clkdiv; +#endif + + /* Inhibit divide by 0 */ + EFM_ASSERT(baudrate); + + /* + * CLKDIV in synchronous mode is given by: + * + * CLKDIV = 256 * (fHFPERCLK/(2 * br) - 1) + */ + + /* HFPERCLK used to clock all USART/UART peripheral modules */ + if (!refFreq) + { + refFreq = CMU_ClockFreqGet(cmuClock_HFPER); + } + +#if defined(_USART_CLKDIV_DIV_MASK) && (_USART_CLKDIV_DIV_MASK >= 0x7FFFF8UL) + /* Calculate and set CLKDIV without fractional bits */ + clkdiv = 2 * baudrate; + clkdiv = (0x100ULL * (uint64_t)refFreq) / clkdiv; + + /* Round up by not subtracting 256 and mask off fractional part */ + clkdiv &= ~0xFF; +#else + /* Calculate and set CLKDIV with fractional bits */ + clkdiv = 2 * refFreq; + clkdiv += baudrate - 1; + clkdiv /= baudrate; + clkdiv -= 4; + clkdiv *= 64; + /* Make sure we don't use fractional bits by rounding CLKDIV */ + /* up (and thus reducing baudrate, not increasing baudrate above */ + /* specified value). */ + clkdiv += 0xc0; + clkdiv &= 0xffffff00; +#endif + + /* Verify that resulting clock divider is within limits */ + EFM_ASSERT(!(clkdiv & ~_USART_CLKDIV_DIV_MASK)); + + /* If EFM_ASSERT is not enabled, make sure we don't write to reserved bits */ + clkdiv &= _USART_CLKDIV_DIV_MASK; + + BUS_RegMaskedWrite(&usart->CLKDIV, _USART_CLKDIV_DIV_MASK, clkdiv); +} + + +/***************************************************************************//** + * @brief + * Enable/disable USART/UART 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). + * + * @param[in] usart + * Pointer to USART/UART peripheral register block. + * + * @param[in] enable + * Select status for receiver/transmitter. + ******************************************************************************/ +void USART_Enable(USART_TypeDef *usart, USART_Enable_TypeDef enable) +{ + uint32_t tmp; + + /* Make sure the module exists on the selected chip */ + EFM_ASSERT( USART_REF_VALID(usart) + || USARTRF_REF_VALID(usart) + || UART_REF_VALID(usart) ); + + /* Disable as specified */ + tmp = ~((uint32_t) (enable)); + tmp &= _USART_CMD_RXEN_MASK | _USART_CMD_TXEN_MASK; + usart->CMD = tmp << 1; + + /* Enable as specified */ + usart->CMD = (uint32_t) (enable); +} + + +/***************************************************************************//** + * @brief + * Init USART/UART for normal asynchronous mode. + * + * @details + * This function will configure basic settings in order to operate in normal + * asynchronous mode. + * + * Special control setup not covered by this function must be done after + * using this function by direct modification of the CTRL register. + * + * Notice that pins used by the USART/UART module must be properly configured + * by the user explicitly, in order for the USART/UART 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.) + * + * @param[in] usart + * Pointer to USART/UART peripheral register block. + * + * @param[in] init + * Pointer to initialization structure used to configure basic async setup. + ******************************************************************************/ +void USART_InitAsync(USART_TypeDef *usart, const USART_InitAsync_TypeDef *init) +{ + /* Make sure the module exists on the selected chip */ + EFM_ASSERT( USART_REF_VALID(usart) + || USARTRF_REF_VALID(usart) + || UART_REF_VALID(usart) ); + + /* Init USART registers to HW reset state. */ + USART_Reset(usart); + +#if defined(USART_INPUT_RXPRS) && defined(USART_CTRL_MVDIS) + /* Disable majority vote if specified. */ + if (init->mvdis) + { + usart->CTRL |= USART_CTRL_MVDIS; + } + + /* Configure PRS input mode. */ + if (init->prsRxEnable) + { + usart->INPUT = (uint32_t) init->prsRxCh | USART_INPUT_RXPRS; + } +#endif + + /* Configure databits, stopbits and parity */ + usart->FRAME = (uint32_t)init->databits + | (uint32_t)init->stopbits + | (uint32_t)init->parity; + + /* Configure baudrate */ + USART_BaudrateAsyncSet(usart, init->refFreq, init->baudrate, init->oversampling); + +#if defined(_USART_TIMING_CSHOLD_MASK) + usart->TIMING = ((init->autoCsHold << _USART_TIMING_CSHOLD_SHIFT) + & _USART_TIMING_CSHOLD_MASK) + | ((init->autoCsSetup << _USART_TIMING_CSSETUP_SHIFT) + & _USART_TIMING_CSSETUP_MASK); + if (init->autoCsEnable) + { + usart->CTRL |= USART_CTRL_AUTOCS; + } +#endif + /* Finally enable (as specified) */ + usart->CMD = (uint32_t)init->enable; +} + + +/***************************************************************************//** + * @brief + * Init USART for synchronous mode. + * + * @details + * This function will configure basic settings in order to operate in + * synchronous mode. + * + * Special control setup not covered by this function must be done after + * using this function by direct modification of the CTRL register. + * + * Notice that pins used by the USART module must be properly configured + * by the user explicitly, in order for the USART 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.) + * + * @param[in] usart + * Pointer to USART peripheral register block. (UART does not support this + * mode.) + * + * @param[in] init + * Pointer to initialization structure used to configure basic async setup. + ******************************************************************************/ +void USART_InitSync(USART_TypeDef *usart, const USART_InitSync_TypeDef *init) +{ + /* Make sure the module exists on the selected chip */ + EFM_ASSERT( USART_REF_VALID(usart) || USARTRF_REF_VALID(usart) ); + + /* Init USART registers to HW reset state. */ + USART_Reset(usart); + + /* Set bits for synchronous mode */ + usart->CTRL |= (USART_CTRL_SYNC) + | (uint32_t)init->clockMode + | (init->msbf ? USART_CTRL_MSBF : 0); + +#if defined(_USART_CTRL_AUTOTX_MASK) + usart->CTRL |= init->autoTx ? USART_CTRL_AUTOTX : 0; +#endif + +#if defined(_USART_INPUT_RXPRS_MASK) + /* Configure PRS input mode. */ + if (init->prsRxEnable) + { + usart->INPUT = (uint32_t)init->prsRxCh | USART_INPUT_RXPRS; + } +#endif + + /* Configure databits, leave stopbits and parity at reset default (not used) */ + usart->FRAME = (uint32_t)init->databits + | USART_FRAME_STOPBITS_DEFAULT + | USART_FRAME_PARITY_DEFAULT; + + /* Configure baudrate */ + USART_BaudrateSyncSet(usart, init->refFreq, init->baudrate); + + /* Finally enable (as specified) */ + if (init->master) + { + usart->CMD = USART_CMD_MASTEREN; + } + +#if defined(_USART_TIMING_CSHOLD_MASK) + usart->TIMING = ((init->autoCsHold << _USART_TIMING_CSHOLD_SHIFT) + & _USART_TIMING_CSHOLD_MASK) + | ((init->autoCsSetup << _USART_TIMING_CSSETUP_SHIFT) + & _USART_TIMING_CSSETUP_MASK); + if (init->autoCsEnable) + { + usart->CTRL |= USART_CTRL_AUTOCS; + } +#endif + + usart->CMD = (uint32_t)init->enable; +} + + +#if defined(USART0) || ((USART_COUNT == 1) && defined(USART1)) +/***************************************************************************//** + * @brief + * Init USART0 for asynchronous IrDA mode. + * + * @details + * This function will configure basic settings in order to operate in + * asynchronous IrDA mode. + * + * Special control setup not covered by this function must be done after + * using this function by direct modification of the CTRL and IRCTRL + * registers. + * + * Notice that pins used by the USART/UART module must be properly configured + * by the user explicitly, in order for the USART/UART 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.) + * + * @param[in] init + * Pointer to initialization structure used to configure async IrDA setup. + * + * @note + * This function only applies to USART0 as IrDA is not supported on the other + * USART modules. + * + ******************************************************************************/ +void USART_InitIrDA(const USART_InitIrDA_TypeDef *init) +{ + #if (USART_COUNT == 1) && defined(USART1) + USART_TypeDef *usart = USART1; + #else + USART_TypeDef *usart = USART0; + #endif + + /* Init USART as async device */ + USART_InitAsync(usart, &(init->async)); + + /* Set IrDA modulation to RZI (return-to-zero-inverted) */ + usart->CTRL |= USART_CTRL_TXINV; + + /* Invert Rx signal before demodulator if enabled */ + if (init->irRxInv) + { + usart->CTRL |= USART_CTRL_RXINV; + } + + /* Configure IrDA */ + usart->IRCTRL |= (uint32_t)init->irPw + | (uint32_t)init->irPrsSel + | ((uint32_t)init->irFilt << _USART_IRCTRL_IRFILT_SHIFT) + | ((uint32_t)init->irPrsEn << _USART_IRCTRL_IRPRSEN_SHIFT); + + /* Enable IrDA */ + usart->IRCTRL |= USART_IRCTRL_IREN; +} +#endif + + +#if defined(_USART_I2SCTRL_MASK) +/***************************************************************************//** + * @brief + * Init USART for I2S mode. + * + * @details + * This function will configure basic settings in order to operate in I2S + * mode. + * + * Special control setup not covered by this function must be done after + * using this function by direct modification of the CTRL and I2SCTRL + * registers. + * + * Notice that pins used by the USART module must be properly configured + * by the user explicitly, in order for the USART 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.) + * + * @param[in] usart + * Pointer to USART peripheral register block. (UART does not support this + * mode.) + * + * @param[in] init + * Pointer to initialization structure used to configure basic I2S setup. + * + * @note + * This function does not apply to all USART's. Refer to chip manuals. + * + ******************************************************************************/ +void USART_InitI2s(USART_TypeDef *usart, USART_InitI2s_TypeDef *init) +{ + USART_Enable_TypeDef enable; + + /* Make sure the module exists on the selected chip */ + EFM_ASSERT(USART_I2S_VALID(usart)); + + /* Override the enable setting. */ + enable = init->sync.enable; + init->sync.enable = usartDisable; + + /* Init USART as a sync device. */ + USART_InitSync(usart, &init->sync); + + /* Configure and enable I2CCTRL register acording to selected mode. */ + usart->I2SCTRL = (uint32_t)init->format + | (uint32_t)init->justify + | (init->delay ? USART_I2SCTRL_DELAY : 0) + | (init->dmaSplit ? USART_I2SCTRL_DMASPLIT : 0) + | (init->mono ? USART_I2SCTRL_MONO : 0) + | USART_I2SCTRL_EN; + + if (enable != usartDisable) + { + USART_Enable(usart, enable); + } +} +#endif + + +/***************************************************************************//** + * @brief + * Initialize automatic transmissions using PRS channel as trigger + * @note + * Initialize USART with USART_Init() before setting up PRS configuration + * + * @param[in] usart Pointer to USART to configure + * @param[in] init Pointer to initialization structure + ******************************************************************************/ +void USART_InitPrsTrigger(USART_TypeDef *usart, const USART_PrsTriggerInit_TypeDef *init) +{ + uint32_t trigctrl; + + /* Clear values that will be reconfigured */ + trigctrl = usart->TRIGCTRL & ~(_USART_TRIGCTRL_RXTEN_MASK + | _USART_TRIGCTRL_TXTEN_MASK +#if defined(USART_TRIGCTRL_AUTOTXTEN) + | _USART_TRIGCTRL_AUTOTXTEN_MASK +#endif + | _USART_TRIGCTRL_TSEL_MASK); + +#if defined(USART_TRIGCTRL_AUTOTXTEN) + if (init->autoTxTriggerEnable) + { + trigctrl |= USART_TRIGCTRL_AUTOTXTEN; + } +#endif + if (init->txTriggerEnable) + { + trigctrl |= USART_TRIGCTRL_TXTEN; + } + if (init->rxTriggerEnable) + { + trigctrl |= USART_TRIGCTRL_RXTEN; + } + trigctrl |= init->prsTriggerChannel; + + /* Enable new configuration */ + usart->TRIGCTRL = trigctrl; +} + + +/***************************************************************************//** + * @brief + * Reset USART/UART to same state as after a HW reset. + * + * @param[in] usart + * Pointer to USART/UART peripheral register block. + ******************************************************************************/ +void USART_Reset(USART_TypeDef *usart) +{ + /* Make sure the module exists on the selected chip */ + EFM_ASSERT( USART_REF_VALID(usart) + || USARTRF_REF_VALID(usart) + || UART_REF_VALID(usart) ); + + /* Make sure disabled first, before resetting other registers */ + usart->CMD = USART_CMD_RXDIS | USART_CMD_TXDIS | USART_CMD_MASTERDIS + | USART_CMD_RXBLOCKDIS | USART_CMD_TXTRIDIS | USART_CMD_CLEARTX + | USART_CMD_CLEARRX; + usart->CTRL = _USART_CTRL_RESETVALUE; + usart->FRAME = _USART_FRAME_RESETVALUE; + usart->TRIGCTRL = _USART_TRIGCTRL_RESETVALUE; + usart->CLKDIV = _USART_CLKDIV_RESETVALUE; + usart->IEN = _USART_IEN_RESETVALUE; + usart->IFC = _USART_IFC_MASK; +#if defined(_USART_ROUTEPEN_MASK) || defined(_UART_ROUTEPEN_MASK) + usart->ROUTEPEN = _USART_ROUTEPEN_RESETVALUE; + usart->ROUTELOC0 = _USART_ROUTELOC0_RESETVALUE; + usart->ROUTELOC1 = _USART_ROUTELOC1_RESETVALUE; +#else + usart->ROUTE = _USART_ROUTE_RESETVALUE; +#endif + + if (USART_IRDA_VALID(usart)) + { + usart->IRCTRL = _USART_IRCTRL_RESETVALUE; + } + +#if defined(_USART_INPUT_RESETVALUE) + usart->INPUT = _USART_INPUT_RESETVALUE; +#endif + +#if defined(_USART_I2SCTRL_RESETVALUE) + if (USART_I2S_VALID(usart)) + { + usart->I2SCTRL = _USART_I2SCTRL_RESETVALUE; + } +#endif +} + + +/***************************************************************************//** + * @brief + * Receive one 4-8 bit frame, (or part of 10-16 bit frame). + * + * @details + * This function is normally used to receive one frame when operating with + * frame length 4-8 bits. Please refer to @ref USART_RxExt() for reception of + * 9 bit frames. + * + * Notice that possible parity/stop bits in asynchronous mode are not + * considered part of specified frame bit length. + * + * @note + * This function will stall if the buffer is empty, until data is received. + * Alternatively the user can explicitly check whether data is available, and + * if data is avaliable, call @ref USART_RxDataGet() to read the RXDATA + * register directly. + * + * @param[in] usart + * Pointer to USART/UART peripheral register block. + * + * @return + * Data received. + ******************************************************************************/ +uint8_t USART_Rx(USART_TypeDef *usart) +{ + while (!(usart->STATUS & USART_STATUS_RXDATAV)) + ; + + return (uint8_t)usart->RXDATA; +} + + +/***************************************************************************//** + * @brief + * Receive two 4-8 bit frames, or one 10-16 bit frame. + * + * @details + * This function is normally used to receive one frame when operating with + * frame length 10-16 bits. Please refer to @ref USART_RxDoubleExt() for + * reception of two 9 bit frames. + * + * 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 empty, until data is received. + * Alternatively the user can explicitly check whether data is available, and + * if data is avaliable, call @ref USART_RxDoubleGet() to read the RXDOUBLE + * register directly. + * + * @param[in] usart + * Pointer to USART/UART peripheral register block. + * + * @return + * Data received. + ******************************************************************************/ +uint16_t USART_RxDouble(USART_TypeDef *usart) +{ + while (!(usart->STATUS & USART_STATUS_RXFULL)) + ; + + return (uint16_t)usart->RXDOUBLE; +} + + +/***************************************************************************//** + * @brief + * Receive two 4-9 bit frames, or one 10-16 bit frame with extended + * information. + * + * @details + * This function is normally used to receive one frame when operating with + * frame length 10-16 bits and additional RX status information is required. + * + * 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 empty, until data is received. + * Alternatively the user can explicitly check whether data is available, and + * if data is avaliable, call @ref USART_RxDoubleXGet() to read the RXDOUBLEX + * register directly. + * + * @param[in] usart + * Pointer to USART/UART peripheral register block. + * + * @return + * Data received. + ******************************************************************************/ +uint32_t USART_RxDoubleExt(USART_TypeDef *usart) +{ + while (!(usart->STATUS & USART_STATUS_RXFULL)) + ; + + return usart->RXDOUBLEX; +} + + +/***************************************************************************//** + * @brief + * Receive one 4-9 bit frame, (or part of 10-16 bit frame) with extended + * information. + * + * @details + * This function is normally used to receive one frame when operating with + * frame length 4-9 bits and additional RX status information is required. + * + * 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 empty, until data is received. + * Alternatively the user can explicitly check whether data is available, and + * if data is avaliable, call @ref USART_RxDataXGet() to read the RXDATAX + * register directly. + * + * @param[in] usart + * Pointer to USART/UART peripheral register block. + * + * @return + * Data received. + ******************************************************************************/ +uint16_t USART_RxExt(USART_TypeDef *usart) +{ + while (!(usart->STATUS & USART_STATUS_RXDATAV)) + ; + + return (uint16_t)usart->RXDATAX; +} + + +/***************************************************************************//** + * @brief + * Perform one 8 bit frame SPI transfer. + * + * @note + * This function will stall if the transmit buffer is full. When a transmit + * buffer becomes available, data is written and the function will wait until + * the data is fully transmitted. The SPI return value is then read out and + * returned. + * + * @param[in] usart + * Pointer to USART peripheral register block. + * + * @param[in] data + * Data to transmit. + * + * @return + * Data received. + ******************************************************************************/ +uint8_t USART_SpiTransfer(USART_TypeDef *usart, uint8_t data) +{ + while (!(usart->STATUS & USART_STATUS_TXBL)) + ; + usart->TXDATA = (uint32_t)data; + while (!(usart->STATUS & USART_STATUS_TXC)) + ; + return (uint8_t)usart->RXDATA; +} + + +/***************************************************************************//** + * @brief + * Transmit one 4-9 bit frame. + * + * @details + * Depending on frame length configuration, 4-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 USART_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] usart + * Pointer to USART/UART peripheral register block. + * + * @param[in] data + * Data to transmit. See details above for further info. + ******************************************************************************/ +void USART_Tx(USART_TypeDef *usart, uint8_t data) +{ + /* Check that transmit buffer is empty */ + while (!(usart->STATUS & USART_STATUS_TXBL)) + ; + usart->TXDATA = (uint32_t)data; +} + + +/***************************************************************************//** + * @brief + * Transmit two 4-9 bit frames, or one 10-16 bit frame. + * + * @details + * Depending on frame length configuration, 4-8 (least significant) bits from + * each byte in @p data are transmitted. If frame length is 9, 8 bits are + * transmitted from each byte in @p data adding one bit as specified by CTRL + * register, BIT8DV field, to each byte. Please refer to USART_TxDoubleExt() + * for transmitting two 9 bit frames with full control of all 9 bits. + * + * If frame length is 10-16, 10-16 (least significant) bits from @p data + * are transmitted. + * + * 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] usart + * Pointer to USART/UART peripheral register block. + * + * @param[in] data + * Data to transmit, the least significant byte holds the frame transmitted + * first. See details above for further info. + ******************************************************************************/ +void USART_TxDouble(USART_TypeDef *usart, uint16_t data) +{ + /* Check that transmit buffer is empty */ + while (!(usart->STATUS & USART_STATUS_TXBL)) + ; + usart->TXDOUBLE = (uint32_t)data; +} + + +/***************************************************************************//** + * @brief + * Transmit two 4-9 bit frames, or one 10-16 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] usart + * Pointer to USART/UART peripheral register block. + * + * @param[in] data + * Data to transmit with extended control. Contains two 16 bit words + * concatenated. Least significant word holds frame transitted first. If frame + * length is 4-9, two frames with 4-9 least significant bits from each 16 bit + * word are transmitted. + * @par + * If frame length is 10-16 bits, 8 data bits are taken from the least + * significant 16 bit word, and the remaining bits from the other 16 bit word. + * @par + * Additional control bits are available as documented in the reference + * manual (set to 0 if not used). For 10-16 bit frame length, these control + * bits are taken from the most significant 16 bit word. + ******************************************************************************/ +void USART_TxDoubleExt(USART_TypeDef *usart, uint32_t data) +{ + /* Check that transmit buffer is empty */ + while (!(usart->STATUS & USART_STATUS_TXBL)) + ; + usart->TXDOUBLEX = data; +} + + +/***************************************************************************//** + * @brief + * Transmit one 4-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] usart + * Pointer to USART/UART 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 USART_TxExt(USART_TypeDef *usart, uint16_t data) +{ + /* Check that transmit buffer is empty */ + while (!(usart->STATUS & USART_STATUS_TXBL)) + ; + usart->TXDATAX = (uint32_t)data; +} + + +/** @} (end addtogroup USART) */ +/** @} (end addtogroup EM_Library) */ +#endif /* defined(USART_COUNT) && (USART_COUNT > 0) */