954 lines
32 KiB
C
954 lines
32 KiB
C
/*
|
|
* Copyright 2017-2019, 2020 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.lpc_minispi"
|
|
#endif
|
|
|
|
/*******************************************************************************
|
|
* Variables
|
|
******************************************************************************/
|
|
#if defined(FSL_SDK_ENABLE_SPI_DRIVER_TRANSACTIONAL_APIS) && (FSL_SDK_ENABLE_SPI_DRIVER_TRANSACTIONAL_APIS)
|
|
/*! @brief SPI internal handle pointer array */
|
|
static spi_master_handle_t *s_spiHandle[FSL_FEATURE_SOC_SPI_COUNT];
|
|
|
|
/*! @brief IRQ name array */
|
|
static const IRQn_Type s_spiIRQ[] = SPI_IRQS;
|
|
|
|
/*! @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;
|
|
#endif /* FSL_SDK_ENABLE_SPI_DRIVER_TRANSACTIONAL_APIS */
|
|
|
|
#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[] = SPI_RSTS_N;
|
|
#endif /* FSL_SDK_DISABLE_DRIVER_RESET_CONTROL */
|
|
|
|
/*! @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 Dummy data for each instance. This data is used when user's tx buffer is NULL*/
|
|
volatile uint16_t s_dummyData[FSL_FEATURE_SOC_SPI_COUNT] = {0};
|
|
|
|
/*******************************************************************************
|
|
* Code
|
|
******************************************************************************/
|
|
|
|
#if defined(FSL_SDK_ENABLE_SPI_DRIVER_TRANSACTIONAL_APIS) && (FSL_SDK_ENABLE_SPI_DRIVER_TRANSACTIONAL_APIS)
|
|
/*!
|
|
* @brief Send a piece of data for SPI.
|
|
*
|
|
* This function will check if TXDAT register ready, and write the data into it.
|
|
* At the same time, this function updates the values in master handle structure.
|
|
*
|
|
* @param base SPI base pointer
|
|
* @param handle Pointer to SPI master handle structure.
|
|
*/
|
|
static void SPI_SendTransfer(SPI_Type *base, spi_master_handle_t *handle)
|
|
{
|
|
uint32_t tmp32 = 0U;
|
|
uint32_t instance = SPI_GetInstance(base);
|
|
|
|
/* If transmit is ready, write data to TXDAT register. */
|
|
if (((uint32_t)kSPI_TxReadyFlag & SPI_GetStatusFlags(base)) != 0U)
|
|
{
|
|
if ((handle->txData) != NULL)
|
|
{
|
|
tmp32 = *(handle->txData++);
|
|
handle->txRemainingBytes--;
|
|
if (handle->dataWidth > (uint8_t)kSPI_Data8Bits)
|
|
{
|
|
tmp32 |= ((uint32_t)(*(handle->txData++)) << 8U);
|
|
handle->txRemainingBytes--;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
tmp32 = (uint32_t)s_dummyData[instance];
|
|
handle->txRemainingBytes--;
|
|
if (handle->dataWidth > (uint8_t)kSPI_Data8Bits)
|
|
{
|
|
handle->txRemainingBytes--;
|
|
}
|
|
}
|
|
/* If this transmit is the last to send, Set the control bits. */
|
|
if (handle->txRemainingBytes == 0U)
|
|
{
|
|
base->TXCTL = handle->lastCommand;
|
|
}
|
|
|
|
base->TXDAT = tmp32;
|
|
}
|
|
}
|
|
|
|
/*!
|
|
* @brief Receive a piece of data for SPI.
|
|
*
|
|
* This function will check if RX register is ready, and write the data to destination address.
|
|
* At the same time, this function updates the values in master handle structure.
|
|
*
|
|
* @param base SPI base pointer
|
|
* @param handle Pointer to SPI master handle structure.
|
|
*/
|
|
static void SPI_ReceiveTransfer(SPI_Type *base, spi_master_handle_t *handle)
|
|
{
|
|
uint32_t tmp32 = 0U;
|
|
|
|
/* If receive is ready, read data from RXDAT register. */
|
|
if (((uint32_t)kSPI_RxReadyFlag & SPI_GetStatusFlags(base)) != 0U)
|
|
{
|
|
tmp32 = SPI_ReadData(base);
|
|
/* Check If receive buffer is NULL. */
|
|
if ((handle->rxData) != NULL)
|
|
{
|
|
*(handle->rxData++) = (uint8_t)tmp32;
|
|
handle->rxRemainingBytes--;
|
|
if (handle->dataWidth > (uint8_t)kSPI_Data8Bits)
|
|
{
|
|
*(handle->rxData++) = (uint8_t)(tmp32 >> 8U);
|
|
handle->rxRemainingBytes--;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif /* FSL_SDK_ENABLE_SPI_DRIVER_TRANSACTIONAL_APIS */
|
|
|
|
/*!
|
|
* @brief Get the instance for SPI module.
|
|
*
|
|
* @param base SPI base address
|
|
*/
|
|
/*! brief Returns instance number for SPI peripheral base address. */
|
|
uint32_t SPI_GetInstance(SPI_Type *base)
|
|
{
|
|
assert(NULL != base);
|
|
|
|
uint32_t i = 0U;
|
|
|
|
for (i = 0; i < (uint32_t)FSL_FEATURE_SOC_SPI_COUNT; i++)
|
|
{
|
|
if ((uint32_t)base == s_spiBaseAddrs[i])
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
assert(i < (uint32_t)FSL_FEATURE_SOC_SPI_COUNT);
|
|
return i;
|
|
}
|
|
|
|
/*!
|
|
* brief Set up the dummy data. This API can change the default data to be transferred
|
|
* when users set the tx buffer to NULL.
|
|
*
|
|
* param base SPI peripheral address.
|
|
* param dummyData Data to be transferred when tx buffer is NULL.
|
|
*/
|
|
void SPI_SetDummyData(SPI_Type *base, uint16_t dummyData)
|
|
{
|
|
uint32_t instance = SPI_GetInstance(base);
|
|
s_dummyData[instance] = dummyData;
|
|
}
|
|
|
|
/* 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);
|
|
/* 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. */
|
|
(void)memset(config, 0, sizeof(*config));
|
|
|
|
config->enableLoopback = false;
|
|
config->enableMaster = true;
|
|
config->clockPolarity = kSPI_ClockPolarityActiveHigh;
|
|
config->clockPhase = kSPI_ClockPhaseFirstEdge;
|
|
config->direction = kSPI_MsbFirst;
|
|
config->baudRate_Bps = 500000U;
|
|
config->dataWidth = (uint8_t)kSPI_Data8Bits;
|
|
config->sselNumber = kSPI_Ssel0Assert;
|
|
config->sselPolarity = kSPI_SpolActiveAllLow;
|
|
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)
|
|
{
|
|
uint32_t instance = 0U;
|
|
status_t result = 0;
|
|
uint32_t tmp = 0U;
|
|
|
|
/* assert params */
|
|
assert(!((NULL == base) || (NULL == config) || (0U == srcClock_Hz)));
|
|
|
|
/* Get instance number */
|
|
instance = SPI_GetInstance(base);
|
|
|
|
#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_MasterSetBaudRate(base, config->baudRate_Bps, srcClock_Hz);
|
|
if (kStatus_Success != result)
|
|
{
|
|
return result;
|
|
}
|
|
/* Set CFG register to configure phase/polarity/direction/master mode/loopback/ssel pin select. */
|
|
tmp |= (SPI_CFG_CPHA(config->clockPhase) | SPI_CFG_CPOL(config->clockPolarity) | SPI_CFG_LSBF(config->direction) |
|
|
SPI_CFG_MASTER(1) | SPI_CFG_LOOP(config->enableLoopback) |
|
|
((uint32_t)config->sselPolarity & (uint32_t)kSPI_SpolActiveAllHigh));
|
|
base->CFG = tmp;
|
|
|
|
/* Set delay configuration. */
|
|
SPI_SetTransferDelay(base, &(config->delayConfig));
|
|
|
|
/* Set dummy data. */
|
|
SPI_SetDummyData(base, (uint8_t)SPI_DUMMYDATA);
|
|
|
|
/* Set TXCTL register. */
|
|
base->TXCTL |= (SPI_TXCTL_LEN(config->dataWidth) | ((uint32_t)config->sselNumber & (uint32_t)kSPI_SselDeAssertAll));
|
|
|
|
/* 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. */
|
|
(void)memset(config, 0, sizeof(*config));
|
|
|
|
config->enableSlave = true;
|
|
config->clockPolarity = kSPI_ClockPolarityActiveHigh;
|
|
config->clockPhase = kSPI_ClockPhaseFirstEdge;
|
|
config->direction = kSPI_MsbFirst;
|
|
config->dataWidth = (uint8_t)kSPI_Data8Bits;
|
|
config->sselPolarity = kSPI_SpolActiveAllLow;
|
|
}
|
|
|
|
/*!
|
|
* brief Initializes the SPI with slave configuration.
|
|
*
|
|
* The configuration structure can be filled by user from scratch or be set with
|
|
* default values by SPI_SlaveGetDefaultConfig().
|
|
* After calling this API, the slave is ready to transfer.
|
|
* Example
|
|
code
|
|
spi_slave_config_t config = {
|
|
.polarity = 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)
|
|
{
|
|
uint32_t instance = 0U;
|
|
uint32_t tmp = 0U;
|
|
|
|
/* assert params */
|
|
assert(!((NULL == base) || (NULL == config)));
|
|
/* Get the instance of SPI. */
|
|
instance = SPI_GetInstance(base);
|
|
|
|
#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 confiuration for phase/polarity/direction/active level for SSEL pins. */
|
|
tmp |= SPI_CFG_CPHA(config->clockPhase) | SPI_CFG_CPOL(config->clockPolarity) | SPI_CFG_LSBF(config->direction) |
|
|
((uint32_t)config->sselPolarity & (uint32_t)kSPI_SpolActiveAllHigh);
|
|
base->CFG = tmp;
|
|
|
|
/* Set dummy data. */
|
|
SPI_SetDummyData(base, (uint8_t)SPI_DUMMYDATA);
|
|
|
|
/* Set TXCTL register. */
|
|
base->TXCTL |= SPI_TXCTL_LEN(config->dataWidth);
|
|
|
|
/* 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 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_MasterSetBaudRate(SPI_Type *base, uint32_t baudrate_Bps, uint32_t srcClock_Hz)
|
|
{
|
|
uint32_t tmp;
|
|
|
|
/* assert params */
|
|
assert(!((NULL == base) || (0U == baudrate_Bps) || (0U == srcClock_Hz)));
|
|
|
|
/* calculate baudrate */
|
|
tmp = (srcClock_Hz / baudrate_Bps) - 1U;
|
|
if (tmp > 0xFFFFU)
|
|
{
|
|
return kStatus_SPI_BaudrateNotSupport;
|
|
}
|
|
base->DIV &= ~SPI_DIV_DIVVAL_MASK;
|
|
base->DIV |= SPI_DIV_DIVVAL(tmp);
|
|
return kStatus_Success;
|
|
}
|
|
|
|
/*!
|
|
* brief Writes a data control info and data into the SPI TX register directly.
|
|
*
|
|
* param base SPI base pointer
|
|
* param value needs to be write.
|
|
*/
|
|
void SPI_WriteDataWithConfigFlags(SPI_Type *base, uint16_t data, uint32_t configFlags)
|
|
{
|
|
uint32_t control = 0;
|
|
/* check params */
|
|
assert(NULL != base);
|
|
/* Read origin command from TXCTL register. */
|
|
control = base->TXCTL & (~(SPI_TXDATCTL_EOT_MASK | SPI_TXDATCTL_EOF_MASK | SPI_TXDATCTL_RXIGNORE_MASK));
|
|
/* Mask configFlags */
|
|
control |= (configFlags & (SPI_TXDATCTL_EOT_MASK | SPI_TXDATCTL_EOF_MASK | SPI_TXDATCTL_RXIGNORE_MASK));
|
|
/* Write data and command to register. */
|
|
base->TXDATCTL = (data | control);
|
|
}
|
|
|
|
/*!
|
|
* brief Transfers a block of data using a polling method.
|
|
*
|
|
* param base SPI base pointer
|
|
* param xfer pointer to spi_xfer_config_t structure
|
|
* retval kStatus_Success Successfully start a transfer.
|
|
* retval kStatus_InvalidArgument Input argument is invalid.
|
|
* retval kStatus_SPI_Timeout The transfer timed out and was aborted.
|
|
*/
|
|
status_t SPI_MasterTransferBlocking(SPI_Type *base, spi_transfer_t *xfer)
|
|
{
|
|
uint32_t tx_ctrl = 0, last_ctrl = 0;
|
|
uint32_t tmp32, remainingBytes, dataWidth;
|
|
uint32_t instance = SPI_GetInstance(base);
|
|
#if SPI_RETRY_TIMES
|
|
uint32_t waitTimes;
|
|
#endif
|
|
/* Check params */
|
|
assert(!((NULL == base) || (NULL == xfer) || ((NULL == xfer->txData) && (NULL == xfer->rxData))));
|
|
|
|
remainingBytes = xfer->dataSize;
|
|
/* Read datawidth and ssel info from TXCTL. */
|
|
tx_ctrl = base->TXCTL & (SPI_TXCTL_LEN_MASK | SPI_TXCTL_RXIGNORE_MASK | SPI_TXCTL_EOF_MASK | SPI_TXCTL_EOT_MASK |
|
|
(uint32_t)kSPI_SselDeAssertAll);
|
|
dataWidth = ((tx_ctrl & SPI_TXCTL_LEN_MASK) >> SPI_TXCTL_LEN_SHIFT);
|
|
|
|
/* Set end of frame configuration. */
|
|
tx_ctrl |= ((xfer->configFlags & (uint32_t)kSPI_EndOfFrame) != 0U) ? (uint32_t)kSPI_EndOfFrame : 0U;
|
|
/* Set ignore configuration. */
|
|
tx_ctrl |= (xfer->configFlags & (uint32_t)kSPI_ReceiveIgnore);
|
|
|
|
/* If rxData is NULL, ignore the receive. */
|
|
if (NULL == xfer->rxData)
|
|
{
|
|
tx_ctrl |= (uint32_t)kSPI_ReceiveIgnore;
|
|
}
|
|
|
|
/* Setup last command for transfer. */
|
|
last_ctrl = tx_ctrl;
|
|
/* Set end of transfer configuration for last command. */
|
|
last_ctrl |= ((xfer->configFlags & (uint32_t)kSPI_EndOfTransfer) != 0U) ? (uint32_t)kSPI_EndOfTransfer : 0U;
|
|
|
|
/* If only on frame to be sent, set the command to last command. */
|
|
if (((remainingBytes == 1U) && (dataWidth < (uint32_t)kSPI_Data9Bits)) ||
|
|
((remainingBytes == 2U) && (dataWidth >= (uint32_t)kSPI_Data9Bits)))
|
|
{
|
|
SPI_WriteConfigFlags(base, last_ctrl);
|
|
}
|
|
else
|
|
{
|
|
SPI_WriteConfigFlags(base, tx_ctrl);
|
|
}
|
|
|
|
/* Index of loop */
|
|
while (remainingBytes != 0U)
|
|
{
|
|
tmp32 = 0U;
|
|
#if SPI_RETRY_TIMES
|
|
waitTimes = SPI_RETRY_TIMES;
|
|
while (((base->STAT & SPI_STAT_TXRDY_MASK) == 0U) && (--waitTimes != 0U))
|
|
#else
|
|
while ((base->STAT & SPI_STAT_TXRDY_MASK) == 0U)
|
|
#endif
|
|
{
|
|
}
|
|
#if SPI_RETRY_TIMES
|
|
if (waitTimes == 0U)
|
|
{
|
|
return kStatus_SPI_Timeout;
|
|
}
|
|
#endif
|
|
|
|
/* If txdata is not NULL. */
|
|
if (xfer->txData != NULL)
|
|
{
|
|
tmp32 = *(xfer->txData++);
|
|
if (dataWidth > (uint32_t)kSPI_Data8Bits)
|
|
{
|
|
tmp32 |= ((uint32_t)(*(xfer->txData++))) << 8U;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
tmp32 = (uint32_t)s_dummyData[instance];
|
|
}
|
|
if ((dataWidth > (uint32_t)kSPI_Data8Bits) ? (remainingBytes == 2U) : (remainingBytes == 1U))
|
|
{
|
|
base->TXDATCTL = (tmp32 | last_ctrl);
|
|
}
|
|
else
|
|
{
|
|
/* Write data to the Transmit register. */
|
|
base->TXDAT = tmp32;
|
|
}
|
|
/* If the RX ignore bits is not set. */
|
|
if ((xfer->configFlags & (uint32_t)kSPI_ReceiveIgnore) == 0U)
|
|
{
|
|
/* Read data from the receive register. */
|
|
#if SPI_RETRY_TIMES
|
|
waitTimes = SPI_RETRY_TIMES;
|
|
while (((base->STAT & SPI_STAT_RXRDY_MASK) == 0U) && (--waitTimes != 0U))
|
|
#else
|
|
while ((base->STAT & SPI_STAT_RXRDY_MASK) == 0U)
|
|
#endif
|
|
{
|
|
}
|
|
#if SPI_RETRY_TIMES
|
|
if (waitTimes == 0U)
|
|
{
|
|
return kStatus_SPI_Timeout;
|
|
}
|
|
#endif
|
|
tmp32 = base->RXDAT;
|
|
|
|
/* If receive buffer is not NULL. */
|
|
if (xfer->rxData != NULL)
|
|
{
|
|
*(xfer->rxData++) = (uint8_t)tmp32;
|
|
if (dataWidth > (uint32_t)kSPI_Data8Bits)
|
|
{
|
|
*(xfer->rxData++) = (uint8_t)(tmp32 >> 8U);
|
|
}
|
|
}
|
|
}
|
|
remainingBytes--;
|
|
if (dataWidth > (uint32_t)kSPI_Data8Bits)
|
|
{
|
|
remainingBytes--;
|
|
}
|
|
}
|
|
|
|
/* 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 & (uint32_t)kSPI_EndOfTransfer) != 0U)
|
|
{
|
|
#if SPI_RETRY_TIMES
|
|
waitTimes = SPI_RETRY_TIMES;
|
|
while (((base->STAT & SPI_STAT_MSTIDLE_MASK) == 0U) && (--waitTimes != 0U))
|
|
#else
|
|
while ((base->STAT & SPI_STAT_MSTIDLE_MASK) == 0U)
|
|
#endif
|
|
{
|
|
}
|
|
#if SPI_RETRY_TIMES
|
|
if (waitTimes == 0U)
|
|
{
|
|
return kStatus_SPI_Timeout;
|
|
}
|
|
#endif
|
|
}
|
|
return kStatus_Success;
|
|
}
|
|
|
|
#if defined(FSL_SDK_ENABLE_SPI_DRIVER_TRANSACTIONAL_APIS) && (FSL_SDK_ENABLE_SPI_DRIVER_TRANSACTIONAL_APIS)
|
|
/*!
|
|
* brief Initializes the SPI master handle.
|
|
*
|
|
* This function initializes the SPI master handle which can be used for other SPI master transactional APIs. Usually,
|
|
* for a specified SPI instance, call this API once to get the initialized handle.
|
|
*
|
|
* param base SPI peripheral base address.
|
|
* param handle SPI handle pointer.
|
|
* param callback Callback function.
|
|
* param userData User data.
|
|
*/
|
|
status_t SPI_MasterTransferCreateHandle(SPI_Type *base,
|
|
spi_master_handle_t *handle,
|
|
spi_master_callback_t callback,
|
|
void *userData)
|
|
{
|
|
/* check 'base' and 'handle'. */
|
|
assert((NULL != base) || (NULL != handle));
|
|
|
|
/* Get SPI instance by 'base' param */
|
|
uint32_t instance = SPI_GetInstance(base);
|
|
|
|
(void)memset(handle, 0, sizeof(*handle));
|
|
handle->dataWidth = (uint8_t)((base->TXCTL & SPI_TXCTL_LEN_MASK) >> SPI_TXCTL_LEN_SHIFT);
|
|
handle->callback = callback;
|
|
handle->userData = userData;
|
|
s_spiHandle[instance] = handle;
|
|
s_spiMasterIsr = SPI_MasterTransferHandleIRQ;
|
|
/* Enable SPI NVIC */
|
|
(void)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)
|
|
{
|
|
(void)SPI_MasterTransferCreateHandle(base, handle, callback, userData);
|
|
s_spiSlaveIsr = SPI_SlaveTransferHandleIRQ;
|
|
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))));
|
|
uint16_t temp = 0U;
|
|
uint32_t instance;
|
|
|
|
/* dataSize (in bytes) is not aligned to 16bit (2B) transfer */
|
|
if ((handle->dataWidth > (uint8_t)kSPI_Data8Bits) && ((xfer->dataSize & 0x1U) != 0U))
|
|
{
|
|
return kStatus_InvalidArgument;
|
|
}
|
|
|
|
/* Check if SPI is busy */
|
|
if (handle->state == (uint32_t)kStatus_SPI_Busy)
|
|
{
|
|
return kStatus_SPI_Busy;
|
|
}
|
|
|
|
/* Set the handle information */
|
|
handle->txData = xfer->txData;
|
|
handle->rxData = xfer->rxData;
|
|
/* Set count */
|
|
handle->txRemainingBytes = (NULL == xfer->txData) ? 0U : xfer->dataSize;
|
|
handle->rxRemainingBytes = (NULL == xfer->rxData) ? 0U : xfer->dataSize;
|
|
handle->totalByteCount = xfer->dataSize;
|
|
/* If the rxData is NULL, ignore the receive. */
|
|
if (NULL == xfer->rxData)
|
|
{
|
|
xfer->configFlags |= (uint32_t)kSPI_ReceiveIgnore;
|
|
}
|
|
|
|
/* If only on frame to be sent, set the command to last command. */
|
|
if (((xfer->dataSize == 1U) && (handle->dataWidth < (uint8_t)kSPI_Data9Bits)) ||
|
|
((xfer->dataSize == 2U) && (handle->dataWidth >= (uint8_t)kSPI_Data9Bits)))
|
|
{
|
|
SPI_WriteConfigFlags(base, xfer->configFlags);
|
|
}
|
|
else
|
|
{
|
|
SPI_WriteConfigFlags(base, (xfer->configFlags & (~SPI_TXDATCTL_EOT_MASK)));
|
|
}
|
|
|
|
/* Set the last command. */
|
|
handle->lastCommand = base->TXCTL & (SPI_TXCTL_LEN_MASK | SPI_TXCTL_RXIGNORE_MASK | SPI_TXCTL_EOF_MASK |
|
|
SPI_TXCTL_EOT_MASK | (uint32_t)kSPI_SselDeAssertAll);
|
|
if ((xfer->configFlags & (uint32_t)kSPI_EndOfTransfer) != 0U)
|
|
{
|
|
handle->lastCommand |= SPI_TXDATCTL_EOT_MASK;
|
|
}
|
|
/* Set the SPI state to busy */
|
|
handle->state = (uint32_t)kStatus_SPI_Busy;
|
|
|
|
/* Write data to TXDAT register to trigger a SPI receive. */
|
|
if (NULL == handle->txData)
|
|
{
|
|
instance = SPI_GetInstance(base);
|
|
temp = s_dummyData[instance];
|
|
}
|
|
else
|
|
{
|
|
temp = *handle->txData++;
|
|
handle->txRemainingBytes--;
|
|
if (handle->dataWidth > (uint8_t)kSPI_Data8Bits)
|
|
{
|
|
temp |= (uint16_t)(*handle->txData++) << 8U;
|
|
handle->txRemainingBytes--;
|
|
}
|
|
}
|
|
|
|
SPI_WriteData(base, temp);
|
|
|
|
/* Enable generating IRQ.
|
|
* If RX ignore bit was set, only enable TX ready interrupt, otherwise,
|
|
* enable RX ready interrupt.
|
|
*/
|
|
if (((uint32_t)kSPI_ReceiveIgnore & xfer->configFlags) != 0U)
|
|
{
|
|
SPI_EnableInterrupts(base, (uint32_t)kSPI_TxReadyInterruptEnable);
|
|
}
|
|
else
|
|
{
|
|
SPI_EnableInterrupts(base, (uint32_t)kSPI_RxReadyInterruptEnable);
|
|
}
|
|
|
|
return kStatus_Success;
|
|
}
|
|
|
|
/*!
|
|
* 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 = kStatus_Success;
|
|
|
|
s_spiSlaveIsr = SPI_SlaveTransferHandleIRQ;
|
|
status = SPI_MasterTransferNonBlocking(base, handle, xfer);
|
|
|
|
return status;
|
|
}
|
|
|
|
/*!
|
|
* brief Gets the master transfer count.
|
|
*
|
|
* This function gets the master transfer count.
|
|
*
|
|
* param base SPI peripheral base address.
|
|
* param handle Pointer to the spi_master_handle_t structure which stores the transfer state.
|
|
* param count The number of bytes transferred by using the non-blocking transaction.
|
|
* return status of status_t.
|
|
*/
|
|
status_t SPI_MasterTransferGetCount(SPI_Type *base, spi_master_handle_t *handle, size_t *count)
|
|
{
|
|
assert(NULL != handle);
|
|
|
|
if (count == NULL)
|
|
{
|
|
return kStatus_InvalidArgument;
|
|
}
|
|
|
|
/* Catch when there is not an active transfer. */
|
|
if (handle->state != (uint32_t)kStatus_SPI_Busy)
|
|
{
|
|
*count = 0U;
|
|
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);
|
|
|
|
SPI_DisableInterrupts(base, (uint32_t)kSPI_TxReadyInterruptEnable | (uint32_t)kSPI_RxReadyInterruptEnable);
|
|
|
|
handle->state = (uint32_t)kStatus_SPI_Idle;
|
|
handle->txRemainingBytes = 0U;
|
|
handle->rxRemainingBytes = 0U;
|
|
}
|
|
|
|
/*!
|
|
* 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));
|
|
|
|
/* IRQ behaviour:
|
|
* - First interrupt is triggered by receive ready interrupt. The transfer function then
|
|
* tries read data and transmit data interleaved that results to strategy to process
|
|
* as many items as possible.
|
|
* - In last interrupt, 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.
|
|
* If the callback function is not NULL, trigger it.
|
|
*/
|
|
|
|
/* Data to send or read or expected to receive */
|
|
if ((handle->rxRemainingBytes) != 0U)
|
|
{
|
|
SPI_ReceiveTransfer(base, handle);
|
|
}
|
|
if ((handle->txRemainingBytes) != 0U)
|
|
{
|
|
SPI_SendTransfer(base, handle);
|
|
}
|
|
if ((0U == handle->txRemainingBytes) && (0U == handle->rxRemainingBytes))
|
|
{
|
|
/* Only finalize the transfer when kSPI_TxReadyFlag is set which means
|
|
the tx register is empty and all data is sent out to bus */
|
|
if ((SPI_GetStatusFlags(base) & (uint32_t)kSPI_TxReadyFlag) != 0U)
|
|
{
|
|
/* Disable TX and RX interrupt. */
|
|
SPI_DisableInterrupts(base, (uint32_t)kSPI_RxReadyInterruptEnable | (uint32_t)kSPI_TxReadyInterruptEnable);
|
|
|
|
/* Set transfer state to idle. */
|
|
handle->state = (uint32_t)kStatus_SPI_Idle;
|
|
/* If callback is not NULL, call this function. */
|
|
if (handle->callback != NULL)
|
|
{
|
|
(handle->callback)(base, handle, handle->state, handle->userData);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*!
|
|
* 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));
|
|
|
|
/* IRQ behaviour:
|
|
* - First interrupt is triggered by receive ready interrupt. The transfer function then
|
|
* tries read data and transmit data interleaved that results to strategy to process
|
|
* as many items as possible.
|
|
* - In the last interrupt, the last state is known by empty rxBuffer. If there is nothing
|
|
* to receive or send - both operations have been finished and interrupt can be disabled.
|
|
* If the callback function is not NULL, call it.
|
|
*/
|
|
|
|
/* Sending data to TXDAT first in case of data missing. */
|
|
if (handle->txRemainingBytes != 0U)
|
|
{
|
|
SPI_SendTransfer(base, handle);
|
|
}
|
|
|
|
/* Read data from RXDAT. */
|
|
if (handle->rxRemainingBytes != 0U)
|
|
{
|
|
SPI_ReceiveTransfer(base, handle);
|
|
}
|
|
|
|
if ((0U == handle->txRemainingBytes) && (0U == handle->rxRemainingBytes))
|
|
{
|
|
/* Only finalize the transfer when kSPI_TxReadyFlag is set which means
|
|
the tx register is empty and all data is sent out to bus */
|
|
if ((SPI_GetStatusFlags(base) & (uint32_t)kSPI_TxReadyFlag) != 0U)
|
|
{
|
|
/* Disable RX interrupt. */
|
|
SPI_DisableInterrupts(base, (uint32_t)kSPI_RxReadyInterruptEnable | (uint32_t)kSPI_TxReadyInterruptEnable);
|
|
/* Set transfer state to idle. */
|
|
handle->state = (uint32_t)kStatus_SPI_Idle;
|
|
/* If callback is not NULL, call this function. */
|
|
if (handle->callback != NULL)
|
|
{
|
|
(handle->callback)(base, handle, handle->state, handle->userData);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
#if defined(SPI0)
|
|
void SPI0_DriverIRQHandler(void);
|
|
void SPI0_DriverIRQHandler(void)
|
|
{
|
|
assert(s_spiHandle[0] != NULL);
|
|
SPI_CommonIRQHandler(SPI0, s_spiHandle[0]);
|
|
}
|
|
#endif
|
|
|
|
#if defined(SPI1)
|
|
void SPI1_DriverIRQHandler(void);
|
|
void SPI1_DriverIRQHandler(void)
|
|
{
|
|
assert(s_spiHandle[1] != NULL);
|
|
SPI_CommonIRQHandler(SPI1, s_spiHandle[1]);
|
|
}
|
|
#endif
|
|
#endif /* FSL_SDK_ENABLE_SPI_TRANSACTIONAL_API */
|