blob: 2cb7770ae2015238cbb2c7986b67ef47354a07fd [file] [log] [blame]
/***************************************************************************//**
* @file em_usart.c
* @brief Universal synchronous/asynchronous receiver/transmitter (USART/UART)
* Peripheral API
* @version 5.6.0
*******************************************************************************
* # License
* <b>Copyright 2016 Silicon Laboratories, Inc. 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"
#if defined(USART_CTRLX_CTSEN)
#include "em_gpio.h"
#endif
/***************************************************************************//**
* @addtogroup emlib
* @{
******************************************************************************/
/***************************************************************************//**
* @addtogroup USART
* @{
******************************************************************************/
/*******************************************************************************
******************************* 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 (_SILICON_LABS_32B_SERIES == 2)
#define USART_IRDA_VALID(ref) USART_REF_VALID(ref)
#elif defined(_SILICON_LABS_32B_SERIES_1)
#if defined(_SILICON_LABS_GECKO_INTERNAL_SDID_100) || defined(_SILICON_LABS_GECKO_INTERNAL_SDID_103)
// If GG11 or TG11
#define USART_IRDA_VALID(ref) (((ref) == USART0) || ((ref) == USART2))
#elif defined(USART3)
#define USART_IRDA_VALID(ref) (((ref) == USART0) || ((ref) == USART1) || ((ref) == USART2) || ((ref) == USART3))
#elif defined(USART2)
#define USART_IRDA_VALID(ref) (((ref) == USART0) || ((ref) == USART1) || ((ref) == USART2))
#else
#define USART_IRDA_VALID(ref) (((ref) == USART0) || ((ref) == USART1))
#endif
#elif defined(_SILICON_LABS_32B_SERIES_0)
#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)
#elif defined(USARTRF0)
#define USART_IRDA_VALID(ref) ((ref) == USARTRF0)
#else
#define USART_IRDA_VALID(ref) (0)
#endif
#endif
#if (_SILICON_LABS_32B_SERIES == 2)
#define USART_I2S_VALID(ref) USART_REF_VALID(ref)
#elif defined(_SILICON_LABS_32B_SERIES_1)
#if defined(USART4)
#define USART_I2S_VALID(ref) (((ref) == USART1) || ((ref) == USART3) || ((ref) == USART4))
#elif defined(USART3)
#define USART_I2S_VALID(ref) (((ref) == USART1) || ((ref) == USART3))
#else
#define USART_I2S_VALID(ref) ((ref) == USART1)
#endif
#elif defined(_SILICON_LABS_32B_SERIES_0)
#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)
#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
#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
#if defined(_USART_CLKDIV_DIVEXT_MASK)
#define CLKDIV_MASK (_USART_CLKDIV_DIV_MASK | _USART_CLKDIV_DIVEXT_MASK)
#else
#define CLKDIV_MASK _USART_CLKDIV_DIV_MASK
#endif
/** @endcond */
/*******************************************************************************
************************** LOCAL FUNCTIONS ********************************
******************************************************************************/
#if !defined(_EFM32_GECKO_FAMILY)
/***************************************************************************//**
* @brief
* Configure a PRS channel as USART Rx input
*
* @param[in] usart
* A pointer to the USART/UART peripheral register block.
*
* @param[in] ch
* PRS channel.
******************************************************************************/
static void prsRxInput(USART_TypeDef *usart, USART_PRS_Channel_t ch)
{
#if defined(_USART_INPUT_MASK)
usart->INPUT = ((uint32_t)ch << _USART_INPUT_RXPRSSEL_SHIFT)
| USART_INPUT_RXPRS;
#elif defined(USART_CTRLX_RXPRSEN)
if (usart == USART0) {
PRS->CONSUMER_USART0_RX = ch;
} else if (usart == USART1) {
PRS->CONSUMER_USART1_RX = ch;
} else if (usart == USART2) {
PRS->CONSUMER_USART2_RX = ch;
}
usart->CTRLX |= USART_CTRLX_RXPRSEN;
#endif
}
#endif
/***************************************************************************//**
* @brief
* Configure a PRS channel as USART Ir input
*
* @param[in] usart
* A pointer to the USART/UART peripheral register block.
*
* @param[in] ch
* PRS channel.
******************************************************************************/
static void prsIrInput(USART_TypeDef *usart, USART_PRS_Channel_t ch)
{
#if defined(_USART_IRCTRL_IRPRSSEL_MASK)
usart->IRCTRL |= ((uint32_t)ch << _USART_IRCTRL_IRPRSSEL_SHIFT)
| USART_IRCTRL_IRPRSEN;
#else
if (usart == USART0) {
PRS->CONSUMER_USART0_IR = ch;
} else if (usart == USART1) {
PRS->CONSUMER_USART1_IR = ch;
} else if (usart == USART2) {
PRS->CONSUMER_USART2_IR = ch;
}
usart->IRCTRL |= USART_IRCTRL_IRPRSEN;
#endif
}
/***************************************************************************//**
* @brief
* Configure a PRS channel as USART Trigger input
*
* @param[in] usart
* A pointer to the USART/UART peripheral register block.
*
* @param[in] ch
* PRS channel.
******************************************************************************/
static void prsTriggerInput(USART_TypeDef *usart, USART_PRS_Channel_t ch)
{
#if defined(_USART_IRCTRL_IRPRSSEL_MASK)
usart->TRIGCTRL = (usart->TRIGCTRL & ~_USART_TRIGCTRL_TSEL_MASK)
| (ch << _USART_TRIGCTRL_TSEL_SHIFT);
#else
if (usart == USART0) {
PRS->CONSUMER_USART0_TRIGGER = ch;
} else if (usart == USART1) {
PRS->CONSUMER_USART1_TRIGGER = ch;
} else if (usart == USART2) {
PRS->CONSUMER_USART2_TRIGGER = ch;
}
#endif
}
/*******************************************************************************
************************** GLOBAL FUNCTIONS *******************************
******************************************************************************/
/***************************************************************************//**
* @brief
* Configure USART/UART operating in asynchronous mode to use a given
* baudrate (or as close as possible to a specified baudrate).
*
* @param[in] usart
* A pointer to the USART/UART peripheral register block.
*
* @param[in] refFreq
* USART/UART reference clock frequency in Hz. 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 the channel more
* vulnerable to bit faults during reception due to clock inaccuracies
* compared to the 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);
/*
* 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 the dividend first before dividing
* to get as small rounding effects as possible.
* Too harsh restrictions on maximum fHFPERCLK value should not be made.
*
* It is possible to factorize 256 and oversample/br. However,
* since the last 6 or 3 bits of CLKDIV are don't care, base the
* 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 1 GHz without overflowing a 32 bit value.
*/
/* HFPERCLK used to clock all USART/UART peripheral modules. */
if (!refFreq) {
#if defined(_SILICON_LABS_32B_SERIES_2)
refFreq = CMU_ClockFreqGet(cmuClock_PCLK);
#else
refFreq = CMU_ClockFreqGet(cmuClock_HFPER);
#endif
}
/* Map oversampling. */
switch (ovs) {
case usartOVS16:
EFM_ASSERT(baudrate <= (refFreq / 16));
oversample = 16;
break;
case usartOVS8:
EFM_ASSERT(baudrate <= (refFreq / 8));
oversample = 8;
break;
case usartOVS6:
EFM_ASSERT(baudrate <= (refFreq / 6));
oversample = 6;
break;
case usartOVS4:
EFM_ASSERT(baudrate <= (refFreq / 4));
oversample = 4;
break;
default:
/* Invalid input */
EFM_ASSERT(0);
return;
}
/* Calculate and set CLKDIV with fractional bits.
* The added (oversample*baudrate)/2 in the first line is to round the
* divisor to the nearest fractional divisor. */
#if defined(_SILICON_LABS_32B_SERIES_0) && !defined(_EFM32_HAPPY_FAMILY)
/* Devices with 2 fractional bits. CLKDIV[7:6] */
clkdiv = 4 * refFreq + (oversample * baudrate) / 2;
clkdiv /= oversample * baudrate;
clkdiv -= 4;
clkdiv *= 64;
#else
/* Devices with 5 fractional bits. CLKDIV[7:3] */
clkdiv = 32 * refFreq + (oversample * baudrate) / 2;
clkdiv /= oversample * baudrate;
clkdiv -= 32;
clkdiv *= 8;
#endif
/* Verify that the resulting clock divider is within limits. */
EFM_ASSERT(clkdiv <= CLKDIV_MASK);
/* Make sure that reserved bits are not written to. */
clkdiv &= CLKDIV_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 the hardware configuration. It can be used
* to determine if a given configuration is sufficiently accurate for the
* application.
*
* @param[in] refFreq
* USART/UART HF peripheral frequency used.
*
* @param[in] clkdiv
* A 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 in 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;
/* Out of bound clkdiv. */
EFM_ASSERT(clkdiv <= CLKDIV_MASK);
/* Mask out unused bits */
clkdiv &= CLKDIV_MASK;
/* 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, i.e., 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))
*
* 256 factor of the dividend is reduced with a
* (part of) oversample part of the divisor.
*/
switch (ovs) {
case usartOVS16:
oversample = 1;
factor = 256 / 16;
break;
case usartOVS8:
oversample = 1;
factor = 256 / 8;
break;
case usartOVS6:
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 the dividend first before dividing
* to get as small rounding effects as possible. Too harsh restrictions
* should not be made on the maximum fHFPERCLK value either.
*
* For division a/b,
*
* a = qb + r
*
* where q is the quotient and r is the remainder, both integers.
*
* The original 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.
*/
/*
* The divisor will never exceed max 32 bit value since
* clkdiv <= _USART_CLKDIV_DIV_MASK (currently 0x1FFFC0 or 0x7FFFF8)
* and 'oversample' has been reduced to <= 3.
*/
divisor = oversample * (256 + clkdiv);
quotient = refFreq / divisor;
remainder = refFreq % divisor;
/* The factor <= 128 and since divisor >= 256, the below cannot exceed the maximum */
/* 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);
/*
* The 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 the 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
* A pointer to the USART/UART peripheral register block.
*
* @return
* The 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. */
#if defined(_SILICON_LABS_32B_SERIES_2)
freq = CMU_ClockFreqGet(cmuClock_PCLK);
#else
freq = CMU_ClockFreqGet(cmuClock_HFPER);
#endif
ovs = (USART_OVS_TypeDef)(usart->CTRL & _USART_CTRL_OVS_MASK);
return USART_BaudrateCalc(freq, usart->CLKDIV, syncmode, ovs);
}
/***************************************************************************//**
* @brief
* Configure the USART operating in synchronous mode to use a given baudrate
* (or as close as possible to a specified baudrate).
*
* @details
* The configuration will be set to use a baudrate <= the specified baudrate
* to ensure that the baudrate does not exceed the specified value.
*
* The fractional clock division is suppressed, although the hardware design allows it.
* It could cause half clock cycles to exceed a specified limit and thus
* potentially violate specifications for the slave device. In some special
* situations, a 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
* A pointer to the USART peripheral register block. (Cannot be used on UART
* modules.)
*
* @param[in] refFreq
* A 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)
{
uint32_t clkdiv;
/* Prevent dividing 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) {
#if defined(_SILICON_LABS_32B_SERIES_2)
refFreq = CMU_ClockFreqGet(cmuClock_PCLK);
#else
refFreq = CMU_ClockFreqGet(cmuClock_HFPER);
#endif
}
clkdiv = (refFreq - 1) / (2 * baudrate);
clkdiv = clkdiv << 8;
/* Verify that resulting clock divider is within limits. */
EFM_ASSERT(!(clkdiv & ~CLKDIV_MASK));
usart->CLKDIV = 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 (if not enabled as part
* of initialization).
*
* @param[in] usart
* A pointer to the USART/UART peripheral register block.
*
* @param[in] enable
* Select the status for the 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));
#if defined(USART_EN_EN)
usart->EN_SET = USART_EN_EN;
#endif
/* 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;
#if defined(USART_EN_EN)
if (enable == usartDisable) {
usart->EN_CLR = USART_EN_EN;
}
#endif
}
/***************************************************************************//**
* @brief
* Initialize USART/UART for normal asynchronous mode.
*
* @details
* This function will configure basic settings to operate in normal
* asynchronous mode.
*
* A 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 for the USART/UART to work as intended.
* (When configuring pins, remember to consider the sequence of
* configuration to avoid unintended pulses/glitches on output
* pins.)
*
* @param[in] usart
* A pointer to the USART/UART peripheral register block.
*
* @param[in] init
* A pointer to the initialization structure used to configure the 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));
/* Initialize USART registers to hardware reset state. */
USART_Reset(usart);
#if defined(USART_EN_EN)
usart->EN_SET = USART_EN_EN;
#endif
#if defined(USART_CTRL_MVDIS)
/* Disable the majority vote if specified. */
if (init->mvdis) {
usart->CTRL |= USART_CTRL_MVDIS;
}
#endif
#if !defined(_EFM32_GECKO_FAMILY)
/* Configure the PRS input mode. */
if (init->prsRxEnable) {
prsRxInput(usart, init->prsRxCh);
}
#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 (init->autoCsEnable) {
usart->CTRL |= USART_CTRL_AUTOCS;
}
#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);
#endif
#if defined(_USART_ROUTEPEN_RTSPEN_MASK) && defined(_USART_ROUTEPEN_CTSPEN_MASK)
usart->ROUTEPEN &= ~(_USART_ROUTEPEN_RTSPEN_MASK | _USART_ROUTEPEN_CTSPEN_MASK);
usart->ROUTEPEN |= init->hwFlowControl;
#elif defined(USART_CTRLX_CTSEN)
if ((init->hwFlowControl == usartHwFlowControlRts)
|| (init->hwFlowControl == usartHwFlowControlCtsAndRts)) {
GPIO->USARTROUTE_SET[USART_NUM(usart)].ROUTEEN = GPIO_USART_ROUTEEN_RTSPEN;
} else {
GPIO->USARTROUTE_CLR[USART_NUM(usart)].ROUTEEN = GPIO_USART_ROUTEEN_RTSPEN;
}
if ((init->hwFlowControl == usartHwFlowControlCts)
|| (init->hwFlowControl == usartHwFlowControlCtsAndRts)) {
usart->CTRLX_SET = USART_CTRLX_CTSEN;
} else {
usart->CTRLX_CLR = USART_CTRLX_CTSEN;
}
#endif
/* Finally, enable (as specified). */
usart->CMD = (uint32_t)init->enable;
}
/***************************************************************************//**
* @brief
* Initialize USART for synchronous mode.
*
* @details
* This function will configure basic settings to operate in
* synchronous mode.
*
* A 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 for the USART to work as intended.
* (When configuring pins remember to consider the sequence of
* configuration to avoid unintended pulses/glitches on output
* pins.)
*
* @param[in] usart
* A pointer to the USART peripheral register block. (UART does not support this
* mode.)
*
* @param[in] init
* A pointer to the 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) );
/* Initialize USART registers to hardware reset state. */
USART_Reset(usart);
#if defined(USART_EN_EN)
usart->EN_SET = USART_EN_EN;
#endif
/* 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(_EFM32_GECKO_FAMILY)
if (init->prsRxEnable) {
prsRxInput(usart, init->prsRxCh);
}
#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 the baudrate. */
USART_BaudrateSyncSet(usart, init->refFreq, init->baudrate);
/* Finally, enable (as specified). */
if (init->master) {
usart->CMD = USART_CMD_MASTEREN;
}
if (init->autoCsEnable) {
usart->CTRL |= USART_CTRL_AUTOCS;
}
#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);
#endif
usart->CMD = (uint32_t)init->enable;
}
/***************************************************************************//**
* @brief
* Initialize USART for asynchronous IrDA mode.
*
* @details
* This function will configure basic settings to operate in
* asynchronous IrDA mode.
*
* A 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 for the USART/UART to work as intended.
* (When configuring pins, remember to consider the sequence of
* configuration to avoid unintended pulses/glitches on output
* pins.)
*
* @param[in] usart
* A pointer to the USART peripheral register block.
*
* @param[in] init
* A pointer to the initialization structure used to configure async IrDA setup.
*
* @note
* Not all USART instances support IrDA. See the data sheet for your device.
*
******************************************************************************/
void USARTn_InitIrDA(USART_TypeDef *usart, const USART_InitIrDA_TypeDef *init)
{
EFM_ASSERT(USART_IRDA_VALID(usart));
/* Initialize USART as an async device. */
USART_InitAsync(usart, &(init->async));
/* Set IrDA modulation to RZI (return-to-zero-inverted). */
usart->CTRL |= USART_CTRL_TXINV;
/* Invert the Rx signal before the demodulator if enabled. */
if (init->irRxInv) {
usart->CTRL |= USART_CTRL_RXINV;
}
/* Configure IrDA. */
usart->IRCTRL = (uint32_t)init->irPw
| ((init->irFilt ? 1UL : 0UL) << _USART_IRCTRL_IRFILT_SHIFT);
if (init->irPrsEn) {
prsIrInput(usart, init->irPrsSel);
}
/* Enable IrDA. */
usart->IRCTRL |= USART_IRCTRL_IREN;
}
#if defined(_USART_I2SCTRL_MASK)
/***************************************************************************//**
* @brief
* Initialize USART for I2S mode.
*
* @details
* This function will configure basic settings to operate in I2S
* mode.
*
* A 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 for the USART to work as intended.
* (When configuring pins, remember to consider the sequence of
* configuration to avoid unintended pulses/glitches on output
* pins.)
*
* @param[in] usart
* A pointer to the USART peripheral register block. (UART does not support this
* mode.)
*
* @param[in] init
* A pointer to the initialization structure used to configure the basic I2S setup.
*
* @note
* This function does not apply to all USART's. See the chip Reference Manual.
*
******************************************************************************/
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;
/* Initialize USART as a sync device. */
USART_InitSync(usart, &init->sync);
/* Configure and enable I2CCTRL register according to the 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 the automatic transmissions using PRS channel as a trigger.
* @note
* Initialize USART with USART_Init() before setting up the PRS configuration.
*
* @param[in] usart A pointer to USART to configure.
* @param[in] init A pointer to the initialization structure.
******************************************************************************/
void USART_InitPrsTrigger(USART_TypeDef *usart, const USART_PrsTriggerInit_TypeDef *init)
{
uint32_t trigctrl;
prsTriggerInput(usart, init->prsTriggerChannel);
/* 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
);
#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;
}
usart->TRIGCTRL = trigctrl;
}
/***************************************************************************//**
* @brief
* Reset USART/UART to the same state that it was in after a hardware reset.
*
* @param[in] usart
* A 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) );
#if defined(USART_EN_EN)
usart->EN_SET = USART_EN_EN;
/* 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->CTRLX = _USART_CTRLX_RESETVALUE;
usart->FRAME = _USART_FRAME_RESETVALUE;
usart->TRIGCTRL = _USART_TRIGCTRL_RESETVALUE;
usart->CLKDIV = _USART_CLKDIV_RESETVALUE;
usart->IEN = _USART_IEN_RESETVALUE;
usart->IF_CLR = _USART_IF_MASK;
usart->TIMING = _USART_TIMING_RESETVALUE;
if (USART_IRDA_VALID(usart)) {
usart->IRCTRL = _USART_IRCTRL_RESETVALUE;
}
if (USART_I2S_VALID(usart)) {
usart->I2SCTRL = _USART_I2SCTRL_RESETVALUE;
}
usart->EN_CLR = USART_EN_EN;
#else
/* 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_TIMING_MASK)
usart->TIMING = _USART_TIMING_RESETVALUE;
#endif
#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
#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. See @ref USART_RxExt() for reception of
* 9 bit frames.
*
* Notice that possible parity/stop bits in asynchronous mode are not
* considered part of a 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.
* If data is available, call @ref USART_RxDataGet() to read the RXDATA
* register directly.
*
* @param[in] usart
* A pointer to the 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. See @ref USART_RxDoubleExt() for
* reception of two 9 bit frames.
*
* Notice that possible parity/stop bits in asynchronous mode are not
* considered part of a 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.
* If data is available, call @ref USART_RxDoubleGet() to read the RXDOUBLE
* register directly.
*
* @param[in] usart
* A pointer to the 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 a 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.
* If data is available, call @ref USART_RxDoubleXGet() to read the RXDOUBLEX
* register directly.
*
* @param[in] usart
* A pointer to the 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 a 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.
* If data is available, call @ref USART_RxDataXGet() to read the RXDATAX
* register directly.
*
* @param[in] usart
* A pointer to the 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
* data is fully transmitted. The SPI return value is then read out and
* returned.
*
* @param[in] usart
* A pointer to the 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 the frame length configuration, 4-8 (least significant) bits from
* @p data are transmitted. If the frame length is 9, 8 bits are transmitted from
* @p data and one bit as specified by CTRL register, BIT8DV field.
* See 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 a specified frame bit length.
*
* @note
* This function will stall if the buffer is full until the buffer becomes available.
*
* @param[in] usart
* A pointer to the USART/UART peripheral register block.
*
* @param[in] data
* Data to transmit. See details above for more information.
******************************************************************************/
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 the 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 the CTRL
* register, BIT8DV field, to each byte. See USART_TxDoubleExt()
* for transmitting two 9 bit frames with full control of all 9 bits.
*
* If the 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 a specified frame bit length.
*
* @note
* This function will stall if the buffer is full until the buffer becomes available.
*
* @param[in] usart
* A pointer to the USART/UART peripheral register block.
*
* @param[in] data
* Data to transmit, the least significant byte holds the frame transmitted
* first. See details above for more 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 a specified frame bit length.
*
* @note
* This function will stall if the buffer is full until the buffer becomes available.
*
* @param[in] usart
* A pointer to the USART/UART peripheral register block.
*
* @param[in] data
* Data to transmit with extended control. Contains two 16 bit words
* concatenated. Least significant word holds the frame transmitted first. If the frame
* length is 4-9, two frames with 4-9 least significant bits from each 16 bit
* word are transmitted.
* @par
* If the 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 a specified frame bit length.
*
* @note
* This function will stall if the buffer is full until the buffer becomes available.
*
* @param[in] usart
* A pointer to the USART/UART peripheral register block.
*
* @param[in] data
* Data to transmit with extended control. Least significant bit contains
* frame bits. 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 the transmit buffer is empty. */
while (!(usart->STATUS & USART_STATUS_TXBL)) {
}
usart->TXDATAX = (uint32_t)data;
}
/** @} (end addtogroup USART) */
/** @} (end addtogroup emlib) */
#endif /* defined(USART_COUNT) && (USART_COUNT > 0) */