/* | |
* Copyright (c) 2016, Freescale Semiconductor, Inc. | |
* Copyright 2016-2018 NXP | |
* All rights reserved. | |
* | |
* SPDX-License-Identifier: BSD-3-Clause | |
*/ | |
#include "fsl_usart.h" | |
#include "fsl_device_registers.h" | |
#include "fsl_flexcomm.h" | |
/* Component ID definition, used by tools. */ | |
#ifndef FSL_COMPONENT_ID | |
#define FSL_COMPONENT_ID "platform.drivers.flexcomm_usart" | |
#endif | |
enum _usart_transfer_states | |
{ | |
kUSART_TxIdle, /* TX idle. */ | |
kUSART_TxBusy, /* TX busy. */ | |
kUSART_RxIdle, /* RX idle. */ | |
kUSART_RxBusy /* RX busy. */ | |
}; | |
/******************************************************************************* | |
* Variables | |
******************************************************************************/ | |
/*! @brief IRQ name array */ | |
static const IRQn_Type s_usartIRQ[] = USART_IRQS; | |
/*! @brief Array to map USART instance number to base address. */ | |
static const uint32_t s_usartBaseAddrs[FSL_FEATURE_SOC_USART_COUNT] = USART_BASE_ADDRS; | |
/******************************************************************************* | |
* Code | |
******************************************************************************/ | |
/* Get the index corresponding to the USART */ | |
/*! brief Returns instance number for USART peripheral base address. */ | |
uint32_t USART_GetInstance(USART_Type *base) | |
{ | |
int i; | |
for (i = 0; i < FSL_FEATURE_SOC_USART_COUNT; i++) | |
{ | |
if ((uint32_t)base == s_usartBaseAddrs[i]) | |
{ | |
return i; | |
} | |
} | |
assert(false); | |
return 0; | |
} | |
/*! | |
* brief Get the length of received data in RX ring buffer. | |
* | |
* param handle USART handle pointer. | |
* return Length of received data in RX ring buffer. | |
*/ | |
size_t USART_TransferGetRxRingBufferLength(usart_handle_t *handle) | |
{ | |
size_t size; | |
/* Check arguments */ | |
assert(NULL != handle); | |
if (handle->rxRingBufferTail > handle->rxRingBufferHead) | |
{ | |
size = (size_t)(handle->rxRingBufferHead + handle->rxRingBufferSize - handle->rxRingBufferTail); | |
} | |
else | |
{ | |
size = (size_t)(handle->rxRingBufferHead - handle->rxRingBufferTail); | |
} | |
return size; | |
} | |
static bool USART_TransferIsRxRingBufferFull(usart_handle_t *handle) | |
{ | |
bool full; | |
/* Check arguments */ | |
assert(NULL != handle); | |
if (USART_TransferGetRxRingBufferLength(handle) == (handle->rxRingBufferSize - 1U)) | |
{ | |
full = true; | |
} | |
else | |
{ | |
full = false; | |
} | |
return full; | |
} | |
/*! | |
* brief Sets up the RX ring buffer. | |
* | |
* This function sets up the RX ring buffer to a specific USART handle. | |
* | |
* When the RX ring buffer is used, data received are stored into the ring buffer even when the | |
* user doesn't call the USART_TransferReceiveNonBlocking() API. If there is already data received | |
* in the ring buffer, the user can get the received data from the ring buffer directly. | |
* | |
* note When using the RX ring buffer, one byte is reserved for internal use. In other | |
* words, if p ringBufferSize is 32, then only 31 bytes are used for saving data. | |
* | |
* param base USART peripheral base address. | |
* param handle USART handle pointer. | |
* param ringBuffer Start address of the ring buffer for background receiving. Pass NULL to disable the ring buffer. | |
* param ringBufferSize size of the ring buffer. | |
*/ | |
void USART_TransferStartRingBuffer(USART_Type *base, usart_handle_t *handle, uint8_t *ringBuffer, size_t ringBufferSize) | |
{ | |
/* Check arguments */ | |
assert(NULL != base); | |
assert(NULL != handle); | |
assert(NULL != ringBuffer); | |
/* Setup the ringbuffer address */ | |
handle->rxRingBuffer = ringBuffer; | |
handle->rxRingBufferSize = ringBufferSize; | |
handle->rxRingBufferHead = 0U; | |
handle->rxRingBufferTail = 0U; | |
/* ring buffer is ready we can start receiving data */ | |
base->FIFOINTENSET |= USART_FIFOINTENSET_RXLVL_MASK | USART_FIFOINTENSET_RXERR_MASK; | |
} | |
/*! | |
* brief Aborts the background transfer and uninstalls the ring buffer. | |
* | |
* This function aborts the background transfer and uninstalls the ring buffer. | |
* | |
* param base USART peripheral base address. | |
* param handle USART handle pointer. | |
*/ | |
void USART_TransferStopRingBuffer(USART_Type *base, usart_handle_t *handle) | |
{ | |
/* Check arguments */ | |
assert(NULL != base); | |
assert(NULL != handle); | |
if (handle->rxState == kUSART_RxIdle) | |
{ | |
base->FIFOINTENCLR = USART_FIFOINTENCLR_RXLVL_MASK | USART_FIFOINTENCLR_RXERR_MASK; | |
} | |
handle->rxRingBuffer = NULL; | |
handle->rxRingBufferSize = 0U; | |
handle->rxRingBufferHead = 0U; | |
handle->rxRingBufferTail = 0U; | |
} | |
/*! | |
* brief Initializes a USART instance with user configuration structure and peripheral clock. | |
* | |
* This function configures the USART module with the user-defined settings. The user can configure the configuration | |
* structure and also get the default configuration by using the USART_GetDefaultConfig() function. | |
* Example below shows how to use this API to configure USART. | |
* code | |
* usart_config_t usartConfig; | |
* usartConfig.baudRate_Bps = 115200U; | |
* usartConfig.parityMode = kUSART_ParityDisabled; | |
* usartConfig.stopBitCount = kUSART_OneStopBit; | |
* USART_Init(USART1, &usartConfig, 20000000U); | |
* endcode | |
* | |
* param base USART peripheral base address. | |
* param config Pointer to user-defined configuration structure. | |
* param srcClock_Hz USART clock source frequency in HZ. | |
* retval kStatus_USART_BaudrateNotSupport Baudrate is not support in current clock source. | |
* retval kStatus_InvalidArgument USART base address is not valid | |
* retval kStatus_Success Status USART initialize succeed | |
*/ | |
status_t USART_Init(USART_Type *base, const usart_config_t *config, uint32_t srcClock_Hz) | |
{ | |
int result; | |
/* check arguments */ | |
assert(!((NULL == base) || (NULL == config) || (0 == srcClock_Hz))); | |
if ((NULL == base) || (NULL == config) || (0 == srcClock_Hz)) | |
{ | |
return kStatus_InvalidArgument; | |
} | |
/* initialize flexcomm to USART mode */ | |
result = FLEXCOMM_Init(base, FLEXCOMM_PERIPH_USART); | |
if (kStatus_Success != result) | |
{ | |
return result; | |
} | |
/* setup baudrate */ | |
result = USART_SetBaudRate(base, config->baudRate_Bps, srcClock_Hz); | |
if (kStatus_Success != result) | |
{ | |
return result; | |
} | |
if (config->enableTx) | |
{ | |
/* empty and enable txFIFO */ | |
base->FIFOCFG |= USART_FIFOCFG_EMPTYTX_MASK | USART_FIFOCFG_ENABLETX_MASK; | |
/* setup trigger level */ | |
base->FIFOTRIG &= ~(USART_FIFOTRIG_TXLVL_MASK); | |
base->FIFOTRIG |= USART_FIFOTRIG_TXLVL(config->txWatermark); | |
/* enable trigger interrupt */ | |
base->FIFOTRIG |= USART_FIFOTRIG_TXLVLENA_MASK; | |
} | |
/* empty and enable rxFIFO */ | |
if (config->enableRx) | |
{ | |
base->FIFOCFG |= USART_FIFOCFG_EMPTYRX_MASK | USART_FIFOCFG_ENABLERX_MASK; | |
/* setup trigger level */ | |
base->FIFOTRIG &= ~(USART_FIFOTRIG_RXLVL_MASK); | |
base->FIFOTRIG |= USART_FIFOTRIG_RXLVL(config->rxWatermark); | |
/* enable trigger interrupt */ | |
base->FIFOTRIG |= USART_FIFOTRIG_RXLVLENA_MASK; | |
} | |
/* setup configuration and enable USART */ | |
base->CFG = USART_CFG_PARITYSEL(config->parityMode) | USART_CFG_STOPLEN(config->stopBitCount) | | |
USART_CFG_DATALEN(config->bitCountPerChar) | USART_CFG_LOOP(config->loopback) | USART_CFG_ENABLE_MASK; | |
return kStatus_Success; | |
} | |
/*! | |
* brief Deinitializes a USART instance. | |
* | |
* This function waits for TX complete, disables TX and RX, and disables the USART clock. | |
* | |
* param base USART peripheral base address. | |
*/ | |
void USART_Deinit(USART_Type *base) | |
{ | |
/* Check arguments */ | |
assert(NULL != base); | |
while (!(base->STAT & USART_STAT_TXIDLE_MASK)) | |
{ | |
} | |
/* Disable interrupts, disable dma requests, disable peripheral */ | |
base->FIFOINTENCLR = USART_FIFOINTENCLR_TXERR_MASK | USART_FIFOINTENCLR_RXERR_MASK | USART_FIFOINTENCLR_TXLVL_MASK | | |
USART_FIFOINTENCLR_RXLVL_MASK; | |
base->FIFOCFG &= ~(USART_FIFOCFG_DMATX_MASK | USART_FIFOCFG_DMARX_MASK); | |
base->CFG &= ~(USART_CFG_ENABLE_MASK); | |
} | |
/*! | |
* brief Gets the default configuration structure. | |
* | |
* This function initializes the USART configuration structure to a default value. The default | |
* values are: | |
* usartConfig->baudRate_Bps = 115200U; | |
* usartConfig->parityMode = kUSART_ParityDisabled; | |
* usartConfig->stopBitCount = kUSART_OneStopBit; | |
* usartConfig->bitCountPerChar = kUSART_8BitsPerChar; | |
* usartConfig->loopback = false; | |
* usartConfig->enableTx = false; | |
* usartConfig->enableRx = false; | |
* | |
* param config Pointer to configuration structure. | |
*/ | |
void USART_GetDefaultConfig(usart_config_t *config) | |
{ | |
/* Check arguments */ | |
assert(NULL != config); | |
/* Initializes the configure structure to zero. */ | |
memset(config, 0, sizeof(*config)); | |
/* Set always all members ! */ | |
config->baudRate_Bps = 115200U; | |
config->parityMode = kUSART_ParityDisabled; | |
config->stopBitCount = kUSART_OneStopBit; | |
config->bitCountPerChar = kUSART_8BitsPerChar; | |
config->loopback = false; | |
config->enableRx = false; | |
config->enableTx = false; | |
config->txWatermark = kUSART_TxFifo0; | |
config->rxWatermark = kUSART_RxFifo1; | |
} | |
/*! | |
* brief Sets the USART instance baud rate. | |
* | |
* This function configures the USART module baud rate. This function is used to update | |
* the USART module baud rate after the USART module is initialized by the USART_Init. | |
* code | |
* USART_SetBaudRate(USART1, 115200U, 20000000U); | |
* endcode | |
* | |
* param base USART peripheral base address. | |
* param baudrate_Bps USART baudrate to be set. | |
* param srcClock_Hz USART clock source freqency in HZ. | |
* retval kStatus_USART_BaudrateNotSupport Baudrate is not support in current clock source. | |
* retval kStatus_Success Set baudrate succeed. | |
* retval kStatus_InvalidArgument One or more arguments are invalid. | |
*/ | |
status_t USART_SetBaudRate(USART_Type *base, uint32_t baudrate_Bps, uint32_t srcClock_Hz) | |
{ | |
uint32_t best_diff = (uint32_t)-1, best_osrval = 0xf, best_brgval = (uint32_t)-1; | |
uint32_t osrval, brgval, diff, baudrate; | |
/* check arguments */ | |
assert(!((NULL == base) || (0 == baudrate_Bps) || (0 == srcClock_Hz))); | |
if ((NULL == base) || (0 == baudrate_Bps) || (0 == srcClock_Hz)) | |
{ | |
return kStatus_InvalidArgument; | |
} | |
/* | |
* Smaller values of OSR can make the sampling position within a data bit less accurate and may | |
* potentially cause more noise errors or incorrect data. | |
*/ | |
for (osrval = best_osrval; osrval >= 8; osrval--) | |
{ | |
brgval = (srcClock_Hz / ((osrval + 1) * baudrate_Bps)) - 1; | |
if (brgval > 0xFFFF) | |
{ | |
continue; | |
} | |
baudrate = srcClock_Hz / ((osrval + 1) * (brgval + 1)); | |
diff = baudrate_Bps < baudrate ? baudrate - baudrate_Bps : baudrate_Bps - baudrate; | |
if (diff < best_diff) | |
{ | |
best_diff = diff; | |
best_osrval = osrval; | |
best_brgval = brgval; | |
} | |
} | |
/* value over range */ | |
if (best_brgval > 0xFFFF) | |
{ | |
return kStatus_USART_BaudrateNotSupport; | |
} | |
base->OSR = best_osrval; | |
base->BRG = best_brgval; | |
return kStatus_Success; | |
} | |
/*! | |
* brief Writes to the TX register using a blocking method. | |
* | |
* This function polls the TX register, waits for the TX register to be empty or for the TX FIFO | |
* to have room and writes data to the TX buffer. | |
* | |
* param base USART peripheral base address. | |
* param data Start address of the data to write. | |
* param length Size of the data to write. | |
*/ | |
void USART_WriteBlocking(USART_Type *base, const uint8_t *data, size_t length) | |
{ | |
/* Check arguments */ | |
assert(!((NULL == base) || (NULL == data))); | |
if ((NULL == base) || (NULL == data)) | |
{ | |
return; | |
} | |
/* Check whether txFIFO is enabled */ | |
if (!(base->FIFOCFG & USART_FIFOCFG_ENABLETX_MASK)) | |
{ | |
return; | |
} | |
for (; length > 0; length--) | |
{ | |
/* Loop until txFIFO get some space for new data */ | |
while (!(base->FIFOSTAT & USART_FIFOSTAT_TXNOTFULL_MASK)) | |
{ | |
} | |
base->FIFOWR = *data; | |
data++; | |
} | |
/* Wait to finish transfer */ | |
while (!(base->STAT & USART_STAT_TXIDLE_MASK)) | |
{ | |
} | |
} | |
/*! | |
* brief Read RX data register using a blocking method. | |
* | |
* This function polls the RX register, waits for the RX register to be full or for RX FIFO to | |
* have data and read data from the TX register. | |
* | |
* param base USART peripheral base address. | |
* param data Start address of the buffer to store the received data. | |
* param length Size of the buffer. | |
* retval kStatus_USART_FramingError Receiver overrun happened while receiving data. | |
* retval kStatus_USART_ParityError Noise error happened while receiving data. | |
* retval kStatus_USART_NoiseError Framing error happened while receiving data. | |
* retval kStatus_USART_RxError Overflow or underflow rxFIFO happened. | |
* retval kStatus_Success Successfully received all data. | |
*/ | |
status_t USART_ReadBlocking(USART_Type *base, uint8_t *data, size_t length) | |
{ | |
uint32_t status; | |
/* check arguments */ | |
assert(!((NULL == base) || (NULL == data))); | |
if ((NULL == base) || (NULL == data)) | |
{ | |
return kStatus_InvalidArgument; | |
} | |
/* Check whether rxFIFO is enabled */ | |
if (!(base->FIFOCFG & USART_FIFOCFG_ENABLERX_MASK)) | |
{ | |
return kStatus_Fail; | |
} | |
for (; length > 0; length--) | |
{ | |
/* loop until rxFIFO have some data to read */ | |
while (!(base->FIFOSTAT & USART_FIFOSTAT_RXNOTEMPTY_MASK)) | |
{ | |
} | |
/* check receive status */ | |
status = base->STAT; | |
if (status & USART_STAT_FRAMERRINT_MASK) | |
{ | |
base->STAT |= USART_STAT_FRAMERRINT_MASK; | |
return kStatus_USART_FramingError; | |
} | |
if (status & USART_STAT_PARITYERRINT_MASK) | |
{ | |
base->STAT |= USART_STAT_PARITYERRINT_MASK; | |
return kStatus_USART_ParityError; | |
} | |
if (status & USART_STAT_RXNOISEINT_MASK) | |
{ | |
base->STAT |= USART_STAT_RXNOISEINT_MASK; | |
return kStatus_USART_NoiseError; | |
} | |
/* check rxFIFO status */ | |
if (base->FIFOSTAT & USART_FIFOSTAT_RXERR_MASK) | |
{ | |
base->FIFOCFG |= USART_FIFOCFG_EMPTYRX_MASK; | |
base->FIFOSTAT |= USART_FIFOSTAT_RXERR_MASK; | |
return kStatus_USART_RxError; | |
} | |
*data = base->FIFORD; | |
data++; | |
} | |
return kStatus_Success; | |
} | |
/*! | |
* brief Initializes the USART handle. | |
* | |
* This function initializes the USART handle which can be used for other USART | |
* transactional APIs. Usually, for a specified USART instance, | |
* call this API once to get the initialized handle. | |
* | |
* param base USART peripheral base address. | |
* param handle USART handle pointer. | |
* param callback The callback function. | |
* param userData The parameter of the callback function. | |
*/ | |
status_t USART_TransferCreateHandle(USART_Type *base, | |
usart_handle_t *handle, | |
usart_transfer_callback_t callback, | |
void *userData) | |
{ | |
int32_t instance = 0; | |
/* Check 'base' */ | |
assert(!((NULL == base) || (NULL == handle))); | |
if ((NULL == base) || (NULL == handle)) | |
{ | |
return kStatus_InvalidArgument; | |
} | |
instance = USART_GetInstance(base); | |
memset(handle, 0, sizeof(*handle)); | |
/* Set the TX/RX state. */ | |
handle->rxState = kUSART_RxIdle; | |
handle->txState = kUSART_TxIdle; | |
/* Set the callback and user data. */ | |
handle->callback = callback; | |
handle->userData = userData; | |
handle->rxWatermark = (usart_rxfifo_watermark_t)USART_FIFOTRIG_RXLVL_GET(base); | |
handle->txWatermark = (usart_txfifo_watermark_t)USART_FIFOTRIG_TXLVL_GET(base); | |
FLEXCOMM_SetIRQHandler(base, (flexcomm_irq_handler_t)USART_TransferHandleIRQ, handle); | |
/* Enable interrupt in NVIC. */ | |
EnableIRQ(s_usartIRQ[instance]); | |
return kStatus_Success; | |
} | |
/*! | |
* brief Transmits a buffer of data using the interrupt method. | |
* | |
* This function sends data using an interrupt method. This is a non-blocking function, which | |
* returns directly without waiting for all data to be written to the TX register. When | |
* all data is written to the TX register in the IRQ handler, the USART driver calls the callback | |
* function and passes the ref kStatus_USART_TxIdle as status parameter. | |
* | |
* note The kStatus_USART_TxIdle is passed to the upper layer when all data is written | |
* to the TX register. However it does not ensure that all data are sent out. Before disabling the TX, | |
* check the kUSART_TransmissionCompleteFlag to ensure that the TX is finished. | |
* | |
* param base USART peripheral base address. | |
* param handle USART handle pointer. | |
* param xfer USART transfer structure. See #usart_transfer_t. | |
* retval kStatus_Success Successfully start the data transmission. | |
* retval kStatus_USART_TxBusy Previous transmission still not finished, data not all written to TX register yet. | |
* retval kStatus_InvalidArgument Invalid argument. | |
*/ | |
status_t USART_TransferSendNonBlocking(USART_Type *base, usart_handle_t *handle, usart_transfer_t *xfer) | |
{ | |
/* Check arguments */ | |
assert(!((NULL == base) || (NULL == handle) || (NULL == xfer))); | |
if ((NULL == base) || (NULL == handle) || (NULL == xfer)) | |
{ | |
return kStatus_InvalidArgument; | |
} | |
/* Check xfer members */ | |
assert(!((0 == xfer->dataSize) || (NULL == xfer->data))); | |
if ((0 == xfer->dataSize) || (NULL == xfer->data)) | |
{ | |
return kStatus_InvalidArgument; | |
} | |
/* Return error if current TX busy. */ | |
if (kUSART_TxBusy == handle->txState) | |
{ | |
return kStatus_USART_TxBusy; | |
} | |
else | |
{ | |
handle->txData = xfer->data; | |
handle->txDataSize = xfer->dataSize; | |
handle->txDataSizeAll = xfer->dataSize; | |
handle->txState = kUSART_TxBusy; | |
/* Enable transmiter interrupt. */ | |
base->FIFOINTENSET |= USART_FIFOINTENSET_TXLVL_MASK; | |
} | |
return kStatus_Success; | |
} | |
/*! | |
* brief Aborts the interrupt-driven data transmit. | |
* | |
* This function aborts the interrupt driven data sending. The user can get the remainBtyes to find out | |
* how many bytes are still not sent out. | |
* | |
* param base USART peripheral base address. | |
* param handle USART handle pointer. | |
*/ | |
void USART_TransferAbortSend(USART_Type *base, usart_handle_t *handle) | |
{ | |
assert(NULL != handle); | |
/* Disable interrupts */ | |
USART_DisableInterrupts(base, kUSART_TxLevelInterruptEnable); | |
/* Empty txFIFO */ | |
base->FIFOCFG |= USART_FIFOCFG_EMPTYTX_MASK; | |
handle->txDataSize = 0; | |
handle->txState = kUSART_TxIdle; | |
} | |
/*! | |
* brief Get the number of bytes that have been written to USART TX register. | |
* | |
* This function gets the number of bytes that have been written to USART TX | |
* register by interrupt method. | |
* | |
* param base USART peripheral base address. | |
* param handle USART handle pointer. | |
* param count Send bytes count. | |
* retval kStatus_NoTransferInProgress No send in progress. | |
* retval kStatus_InvalidArgument Parameter is invalid. | |
* retval kStatus_Success Get successfully through the parameter \p count; | |
*/ | |
status_t USART_TransferGetSendCount(USART_Type *base, usart_handle_t *handle, uint32_t *count) | |
{ | |
assert(NULL != handle); | |
assert(NULL != count); | |
if (kUSART_TxIdle == handle->txState) | |
{ | |
return kStatus_NoTransferInProgress; | |
} | |
*count = handle->txDataSizeAll - handle->txDataSize; | |
return kStatus_Success; | |
} | |
/*! | |
* brief Receives a buffer of data using an interrupt method. | |
* | |
* This function receives data using an interrupt method. This is a non-blocking function, which | |
* returns without waiting for all data to be received. | |
* If the RX ring buffer is used and not empty, the data in the ring buffer is copied and | |
* the parameter p receivedBytes shows how many bytes are copied from the ring buffer. | |
* After copying, if the data in the ring buffer is not enough to read, the receive | |
* request is saved by the USART driver. When the new data arrives, the receive request | |
* is serviced first. When all data is received, the USART driver notifies the upper layer | |
* through a callback function and passes the status parameter ref kStatus_USART_RxIdle. | |
* For example, the upper layer needs 10 bytes but there are only 5 bytes in the ring buffer. | |
* The 5 bytes are copied to the xfer->data and this function returns with the | |
* parameter p receivedBytes set to 5. For the left 5 bytes, newly arrived data is | |
* saved from the xfer->data[5]. When 5 bytes are received, the USART driver notifies the upper layer. | |
* If the RX ring buffer is not enabled, this function enables the RX and RX interrupt | |
* to receive data to the xfer->data. When all data is received, the upper layer is notified. | |
* | |
* param base USART peripheral base address. | |
* param handle USART handle pointer. | |
* param xfer USART transfer structure, see #usart_transfer_t. | |
* param receivedBytes Bytes received from the ring buffer directly. | |
* retval kStatus_Success Successfully queue the transfer into transmit queue. | |
* retval kStatus_USART_RxBusy Previous receive request is not finished. | |
* retval kStatus_InvalidArgument Invalid argument. | |
*/ | |
status_t USART_TransferReceiveNonBlocking(USART_Type *base, | |
usart_handle_t *handle, | |
usart_transfer_t *xfer, | |
size_t *receivedBytes) | |
{ | |
uint32_t i; | |
/* How many bytes to copy from ring buffer to user memory. */ | |
size_t bytesToCopy = 0U; | |
/* How many bytes to receive. */ | |
size_t bytesToReceive; | |
/* How many bytes currently have received. */ | |
size_t bytesCurrentReceived; | |
uint32_t regPrimask = 0U; | |
/* Check arguments */ | |
assert(!((NULL == base) || (NULL == handle) || (NULL == xfer))); | |
if ((NULL == base) || (NULL == handle) || (NULL == xfer)) | |
{ | |
return kStatus_InvalidArgument; | |
} | |
/* Check xfer members */ | |
assert(!((0 == xfer->dataSize) || (NULL == xfer->data))); | |
if ((0 == xfer->dataSize) || (NULL == xfer->data)) | |
{ | |
return kStatus_InvalidArgument; | |
} | |
/* How to get data: | |
1. If RX ring buffer is not enabled, then save xfer->data and xfer->dataSize | |
to uart handle, enable interrupt to store received data to xfer->data. When | |
all data received, trigger callback. | |
2. If RX ring buffer is enabled and not empty, get data from ring buffer first. | |
If there are enough data in ring buffer, copy them to xfer->data and return. | |
If there are not enough data in ring buffer, copy all of them to xfer->data, | |
save the xfer->data remained empty space to uart handle, receive data | |
to this empty space and trigger callback when finished. */ | |
if (kUSART_RxBusy == handle->rxState) | |
{ | |
return kStatus_USART_RxBusy; | |
} | |
else | |
{ | |
bytesToReceive = xfer->dataSize; | |
bytesCurrentReceived = 0U; | |
/* If RX ring buffer is used. */ | |
if (handle->rxRingBuffer) | |
{ | |
/* Disable IRQ, protect ring buffer. */ | |
regPrimask = DisableGlobalIRQ(); | |
/* How many bytes in RX ring buffer currently. */ | |
bytesToCopy = USART_TransferGetRxRingBufferLength(handle); | |
if (bytesToCopy) | |
{ | |
bytesToCopy = MIN(bytesToReceive, bytesToCopy); | |
bytesToReceive -= bytesToCopy; | |
/* Copy data from ring buffer to user memory. */ | |
for (i = 0U; i < bytesToCopy; i++) | |
{ | |
xfer->data[bytesCurrentReceived++] = handle->rxRingBuffer[handle->rxRingBufferTail]; | |
/* Wrap to 0. Not use modulo (%) because it might be large and slow. */ | |
if (handle->rxRingBufferTail + 1U == handle->rxRingBufferSize) | |
{ | |
handle->rxRingBufferTail = 0U; | |
} | |
else | |
{ | |
handle->rxRingBufferTail++; | |
} | |
} | |
} | |
/* If ring buffer does not have enough data, still need to read more data. */ | |
if (bytesToReceive) | |
{ | |
/* No data in ring buffer, save the request to UART handle. */ | |
handle->rxData = xfer->data + bytesCurrentReceived; | |
handle->rxDataSize = bytesToReceive; | |
handle->rxDataSizeAll = bytesToReceive; | |
handle->rxState = kUSART_RxBusy; | |
} | |
/* Enable IRQ if previously enabled. */ | |
EnableGlobalIRQ(regPrimask); | |
/* Call user callback since all data are received. */ | |
if (0 == bytesToReceive) | |
{ | |
if (handle->callback) | |
{ | |
handle->callback(base, handle, kStatus_USART_RxIdle, handle->userData); | |
} | |
} | |
} | |
/* Ring buffer not used. */ | |
else | |
{ | |
handle->rxData = xfer->data + bytesCurrentReceived; | |
handle->rxDataSize = bytesToReceive; | |
handle->rxDataSizeAll = bytesToReceive; | |
handle->rxState = kUSART_RxBusy; | |
/* Enable RX interrupt. */ | |
base->FIFOINTENSET |= USART_FIFOINTENSET_RXLVL_MASK; | |
} | |
/* Return the how many bytes have read. */ | |
if (receivedBytes) | |
{ | |
*receivedBytes = bytesCurrentReceived; | |
} | |
} | |
return kStatus_Success; | |
} | |
/*! | |
* brief Aborts the interrupt-driven data receiving. | |
* | |
* This function aborts the interrupt-driven data receiving. The user can get the remainBytes to find out | |
* how many bytes not received yet. | |
* | |
* param base USART peripheral base address. | |
* param handle USART handle pointer. | |
*/ | |
void USART_TransferAbortReceive(USART_Type *base, usart_handle_t *handle) | |
{ | |
assert(NULL != handle); | |
/* Only abort the receive to handle->rxData, the RX ring buffer is still working. */ | |
if (!handle->rxRingBuffer) | |
{ | |
/* Disable interrupts */ | |
USART_DisableInterrupts(base, kUSART_RxLevelInterruptEnable); | |
/* Empty rxFIFO */ | |
base->FIFOCFG |= USART_FIFOCFG_EMPTYRX_MASK; | |
} | |
handle->rxDataSize = 0U; | |
handle->rxState = kUSART_RxIdle; | |
} | |
/*! | |
* brief Get the number of bytes that have been received. | |
* | |
* This function gets the number of bytes that have been received. | |
* | |
* param base USART peripheral base address. | |
* param handle USART handle pointer. | |
* param count Receive bytes count. | |
* retval kStatus_NoTransferInProgress No receive in progress. | |
* retval kStatus_InvalidArgument Parameter is invalid. | |
* retval kStatus_Success Get successfully through the parameter \p count; | |
*/ | |
status_t USART_TransferGetReceiveCount(USART_Type *base, usart_handle_t *handle, uint32_t *count) | |
{ | |
assert(NULL != handle); | |
assert(NULL != count); | |
if (kUSART_RxIdle == handle->rxState) | |
{ | |
return kStatus_NoTransferInProgress; | |
} | |
*count = handle->rxDataSizeAll - handle->rxDataSize; | |
return kStatus_Success; | |
} | |
/*! | |
* brief USART IRQ handle function. | |
* | |
* This function handles the USART transmit and receive IRQ request. | |
* | |
* param base USART peripheral base address. | |
* param handle USART handle pointer. | |
*/ | |
void USART_TransferHandleIRQ(USART_Type *base, usart_handle_t *handle) | |
{ | |
/* Check arguments */ | |
assert((NULL != base) && (NULL != handle)); | |
bool receiveEnabled = (handle->rxDataSize) || (handle->rxRingBuffer); | |
bool sendEnabled = handle->txDataSize; | |
/* If RX overrun. */ | |
if (base->FIFOSTAT & USART_FIFOSTAT_RXERR_MASK) | |
{ | |
/* Clear rx error state. */ | |
base->FIFOSTAT |= USART_FIFOSTAT_RXERR_MASK; | |
/* clear rxFIFO */ | |
base->FIFOCFG |= USART_FIFOCFG_EMPTYRX_MASK; | |
/* Trigger callback. */ | |
if (handle->callback) | |
{ | |
handle->callback(base, handle, kStatus_USART_RxError, handle->userData); | |
} | |
} | |
while ((receiveEnabled && (base->FIFOSTAT & USART_FIFOSTAT_RXNOTEMPTY_MASK)) || | |
(sendEnabled && (base->FIFOSTAT & USART_FIFOSTAT_TXNOTFULL_MASK))) | |
{ | |
/* Receive data */ | |
if (receiveEnabled && (base->FIFOSTAT & USART_FIFOSTAT_RXNOTEMPTY_MASK)) | |
{ | |
/* Receive to app bufffer if app buffer is present */ | |
if (handle->rxDataSize) | |
{ | |
*handle->rxData = base->FIFORD; | |
handle->rxDataSize--; | |
handle->rxData++; | |
receiveEnabled = ((handle->rxDataSize != 0) || (handle->rxRingBuffer)); | |
if (!handle->rxDataSize) | |
{ | |
if (!handle->rxRingBuffer) | |
{ | |
base->FIFOINTENCLR = USART_FIFOINTENCLR_RXLVL_MASK | USART_FIFOINTENSET_RXERR_MASK; | |
} | |
handle->rxState = kUSART_RxIdle; | |
if (handle->callback) | |
{ | |
handle->callback(base, handle, kStatus_USART_RxIdle, handle->userData); | |
} | |
} | |
} | |
/* Otherwise receive to ring buffer if ring buffer is present */ | |
else | |
{ | |
if (handle->rxRingBuffer) | |
{ | |
/* If RX ring buffer is full, trigger callback to notify over run. */ | |
if (USART_TransferIsRxRingBufferFull(handle)) | |
{ | |
if (handle->callback) | |
{ | |
handle->callback(base, handle, kStatus_USART_RxRingBufferOverrun, handle->userData); | |
} | |
} | |
/* If ring buffer is still full after callback function, the oldest data is overrided. */ | |
if (USART_TransferIsRxRingBufferFull(handle)) | |
{ | |
/* Increase handle->rxRingBufferTail to make room for new data. */ | |
if (handle->rxRingBufferTail + 1U == handle->rxRingBufferSize) | |
{ | |
handle->rxRingBufferTail = 0U; | |
} | |
else | |
{ | |
handle->rxRingBufferTail++; | |
} | |
} | |
/* Read data. */ | |
handle->rxRingBuffer[handle->rxRingBufferHead] = base->FIFORD; | |
/* Increase handle->rxRingBufferHead. */ | |
if (handle->rxRingBufferHead + 1U == handle->rxRingBufferSize) | |
{ | |
handle->rxRingBufferHead = 0U; | |
} | |
else | |
{ | |
handle->rxRingBufferHead++; | |
} | |
} | |
} | |
} | |
/* Send data */ | |
if (sendEnabled && (base->FIFOSTAT & USART_FIFOSTAT_TXNOTFULL_MASK)) | |
{ | |
base->FIFOWR = *handle->txData; | |
handle->txDataSize--; | |
handle->txData++; | |
sendEnabled = handle->txDataSize != 0; | |
if (!sendEnabled) | |
{ | |
base->FIFOINTENCLR = USART_FIFOINTENCLR_TXLVL_MASK; | |
handle->txState = kUSART_TxIdle; | |
if (handle->callback) | |
{ | |
handle->callback(base, handle, kStatus_USART_TxIdle, handle->userData); | |
} | |
} | |
} | |
} | |
/* ring buffer is not used */ | |
if (NULL == handle->rxRingBuffer) | |
{ | |
/* restore if rx transfer ends and rxLevel is different from default value */ | |
if ((handle->rxDataSize == 0) && (USART_FIFOTRIG_RXLVL_GET(base) != handle->rxWatermark)) | |
{ | |
base->FIFOTRIG = | |
(base->FIFOTRIG & (~USART_FIFOTRIG_RXLVL_MASK)) | USART_FIFOTRIG_RXLVL(handle->rxWatermark); | |
} | |
/* decrease level if rx transfer is bellow */ | |
if ((handle->rxDataSize != 0) && (handle->rxDataSize < (USART_FIFOTRIG_RXLVL_GET(base) + 1))) | |
{ | |
base->FIFOTRIG = | |
(base->FIFOTRIG & (~USART_FIFOTRIG_RXLVL_MASK)) | (USART_FIFOTRIG_RXLVL(handle->rxDataSize - 1)); | |
} | |
} | |
} |