MCUXpresso_LPC55S69/devices/LPC55S69/drivers/fsl_spi.c

1077 lines
37 KiB
C

/*
* Copyright (c) 2016, Freescale Semiconductor, Inc.
* Copyright 2016-2020,2022 NXP
* All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "fsl_spi.h"
#include "fsl_flexcomm.h"
/*******************************************************************************
* Definitions
******************************************************************************/
/* Component ID definition, used by tools. */
#ifndef FSL_COMPONENT_ID
#define FSL_COMPONENT_ID "platform.drivers.flexcomm_spi"
#endif
/* Note: FIFOCFG[SIZE] has always value 1 = 8 items depth */
#if defined(FSL_FEATURE_SPI_FIFOSIZE_CFG) && (FSL_FEATURE_SPI_FIFOSIZE_CFG)
#define SPI_FIFO_DEPTH(base) 4
#else
#define SPI_FIFO_DEPTH(base) ((((base)->FIFOCFG & SPI_FIFOCFG_SIZE_MASK) >> SPI_FIFOCFG_SIZE_SHIFT) << 3)
#endif /*FSL_FEATURE_SPI_FIFOSIZE_CFG*/
/* Convert transfer count to transfer bytes. dataWidth is a
* range <0,15>. Range <8,15> represents 2B transfer */
#define SPI_COUNT_TO_BYTES(dataWidth, count) ((count) << ((dataWidth) >> 3U))
#define SPI_BYTES_TO_COUNT(dataWidth, bytes) ((bytes) >> ((dataWidth) >> 3U))
#if defined(FSL_FEATURE_SPI_IS_SSEL_PIN_COUNT_EQUAL_TO_THREE) && (FSL_FEATURE_SPI_IS_SSEL_PIN_COUNT_EQUAL_TO_THREE)
#define SPI_SSELPOL_MASK ((SPI_CFG_SPOL0_MASK) | (SPI_CFG_SPOL1_MASK) | (SPI_CFG_SPOL2_MASK))
#else
#if (defined(FSL_FEATURE_SPI_SSEL_COUNT) && (FSL_FEATURE_SPI_SSEL_COUNT == 2))
#define SPI_SSELPOL_MASK ((SPI_CFG_SPOL0_MASK) | (SPI_CFG_SPOL1_MASK))
#else
#define SPI_SSELPOL_MASK ((SPI_CFG_SPOL0_MASK) | (SPI_CFG_SPOL1_MASK) | (SPI_CFG_SPOL2_MASK) | (SPI_CFG_SPOL3_MASK))
#endif /* FSL_FEATURE_SPI_SSEL_COUNT == 2 */
#endif /*FSL_FEATURE_SPI_IS_SSEL_PIN_COUNT_EQUAL_TO_THREE*/
/*!
* @brief Used for conversion from `flexcomm_irq_handler_t` to `flexcomm_spi_master_irq_handler_t` and
* `flexcomm_spi_slave_irq_handler_t`.
*/
typedef union spi_to_flexcomm
{
flexcomm_spi_master_irq_handler_t spi_master_handler;
flexcomm_spi_slave_irq_handler_t spi_slave_handler;
flexcomm_irq_handler_t flexcomm_handler;
} spi_to_flexcomm_t;
/*******************************************************************************
* Variables
******************************************************************************/
/*! @brief internal SPI config array */
static spi_config_t g_configs[FSL_FEATURE_SOC_SPI_COUNT] = {(spi_data_width_t)0};
/*! @brief Array to map SPI instance number to base address. */
static const uint32_t s_spiBaseAddrs[FSL_FEATURE_SOC_SPI_COUNT] = SPI_BASE_ADDRS;
/*! @brief IRQ name array */
static const IRQn_Type s_spiIRQ[] = SPI_IRQS;
/* @brief Dummy data for each instance. This data is used when user's tx buffer is NULL*/
volatile uint8_t s_dummyData[FSL_FEATURE_SOC_SPI_COUNT] = {0};
/*******************************************************************************
* Code
******************************************************************************/
/* Get the index corresponding to the FLEXCOMM */
/*! brief Returns instance number for SPI peripheral base address. */
uint32_t SPI_GetInstance(SPI_Type *base)
{
uint32_t i;
for (i = 0U; i < (uint32_t)FSL_FEATURE_SOC_SPI_COUNT; i++)
{
if ((uint32_t)base == s_spiBaseAddrs[i])
{
break;
}
}
assert(i < (uint32_t)FSL_FEATURE_SOC_SPI_COUNT);
return i;
}
/*!
* brief Set up the dummy data.
*
* param base SPI peripheral address.
* param dummyData Data to be transferred when tx buffer is NULL.
*/
void SPI_SetDummyData(SPI_Type *base, uint8_t dummyData)
{
uint32_t instance = SPI_GetInstance(base);
s_dummyData[instance] = dummyData;
}
/*!
* brief Returns the configurations.
*
* param base SPI peripheral address.
* return return configurations which contain datawidth and SSEL numbers.
* return data type is a pointer of spi_config_t.
*/
void *SPI_GetConfig(SPI_Type *base)
{
uint32_t instance;
instance = SPI_GetInstance(base);
return &g_configs[instance];
}
/*!
* brief Sets the SPI master configuration structure to default values.
*
* The purpose of this API is to get the configuration structure initialized for use in SPI_MasterInit().
* User may use the initialized structure unchanged in SPI_MasterInit(), or modify
* some fields of the structure before calling SPI_MasterInit(). After calling this API,
* the master is ready to transfer.
* Example:
code
spi_master_config_t config;
SPI_MasterGetDefaultConfig(&config);
endcode
*
* param config pointer to master config structure
*/
void SPI_MasterGetDefaultConfig(spi_master_config_t *config)
{
assert(NULL != config);
/* Initializes the configure structure to zero. */
(void)memset(config, 0, sizeof(*config));
config->enableLoopback = false;
config->enableMaster = true;
config->polarity = kSPI_ClockPolarityActiveHigh;
config->phase = kSPI_ClockPhaseFirstEdge;
config->direction = kSPI_MsbFirst;
config->baudRate_Bps = 500000U;
config->dataWidth = kSPI_Data8Bits;
config->sselNum = kSPI_Ssel0;
config->txWatermark = (uint8_t)kSPI_TxFifo0;
config->rxWatermark = (uint8_t)kSPI_RxFifo1;
config->sselPol = kSPI_SpolActiveAllLow;
config->delayConfig.preDelay = 0U;
config->delayConfig.postDelay = 0U;
config->delayConfig.frameDelay = 0U;
config->delayConfig.transferDelay = 0U;
}
/*!
* brief Initializes the SPI with master configuration.
*
* The configuration structure can be filled by user from scratch, or be set with default
* values by SPI_MasterGetDefaultConfig(). After calling this API, the slave is ready to transfer.
* Example
code
spi_master_config_t config = {
.baudRate_Bps = 400000,
...
};
SPI_MasterInit(SPI0, &config);
endcode
*
* param base SPI base pointer
* param config pointer to master configuration structure
* param srcClock_Hz Source clock frequency.
*/
status_t SPI_MasterInit(SPI_Type *base, const spi_master_config_t *config, uint32_t srcClock_Hz)
{
status_t result = kStatus_Success;
uint32_t instance;
uint32_t tmpConfig;
/* assert params */
assert(!((NULL == base) || (NULL == config) || (0U == srcClock_Hz)));
if ((NULL == base) || (NULL == config) || (0U == srcClock_Hz))
{
return kStatus_InvalidArgument;
}
/* initialize flexcomm to SPI mode */
result = FLEXCOMM_Init(base, FLEXCOMM_PERIPH_SPI);
if (kStatus_Success != result)
{
return result;
}
/* set divider */
result = SPI_MasterSetBaud(base, config->baudRate_Bps, srcClock_Hz);
if (kStatus_Success != result)
{
return result;
}
/* get instance number */
instance = SPI_GetInstance(base);
/* configure SPI mode */
tmpConfig = base->CFG;
tmpConfig &= ~(SPI_CFG_MASTER_MASK | SPI_CFG_LSBF_MASK | SPI_CFG_CPHA_MASK | SPI_CFG_CPOL_MASK | SPI_CFG_LOOP_MASK |
SPI_CFG_ENABLE_MASK | SPI_SSELPOL_MASK);
/* phase */
tmpConfig |= SPI_CFG_CPHA(config->phase);
/* polarity */
tmpConfig |= SPI_CFG_CPOL(config->polarity);
/* direction */
tmpConfig |= SPI_CFG_LSBF(config->direction);
/* master mode */
tmpConfig |= SPI_CFG_MASTER(1);
/* loopback */
tmpConfig |= SPI_CFG_LOOP(config->enableLoopback);
/* configure active level for all CS */
tmpConfig |= ((uint32_t)config->sselPol & (SPI_SSELPOL_MASK));
base->CFG = tmpConfig;
/* store configuration */
g_configs[instance].dataWidth = config->dataWidth;
g_configs[instance].sselNum = config->sselNum;
/* enable FIFOs */
base->FIFOCFG |= SPI_FIFOCFG_EMPTYTX_MASK | SPI_FIFOCFG_EMPTYRX_MASK;
base->FIFOCFG |= SPI_FIFOCFG_ENABLETX_MASK | SPI_FIFOCFG_ENABLERX_MASK;
/* trigger level - empty txFIFO, one item in rxFIFO */
tmpConfig = base->FIFOTRIG & (~(SPI_FIFOTRIG_RXLVL_MASK | SPI_FIFOTRIG_TXLVL_MASK));
tmpConfig |= SPI_FIFOTRIG_TXLVL(config->txWatermark) | SPI_FIFOTRIG_RXLVL(config->rxWatermark);
/* enable generating interrupts for FIFOTRIG levels */
tmpConfig |= SPI_FIFOTRIG_TXLVLENA_MASK | SPI_FIFOTRIG_RXLVLENA_MASK;
/* set FIFOTRIG */
base->FIFOTRIG = tmpConfig;
/* Set the delay configuration. */
SPI_SetTransferDelay(base, &config->delayConfig);
/* Set the dummy data. */
SPI_SetDummyData(base, (uint8_t)SPI_DUMMYDATA);
SPI_Enable(base, config->enableMaster);
return kStatus_Success;
}
/*!
* brief Sets the SPI slave configuration structure to default values.
*
* The purpose of this API is to get the configuration structure initialized for use in SPI_SlaveInit().
* Modify some fields of the structure before calling SPI_SlaveInit().
* Example:
code
spi_slave_config_t config;
SPI_SlaveGetDefaultConfig(&config);
endcode
*
* param config pointer to slave configuration structure
*/
void SPI_SlaveGetDefaultConfig(spi_slave_config_t *config)
{
assert(NULL != config);
/* Initializes the configure structure to zero. */
(void)memset(config, 0, sizeof(*config));
config->enableSlave = true;
config->polarity = kSPI_ClockPolarityActiveHigh;
config->phase = kSPI_ClockPhaseFirstEdge;
config->direction = kSPI_MsbFirst;
config->dataWidth = kSPI_Data8Bits;
config->txWatermark = (uint8_t)kSPI_TxFifo0;
config->rxWatermark = (uint8_t)kSPI_RxFifo1;
config->sselPol = kSPI_SpolActiveAllLow;
}
/*!
* brief Initializes the SPI with slave configuration.
*
* The configuration structure can be filled by user from scratch or be set with
* default values by SPI_SlaveGetDefaultConfig().
* After calling this API, the slave is ready to transfer.
* Example
code
spi_slave_config_t config = {
.polarity = flexSPIClockPolarity_ActiveHigh;
.phase = flexSPIClockPhase_FirstEdge;
.direction = flexSPIMsbFirst;
...
};
SPI_SlaveInit(SPI0, &config);
endcode
*
* param base SPI base pointer
* param config pointer to slave configuration structure
*/
status_t SPI_SlaveInit(SPI_Type *base, const spi_slave_config_t *config)
{
status_t result = kStatus_Success;
uint32_t instance;
uint32_t tmpConfig;
/* assert params */
assert(!((NULL == base) || (NULL == config)));
if ((NULL == base) || (NULL == config))
{
return kStatus_InvalidArgument;
}
/* configure flexcomm to SPI, enable clock gate */
result = FLEXCOMM_Init(base, FLEXCOMM_PERIPH_SPI);
if (kStatus_Success != result)
{
return result;
}
instance = SPI_GetInstance(base);
/* configure SPI mode */
tmpConfig = base->CFG;
tmpConfig &= ~(SPI_CFG_MASTER_MASK | SPI_CFG_LSBF_MASK | SPI_CFG_CPHA_MASK | SPI_CFG_CPOL_MASK |
SPI_CFG_ENABLE_MASK | SPI_SSELPOL_MASK);
/* phase */
tmpConfig |= SPI_CFG_CPHA(config->phase);
/* polarity */
tmpConfig |= SPI_CFG_CPOL(config->polarity);
/* direction */
tmpConfig |= SPI_CFG_LSBF(config->direction);
/* configure active level for all CS */
tmpConfig |= ((uint32_t)config->sselPol & (SPI_SSELPOL_MASK));
base->CFG = tmpConfig;
/* store configuration */
g_configs[instance].dataWidth = config->dataWidth;
/* empty and enable FIFOs */
base->FIFOCFG |= SPI_FIFOCFG_EMPTYTX_MASK | SPI_FIFOCFG_EMPTYRX_MASK;
base->FIFOCFG |= SPI_FIFOCFG_ENABLETX_MASK | SPI_FIFOCFG_ENABLERX_MASK;
/* trigger level - empty txFIFO, one item in rxFIFO */
tmpConfig = base->FIFOTRIG & (~(SPI_FIFOTRIG_RXLVL_MASK | SPI_FIFOTRIG_TXLVL_MASK));
tmpConfig |= SPI_FIFOTRIG_TXLVL(config->txWatermark) | SPI_FIFOTRIG_RXLVL(config->rxWatermark);
/* enable generating interrupts for FIFOTRIG levels */
tmpConfig |= SPI_FIFOTRIG_TXLVLENA_MASK | SPI_FIFOTRIG_RXLVLENA_MASK;
/* set FIFOTRIG */
base->FIFOTRIG = tmpConfig;
SPI_SetDummyData(base, (uint8_t)SPI_DUMMYDATA);
SPI_Enable(base, config->enableSlave);
return kStatus_Success;
}
/*!
* brief De-initializes the SPI.
*
* Calling this API resets the SPI module, gates the SPI clock.
* The SPI module can't work unless calling the SPI_MasterInit/SPI_SlaveInit to initialize module.
*
* param base SPI base pointer
*/
void SPI_Deinit(SPI_Type *base)
{
/* Assert arguments */
assert(NULL != base);
/* Disable interrupts, disable dma requests, disable peripheral */
base->FIFOINTENCLR = SPI_FIFOINTENCLR_TXERR_MASK | SPI_FIFOINTENCLR_RXERR_MASK | SPI_FIFOINTENCLR_TXLVL_MASK |
SPI_FIFOINTENCLR_RXLVL_MASK;
base->FIFOCFG &= ~(SPI_FIFOCFG_DMATX_MASK | SPI_FIFOCFG_DMARX_MASK);
base->CFG &= ~(SPI_CFG_ENABLE_MASK);
}
/*!
* brief Enables the DMA request from SPI txFIFO.
*
* param base SPI base pointer
* param enable True means enable DMA, false means disable DMA
*/
void SPI_EnableTxDMA(SPI_Type *base, bool enable)
{
if (enable)
{
base->FIFOCFG |= SPI_FIFOCFG_DMATX_MASK;
}
else
{
base->FIFOCFG &= ~SPI_FIFOCFG_DMATX_MASK;
}
}
/*!
* brief Enables the DMA request from SPI rxFIFO.
*
* param base SPI base pointer
* param enable True means enable DMA, false means disable DMA
*/
void SPI_EnableRxDMA(SPI_Type *base, bool enable)
{
if (enable)
{
base->FIFOCFG |= SPI_FIFOCFG_DMARX_MASK;
}
else
{
base->FIFOCFG &= ~SPI_FIFOCFG_DMARX_MASK;
}
}
/*!
* brief Sets the baud rate for SPI transfer. This is only used in master.
*
* param base SPI base pointer
* param baudrate_Bps baud rate needed in Hz.
* param srcClock_Hz SPI source clock frequency in Hz.
*/
status_t SPI_MasterSetBaud(SPI_Type *base, uint32_t baudrate_Bps, uint32_t srcClock_Hz)
{
uint32_t tmpDiv;
/* assert params */
assert(!((NULL == base) || (0U == baudrate_Bps) || (0U == srcClock_Hz)));
if ((NULL == base) || (0U == baudrate_Bps) || (0U == srcClock_Hz))
{
return kStatus_InvalidArgument;
}
/* calculate baudrate, round up the result */
tmpDiv = ((srcClock_Hz * 10U) / baudrate_Bps + 5U) / 10U - 1U;
if (tmpDiv > 0xFFFFU)
{
return kStatus_SPI_BaudrateNotSupport;
}
base->DIV &= ~SPI_DIV_DIVVAL_MASK;
base->DIV |= SPI_DIV_DIVVAL(tmpDiv);
return kStatus_Success;
}
/*!
* brief Writes a data into the SPI data register.
*
* param base SPI base pointer
* param data needs to be write.
* param configFlags transfer configuration options ref spi_xfer_option_t
*/
void SPI_WriteData(SPI_Type *base, uint16_t data, uint32_t configFlags)
{
uint32_t control = 0U;
uint32_t instance;
/* check params */
assert(NULL != base);
/* get and check instance */
instance = SPI_GetInstance(base);
/* set data width */
control |= (uint32_t)SPI_FIFOWR_LEN((g_configs[instance].dataWidth));
/* set sssel */
control |= (SPI_DEASSERT_ALL & (~SPI_DEASSERTNUM_SSEL((uint32_t)(g_configs[instance].sselNum))));
/* mask configFlags */
control |= (configFlags & (uint32_t)SPI_FIFOWR_FLAGS_MASK);
/* control should not affect lower 16 bits */
assert(0U == (control & 0xFFFFU));
base->FIFOWR = data | control;
}
/*!
* brief Initializes the SPI master handle.
*
* This function initializes the SPI master handle which can be used for other SPI master transactional APIs. Usually,
* for a specified SPI instance, call this API once to get the initialized handle.
*
* param base SPI peripheral base address.
* param handle SPI handle pointer.
* param callback Callback function.
* param userData User data.
*/
status_t SPI_MasterTransferCreateHandle(SPI_Type *base,
spi_master_handle_t *handle,
spi_master_callback_t callback,
void *userData)
{
/* check 'base' */
assert(NULL != base);
/* check 'handle' */
assert(NULL != handle);
uint32_t instance;
spi_to_flexcomm_t handler;
/* get flexcomm instance by 'base' param */
instance = SPI_GetInstance(base);
(void)memset(handle, 0, sizeof(*handle));
/* Initialize the handle */
if ((base->CFG & SPI_CFG_MASTER_MASK) != 0U)
{
handler.spi_master_handler = SPI_MasterTransferHandleIRQ;
FLEXCOMM_SetIRQHandler(base, handler.flexcomm_handler, handle);
}
else
{
handler.spi_slave_handler = SPI_SlaveTransferHandleIRQ;
FLEXCOMM_SetIRQHandler(base, handler.flexcomm_handler, handle);
}
handle->dataWidth = (uint8_t)(g_configs[instance].dataWidth);
/* in slave mode, the sselNum is not important */
handle->sselNum = (uint8_t)(g_configs[instance].sselNum);
handle->txWatermark = (uint8_t)SPI_FIFOTRIG_TXLVL_GET(base);
handle->rxWatermark = (uint8_t)SPI_FIFOTRIG_RXLVL_GET(base);
handle->callback = callback;
handle->userData = userData;
/* Enable SPI NVIC */
(void)EnableIRQ(s_spiIRQ[instance]);
return kStatus_Success;
}
/*!
* brief Transfers a block of data using a polling method.
*
* param base SPI base pointer
* param xfer pointer to spi_xfer_config_t structure
* retval kStatus_Success Successfully start a transfer.
* retval kStatus_InvalidArgument Input argument is invalid.
* retval kStatus_SPI_Timeout The transfer timed out and was aborted.
*/
status_t SPI_MasterTransferBlocking(SPI_Type *base, spi_transfer_t *xfer)
{
uint32_t instance;
uint32_t tx_ctrl = 0U, last_ctrl = 0U;
uint32_t tmp32, rxRemainingBytes, txRemainingBytes, dataWidth;
uint32_t toReceiveCount = 0;
uint8_t *txData, *rxData;
uint32_t fifoDepth;
#if SPI_RETRY_TIMES
uint32_t waitTimes = SPI_RETRY_TIMES;
#endif
/* check params */
assert(!((NULL == base) || (NULL == xfer) || ((NULL == xfer->txData) && (NULL == xfer->rxData))));
if ((NULL == base) || (NULL == xfer) || ((NULL == xfer->txData) && (NULL == xfer->rxData)))
{
return kStatus_InvalidArgument;
}
fifoDepth = SPI_FIFO_DEPTH(base);
txData = xfer->txData;
rxData = xfer->rxData;
txRemainingBytes = (txData != NULL) ? xfer->dataSize : 0U;
rxRemainingBytes = (rxData != NULL) ? xfer->dataSize : 0U;
instance = SPI_GetInstance(base);
dataWidth = (uint32_t)(g_configs[instance].dataWidth);
/* dataSize (in bytes) is not aligned to 16bit (2B) transfer */
if ((dataWidth > (uint32_t)kSPI_Data8Bits) && ((xfer->dataSize & 0x1U) != 0U))
{
return kStatus_InvalidArgument;
}
/* clear tx/rx errors and empty FIFOs */
base->FIFOCFG |= SPI_FIFOCFG_EMPTYTX_MASK | SPI_FIFOCFG_EMPTYRX_MASK;
base->FIFOSTAT |= SPI_FIFOSTAT_TXERR_MASK | SPI_FIFOSTAT_RXERR_MASK;
/* select slave to talk with */
tx_ctrl |= (SPI_DEASSERT_ALL & (~SPI_DEASSERTNUM_SSEL((uint32_t)(g_configs[instance].sselNum))));
/* set width of data - range asserted at entry */
tx_ctrl |= SPI_FIFOWR_LEN(dataWidth);
/* delay for frames */
tx_ctrl |= ((xfer->configFlags & (uint32_t)kSPI_FrameDelay) != 0U) ? (uint32_t)kSPI_FrameDelay : 0U;
/* end of transfer */
last_ctrl |= ((xfer->configFlags & (uint32_t)kSPI_FrameAssert) != 0U) ? (uint32_t)kSPI_FrameAssert : 0U;
/* last index of loop */
while ((txRemainingBytes != 0U) || (rxRemainingBytes != 0U) || (toReceiveCount != 0U))
{
#if SPI_RETRY_TIMES
if (--waitTimes == 0U)
{
return kStatus_SPI_Timeout;
}
#endif
/* if rxFIFO is not empty */
if ((base->FIFOSTAT & SPI_FIFOSTAT_RXNOTEMPTY_MASK) != 0U)
{
tmp32 = base->FIFORD;
/* rxBuffer is not empty */
if (rxRemainingBytes != 0U)
{
*(rxData++) = (uint8_t)tmp32;
rxRemainingBytes--;
/* read 16 bits at once */
if (dataWidth > 8U)
{
*(rxData++) = (uint8_t)(tmp32 >> 8);
rxRemainingBytes--;
}
}
/* decrease number of data expected to receive */
toReceiveCount -= 1U;
}
/* transmit if txFIFO is not full and data to receive does not exceed FIFO depth */
if (((base->FIFOSTAT & SPI_FIFOSTAT_TXNOTFULL_MASK) != 0U) && (toReceiveCount < fifoDepth) &&
((txRemainingBytes != 0U) || (rxRemainingBytes >= SPI_COUNT_TO_BYTES(dataWidth, toReceiveCount + 1U))))
{
/* txBuffer is not empty */
if (txRemainingBytes != 0U)
{
tmp32 = *(txData++);
txRemainingBytes--;
/* write 16 bit at once */
if (dataWidth > 8U)
{
tmp32 |= ((uint32_t)(*(txData++))) << 8U;
txRemainingBytes--;
}
if (txRemainingBytes == 0U)
{
tx_ctrl |= last_ctrl;
}
}
else
{
tmp32 = (uint32_t)s_dummyData[instance];
tmp32 |= (uint32_t)s_dummyData[instance] << 8U;
/* last transfer */
if (rxRemainingBytes == SPI_COUNT_TO_BYTES(dataWidth, toReceiveCount + 1U))
{
tx_ctrl |= last_ctrl;
}
}
/* send data */
tmp32 = tx_ctrl | tmp32;
base->FIFOWR = tmp32;
toReceiveCount += 1U;
}
}
/* wait if TX FIFO of previous transfer is not empty */
#if SPI_RETRY_TIMES
waitTimes = SPI_RETRY_TIMES;
while ((0U == (base->FIFOSTAT & SPI_FIFOSTAT_TXEMPTY_MASK)) && (0U != --waitTimes))
#else
while (0U == (base->FIFOSTAT & SPI_FIFOSTAT_TXEMPTY_MASK))
#endif
{
}
#if SPI_RETRY_TIMES
if (waitTimes == 0U)
{
return kStatus_SPI_Timeout;
}
#endif
return kStatus_Success;
}
/*!
* brief Performs a non-blocking SPI interrupt transfer.
*
* param base SPI peripheral base address.
* param handle pointer to spi_master_handle_t structure which stores the transfer state
* param xfer pointer to spi_xfer_config_t structure
* retval kStatus_Success Successfully start a transfer.
* retval kStatus_InvalidArgument Input argument is invalid.
* retval kStatus_SPI_Busy SPI is not idle, is running another transfer.
*/
status_t SPI_MasterTransferNonBlocking(SPI_Type *base, spi_master_handle_t *handle, spi_transfer_t *xfer)
{
/* check params */
assert(
!((NULL == base) || (NULL == handle) || (NULL == xfer) || ((NULL == xfer->txData) && (NULL == xfer->rxData))));
if ((NULL == base) || (NULL == handle) || (NULL == xfer) || ((NULL == xfer->txData) && (NULL == xfer->rxData)))
{
return kStatus_InvalidArgument;
}
/* dataSize (in bytes) is not aligned to 16bit (2B) transfer */
assert(!((handle->dataWidth > (uint8_t)kSPI_Data8Bits) && ((xfer->dataSize & 0x1U) != 0U)));
if ((handle->dataWidth > (uint8_t)kSPI_Data8Bits) && ((xfer->dataSize & 0x1U) != 0U))
{
return kStatus_InvalidArgument;
}
/* Check if SPI is busy */
if (handle->state == (uint32_t)kStatus_SPI_Busy)
{
return kStatus_SPI_Busy;
}
/* Set the handle information */
handle->txData = xfer->txData;
handle->rxData = xfer->rxData;
/* set count */
handle->txRemainingBytes = (xfer->txData != NULL) ? xfer->dataSize : 0U;
handle->rxRemainingBytes = (xfer->rxData != NULL) ? xfer->dataSize : 0U;
handle->totalByteCount = xfer->dataSize;
/* other options */
handle->toReceiveCount = 0;
handle->configFlags = xfer->configFlags;
/* Set the SPI state to busy */
handle->state = (uint32_t)kStatus_SPI_Busy;
/* clear FIFOs when transfer starts */
base->FIFOCFG |= SPI_FIFOCFG_EMPTYTX_MASK | SPI_FIFOCFG_EMPTYRX_MASK;
base->FIFOSTAT |= SPI_FIFOSTAT_TXERR_MASK | SPI_FIFOSTAT_RXERR_MASK;
/* enable generating txIRQ and rxIRQ, first transfer is fired by empty txFIFO */
base->FIFOINTENSET |= SPI_FIFOINTENSET_TXLVL_MASK | SPI_FIFOINTENSET_RXLVL_MASK;
return kStatus_Success;
}
/*!
* brief Transfers a block of data using a polling method.
*
* This function will do a half-duplex transfer for SPI master, This is a blocking function,
* which does not retuen until all transfer have been completed. And data transfer mechanism is half-duplex,
* users can set transmit first or receive first.
*
* param base SPI base pointer
* param xfer pointer to spi_half_duplex_transfer_t structure
* return status of status_t.
*/
status_t SPI_MasterHalfDuplexTransferBlocking(SPI_Type *base, spi_half_duplex_transfer_t *xfer)
{
assert(xfer != NULL);
spi_transfer_t tempXfer = {0};
status_t status;
if (xfer->isTransmitFirst)
{
tempXfer.txData = xfer->txData;
tempXfer.rxData = NULL;
tempXfer.dataSize = xfer->txDataSize;
}
else
{
tempXfer.txData = NULL;
tempXfer.rxData = xfer->rxData;
tempXfer.dataSize = xfer->rxDataSize;
}
/* If the pcs pin keep assert between transmit and receive. */
if (xfer->isPcsAssertInTransfer)
{
tempXfer.configFlags = (xfer->configFlags) & (~(uint32_t)kSPI_FrameAssert);
}
else
{
tempXfer.configFlags = (xfer->configFlags) | (uint32_t)kSPI_FrameAssert;
}
status = SPI_MasterTransferBlocking(base, &tempXfer);
if (status != kStatus_Success)
{
return status;
}
if (xfer->isTransmitFirst)
{
tempXfer.txData = NULL;
tempXfer.rxData = xfer->rxData;
tempXfer.dataSize = xfer->rxDataSize;
}
else
{
tempXfer.txData = xfer->txData;
tempXfer.rxData = NULL;
tempXfer.dataSize = xfer->txDataSize;
}
tempXfer.configFlags = xfer->configFlags;
/* SPI transfer blocking. */
status = SPI_MasterTransferBlocking(base, &tempXfer);
return status;
}
/*!
* brief Performs a non-blocking SPI interrupt transfer.
*
* This function using polling way to do the first half transimission and using interrupts to
* do the second half transimission, the transfer mechanism is half-duplex.
* When do the second half transimission, code will return right away. When all data is transferred,
* the callback function is called.
*
* param base SPI peripheral base address.
* param handle pointer to spi_master_handle_t structure which stores the transfer state
* param xfer pointer to spi_half_duplex_transfer_t structure
* return status of status_t.
*/
status_t SPI_MasterHalfDuplexTransferNonBlocking(SPI_Type *base,
spi_master_handle_t *handle,
spi_half_duplex_transfer_t *xfer)
{
assert(xfer != NULL);
assert(handle != NULL);
spi_transfer_t tempXfer = {0};
status_t status;
if (xfer->isTransmitFirst)
{
tempXfer.txData = xfer->txData;
tempXfer.rxData = NULL;
tempXfer.dataSize = xfer->txDataSize;
}
else
{
tempXfer.txData = NULL;
tempXfer.rxData = xfer->rxData;
tempXfer.dataSize = xfer->rxDataSize;
}
/* If the PCS pin keep assert between transmit and receive. */
if (xfer->isPcsAssertInTransfer)
{
tempXfer.configFlags = (xfer->configFlags) & (~(uint32_t)kSPI_FrameAssert);
}
else
{
tempXfer.configFlags = (xfer->configFlags) | (uint32_t)kSPI_FrameAssert;
}
status = SPI_MasterTransferBlocking(base, &tempXfer);
if (status != kStatus_Success)
{
return status;
}
if (xfer->isTransmitFirst)
{
tempXfer.txData = NULL;
tempXfer.rxData = xfer->rxData;
tempXfer.dataSize = xfer->rxDataSize;
}
else
{
tempXfer.txData = xfer->txData;
tempXfer.rxData = NULL;
tempXfer.dataSize = xfer->txDataSize;
}
tempXfer.configFlags = xfer->configFlags;
status = SPI_MasterTransferNonBlocking(base, handle, &tempXfer);
return status;
}
/*!
* brief Gets the master transfer count.
*
* This function gets the master transfer count.
*
* param base SPI peripheral base address.
* param handle Pointer to the spi_master_handle_t structure which stores the transfer state.
* param count The number of bytes transferred by using the non-blocking transaction.
* return status of status_t.
*/
status_t SPI_MasterTransferGetCount(SPI_Type *base, spi_master_handle_t *handle, size_t *count)
{
assert(NULL != handle);
if (NULL == count)
{
return kStatus_InvalidArgument;
}
/* Catch when there is not an active transfer. */
if (handle->state != (uint32_t)kStatus_SPI_Busy)
{
*count = 0;
return kStatus_NoTransferInProgress;
}
*count = handle->totalByteCount - handle->rxRemainingBytes;
return kStatus_Success;
}
/*!
* brief SPI master aborts a transfer using an interrupt.
*
* This function aborts a transfer using an interrupt.
*
* param base SPI peripheral base address.
* param handle Pointer to the spi_master_handle_t structure which stores the transfer state.
*/
void SPI_MasterTransferAbort(SPI_Type *base, spi_master_handle_t *handle)
{
assert(NULL != handle);
/* Disable interrupt requests*/
base->FIFOINTENSET &= ~(SPI_FIFOINTENSET_TXLVL_MASK | SPI_FIFOINTENSET_RXLVL_MASK);
/* Empty FIFOs */
base->FIFOCFG |= SPI_FIFOCFG_EMPTYTX_MASK | SPI_FIFOCFG_EMPTYRX_MASK;
handle->state = (uint32_t)kStatus_SPI_Idle;
handle->txRemainingBytes = 0U;
handle->rxRemainingBytes = 0U;
}
static void SPI_TransferHandleIRQInternal(SPI_Type *base, spi_master_handle_t *handle)
{
uint32_t tx_ctrl = 0U, last_ctrl = 0U, tmp32;
bool loopContinue;
uint32_t fifoDepth;
/* Get flexcomm instance by 'base' param */
uint32_t instance = SPI_GetInstance(base);
size_t txRemainingBytes;
size_t rxRemainingBytes;
uint8_t toReceiveCount;
/* check params */
assert((NULL != base) && (NULL != handle) && ((NULL != handle->txData) || (NULL != handle->rxData)));
fifoDepth = SPI_FIFO_DEPTH(base);
/* select slave to talk with */
tx_ctrl |= ((uint32_t)SPI_DEASSERT_ALL & (uint32_t)SPI_ASSERTNUM_SSEL(handle->sselNum));
/* set width of data */
tx_ctrl |= SPI_FIFOWR_LEN(handle->dataWidth);
/* delay for frames */
tx_ctrl |= ((handle->configFlags & (uint32_t)kSPI_FrameDelay) != 0U) ? (uint32_t)kSPI_FrameDelay : 0U;
/* end of transfer */
last_ctrl |= ((handle->configFlags & (uint32_t)kSPI_FrameAssert) != 0U) ? (uint32_t)kSPI_FrameAssert : 0U;
do
{
loopContinue = false;
/* rxFIFO is not empty */
if ((base->FIFOSTAT & SPI_FIFOSTAT_RXNOTEMPTY_MASK) != 0U)
{
tmp32 = base->FIFORD;
/* rxBuffer is not empty */
if (handle->rxRemainingBytes != 0U)
{
/* low byte must go first */
*(handle->rxData++) = (uint8_t)tmp32;
handle->rxRemainingBytes--;
/* read 16 bits at once */
if (handle->dataWidth > (uint8_t)kSPI_Data8Bits)
{
*(handle->rxData++) = (uint8_t)(tmp32 >> 8);
handle->rxRemainingBytes--;
}
}
/* decrease number of data expected to receive */
handle->toReceiveCount -= 1;
loopContinue = true;
}
/* - txFIFO is not full
* - we cannot cause rxFIFO overflow by sending more data than is the depth of FIFO
* - txBuffer is not empty or the next 'toReceiveCount' data can fit into rxBuffer
*/
txRemainingBytes = handle->txRemainingBytes;
rxRemainingBytes = handle->rxRemainingBytes;
toReceiveCount = (handle->toReceiveCount > 0) ? (uint8_t)handle->toReceiveCount : 0U;
if (((base->FIFOSTAT & SPI_FIFOSTAT_TXNOTFULL_MASK) != 0U) && ((uint32_t)toReceiveCount < fifoDepth) &&
((txRemainingBytes != 0U) ||
(rxRemainingBytes >= SPI_COUNT_TO_BYTES(handle->dataWidth, (uint32_t)toReceiveCount + 1U))))
{
/* txBuffer is not empty */
if ((txRemainingBytes != 0U) && (handle->txData != NULL))
{
/* low byte must go first */
tmp32 = *(handle->txData++);
handle->txRemainingBytes--;
txRemainingBytes = handle->txRemainingBytes;
/* write 16 bit at once */
if (handle->dataWidth > (uint8_t)kSPI_Data8Bits)
{
tmp32 |= ((uint32_t)(*(handle->txData++))) << 8U;
handle->txRemainingBytes--;
txRemainingBytes = handle->txRemainingBytes;
}
/* last transfer */
if (handle->txRemainingBytes == 0U)
{
tx_ctrl |= last_ctrl;
}
}
else
{
tmp32 = (uint32_t)s_dummyData[instance];
tmp32 |= (uint32_t)s_dummyData[instance] << 8U;
/* last transfer */
if (rxRemainingBytes == SPI_COUNT_TO_BYTES(handle->dataWidth, (uint32_t)toReceiveCount + 1U))
{
tx_ctrl |= last_ctrl;
}
}
/* send data */
tmp32 = tx_ctrl | tmp32;
base->FIFOWR = tmp32;
/* increase number of expected data to receive */
handle->toReceiveCount += 1;
toReceiveCount = (handle->toReceiveCount > 0) ? (uint8_t)handle->toReceiveCount : 0U;
loopContinue = true;
}
} while (loopContinue);
}
/*!
* brief Interrupts the handler for the SPI.
*
* param base SPI peripheral base address.
* param handle pointer to spi_master_handle_t structure which stores the transfer state.
*/
void SPI_MasterTransferHandleIRQ(SPI_Type *base, spi_master_handle_t *handle)
{
assert((NULL != base) && (NULL != handle));
size_t txRemainingBytes;
uint8_t toReceiveCount;
/* IRQ behaviour:
* - first interrupt is triggered by empty txFIFO. The transfer function
* then tries empty rxFIFO and fill txFIFO interleaved that results to
* strategy to process as many items as possible.
* - the next IRQs can be:
* rxIRQ from nonempty rxFIFO which requires to empty rxFIFO.
* txIRQ from empty txFIFO which requires to refill txFIFO.
* - last interrupt is triggered by empty txFIFO. The last state is
* known by empty rxBuffer and txBuffer. If there is nothing to receive
* or send - both operations have been finished and interrupts can be
* disabled.
*/
/* Data to send or read or expected to receive */
if ((handle->txRemainingBytes != 0U) || (handle->rxRemainingBytes != 0U) || (handle->toReceiveCount != 0))
{
/* Transmit or receive data */
SPI_TransferHandleIRQInternal(base, handle);
/* No data to send or read or receive. Transfer ends. Set txTrigger to 0 level and
* enable txIRQ to confirm when txFIFO becomes empty */
if ((0U == handle->txRemainingBytes) && (0U == handle->rxRemainingBytes) && (0 == handle->toReceiveCount))
{
base->FIFOTRIG = base->FIFOTRIG & (~SPI_FIFOTRIG_TXLVL_MASK);
base->FIFOINTENSET |= SPI_FIFOINTENSET_TXLVL_MASK;
}
else
{
uint32_t rxRemainingCount = SPI_BYTES_TO_COUNT(handle->dataWidth, handle->rxRemainingBytes);
/* If, there are no data to send or rxFIFO is already filled with necessary number of dummy data,
* disable txIRQ. From this point only rxIRQ is used to receive data without any transmission */
toReceiveCount = (handle->toReceiveCount > 0) ? (uint8_t)handle->toReceiveCount : 0U;
if ((0U == handle->txRemainingBytes) && (rxRemainingCount <= toReceiveCount))
{
base->FIFOINTENCLR = SPI_FIFOINTENCLR_TXLVL_MASK;
}
/* Nothing to receive or transmit, but we still have pending data which are bellow rxLevel.
* Cannot clear rxFIFO, txFIFO might be still active */
if (rxRemainingCount == 0U)
{
txRemainingBytes = handle->txRemainingBytes;
if ((txRemainingBytes == 0U) && (toReceiveCount != 0U) &&
(toReceiveCount < SPI_FIFOTRIG_RXLVL_GET(base) + 1U))
{
base->FIFOTRIG = (base->FIFOTRIG & (~SPI_FIFOTRIG_RXLVL_MASK)) |
SPI_FIFOTRIG_RXLVL((uint32_t)toReceiveCount - 1U);
}
}
else
{
/* Expected to receive less data than rxLevel value, we have to update rxLevel */
if (rxRemainingCount < (SPI_FIFOTRIG_RXLVL_GET(base) + 1U))
{
base->FIFOTRIG =
(base->FIFOTRIG & (~SPI_FIFOTRIG_RXLVL_MASK)) | SPI_FIFOTRIG_RXLVL(rxRemainingCount - 1U);
}
}
}
}
else
{
/* Empty txFIFO is confirmed. Disable IRQs and restore triggers values */
base->FIFOINTENCLR = SPI_FIFOINTENCLR_RXLVL_MASK | SPI_FIFOINTENCLR_TXLVL_MASK;
base->FIFOTRIG = (base->FIFOTRIG & (~(SPI_FIFOTRIG_RXLVL_MASK | SPI_FIFOTRIG_RXLVL_MASK))) |
SPI_FIFOTRIG_RXLVL(handle->rxWatermark) | SPI_FIFOTRIG_TXLVL(handle->txWatermark);
/* set idle state and call user callback */
handle->state = (uint32_t)kStatus_SPI_Idle;
if (handle->callback != NULL)
{
(handle->callback)(base, handle, handle->state, handle->userData);
}
}
}