MCUXpresso_LPC54102/devices/LPC54102/drivers/fsl_spi.c

1617 lines
54 KiB
C

/*
* Copyright 2017 NXP
* All rights reserved.
*
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "fsl_spi.h"
/*******************************************************************************
* Definitions
******************************************************************************/
/* Component ID definition, used by tools. */
#ifndef FSL_COMPONENT_ID
#define FSL_COMPONENT_ID "platform.drivers.vspi"
#endif
/* 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))
#define SPI_SSELPOL_MASK ((SPI_CFG_SPOL0_MASK) | (SPI_CFG_SPOL1_MASK) | (SPI_CFG_SPOL2_MASK) | (SPI_CFG_SPOL3_MASK))
/*******************************************************************************
* Variables
******************************************************************************/
/*! @brief SPI internal handle pointer array */
static spi_master_handle_t *s_spiHandle[FSL_FEATURE_SOC_SPI_COUNT];
/*! @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;
#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
/* @brief Array to map SPI instance number to CLOCK names */
static const clock_ip_name_t s_spiClock[] = SPI_CLOCKS;
#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
#if !(defined(FSL_SDK_DISABLE_DRIVER_RESET_CONTROL) && FSL_SDK_DISABLE_DRIVER_RESET_CONTROL)
/* @brief Array to map SPI reset. */
static const SYSCON_RSTn_t s_spiReset[2] = {kSPI0_RST_SHIFT_RSTn, kSPI1_RST_SHIFT_RSTn};
#endif /* FSL_SDK_DISABLE_DRIVER_RESET_CONTROL */
/*! @brief Typedef for spi master interrupt handler. spi master and slave handle is the same. */
typedef void (*spi_isr_t)(SPI_Type *base, spi_master_handle_t *spiHandle);
/*! @brief Pointer to master IRQ handler for each instance. */
static spi_isr_t s_spiMasterIsr;
static spi_isr_t s_spiSlaveIsr;
/* @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)
{
assert(NULL != base);
int i = 0U;
for (i = 0; i < FSL_FEATURE_SOC_SPI_COUNT; i++)
{
if ((uint32_t)base == s_spiBaseAddrs[i])
{
return i;
}
}
assert(false);
return 0;
}
/*!
* 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;
}
/* Get the information of datawidth and SSEL number. */
/*!
* 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)
{
int32_t instance = SPI_GetInstance(base);
return &g_configs[instance];
}
/* Get status flags from FIFO status register. */
/*!
* brief Gets the FIFO status flag for SPI transfer.
*
* param base SPI base pointer
* return SPI Status, use status flag to AND ref _spi_fifo_status_flags could get the related status.
*/
uint32_t SPI_GetFifoStatusFlags(SPI_Type *base)
{
uint32_t instance = SPI_GetInstance(base);
return (VFIFO->SPI[instance].STATSPI);
}
/* Clear status flags of FIFO status register. */
/*!
* brief Clear the FIFO status flag for SPI transfer.
* Only kSPI_RxFifoTimeOutFlag and kSPI_FifoBusErrorFlag can be cleared.
* param base SPI base pointer
* param mask use status flag to AND ref _spi_status_flags could get the related status.
*/
void SPI_ClearFifoStatusFlags(SPI_Type *base, uint32_t mask)
{
uint32_t instance = SPI_GetInstance(base);
VFIFO->SPI[instance].STATSPI |= mask;
}
/* Enable FIFO interrupt from FIFO CTLSETSPI register. */
/*!
* brief Enables the FIFO interrupt for the SPI.
*
* param base SPI base pointer
* param irqs SPI interrupt source. The parameter can be any combination of the following values:
* arg kSPI_RxFifoThresholdInterruptEnable
* arg kSPI_TxFifoThresholdInterruptEnable
*/
void SPI_EnableFifoInterrupts(SPI_Type *base, uint32_t irqs)
{
uint32_t instance = SPI_GetInstance(base);
VFIFO->SPI[instance].CTLSETSPI |= irqs;
}
/* Clear FIFO interrupt from FIFO CTLCLRSPI register. */
/*!
* brief Disables the FIFO interrupt for the SPI.
*
* param base SPI base pointer
* param irqs SPI interrupt source. The parameter can be any combination of the following values:
* arg kSPI_RxFifoThresholdInterruptEnable
* arg kSPI_TxFifoThresholdInterruptEnable
*/
void SPI_DisableFifoInterrupts(SPI_Type *base, uint32_t irqs)
{
uint32_t instance = SPI_GetInstance(base);
VFIFO->SPI[instance].CTLCLRSPI |= irqs;
}
/* Flush the FIFO for SPI transfer. */
/*!
* brief Flush the FIFO buffer.
*
* This function will Flush tHE fifo buffer.
*
* param base SPI peripheral base address.
* param direction the fifo direction need to flushed, Tx FIFO or Rx FIFO.
*/
void SPI_FifoFlush(SPI_Type *base, uint32_t direction)
{
uint32_t instance = SPI_GetInstance(base);
if (kSPI_FifoTx & direction)
{
VFIFO->SPI[instance].CTLSETSPI |= VFIFO_SPI_CTLSETSPI_TXFLUSH_MASK;
VFIFO->SPI[instance].CTLCLRSPI |= VFIFO_SPI_CTLCLRSPI_TXFLUSHCLR_MASK;
}
if (kSPI_FifoRx & direction)
{
VFIFO->SPI[instance].CTLSETSPI |= VFIFO_SPI_CTLSETSPI_RXFLUSH_MASK;
VFIFO->SPI[instance].CTLCLRSPI |= VFIFO_SPI_CTLCLRSPI_RXFLUSHCLR_MASK;
}
}
/* Enable SPI FIFO function. */
/*!
* brief Enable FIFO for SPI.
*
* This function will enable the FIFO for SPI according to pointer of the configure struct.
* Note: If this API is called, please reset the baudrate to adapt your demand after this API
* was called.
*
* param base SPI peripheral base address.
* param config pointer to FIFO configuration structure.
*/
void SPI_EnableFifo(SPI_Type *base, const spi_fifo_config_t *config)
{
uint32_t instance = 0U;
assert((config->enableRxFifo) || (config->enableTxFifo));
instance = SPI_GetInstance(base);
#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
/* Enable the clock. */
CLOCK_EnableClock(kCLOCK_Fifo);
#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
/* Configure the FIFOCTRL register to enable the SPI TX/RX FIFO . */
switch (instance)
{
case 0:
SYSCON->FIFOCTRL &= ~(SYSCON_FIFOCTRL_SPI0TXFIFOEN_MASK | SYSCON_FIFOCTRL_SPI0RXFIFOEN_MASK);
SYSCON->FIFOCTRL |=
SYSCON_FIFOCTRL_SPI0TXFIFOEN(config->enableTxFifo) | SYSCON_FIFOCTRL_SPI0RXFIFOEN(config->enableRxFifo);
break;
case 1:
SYSCON->FIFOCTRL &= ~(SYSCON_FIFOCTRL_SPI1TXFIFOEN_MASK | SYSCON_FIFOCTRL_SPI1RXFIFOEN_MASK);
SYSCON->FIFOCTRL |=
SYSCON_FIFOCTRL_SPI1TXFIFOEN(config->enableTxFifo) | SYSCON_FIFOCTRL_SPI1RXFIFOEN(config->enableRxFifo);
break;
default:
break;
}
/* Pause the SPI FIFO for setting. */
while (
!((VFIFO->FIFOCTLSPI & VFIFO_FIFOCTLSPI_RXPAUSED_MASK) && (VFIFO->FIFOCTLSPI & VFIFO_FIFOCTLSPI_TXPAUSED_MASK)))
{
VFIFO->FIFOCTLSPI |= (VFIFO_FIFOCTLSPI_RXPAUSE_MASK | VFIFO_FIFOCTLSPI_TXPAUSE_MASK);
}
/* Flush the TX and RX FIFO buffer. */
SPI_FifoFlush(base, kSPI_FifoTx | kSPI_FifoRx);
/* Set the TX and RX FIFO size. */
VFIFO->FIFOCFGSPI[instance] =
VFIFO_FIFOCFGSPI_RXSIZE(config->rxFifoSize) | VFIFO_FIFOCFGSPI_TXSIZE(config->txFifoSize);
/* Update all the SPI FIFO size. */
VFIFO->FIFOUPDATESPI |= VFIFO_FIFOUPDATESPI_SPI0RXUPDATESIZE_MASK | VFIFO_FIFOUPDATESPI_SPI0TXUPDATESIZE_MASK |
VFIFO_FIFOUPDATESPI_SPI1RXUPDATESIZE_MASK | VFIFO_FIFOUPDATESPI_SPI1TXUPDATESIZE_MASK;
/* Set the TX and RX FIFO threshold size. */
VFIFO->SPI[instance].CFGSPI &= ~(VFIFO_SPI_CFGSPI_RXTHRESHOLD_MASK | VFIFO_SPI_CFGSPI_TXTHRESHOLD_MASK);
VFIFO->SPI[instance].CFGSPI |=
VFIFO_SPI_CFGSPI_RXTHRESHOLD(config->rxFifoThreshold) | VFIFO_SPI_CFGSPI_TXTHRESHOLD(config->txFifoThreshold);
/* Unpause the system FIFO for transfer. */
VFIFO->FIFOCTLSPI &= ~(VFIFO_FIFOCTLSPI_RXPAUSE_MASK | VFIFO_FIFOCTLSPI_TXPAUSE_MASK);
}
/* Disable system FIFO. */
/*!
* brief Disable FIFO for SPI.
*
* This function will Disable the FIFO for SPI transfer.
* disable interrupts, clear status flags, disable the TX/RX FIFO, set fifo size to zero.
* But will not disable the system FIFO, because other instance like USART may using the FIFO.
*
* param base SPI peripheral base address.
* param config pointer to FIFO configuration structure.
*/
void SPI_DisableFifo(SPI_Type *base)
{
assert(NULL != base);
uint32_t instance = SPI_GetInstance(base);
/* Pause the SPI FIFO for setting. */
while (
!((VFIFO->FIFOCTLSPI & VFIFO_FIFOCTLSPI_RXPAUSED_MASK) && (VFIFO->FIFOCTLSPI & VFIFO_FIFOCTLSPI_TXPAUSED_MASK)))
{
VFIFO->FIFOCTLSPI |= (VFIFO_FIFOCTLSPI_RXPAUSE_MASK | VFIFO_FIFOCTLSPI_TXPAUSE_MASK);
}
/* Flush the TX and RX FIFO buffer. */
SPI_FifoFlush(base, kSPI_FifoTx | kSPI_FifoRx);
/* Set the TX and RX FIFO size to zero. */
VFIFO->FIFOCFGSPI[instance] = VFIFO_FIFOCFGSPI_RXSIZE(0U) | VFIFO_FIFOCFGSPI_TXSIZE(0U);
/* Update all the SPI FIFO size. */
VFIFO->FIFOUPDATESPI |= VFIFO_FIFOUPDATESPI_SPI0RXUPDATESIZE_MASK | VFIFO_FIFOUPDATESPI_SPI0TXUPDATESIZE_MASK |
VFIFO_FIFOUPDATESPI_SPI1RXUPDATESIZE_MASK | VFIFO_FIFOUPDATESPI_SPI1TXUPDATESIZE_MASK;
/* Disable all FIFO interrupts. */
SPI_DisableFifoInterrupts(base, kSPI_AllFifoInterruptEnable);
/* Clear all FIFO status flags, only receive timeout and bus error flag can be cleared. */
SPI_ClearFifoStatusFlags(base, kSPI_RxFifoTimeOutFlag | kSPI_FifoBusErrorFlag);
/* Unpause the system FIFO for transfer. */
VFIFO->FIFOCTLSPI &= ~(VFIFO_FIFOCTLSPI_RXPAUSE_MASK | VFIFO_FIFOCTLSPI_TXPAUSE_MASK);
/* Disable the TX/RX FIFO for tansfer. */
switch (instance)
{
case 0:
SYSCON->FIFOCTRL &= ~(SYSCON_FIFOCTRL_SPI0TXFIFOEN_MASK | SYSCON_FIFOCTRL_SPI0RXFIFOEN_MASK);
break;
case 1:
SYSCON->FIFOCTRL &= ~(SYSCON_FIFOCTRL_SPI1TXFIFOEN_MASK | SYSCON_FIFOCTRL_SPI1RXFIFOEN_MASK);
break;
default:
break;
}
}
/* Check if TX FIFO enabled. */
/*!
* brief Is TX FIFO enabled.
*
* This function will return status if the transmit fifo is enabled. true for enabled and false for not enabled.
*
* param base SPI peripheral base address.
* return true for enabled and false for not enabled.
*/
bool SPI_IsTxFifoEnabled(SPI_Type *base)
{
uint32_t instance = SPI_GetInstance(base);
uint32_t fifosize = VFIFO->FIFOCFGSPI[instance];
bool status = false;
/* Check if system FIFO control TX is enabled. */
switch (instance)
{
case 0:
if (SYSCON->FIFOCTRL & SYSCON_FIFOCTRL_SPI0TXFIFOEN_MASK)
{
status = true;
}
break;
case 1:
if (SYSCON->FIFOCTRL & SYSCON_FIFOCTRL_SPI1TXFIFOEN_MASK)
{
status = true;
}
break;
default:
break;
}
if (status != true)
{
return status;
}
/* Check if TX FIFO size is available. */
return (fifosize & VFIFO_FIFOCFGSPI_TXSIZE_MASK) ? (true) : (false);
}
/* Check if RX FIFO enabled. */
/*!
* brief Is RX FIFO enabled.
*
* This function will return status if the receive fifo is enabled. true for enabled and false for not enabled.
*
* param base SPI peripheral base address.
* return true for enabled and false for not enabled.
*/
bool SPI_IsRxFifoEnabled(SPI_Type *base)
{
uint32_t instance = SPI_GetInstance(base);
uint32_t fifosize = VFIFO->FIFOCFGSPI[instance];
bool status = false;
/* Check if system FIFO control RX is enabled. */
switch (instance)
{
case 0:
if (SYSCON->FIFOCTRL & SYSCON_FIFOCTRL_SPI0RXFIFOEN_MASK)
{
status = true;
}
break;
case 1:
if (SYSCON->FIFOCTRL & SYSCON_FIFOCTRL_SPI1RXFIFOEN_MASK)
{
status = true;
}
break;
default:
break;
}
if (status != true)
{
return status;
}
/* Check if RX FIFO size is available. */
return (fifosize & VFIFO_FIFOCFGSPI_RXSIZE_MASK) ? (true) : (false);
}
static void SPI_SendTransfer(SPI_Type *base, spi_master_handle_t *handle)
{
uint32_t tmp32 = 0U;
uint32_t instance = SPI_GetInstance(base);
if (handle->isTxFifoEnabled)
{
/* Fill data to tx fifo buffer until the data in FIFO reach the TX threshold or no data to be sent. */
while ((VFIFO->SPI[instance].STATSPI & VFIFO_SPI_STATSPI_TXTH_MASK) && (handle->txRemainingBytes))
{
if (handle->txData)
{
tmp32 = *(handle->txData++);
handle->txRemainingBytes--;
if (handle->dataWidth > 7U)
{
tmp32 |= ((uint32_t)(*(handle->txData++)) << 8U);
handle->txRemainingBytes--;
}
}
else
{
tmp32 = s_dummyData[instance];
handle->txRemainingBytes--;
if (handle->dataWidth > 7U)
{
tmp32 |= ((uint32_t)s_dummyData[instance] << 8U);
handle->txRemainingBytes--;
}
}
/* If this transmit is the last to send, Set the control bits and disable Tx interrupt. */
if (handle->txRemainingBytes == 0U)
{
VFIFO->SPI[instance].TXDATSPI = (tmp32 | handle->lastCommand);
SPI_DisableFifoInterrupts(base, kSPI_TxFifoThresholdInterruptEnable);
}
else
{
VFIFO->SPI[instance].TXDATSPI = (tmp32 | handle->commonCommand);
}
}
}
else
{
/* If transmit is ready, write data once to TXDATCTL register. */
if ((kSPI_TxReadyFlag & SPI_GetStatusFlags(base)) && (handle->txRemainingBytes))
{
if (handle->txData)
{
tmp32 = *(handle->txData++);
handle->txRemainingBytes--;
if (handle->dataWidth > 7U)
{
tmp32 |= ((uint32_t)(*(handle->txData++)) << 8U);
handle->txRemainingBytes--;
}
}
else
{
tmp32 = ((uint32_t)s_dummyData[instance] << 8U) | s_dummyData[instance];
handle->txRemainingBytes--;
if (handle->dataWidth > 7U)
{
handle->txRemainingBytes--;
}
}
/* If this transmit is the last to send, Set the control bits and disable Tx interrupt. */
if (handle->txRemainingBytes == 0U)
{
base->TXDATCTL = (tmp32 | handle->lastCommand);
SPI_DisableInterrupts(base, kSPI_TxReadyInterruptEnable);
}
else
{
base->TXDATCTL = (tmp32 | handle->commonCommand);
}
}
}
}
static void SPI_ReceiveTransfer(SPI_Type *base, spi_master_handle_t *handle)
{
uint32_t tmp32 = 0U;
uint32_t instance = SPI_GetInstance(base);
if (handle->isRxFifoEnabled)
{
/* Read data from fifo buffer until the rx fifo is empty or to receive bytes is zero. */
while ((!(VFIFO->SPI[instance].STATSPI & VFIFO_SPI_STATSPI_RXEMPTY_MASK)) && (handle->rxRemainingBytes))
{
tmp32 = VFIFO->SPI[instance].RXDATSPI;
/* Check If receive buffer is NULL. */
if (handle->rxData)
{
*(handle->rxData++) = tmp32;
handle->rxRemainingBytes--;
if (handle->dataWidth > 7U)
{
*(handle->rxData++) = (tmp32 >> 8U);
handle->rxRemainingBytes--;
}
}
}
}
else
{
/* If receive is ready, read data from RXDAT register. */
if ((kSPI_RxReadyFlag & SPI_GetStatusFlags(base)) && (handle->rxRemainingBytes))
{
tmp32 = (base->RXDAT & 0x0000FFFFU);
/* Check If receive buffer is NULL. */
if (handle->rxData)
{
*(handle->rxData++) = tmp32;
handle->rxRemainingBytes--;
if (handle->dataWidth > 7U)
{
*(handle->rxData++) = (tmp32 >> 8U);
handle->rxRemainingBytes--;
}
}
}
}
}
/* Set delay time for SPI transfer. */
/*!
* brief Set delay time for transfer.
* the delay uint is SPI clock time, maximum value is 0xF.
* param base SPI base pointer
* param config configuration for delay option ref spi_delay_config_t.
*/
void SPI_SetTransferDelay(SPI_Type *base, const spi_delay_config_t *config)
{
assert(NULL != config);
/* Clear the DLY register. */
base->DLY &=
~(SPI_DLY_PRE_DELAY_MASK | SPI_DLY_POST_DELAY_MASK | SPI_DLY_FRAME_DELAY_MASK | SPI_DLY_TRANSFER_DELAY_MASK);
/* Set the delay configuration. */
base->DLY |= (SPI_DLY_PRE_DELAY(config->preDelay) | SPI_DLY_POST_DELAY(config->postDelay) |
SPI_DLY_FRAME_DELAY(config->frameDelay) | SPI_DLY_TRANSFER_DELAY(config->transferDelay));
}
/*!
* 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. */
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->sselPol = kSPI_SpolActiveAllLow;
config->fifoConfig.enableRxFifo = false;
config->fifoConfig.enableTxFifo = false;
config->fifoConfig.rxFifoSize = 0U;
config->fifoConfig.txFifoSize = 0U;
config->fifoConfig.rxFifoThreshold = 0U;
config->fifoConfig.txFifoThreshold = 1U;
config->delayConfig.frameDelay = 0U;
config->delayConfig.postDelay = 0U;
config->delayConfig.preDelay = 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 = 500000,
...
};
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)
{
int32_t result = 0, instance = 0;
uint32_t tmp = 0U;
/* assert params */
assert(!((NULL == base) || (NULL == config) || (0 == srcClock_Hz)));
/* Get instance number */
instance = SPI_GetInstance(base);
/* Config FIFOs if the system FIFO is enabled.
* Note: The FIFO should be enabled before the baudrate has been configured.
*/
if ((config->fifoConfig.enableRxFifo) || (config->fifoConfig.enableTxFifo))
{
SPI_EnableFifo(base, &(config->fifoConfig));
}
#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
/* Enable the clock. */
CLOCK_EnableClock(s_spiClock[instance]);
#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
#if !(defined(FSL_SDK_DISABLE_DRIVER_RESET_CONTROL) && FSL_SDK_DISABLE_DRIVER_RESET_CONTROL)
/* Reset the module. */
RESET_PeripheralReset(s_spiReset[instance]);
#endif /* FSL_SDK_DISABLE_DRIVER_RESET_CONTROL */
/* set divider */
result = SPI_MasterSetBaud(base, config->baudRate_Bps, srcClock_Hz);
if (kStatus_Success != result)
{
return result;
}
/* configure SPI mode */
tmp &= ~(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);
/* Setup configuration. */
tmp |= SPI_CFG_CPHA(config->phase) | SPI_CFG_CPOL(config->polarity) | SPI_CFG_LSBF(config->direction) |
SPI_CFG_MASTER(1) | SPI_CFG_LOOP(config->enableLoopback) | ((uint32_t)config->sselPol & (SPI_SSELPOL_MASK));
base->CFG = tmp;
/* Set delay configuration. */
SPI_SetTransferDelay(base, &(config->delayConfig));
/* store configuration */
g_configs[instance].dataWidth = config->dataWidth;
g_configs[instance].sselNum = config->sselNum;
SPI_SetDummyData(base, (uint8_t)SPI_DUMMYDATA);
/* Enable the SPI module. */
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. */
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->sselPol = kSPI_SpolActiveAllLow;
config->fifoConfig.enableRxFifo = false;
config->fifoConfig.enableTxFifo = false;
config->fifoConfig.rxFifoSize = 0U;
config->fifoConfig.txFifoSize = 0U;
config->fifoConfig.rxFifoThreshold = 0U;
config->fifoConfig.txFifoThreshold = 0U;
}
/*!
* 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 = kSPI_ClockPolarityActiveHigh;
.phase = kSPI_ClockPhaseFirstEdge;
.direction = kSPI_MsbFirst;
...
};
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)
{
int32_t instance = 0U;
uint32_t tmp = 0U;
/* assert params */
assert(!((NULL == base) || (NULL == config)));
/* Get the instance of SPI. */
instance = SPI_GetInstance(base);
/* Config FIFOs if the system FIFO is enabled. */
if ((config->fifoConfig.enableRxFifo) || (config->fifoConfig.enableTxFifo))
{
SPI_EnableFifo(base, &(config->fifoConfig));
}
#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
/* Enable the clock. */
CLOCK_EnableClock(s_spiClock[instance]);
#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
#if !(defined(FSL_SDK_DISABLE_DRIVER_RESET_CONTROL) && FSL_SDK_DISABLE_DRIVER_RESET_CONTROL)
/* Reset the module. */
RESET_PeripheralReset(s_spiReset[instance]);
#endif /* FSL_SDK_DISABLE_DRIVER_RESET_CONTROL */
/* configure SPI mode */
tmp &= ~(SPI_CFG_MASTER_MASK | SPI_CFG_LSBF_MASK | SPI_CFG_CPHA_MASK | SPI_CFG_CPOL_MASK | SPI_CFG_ENABLE_MASK |
SPI_SSELPOL_MASK);
/* Configure clock phase, clock polarity, shift direction and active level for SSEL pin. */
tmp |= SPI_CFG_CPHA(config->phase) | SPI_CFG_CPOL(config->polarity) | SPI_CFG_LSBF(config->direction) |
((uint32_t)config->sselPol & (SPI_SSELPOL_MASK));
/* Write configuration to register. */
base->CFG = tmp;
/* store configuration */
g_configs[instance].dataWidth = config->dataWidth;
SPI_SetDummyData(base, (uint8_t)SPI_DUMMYDATA);
/* Enable the SPI module. */
SPI_Enable(base, config->enableSlave);
return kStatus_Success;
}
/*!
* brief De-initializes the SPI.
*
* Calling this API resets the SPI module, gates the SPI clock.
* Disable the fifo if enabled.
* 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);
uint32_t instance = SPI_GetInstance(base);
/* Disable FIFO if enabled. */
if ((SPI_IsTxFifoEnabled(base)) || (SPI_IsTxFifoEnabled(base)))
{
SPI_DisableFifo(base);
}
/* Disable SPI module before shutting down the clock. */
SPI_Enable(base, false);
#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
/* Disable the clock. */
CLOCK_DisableClock(s_spiClock[instance]);
#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
}
/*!
* 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 tmp;
/* assert params */
assert(!((NULL == base) || (0 == baudrate_Bps) || (0 == srcClock_Hz)));
/* calculate baudrate */
tmp = (srcClock_Hz / baudrate_Bps) - 1;
if (tmp > 0xFFFF)
{
return kStatus_SPI_BaudrateNotSupport;
}
base->DIV &= ~SPI_DIV_DIVVAL_MASK;
base->DIV |= SPI_DIV_DIVVAL(tmp);
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 = 0;
int32_t instance;
/* check params */
assert(NULL != base);
/* get and check instance */
instance = SPI_GetInstance(base);
/* Set data width */
control |= SPI_TXDATCTL_LEN(g_configs[instance].dataWidth);
/* Set sssel */
control |= (SPI_DEASSERT_ALL & (~SPI_DEASSERT_SSELNUM(g_configs[instance].sselNum)));
/* Mask configFlags */
control |= (configFlags & (SPI_TXDATCTL_EOT_MASK | SPI_TXDATCTL_EOF_MASK | SPI_TXDATCTL_RXIGNORE_MASK));
/* Control should not affect lower 16 bits */
assert(!(control & 0xFFFF));
if (SPI_IsTxFifoEnabled(base))
{
VFIFO->SPI[instance].TXDATSPI = data | control;
}
else
{
base->TXDATCTL = (data | control);
}
}
/*!
* brief Gets a data from the SPI data register.
*
* param base SPI base pointer
* return Data in the register.
*/
uint32_t SPI_ReadData(SPI_Type *base)
{
assert(NULL != base);
uint32_t instance = SPI_GetInstance(base);
if (SPI_IsRxFifoEnabled(base))
{
return (VFIFO->SPI[instance].RXDATSPI);
}
else
{
return (base->RXDAT & 0x0000FFFFU);
}
}
/*!
* 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)
{
int32_t instance = 0;
/* check 'base' */
assert(!(NULL == base));
/* check 'handle' */
assert(!(NULL == handle));
/* Get SPI instance by 'base' param */
instance = SPI_GetInstance(base);
memset(handle, 0, sizeof(*handle));
handle->dataWidth = g_configs[instance].dataWidth;
/* in slave mode, the sselNum is not important */
handle->sselNum = g_configs[instance].sselNum;
handle->isRxFifoEnabled = SPI_IsRxFifoEnabled(base);
handle->isTxFifoEnabled = SPI_IsTxFifoEnabled(base);
if (handle->isRxFifoEnabled)
{
handle->rxFifoThreshold = SPI_FIFO_GETRXTHRESHOLD(base);
handle->rxFIFOSize =
(VFIFO->FIFOCFGSPI[instance] & VFIFO_FIFOCFGSPI_RXSIZE_MASK) >> VFIFO_FIFOCFGSPI_RXSIZE_SHIFT;
}
if (handle->isTxFifoEnabled)
{
handle->txFifoThreshold = SPI_FIFO_GETTXTHRESHOLD(base);
}
handle->callback = callback;
handle->userData = userData;
s_spiHandle[instance] = handle;
s_spiMasterIsr = SPI_MasterTransferHandleIRQ;
/* Enable SPI NVIC */
EnableIRQ(s_spiIRQ[instance]);
return kStatus_Success;
}
/*!
* brief Initializes the SPI slave handle.
*
* This function initializes the SPI slave handle which can be used for other SPI slave 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_SlaveTransferCreateHandle(SPI_Type *base,
spi_slave_handle_t *handle,
spi_slave_callback_t callback,
void *userData)
{
SPI_MasterTransferCreateHandle(base, handle, callback, userData);
s_spiSlaveIsr = SPI_SlaveTransferHandleIRQ;
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.
*/
status_t SPI_MasterTransferBlocking(SPI_Type *base, spi_transfer_t *xfer)
{
/* Check params */
assert(!((NULL == base) || (NULL == xfer) || ((NULL == xfer->txData) && (NULL == xfer->rxData))));
int32_t instance = SPI_GetInstance(base);
uint32_t tx_ctrl = 0, last_ctrl = 0;
uint32_t tmp32 = 0, dataWidth = 0, dummuData = 0;
uint32_t txRemainingBytes = 0, rxRemainingBytes = 0, rxFIFOSize = 0, txFIFOSize = 0;
/* Check if the FIFO is used. */
bool isTxFifoEnabled = SPI_IsTxFifoEnabled(base);
if ((xfer->rxData == NULL) || (xfer->configFlags & (uint32_t)SPI_TXDATCTL_RXIGNORE_MASK))
{
rxRemainingBytes = 0U;
last_ctrl |= SPI_TXDATCTL_RXIGNORE_MASK;
}
else
{
rxRemainingBytes = xfer->dataSize;
}
txRemainingBytes = xfer->dataSize;
rxFIFOSize = ((VFIFO->SPI->STATSPI & VFIFO_SPI_STATSPI_RXCOUNT_MASK) >> VFIFO_SPI_STATSPI_RXCOUNT_SHIFT);
txFIFOSize = ((VFIFO->SPI->STATSPI & VFIFO_SPI_STATSPI_TXCOUNT_MASK) >> VFIFO_SPI_STATSPI_TXCOUNT_SHIFT);
dataWidth = g_configs[instance].dataWidth;
dummuData = ((uint32_t)s_dummyData[instance] << 8U) | s_dummyData[instance];
/* Set the configuration of last command. */
last_ctrl |= (SPI_DEASSERT_ALL & (~SPI_DEASSERT_SSELNUM(g_configs[instance].sselNum))) |
SPI_TXDATCTL_LEN(dataWidth) | (xfer->configFlags);
/* Set the configuration of command, no need to set EOT bit. */
tx_ctrl = last_ctrl & (~SPI_TXDATCTL_EOT_MASK);
/* Due to the SPI is duplex transfer, to take full advantage of VFIFO, it is better
* to make the RX FIFO equals to TX FIFO, so only check tx FIFO here.
*/
if (isTxFifoEnabled)
{
/* Index of loop */
while (txRemainingBytes || rxRemainingBytes)
{
tmp32 = 0U;
/* If the FIFO is not full and still has data to be written. */
while ((VFIFO->SPI[instance].STATSPI & VFIFO_SPI_STATSPI_TXCOUNT_MASK) && txRemainingBytes)
{
if (xfer->txData)
{
tmp32 = *(xfer->txData++);
if (dataWidth > 7U)
{
tmp32 |= ((uint32_t)(*(xfer->txData++))) << 8U;
}
}
else
{
tmp32 = dummuData;
}
/* Count TX remaining bytes. */
txRemainingBytes--;
if (dataWidth > 7U)
{
txRemainingBytes--;
}
/* Write data to TX register. */
if (txRemainingBytes == 0U)
{
VFIFO->SPI[instance].TXDATSPI = (tmp32 | last_ctrl);
}
else
{
VFIFO->SPI[instance].TXDATSPI = (tmp32 | tx_ctrl);
}
/* Ensure the receive FIFO will not overflow. */
if (rxRemainingBytes - txRemainingBytes == rxFIFOSize)
{
break;
}
}
/* Read data from the receive register. */
while ((VFIFO->SPI[instance].STATSPI & VFIFO_SPI_STATSPI_RXCOUNT_MASK) && rxRemainingBytes)
{
tmp32 = VFIFO->SPI[instance].RXDATSPI;
/* If receive buffer is NULL. */
if (xfer->rxData)
{
*(xfer->rxData++) = tmp32;
rxRemainingBytes--;
if (dataWidth > 7U)
{
*(xfer->rxData++) = (tmp32 >> 8U);
rxRemainingBytes--;
}
}
}
}
/* Waiting for the data in FIFO is all sent out. */
while (!(txFIFOSize ==
((VFIFO->SPI->STATSPI & VFIFO_SPI_STATSPI_TXCOUNT_MASK) >> VFIFO_SPI_STATSPI_TXCOUNT_SHIFT)))
{
}
}
else
{
/* Write the TX control command first. */
base->TXCTL = tx_ctrl;
/* Index of loop */
while (txRemainingBytes)
{
tmp32 = 0U;
/* Wait for Transmit is ready. */
while (!(base->STAT & SPI_STAT_TXRDY_MASK))
{
}
/* If txdata is not NULL. */
if (xfer->txData)
{
tmp32 = *(xfer->txData++);
if (dataWidth > 7U)
{
tmp32 |= ((uint32_t)(*(xfer->txData++))) << 8U;
}
}
else
{
tmp32 = dummuData;
}
/* Count the TX remaining bytes. */
txRemainingBytes--;
if (dataWidth > 7U)
{
txRemainingBytes--;
}
/* Write data to the register. */
if (txRemainingBytes == 0U)
{
base->TXDATCTL = (tmp32 | last_ctrl);
}
else
{
base->TXDAT = tmp32;
}
/* If the receive ingore feature is not used, read the data from register. */
if (!(tx_ctrl & SPI_TXDATCTL_RXIGNORE_MASK))
{
/* If RX register is ready. */
while (!(base->STAT & SPI_STAT_RXRDY_MASK))
{
}
tmp32 = base->RXDAT;
/* If receive buffer is not NULL. */
if (xfer->rxData)
{
*(xfer->rxData++) = tmp32;
if (dataWidth > 7U)
{
*(xfer->rxData++) = (tmp32 >> 8U);
}
}
}
}
}
/* Note that: the MSTIDLE status is related to the EOT bit, if the EOT is not set, the MSTIDLE bit will never be set
* even though there is no data in the FIFO and no data will be shifted by the bus line. so, please don't check the
* MSTIDLE status if the EOT bit is not set.
*/
if (xfer->configFlags & kSPI_FrameAssert)
{
while (!(base->STAT & SPI_STAT_MSTIDLE_MASK))
{
}
}
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))));
uint32_t instance = SPI_GetInstance(base);
/* dataSize (in bytes) is not aligned to 16bit (2B) transfer */
if ((handle->dataWidth > kSPI_Data8Bits) && (xfer->dataSize & 0x1))
{
return kStatus_InvalidArgument;
}
/* Check if SPI is busy */
if (handle->state == kStatus_SPI_Busy)
{
return kStatus_SPI_Busy;
}
/* Clear commom command and last command. */
handle->commonCommand = handle->lastCommand = 0U;
/* Set the handle information */
handle->txData = xfer->txData;
handle->rxData = xfer->rxData;
/* Set count */
handle->txRemainingBytes = xfer->dataSize;
/* If the receive data buffer is NULL or receive ignore feature is selected. */
if ((xfer->rxData == NULL) || (xfer->configFlags & SPI_TXDATCTL_RXIGNORE_MASK))
{
handle->rxRemainingBytes = 0U;
handle->lastCommand |= SPI_TXDATCTL_RXIGNORE_MASK;
}
else
{
handle->rxRemainingBytes = xfer->dataSize;
}
handle->totalByteCount = xfer->dataSize;
/* Set the last command according to the configuration. */
handle->lastCommand |= (SPI_DEASSERT_ALL & (~SPI_DEASSERT_SSELNUM(g_configs[instance].sselNum))) |
SPI_TXDATCTL_LEN(handle->dataWidth) | xfer->configFlags;
/* Common command no need to set the EOT bit. */
handle->commonCommand = (handle->lastCommand & ~SPI_TXDATCTL_EOT_MASK);
/* Set the SPI state to busy */
handle->state = kStatus_SPI_Busy;
/* FIFO options. */
if (SPI_IsTxFifoEnabled(base))
{
SPI_EnableFifoInterrupts(base, kSPI_TxFifoThresholdInterruptEnable);
}
else
{
/* Enable generating txIRQ, first transfer is fired by TX ready interrupt. */
SPI_EnableInterrupts(base, kSPI_TxReadyInterruptEnable);
}
if (SPI_IsRxFifoEnabled(base))
{
SPI_EnableFifoInterrupts(base, kSPI_RxFifoThresholdInterruptEnable);
}
else
{
/* Enable generating rxIRQ. */
SPI_EnableInterrupts(base, kSPI_RxReadyInterruptEnable);
}
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 will be 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);
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) | 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 srcond 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);
assert(handle);
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) | 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 Performs a non-blocking SPI slave interrupt transfer.
*
* note The API returns immediately after the transfer initialization is finished.
*
* 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_SlaveTransferNonBlocking(SPI_Type *base, spi_slave_handle_t *handle, spi_transfer_t *xfer)
{
status_t status = 0U;
status = SPI_MasterTransferNonBlocking(base, handle, xfer);
if (kStatus_Success != status)
{
return status;
}
s_spiSlaveIsr = SPI_SlaveTransferHandleIRQ;
return kStatus_Success;
}
/*!
* 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 (!count)
{
return kStatus_InvalidArgument;
}
/* Catch when there is not an active transfer. */
if (handle->state != 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*/
if (SPI_IsTxFifoEnabled(base))
{
SPI_DisableFifoInterrupts(base, kSPI_TxFifoThresholdInterruptEnable);
}
else
{
SPI_DisableInterrupts(base, kSPI_TxReadyInterruptEnable);
}
if (SPI_IsRxFifoEnabled(base))
{
SPI_DisableFifoInterrupts(base, kSPI_RxFifoThresholdInterruptEnable);
}
else
{
SPI_DisableInterrupts(base, kSPI_RxReadyInterruptEnable);
}
handle->state = kStatus_SPI_Idle;
handle->txRemainingBytes = 0;
handle->rxRemainingBytes = 0;
}
/*!
* 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));
uint32_t instance = SPI_GetInstance(base);
/* IRQ behaviour:
* - first interrupt is triggered by transmit ready interrupt or tx threshold reached if
* FIFO is enabled. The transfer function then tries read data and transmit data
* interleaved that results to strategy to process as many items as possible.
* - last interrupt is triggered by receive data ready. 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.
* - Note(case with the fifo used): Because the SPI0 and SPI1 share the same tx buffer and
* rx buffer, So the threshold interrupt may occurred but the data in the buffer
* for SPIx may not reach the threshold separately if SPI0 and SPI1 are receiving
* data at the same time. The similar issue may happen when SPI0 and SPI1 are sending
* data at the same time with the FIFO used.
*/
/* Data to send or read or expected to receive */
if (handle->rxRemainingBytes)
{
SPI_ReceiveTransfer(base, handle);
}
if (handle->txRemainingBytes)
{
SPI_SendTransfer(base, handle);
}
if ((0U == handle->txRemainingBytes) && (0U == handle->rxRemainingBytes))
{
if (SPI_IsRxFifoEnabled(base))
{
SPI_EnableFifoInterrupts(base, kSPI_RxFifoThresholdInterruptEnable);
}
else
{
/* Disable TX and RX interrupt. */
SPI_DisableInterrupts(base, kSPI_RxReadyInterruptEnable);
}
/* Set transfer state to idle. */
handle->state = kStatus_SPI_Idle;
/* If callback is not NULL, call this function. */
if (handle->callback)
{
(handle->callback)(base, handle, handle->state, handle->userData);
}
}
/* Check when the last receive, if the rx threshold can be triggered with the fifo enabled. */
if (SPI_IsRxFifoEnabled(base))
{
/* If the bytes to receive is not larger then the rx threshold, the interrupt will not be triggerred.
* So need to reload the rx threshold value to trigger the last receive.
*/
if ((handle->rxRemainingBytes != 0U) && (handle->rxRemainingBytes <= handle->rxFifoThreshold))
{
VFIFO->SPI[instance].CFGSPI = ((VFIFO->SPI[instance].CFGSPI & ~VFIFO_SPI_CFGSPI_RXTHRESHOLD_MASK) |
VFIFO_SPI_CFGSPI_RXTHRESHOLD(handle->rxRemainingBytes - 1));
}
/* If the rx threshold value in the register is not the default value in handle. Reload it to default value. */
if ((handle->rxRemainingBytes == 0U) && (SPI_FIFO_GETRXTHRESHOLD(base) != handle->rxFifoThreshold))
{
VFIFO->SPI[instance].CFGSPI = ((VFIFO->SPI[instance].CFGSPI & ~VFIFO_SPI_CFGSPI_RXTHRESHOLD_MASK) |
VFIFO_SPI_CFGSPI_RXTHRESHOLD(handle->rxFifoThreshold));
}
}
}
/*!
* brief Interrupts a handler for the SPI slave.
*
* param base SPI peripheral base address.
* param handle pointer to spi_slave_handle_t structure which stores the transfer state
*/
void SPI_SlaveTransferHandleIRQ(SPI_Type *base, spi_slave_handle_t *handle)
{
assert((NULL != base) && (NULL != handle));
uint32_t instance = SPI_GetInstance(base);
/* IRQ behaviour:
* - first interrupt is triggered by transmit ready interrupt or tx threshold reached if
* FIFO is enabled. The transfer function then tries read data and transmit data
* interleaved that results to strategy to process as many items as possible.
* - last interrupt is triggered by receive data ready. 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.
* - Note(case with the fifo used): Because the SPI0 and SPI1 share the same tx buffer and
* rx buffer, So the threshold interrupt may occurred but the data in the buffer
* for SPIx may not reach the threshold separately if SPI0 and SPI1 are receiving
* data at the same time. The similar issue may happen when SPI0 and SPI1 are sending
* data at the same time with the FIFO used.
*/
/* Data to send or read or expected to receive */
if (handle->rxRemainingBytes)
{
SPI_ReceiveTransfer(base, handle);
}
if (handle->txRemainingBytes)
{
SPI_SendTransfer(base, handle);
}
if ((0U == handle->txRemainingBytes) && (0U == handle->rxRemainingBytes))
{
if (SPI_IsRxFifoEnabled(base))
{
SPI_EnableFifoInterrupts(base, kSPI_RxFifoThresholdInterruptEnable);
}
else
{
/* Disable TX and RX interrupt. */
SPI_DisableInterrupts(base, kSPI_RxReadyInterruptEnable);
}
/* Set transfer state to idle. */
handle->state = kStatus_SPI_Idle;
/* If callback is not NULL, call this function. */
if (handle->callback)
{
(handle->callback)(base, handle, handle->state, handle->userData);
}
}
/* Check when the last receive, if the rx threshold can be triggered with the fifo enabled. */
if (SPI_IsRxFifoEnabled(base))
{
/* If the bytes to receive is not larger then the rx threshold, the interrupt will not be triggerred.
* So need to reload the rx threshold value to trigger the last receive.
*/
if ((handle->rxRemainingBytes != 0U) && (handle->rxRemainingBytes <= handle->rxFifoThreshold))
{
VFIFO->SPI[instance].CFGSPI = ((VFIFO->SPI[instance].CFGSPI & ~VFIFO_SPI_CFGSPI_RXTHRESHOLD_MASK) |
VFIFO_SPI_CFGSPI_RXTHRESHOLD(handle->rxRemainingBytes - 1));
}
/* If the rx threshold value in the register is not the default value in handle. Reload it to default value. */
if ((handle->rxRemainingBytes == 0U) && (SPI_FIFO_GETRXTHRESHOLD(base) != handle->rxFifoThreshold))
{
VFIFO->SPI[instance].CFGSPI = ((VFIFO->SPI[instance].CFGSPI & ~VFIFO_SPI_CFGSPI_RXTHRESHOLD_MASK) |
VFIFO_SPI_CFGSPI_RXTHRESHOLD(handle->rxFifoThreshold));
}
}
}
static void SPI_CommonIRQHandler(SPI_Type *base, void *param)
{
if (SPI_IsMaster(base))
{
s_spiMasterIsr(base, (spi_master_handle_t *)param);
}
else
{
s_spiSlaveIsr(base, (spi_slave_handle_t *)param);
}
/* Add for ARM errata 838869, affects Cortex-M4, Cortex-M4F Store immediate overlapping
exception return operation might vector to incorrect interrupt */
#if defined __CORTEX_M && (__CORTEX_M == 4U)
__DSB();
#endif
}
#if defined(SPI0)
void SPI0_DriverIRQHandler(void)
{
assert(s_spiHandle[0]);
SPI_CommonIRQHandler(SPI0, s_spiHandle[0]);
}
#endif
#if defined(SPI1)
void SPI1_DriverIRQHandler(void)
{
assert(s_spiHandle[1]);
SPI_CommonIRQHandler(SPI1, s_spiHandle[1]);
}
#endif