1617 lines
54 KiB
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
|