MCUXpresso_MIMXRT1052xxxxB/devices/MIMXRT1052/drivers/fsl_lpspi.c

2606 lines
91 KiB
C

/*
* Copyright (c) 2015, Freescale Semiconductor, Inc.
* Copyright 2016-2022 NXP
* All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "fsl_lpspi.h"
/*
* $Coverage Justification Reference$
*
* $Justification fsl_lpspi_c_ref_1$
* The default branch cannot be executed in any circumstances, it is only added to avoid MISRA violation.
*
* $Justification fsl_lpspi_c_ref_2$
* The FIFO was emptied before the check and there was no writing after the FIFO was emptied, so the obtained FIFO count
* is 0, and the #kStatus_LPSPI_Timeout cannot be obtained.(will improve)
*
* $Justification fsl_lpspi_c_ref_3$
* The fifosize is determined by the hardware.
*
* $Justification fsl_lpspi_c_ref_4$
* If an interrupt is generated by this error, the corresponding interrupt bit must be set.(will improve)
*
*/
/*******************************************************************************
* Definitions
******************************************************************************/
/* Component ID definition, used by tools. */
#ifndef FSL_COMPONENT_ID
#define FSL_COMPONENT_ID "platform.drivers.lpspi"
#endif
#if defined(LPSPI_RSTS)
#define LPSPI_RESETS_ARRAY LPSPI_RSTS
#endif
/*!
* @brief Default watermark values.
*
* The default watermarks are set to zero.
*/
enum _lpspi_default_watermarks
{
kLpspiDefaultTxWatermark = 0,
kLpspiDefaultRxWatermark = 0,
};
/*
* <! Structure definition for variables that passed as parameters in LPSPI_MasterTransferBlocking.
* The structure is private.
*/
typedef struct _lpspi_transfer_blocking_param
{
bool isTxMask;
bool isPcsContinuous;
uint8_t bytesEachWrite;
uint8_t bytesEachRead;
uint8_t *txData;
uint8_t *rxData;
uint32_t rxRemainingByteCount;
} lpspi_transfer_blocking_param_t;
/*! @brief Typedef for master interrupt handler. */
typedef void (*lpspi_master_isr_t)(LPSPI_Type *base, lpspi_master_handle_t *handle);
/*! @brief Typedef for slave interrupt handler. */
typedef void (*lpspi_slave_isr_t)(LPSPI_Type *base, lpspi_slave_handle_t *handle);
/*******************************************************************************
* Prototypes
******************************************************************************/
/*!
* @brief Configures the LPSPI peripheral chip select polarity.
*
* This function takes in the desired peripheral chip select (Pcs) and it's corresponding desired polarity and
* configures the Pcs signal to operate with the desired characteristic.
*
* @param base LPSPI peripheral address.
* @param pcs The particular peripheral chip select (parameter value is of type lpspi_which_pcs_t) for which we wish to
* apply the active high or active low characteristic.
* @param activeLowOrHigh The setting for either "active high, inactive low (0)" or "active low, inactive high(1)" of
* type lpspi_pcs_polarity_config_t.
*/
static void LPSPI_SetOnePcsPolarity(LPSPI_Type *base,
lpspi_which_pcs_t pcs,
lpspi_pcs_polarity_config_t activeLowOrHigh);
/*!
* @brief Combine the write data for 1 byte to 4 bytes.
* This is not a public API.
*/
static uint32_t LPSPI_CombineWriteData(uint8_t *txData, uint8_t bytesEachWrite, bool isByteSwap);
/*!
* @brief Separate the read data for 1 byte to 4 bytes.
* This is not a public API.
*/
static void LPSPI_SeparateReadData(uint8_t *rxData, uint32_t readData, uint8_t bytesEachRead, bool isByteSwap);
/*!
* @brief Master fill up the TX FIFO with data.
* This is not a public API.
*/
static void LPSPI_MasterTransferFillUpTxFifo(LPSPI_Type *base, lpspi_master_handle_t *handle);
/*!
* @brief Master finish up a transfer.
* It would call back if there is callback function and set the state to idle.
* This is not a public API.
*/
static void LPSPI_MasterTransferComplete(LPSPI_Type *base, lpspi_master_handle_t *handle);
/*!
* @brief Slave fill up the TX FIFO with data.
* This is not a public API.
*/
static void LPSPI_SlaveTransferFillUpTxFifo(LPSPI_Type *base, lpspi_slave_handle_t *handle);
/*!
* @brief Slave finish up a transfer.
* It would call back if there is callback function and set the state to idle.
* This is not a public API.
*/
static void LPSPI_SlaveTransferComplete(LPSPI_Type *base, lpspi_slave_handle_t *handle);
/*!
* @brief LPSPI common interrupt handler.
*
* @param handle pointer to s_lpspiHandle which stores the transfer state.
*/
static void LPSPI_CommonIRQHandler(LPSPI_Type *base, void *param);
/*!
* @brief introduce function static bool LPSPI_MasterTransferWriteAllTxData.
* This function was deal with write all Txdata.
*
* @param base LPSPI peripheral address.
* @param stateParams Pass the address of the parent function variable.
*
* @return default is true when No abnormality.
* @return false when time out.
*/
static bool LPSPI_MasterTransferWriteAllTxData(LPSPI_Type *base,
lpspi_transfer_t *transfer,
lpspi_transfer_blocking_param_t *stateParams);
/*!
* @brief introduce function LPSPI_MasterTransferClearTCR.
* This function was deal with clear TCR.
*
* @param base LPSPI peripheral address.
* @param stateParams Pass the address of the parent function variable.
*
* @return default is true when No abnormality.
* @return false when time out.
*/
static bool LPSPI_MasterTransferClearTCR(LPSPI_Type *base, lpspi_transfer_blocking_param_t *stateParams);
/*!
* @brief introduce function LPSPI_MasterTransferReadDataInFifo.
* This function was deal with read data in fifo.
*
* @param base LPSPI peripheral address.
* @param stateParams Pass the address of the parent function variable.
*
* @return default is true when No abnormality.
* @return false when time out.
*/
static bool LPSPI_MasterTransferReadDataInFifo(LPSPI_Type *base,
lpspi_transfer_t *transfer,
lpspi_transfer_blocking_param_t *stateParams);
/*!
* @brief introduce function LPSPI_MasterTransferReadDataInFifoNoBuf.
* This function was deal with no buf in fifo.
*
* @param base LPSPI peripheral address.
* @param stateParams Pass the address of the parent function variable.
*
* @return default is true when No abnormality.
* @return false when time out.
*/
static bool LPSPI_MasterTransferReadDataInFifoNoBuf(LPSPI_Type *base, lpspi_transfer_blocking_param_t *stateParams);
/*******************************************************************************
* Variables
******************************************************************************/
/* Defines constant value arrays for the baud rate pre-scalar and scalar divider values.*/
static const uint8_t s_baudratePrescaler[] = {1, 2, 4, 8, 16, 32, 64, 128};
/*! @brief Pointers to lpspi bases for each instance. */
static LPSPI_Type *const s_lpspiBases[] = LPSPI_BASE_PTRS;
/*! @brief Pointers to lpspi IRQ number for each instance. */
static const IRQn_Type s_lpspiIRQ[] = LPSPI_IRQS;
#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
/*! @brief Pointers to lpspi clocks for each instance. */
static const clock_ip_name_t s_lpspiClocks[] = LPSPI_CLOCKS;
#if defined(LPSPI_PERIPH_CLOCKS)
static const clock_ip_name_t s_LpspiPeriphClocks[] = LPSPI_PERIPH_CLOCKS;
#endif
#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
#if defined(LPSPI_RESETS_ARRAY)
/* Reset array */
static const reset_ip_name_t s_lpspiResets[] = LPSPI_RESETS_ARRAY;
#endif
/*! @brief Pointers to lpspi handles for each instance. */
static void *s_lpspiHandle[ARRAY_SIZE(s_lpspiBases)];
/*! @brief Pointer to master IRQ handler for each instance. */
static lpspi_master_isr_t s_lpspiMasterIsr;
/*! @brief Pointer to slave IRQ handler for each instance. */
static lpspi_slave_isr_t s_lpspiSlaveIsr;
/* @brief Dummy data for each instance. This data is used when user's tx buffer is NULL*/
volatile uint8_t g_lpspiDummyData[ARRAY_SIZE(s_lpspiBases)] = {0};
/**********************************************************************************************************************
* Code
*********************************************************************************************************************/
/*!
* brief Get the LPSPI instance from peripheral base address.
*
* param base LPSPI peripheral base address.
* return LPSPI instance.
*/
uint32_t LPSPI_GetInstance(LPSPI_Type *base)
{
uint8_t instance = 0;
/* Find the instance index from base address mappings. */
for (instance = 0; instance < ARRAY_SIZE(s_lpspiBases); instance++)
{
if (s_lpspiBases[instance] == base)
{
break;
}
}
assert(instance < ARRAY_SIZE(s_lpspiBases));
return instance;
}
/*!
* brief Set up the dummy data.
*
* param base LPSPI peripheral address.
* param dummyData Data to be transferred when tx buffer is NULL.
* Note:
* This API has no effect when LPSPI in slave interrupt mode, because driver
* will set the TXMSK bit to 1 if txData is NULL, no data is loaded from transmit
* FIFO and output pin is tristated.
*/
void LPSPI_SetDummyData(LPSPI_Type *base, uint8_t dummyData)
{
uint32_t instance = LPSPI_GetInstance(base);
g_lpspiDummyData[instance] = dummyData;
}
/*!
* brief Initializes the LPSPI master.
*
* param base LPSPI peripheral address.
* param masterConfig Pointer to structure lpspi_master_config_t.
* param srcClock_Hz Module source input clock in Hertz
*/
void LPSPI_MasterInit(LPSPI_Type *base, const lpspi_master_config_t *masterConfig, uint32_t srcClock_Hz)
{
assert(masterConfig != NULL);
uint32_t tcrPrescaleValue = 0;
#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
uint32_t instance = LPSPI_GetInstance(base);
/* Enable LPSPI clock */
(void)CLOCK_EnableClock(s_lpspiClocks[instance]);
#if defined(LPSPI_PERIPH_CLOCKS)
(void)CLOCK_EnableClock(s_LpspiPeriphClocks[instance]);
#endif
#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
#if defined(LPSPI_RESETS_ARRAY)
RESET_ReleasePeripheralReset(s_lpspiResets[LPSPI_GetInstance(base)]);
#endif
/* if register exist,software manual reset the register to default and clear FIFO */
base->CR |= LPSPI_CR_RRF_MASK | LPSPI_CR_RTF_MASK;
base->IER = 0U;
base->CR = 0U;
/* Disable LPSPI first */
LPSPI_Enable(base, false);
/* Set LPSPI to master */
LPSPI_SetMasterSlaveMode(base, kLPSPI_Master);
/* Set specific PCS to active high or low */
LPSPI_SetOnePcsPolarity(base, masterConfig->whichPcs, masterConfig->pcsActiveHighOrLow);
/* Set Configuration Register 1 related setting.*/
base->CFGR1 = (base->CFGR1 & ~(LPSPI_CFGR1_OUTCFG_MASK | LPSPI_CFGR1_PINCFG_MASK | LPSPI_CFGR1_NOSTALL_MASK |
LPSPI_CFGR1_SAMPLE_MASK)) |
LPSPI_CFGR1_OUTCFG(masterConfig->dataOutConfig) | LPSPI_CFGR1_PINCFG(masterConfig->pinCfg) |
#if !(defined(FSL_FEATURE_LPSPI_HAS_NO_PCSCFG) && FSL_FEATURE_LPSPI_HAS_NO_PCSCFG)
LPSPI_CFGR1_PCSCFG(masterConfig->pcsFunc) |
#endif
LPSPI_CFGR1_NOSTALL(0) | LPSPI_CFGR1_SAMPLE((uint32_t)masterConfig->enableInputDelay);
if ((masterConfig->pinCfg == kLPSPI_SdiInSdiOut) || (masterConfig->pinCfg == kLPSPI_SdoInSdoOut))
{
base->CFGR1 |= LPSPI_CFGR1_OUTCFG_MASK;
}
/* Set baudrate and delay times*/
(void)LPSPI_MasterSetBaudRate(base, masterConfig->baudRate, srcClock_Hz, &tcrPrescaleValue);
/* Set default watermarks */
LPSPI_SetFifoWatermarks(base, (uint32_t)kLpspiDefaultTxWatermark, (uint32_t)kLpspiDefaultRxWatermark);
/* Set Transmit Command Register*/
base->TCR = LPSPI_TCR_CPOL(masterConfig->cpol) | LPSPI_TCR_CPHA(masterConfig->cpha) |
LPSPI_TCR_LSBF(masterConfig->direction) | LPSPI_TCR_FRAMESZ(masterConfig->bitsPerFrame - 1U) |
LPSPI_TCR_PRESCALE(tcrPrescaleValue) | LPSPI_TCR_PCS(masterConfig->whichPcs);
LPSPI_Enable(base, true);
(void)LPSPI_MasterSetDelayTimes(base, masterConfig->pcsToSckDelayInNanoSec, kLPSPI_PcsToSck, srcClock_Hz);
(void)LPSPI_MasterSetDelayTimes(base, masterConfig->lastSckToPcsDelayInNanoSec, kLPSPI_LastSckToPcs, srcClock_Hz);
(void)LPSPI_MasterSetDelayTimes(base, masterConfig->betweenTransferDelayInNanoSec, kLPSPI_BetweenTransfer,
srcClock_Hz);
LPSPI_SetDummyData(base, LPSPI_DUMMY_DATA);
}
/*!
* brief Sets the lpspi_master_config_t structure to default values.
*
* This API initializes the configuration structure for LPSPI_MasterInit().
* The initialized structure can remain unchanged in LPSPI_MasterInit(), or can be modified
* before calling the LPSPI_MasterInit().
* Example:
* code
* lpspi_master_config_t masterConfig;
* LPSPI_MasterGetDefaultConfig(&masterConfig);
* endcode
* param masterConfig pointer to lpspi_master_config_t structure
*/
void LPSPI_MasterGetDefaultConfig(lpspi_master_config_t *masterConfig)
{
assert(masterConfig != NULL);
/* Initializes the configure structure to zero. */
(void)memset(masterConfig, 0, sizeof(*masterConfig));
masterConfig->baudRate = 500000;
masterConfig->bitsPerFrame = 8;
masterConfig->cpol = kLPSPI_ClockPolarityActiveHigh;
masterConfig->cpha = kLPSPI_ClockPhaseFirstEdge;
masterConfig->direction = kLPSPI_MsbFirst;
#if !(defined(FSL_FEATURE_LPSPI_HAS_NO_PCSCFG) && FSL_FEATURE_LPSPI_HAS_NO_PCSCFG)
masterConfig->pcsFunc = kLPSPI_PcsAsCs;
#endif
masterConfig->pcsToSckDelayInNanoSec = (1000000000U / masterConfig->baudRate) / 2U;
masterConfig->lastSckToPcsDelayInNanoSec = (1000000000U / masterConfig->baudRate) / 2U;
masterConfig->betweenTransferDelayInNanoSec = (1000000000U / masterConfig->baudRate) / 2U;
masterConfig->whichPcs = kLPSPI_Pcs0;
masterConfig->pcsActiveHighOrLow = kLPSPI_PcsActiveLow;
masterConfig->pinCfg = kLPSPI_SdiInSdoOut;
masterConfig->dataOutConfig = kLpspiDataOutRetained;
masterConfig->enableInputDelay = false;
}
/*!
* brief LPSPI slave configuration.
*
* param base LPSPI peripheral address.
* param slaveConfig Pointer to a structure lpspi_slave_config_t.
*/
void LPSPI_SlaveInit(LPSPI_Type *base, const lpspi_slave_config_t *slaveConfig)
{
assert(slaveConfig != NULL);
#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
uint32_t instance = LPSPI_GetInstance(base);
/* Enable LPSPI clock */
(void)CLOCK_EnableClock(s_lpspiClocks[instance]);
#if defined(LPSPI_PERIPH_CLOCKS)
(void)CLOCK_EnableClock(s_LpspiPeriphClocks[instance]);
#endif
#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
#if defined(LPSPI_RESETS_ARRAY)
RESET_ReleasePeripheralReset(s_lpspiResets[LPSPI_GetInstance(base)]);
#endif
LPSPI_SetMasterSlaveMode(base, kLPSPI_Slave);
LPSPI_SetOnePcsPolarity(base, slaveConfig->whichPcs, slaveConfig->pcsActiveHighOrLow);
base->CFGR1 = (base->CFGR1 & ~(LPSPI_CFGR1_OUTCFG_MASK | LPSPI_CFGR1_PINCFG_MASK)) |
LPSPI_CFGR1_OUTCFG(slaveConfig->dataOutConfig) | LPSPI_CFGR1_PINCFG(slaveConfig->pinCfg);
if ((slaveConfig->pinCfg == kLPSPI_SdiInSdiOut) || (slaveConfig->pinCfg == kLPSPI_SdoInSdoOut))
{
base->CFGR1 |= LPSPI_CFGR1_OUTCFG_MASK;
}
LPSPI_SetFifoWatermarks(base, (uint32_t)kLpspiDefaultTxWatermark, (uint32_t)kLpspiDefaultRxWatermark);
base->TCR = LPSPI_TCR_CPOL(slaveConfig->cpol) | LPSPI_TCR_CPHA(slaveConfig->cpha) |
LPSPI_TCR_LSBF(slaveConfig->direction) | LPSPI_TCR_FRAMESZ(slaveConfig->bitsPerFrame - 1U);
/* This operation will set the dummy data for edma transfer, no effect in interrupt way. */
LPSPI_SetDummyData(base, LPSPI_DUMMY_DATA);
LPSPI_Enable(base, true);
}
/*!
* brief Sets the lpspi_slave_config_t structure to default values.
*
* This API initializes the configuration structure for LPSPI_SlaveInit().
* The initialized structure can remain unchanged in LPSPI_SlaveInit() or can be modified
* before calling the LPSPI_SlaveInit().
* Example:
* code
* lpspi_slave_config_t slaveConfig;
* LPSPI_SlaveGetDefaultConfig(&slaveConfig);
* endcode
* param slaveConfig pointer to lpspi_slave_config_t structure.
*/
void LPSPI_SlaveGetDefaultConfig(lpspi_slave_config_t *slaveConfig)
{
assert(slaveConfig != NULL);
/* Initializes the configure structure to zero. */
(void)memset(slaveConfig, 0, sizeof(*slaveConfig));
slaveConfig->bitsPerFrame = 8; /*!< Bits per frame, minimum 8, maximum 4096.*/
slaveConfig->cpol = kLPSPI_ClockPolarityActiveHigh; /*!< Clock polarity. */
slaveConfig->cpha = kLPSPI_ClockPhaseFirstEdge; /*!< Clock phase. */
slaveConfig->direction = kLPSPI_MsbFirst; /*!< MSB or LSB data shift direction. */
slaveConfig->whichPcs = kLPSPI_Pcs0; /*!< Desired Peripheral Chip Select (pcs) */
slaveConfig->pcsActiveHighOrLow = kLPSPI_PcsActiveLow; /*!< Desired PCS active high or low */
slaveConfig->pinCfg = kLPSPI_SdiInSdoOut;
slaveConfig->dataOutConfig = kLpspiDataOutRetained;
}
/*!
* brief Restores the LPSPI peripheral to reset state. Note that this function
* sets all registers to reset state. As a result, the LPSPI module can't work after calling
* this API.
* param base LPSPI peripheral address.
*/
void LPSPI_Reset(LPSPI_Type *base)
{
/* Reset all internal logic and registers, except the Control Register. Remains set until cleared by software.*/
base->CR |= LPSPI_CR_RST_MASK;
/* Software reset doesn't reset the CR, so manual reset the FIFOs */
base->CR |= LPSPI_CR_RRF_MASK | LPSPI_CR_RTF_MASK;
/* Master logic is not reset and module is disabled.*/
base->CR = 0x00U;
}
/*!
* brief De-initializes the LPSPI peripheral. Call this API to disable the LPSPI clock.
* param base LPSPI peripheral address.
*/
void LPSPI_Deinit(LPSPI_Type *base)
{
/* Reset to default value */
LPSPI_Reset(base);
#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
uint32_t instance = LPSPI_GetInstance(base);
/* Enable LPSPI clock */
(void)CLOCK_DisableClock(s_lpspiClocks[instance]);
#if defined(LPSPI_PERIPH_CLOCKS)
(void)CLOCK_DisableClock(s_LpspiPeriphClocks[instance]);
#endif
#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
}
static void LPSPI_SetOnePcsPolarity(LPSPI_Type *base,
lpspi_which_pcs_t pcs,
lpspi_pcs_polarity_config_t activeLowOrHigh)
{
uint32_t cfgr1Value = 0;
/* Clear the PCS polarity bit */
cfgr1Value = base->CFGR1 & ~(1UL << (LPSPI_CFGR1_PCSPOL_SHIFT + (uint32_t)pcs));
/* Configure the PCS polarity bit according to the activeLowOrHigh setting */
base->CFGR1 = cfgr1Value | ((uint32_t)activeLowOrHigh << (LPSPI_CFGR1_PCSPOL_SHIFT + (uint32_t)pcs));
}
/*!
* brief Sets the LPSPI baud rate in bits per second.
*
* This function takes in the desired bitsPerSec (baud rate) and calculates the nearest
* possible baud rate without exceeding the desired baud rate and returns the
* calculated baud rate in bits-per-second. It requires the caller to provide
* the frequency of the module source clock (in Hertz). Note that the baud rate
* does not go into effect until the Transmit Control Register (TCR) is programmed
* with the prescale value. Hence, this function returns the prescale tcrPrescaleValue
* parameter for later programming in the TCR. The higher level
* peripheral driver should alert the user of an out of range baud rate input.
*
* Note that the LPSPI module must first be disabled before configuring this.
* Note that the LPSPI module must be configured for master mode before configuring this.
*
* param base LPSPI peripheral address.
* param baudRate_Bps The desired baud rate in bits per second.
* param srcClock_Hz Module source input clock in Hertz.
* param tcrPrescaleValue The TCR prescale value needed to program the TCR.
* return The actual calculated baud rate. This function may also return a "0" if the
* LPSPI is not configured for master mode or if the LPSPI module is not disabled.
*/
uint32_t LPSPI_MasterSetBaudRate(LPSPI_Type *base,
uint32_t baudRate_Bps,
uint32_t srcClock_Hz,
uint32_t *tcrPrescaleValue)
{
assert(tcrPrescaleValue != NULL);
/* For master mode configuration only, if slave mode detected, return 0.
* Also, the LPSPI module needs to be disabled first, if enabled, return 0
*/
if ((!LPSPI_IsMaster(base)) || ((base->CR & LPSPI_CR_MEN_MASK) != 0U))
{
return 0U;
}
uint32_t prescaler, bestPrescaler;
uint32_t scaler, bestScaler;
uint32_t realBaudrate, bestBaudrate;
uint32_t diff, min_diff;
uint32_t desiredBaudrate = baudRate_Bps;
/* find combination of prescaler and scaler resulting in baudrate closest to the
* requested value
*/
min_diff = 0xFFFFFFFFU;
/* Set to maximum divisor value bit settings so that if baud rate passed in is less
* than the minimum possible baud rate, then the SPI will be configured to the lowest
* possible baud rate
*/
bestPrescaler = 7;
bestScaler = 255;
bestBaudrate = 0; /* required to avoid compilation warning */
/* In all for loops, if min_diff = 0, the exit for loop*/
for (prescaler = 0U; prescaler < 8U; prescaler++)
{
if (min_diff == 0U)
{
break;
}
for (scaler = 0U; scaler < 256U; scaler++)
{
if (min_diff == 0U)
{
break;
}
realBaudrate = (srcClock_Hz / (s_baudratePrescaler[prescaler] * (scaler + 2U)));
/* calculate the baud rate difference based on the conditional statement
* that states that the calculated baud rate must not exceed the desired baud rate
*/
if (desiredBaudrate >= realBaudrate)
{
diff = desiredBaudrate - realBaudrate;
if (min_diff > diff)
{
/* a better match found */
min_diff = diff;
bestPrescaler = prescaler;
bestScaler = scaler;
bestBaudrate = realBaudrate;
}
}
}
}
/* Write the best baud rate scalar to the CCR.
* Note, no need to check for error since we've already checked to make sure the module is
* disabled and in master mode. Also, there is a limit on the maximum divider so we will not
* exceed this.
*/
#if defined(FSL_FEATURE_LPSPI_HAS_CCR1) && FSL_FEATURE_LPSPI_HAS_CCR1
/* When CCR1 is present, the CCR[DBT] and CCR[SCKDIV] is write only, all read will return 0
The real DBT and SCKDIV can be obtained in CCR1, CCR[DBT]=CCR1[SCKSCK] and CCR[SCKDIV]=CCR1[SCKHLD]+CCR1[SCKSET]
So when changing either CCR[DBT] or CCR[SCKDIV] make sure the other value is not overwritten by 0 */
base->CCR = base->CCR | LPSPI_CCR_DBT((base->CCR1 & LPSPI_CCR1_SCKSCK_MASK) >> LPSPI_CCR1_SCKSCK_SHIFT) |
LPSPI_CCR_SCKDIV(bestScaler);
#else
base->CCR = (base->CCR & ~LPSPI_CCR_SCKDIV_MASK) | LPSPI_CCR_SCKDIV(bestScaler);
#endif /* FSL_FEATURE_LPSPI_HAS_CCR1 */
/* return the best prescaler value for user to use later */
*tcrPrescaleValue = bestPrescaler;
/* return the actual calculated baud rate */
return bestBaudrate;
}
/*!
* brief Manually configures a specific LPSPI delay parameter (module must be disabled to
* change the delay values).
*
* This function configures the following:
* SCK to PCS delay, or
* PCS to SCK delay, or
* The configurations must occur between the transfer delay.
*
* The delay names are available in type lpspi_delay_type_t.
*
* The user passes the desired delay along with the delay value.
* This allows the user to directly set the delay values if they have
* pre-calculated them or if they simply wish to manually increment the value.
*
* Note that the LPSPI module must first be disabled before configuring this.
* Note that the LPSPI module must be configured for master mode before configuring this.
*
* param base LPSPI peripheral address.
* param scaler The 8-bit delay value 0x00 to 0xFF (255).
* param whichDelay The desired delay to configure, must be of type lpspi_delay_type_t.
*/
void LPSPI_MasterSetDelayScaler(LPSPI_Type *base, uint32_t scaler, lpspi_delay_type_t whichDelay)
{
/*These settings are only relevant in master mode */
#if defined(FSL_FEATURE_LPSPI_HAS_CCR1) && FSL_FEATURE_LPSPI_HAS_CCR1
/* When CCR1 is present, the CCR[DBT] and CCR[SCKDIV] is write only, all read will return 0
The real DBT and SCKDIV can be obtained in CCR1, CCR[DBT]=CCR1[SCKSCK] and CCR[SCKDIV]=CCR1[SCKHLD]+CCR1[SCKSET]
So when changing either CCR[DBT] or CCR[SCKDIV] make sure the other value is not overwritten by 0 */
uint32_t dbt = (base->CCR1 & LPSPI_CCR1_SCKSCK_MASK) >> LPSPI_CCR1_SCKSCK_SHIFT;
uint32_t sckdiv = (base->CCR1 & LPSPI_CCR1_SCKHLD_MASK) >> LPSPI_CCR1_SCKHLD_SHIFT;
sckdiv += (base->CCR1 & LPSPI_CCR1_SCKSET_MASK) >> LPSPI_CCR1_SCKSET_SHIFT;
switch (whichDelay)
{
case kLPSPI_PcsToSck:
base->CCR = (base->CCR & (~LPSPI_CCR_PCSSCK_MASK)) | LPSPI_CCR_PCSSCK(scaler) | LPSPI_CCR_DBT(dbt) |
LPSPI_CCR_SCKDIV(sckdiv);
break;
case kLPSPI_LastSckToPcs:
base->CCR = (base->CCR & (~LPSPI_CCR_SCKPCS_MASK)) | LPSPI_CCR_SCKPCS(scaler) | LPSPI_CCR_DBT(dbt) |
LPSPI_CCR_SCKDIV(sckdiv);
break;
case kLPSPI_BetweenTransfer:
base->CCR = base->CCR | LPSPI_CCR_DBT(scaler) | LPSPI_CCR_SCKDIV(sckdiv);
#else
switch (whichDelay)
{
case kLPSPI_PcsToSck:
base->CCR = (base->CCR & (~LPSPI_CCR_PCSSCK_MASK)) | LPSPI_CCR_PCSSCK(scaler);
break;
case kLPSPI_LastSckToPcs:
base->CCR = (base->CCR & (~LPSPI_CCR_SCKPCS_MASK)) | LPSPI_CCR_SCKPCS(scaler);
break;
case kLPSPI_BetweenTransfer:
base->CCR = (base->CCR & (~LPSPI_CCR_DBT_MASK)) | LPSPI_CCR_DBT(scaler);
#endif /* FSL_FEATURE_LPSPI_HAS_CCR1 */
break;
default:
assert(false);
break;
}
}
/*!
* brief Calculates the delay based on the desired delay input in nanoseconds (module must be
* disabled to change the delay values).
*
* This function calculates the values for the following:
* SCK to PCS delay, or
* PCS to SCK delay, or
* The configurations must occur between the transfer delay.
*
* The delay names are available in type lpspi_delay_type_t.
*
* The user passes the desired delay and the desired delay value in
* nano-seconds. The function calculates the value needed for the desired delay parameter
* and returns the actual calculated delay because an exact delay match may not be possible. In this
* case, the closest match is calculated without going below the desired delay value input.
* It is possible to input a very large delay value that exceeds the capability of the part, in
* which case the maximum supported delay is returned. It is up to the higher level
* peripheral driver to alert the user of an out of range delay input.
*
* Note that the LPSPI module must be configured for master mode before configuring this. And note that
* the delayTime = LPSPI_clockSource / (PRESCALE * Delay_scaler).
*
* param base LPSPI peripheral address.
* param delayTimeInNanoSec The desired delay value in nano-seconds.
* param whichDelay The desired delay to configuration, which must be of type lpspi_delay_type_t.
* param srcClock_Hz Module source input clock in Hertz.
* return actual Calculated delay value in nano-seconds.
*/
uint32_t LPSPI_MasterSetDelayTimes(LPSPI_Type *base,
uint32_t delayTimeInNanoSec,
lpspi_delay_type_t whichDelay,
uint32_t srcClock_Hz)
{
uint64_t realDelay, bestDelay;
uint32_t scaler, bestScaler;
uint32_t diff, min_diff;
uint64_t initialDelayNanoSec;
uint32_t clockDividedPrescaler;
/* For delay between transfer, an additional scaler value is needed */
uint32_t additionalScaler = 0;
/*As the RM note, the LPSPI baud rate clock is itself divided by the PRESCALE setting, which can vary between
* transfers.*/
clockDividedPrescaler =
srcClock_Hz / s_baudratePrescaler[(base->TCR & LPSPI_TCR_PRESCALE_MASK) >> LPSPI_TCR_PRESCALE_SHIFT];
/* Find combination of prescaler and scaler resulting in the delay closest to the requested value.*/
min_diff = 0xFFFFFFFFU;
/* Initialize scaler to max value to generate the max delay */
bestScaler = 0xFFU;
/* Calculate the initial (min) delay and maximum possible delay based on the specific delay as
* the delay divisors are slightly different based on which delay we are configuring.
*/
if (whichDelay == kLPSPI_BetweenTransfer)
{
/* First calculate the initial, default delay, note min delay is 2 clock cycles. Due to large size of
calculated values (uint64_t), we need to break up the calculation into several steps to ensure
accurate calculated results
*/
initialDelayNanoSec = 1000000000U;
initialDelayNanoSec *= 2U;
initialDelayNanoSec /= clockDividedPrescaler;
/* Calculate the maximum delay */
bestDelay = 1000000000U;
bestDelay *= 257U; /* based on DBT+2, or 255 + 2 */
bestDelay /= clockDividedPrescaler;
additionalScaler = 1U;
}
else
{
/* First calculate the initial, default delay, min delay is 1 clock cycle. Due to large size of calculated
values (uint64_t), we need to break up the calculation into several steps to ensure accurate calculated
results.
*/
initialDelayNanoSec = 1000000000U;
initialDelayNanoSec /= clockDividedPrescaler;
/* Calculate the maximum delay */
bestDelay = 1000000000U;
bestDelay *= 256U; /* based on SCKPCS+1 or PCSSCK+1, or 255 + 1 */
bestDelay /= clockDividedPrescaler;
additionalScaler = 0U;
}
/* If the initial, default delay is already greater than the desired delay, then
* set the delay to their initial value (0) and return the delay. In other words,
* there is no way to decrease the delay value further.
*/
if (initialDelayNanoSec >= delayTimeInNanoSec)
{
LPSPI_MasterSetDelayScaler(base, 0, whichDelay);
return (uint32_t)initialDelayNanoSec;
}
/* If min_diff = 0, the exit for loop */
for (scaler = 0U; scaler < 256U; scaler++)
{
if (min_diff == 0U)
{
break;
}
/* Calculate the real delay value as we cycle through the scaler values.
Due to large size of calculated values (uint64_t), we need to break up the
calculation into several steps to ensure accurate calculated results
*/
realDelay = 1000000000U;
realDelay *= ((uint64_t)scaler + 1UL + (uint64_t)additionalScaler);
realDelay /= clockDividedPrescaler;
/* calculate the delay difference based on the conditional statement
* that states that the calculated delay must not be less then the desired delay
*/
if (realDelay >= delayTimeInNanoSec)
{
diff = (uint32_t)(realDelay - (uint64_t)delayTimeInNanoSec);
if (min_diff > diff)
{
/* a better match found */
min_diff = diff;
bestScaler = scaler;
bestDelay = realDelay;
}
}
}
/* write the best scaler value for the delay */
LPSPI_MasterSetDelayScaler(base, bestScaler, whichDelay);
/* return the actual calculated delay value (in ns) */
return (uint32_t)bestDelay;
}
/*Transactional APIs -- Master*/
/*!
* brief Initializes the LPSPI master handle.
*
* This function initializes the LPSPI handle, which can be used for other LPSPI transactional APIs. Usually, for a
* specified LPSPI instance, call this API once to get the initialized handle.
* param base LPSPI peripheral address.
* param handle LPSPI handle pointer to lpspi_master_handle_t.
* param callback DSPI callback.
* param userData callback function parameter.
*/
void LPSPI_MasterTransferCreateHandle(LPSPI_Type *base,
lpspi_master_handle_t *handle,
lpspi_master_transfer_callback_t callback,
void *userData)
{
assert(handle != NULL);
/* Zero the handle. */
(void)memset(handle, 0, sizeof(*handle));
s_lpspiHandle[LPSPI_GetInstance(base)] = handle;
/* Set irq handler. */
s_lpspiMasterIsr = LPSPI_MasterTransferHandleIRQ;
handle->callback = callback;
handle->userData = userData;
}
/*!
* brief Check the argument for transfer .
*
* param base LPSPI peripheral address.
* param transfer the transfer struct to be used.
* param isEdma True to check for EDMA transfer, false to check interrupt non-blocking transfer
* return Return true for right and false for wrong.
*/
bool LPSPI_CheckTransferArgument(LPSPI_Type *base, lpspi_transfer_t *transfer, bool isEdma)
{
assert(transfer != NULL);
uint32_t bitsPerFrame = ((base->TCR & LPSPI_TCR_FRAMESZ_MASK) >> LPSPI_TCR_FRAMESZ_SHIFT) + 1U;
uint32_t bytesPerFrame = (bitsPerFrame + 7U) / 8U;
uint32_t temp = (base->CFGR1 & LPSPI_CFGR1_PINCFG_MASK);
/* If the transfer count is zero, then return immediately.*/
if (transfer->dataSize == 0U)
{
return false;
}
/* If both send buffer and receive buffer is null */
if ((NULL == (transfer->txData)) && (NULL == (transfer->rxData)))
{
return false;
}
/*The transfer data size should be integer multiples of bytesPerFrame if bytesPerFrame is less than or equal to 4 .
*For bytesPerFrame greater than 4 situation:
*the transfer data size should be equal to bytesPerFrame if the bytesPerFrame is not integer multiples of 4 ,
*otherwise , the transfer data size can be integer multiples of bytesPerFrame.
*/
if (bytesPerFrame <= 4U)
{
if ((transfer->dataSize % bytesPerFrame) != 0U)
{
return false;
}
}
else
{
if ((bytesPerFrame % 4U) != 0U)
{
if (transfer->dataSize != bytesPerFrame)
{
return false;
}
}
else
{
if ((transfer->dataSize % bytesPerFrame) != 0U)
{
return false;
}
}
}
/* Check if using 3-wire mode and the txData is NULL, set the output pin to tristated. */
if ((temp == LPSPI_CFGR1_PINCFG(kLPSPI_SdiInSdiOut)) || (temp == LPSPI_CFGR1_PINCFG(kLPSPI_SdoInSdoOut)))
{
/* The 3-wire mode can't send and receive data at the same time. */
if ((transfer->txData != NULL) && (transfer->rxData != NULL))
{
return false;
}
}
if (isEdma && ((bytesPerFrame % 4U) == 3U))
{
return false;
}
return true;
}
static bool LPSPI_MasterTransferWriteAllTxData(LPSPI_Type *base,
lpspi_transfer_t *transfer,
lpspi_transfer_blocking_param_t *stateParams)
{
uint8_t dummyData = g_lpspiDummyData[LPSPI_GetInstance(base)];
uint32_t bytesPerFrame = ((base->TCR & LPSPI_TCR_FRAMESZ_MASK) >> LPSPI_TCR_FRAMESZ_SHIFT) / 8U + 1U;
uint32_t txRemainingByteCount = transfer->dataSize;
bool isByteSwap = ((transfer->configFlags & (uint32_t)kLPSPI_MasterByteSwap) != 0U);
uint32_t wordToSend =
((uint32_t)dummyData) | ((uint32_t)dummyData << 8) | ((uint32_t)dummyData << 16) | ((uint32_t)dummyData << 24);
uint32_t rxFifoMaxBytes = MIN(bytesPerFrame, 4U) * LPSPI_GetRxFifoSize(base);
uint32_t readData;
uint8_t fifo_size = LPSPI_GetRxFifoSize(base);
/*Write the TX data until txRemainingByteCount is equal to 0 */
while (txRemainingByteCount > 0U)
{
if (txRemainingByteCount < (stateParams->bytesEachWrite))
{
(stateParams->bytesEachWrite) = (uint8_t)txRemainingByteCount;
}
/*Wait until TX FIFO is not full*/
#if SPI_RETRY_TIMES
uint32_t waitTimes = SPI_RETRY_TIMES;
while (LPSPI_GetTxFifoCount(base) == fifo_size) && ((--waitTimes) != 0U))
#else
while (LPSPI_GetTxFifoCount(base) == fifo_size)
#endif
{
}
#if SPI_RETRY_TIMES
if (waitTimes == 0U)
{
return false;
}
#endif
/* To prevent rxfifo overflow, ensure transmitting and receiving are executed in parallel */
if (((NULL == (stateParams->rxData)) ||
((stateParams->rxRemainingByteCount) - txRemainingByteCount) < rxFifoMaxBytes))
{
if (stateParams->isTxMask)
{
/* When TCR[TXMSK]=1, transfer is initiate by writting a new command word to TCR. TCR[TXMSK] is cleared
by hardware every time when TCR[FRAMESZ] bit of data is transfered.
In this case TCR[TXMSK] should be set to initiate each transfer. */
base->TCR |= LPSPI_TCR_TXMSK_MASK;
if (stateParams->isPcsContinuous && (txRemainingByteCount == bytesPerFrame))
{
/* For the last piece of frame size of data, if is PCS continous mode(TCR[CONT]), TCR[CONTC] should
* be cleared to de-assert the PCS. Be sure to clear the TXMSK as well otherwise another FRAMESZ
* of data will be received. */
base->TCR &= ~(LPSPI_TCR_CONTC_MASK | LPSPI_TCR_CONT_MASK | LPSPI_TCR_TXMSK_MASK);
}
else
{
if (!LPSPI_WaitTxFifoEmpty(base))
{
return false;
}
}
txRemainingByteCount -= bytesPerFrame;
}
else
{
if ((stateParams->txData) != NULL)
{
if (stateParams->bytesEachWrite != 1U)
{
wordToSend =
LPSPI_CombineWriteData((stateParams->txData), (stateParams->bytesEachWrite), isByteSwap);
(stateParams->txData) += (stateParams->bytesEachWrite);
}
else
{
wordToSend = *(stateParams->txData);
(stateParams->txData) += 1U;
}
}
/* Otherwise push data to tx FIFO to initiate transfer */
LPSPI_WriteData(base, wordToSend);
txRemainingByteCount -= (stateParams->bytesEachWrite);
}
}
/* Check whether there is RX data in RX FIFO . Read out the RX data so that the RX FIFO would not overrun. */
/*
* $Branch Coverage Justification$
* Data will be transferred in the inner loop until complete, after which the interrupt will end.(will improve)
*/
if (((stateParams->rxData) != NULL) && ((stateParams->rxRemainingByteCount) != 0U))
{
/* To ensure parallel execution in 3-wire mode, after writting 1 to TXMSK to generate clock of
bytesPerFrame's data wait until bytesPerFrame's data is received. */
while ((stateParams->isTxMask) && (LPSPI_GetRxFifoCount(base) == 0U))
{
}
#if SPI_RETRY_TIMES
uint32_t waitTimes = SPI_RETRY_TIMES;
while ((LPSPI_GetRxFifoCount(base) != 0U) && (--waitTimes != 0U))
#else
while (LPSPI_GetRxFifoCount(base) != 0U)
#endif
{
readData = LPSPI_ReadData(base);
/*
* $Branch Coverage Justification$
* rxRemainingByteCount must be an integer multiple of bytesEachRead, otherwise it cannot pass the check
* of #LPSPI_CheckTransferArgument,so it doesn't happen here.(will improve)
*/
if ((stateParams->rxRemainingByteCount) < (stateParams->bytesEachRead))
{
(stateParams->bytesEachRead) = (uint8_t)(stateParams->rxRemainingByteCount);
}
LPSPI_SeparateReadData((stateParams->rxData), readData, (stateParams->bytesEachRead), isByteSwap);
(stateParams->rxData) += (stateParams->bytesEachRead);
(stateParams->rxRemainingByteCount) -= (stateParams->bytesEachRead);
}
#if SPI_RETRY_TIMES
if (waitTimes == 0U)
{
return false;
}
#endif
}
}
return true;
}
static bool LPSPI_MasterTransferClearTCR(LPSPI_Type *base, lpspi_transfer_blocking_param_t *stateParams)
{
#if SPI_RETRY_TIMES
uint32_t waitTimes = SPI_RETRY_TIMES;
while ((LPSPI_GetTxFifoCount(base) == LPSPI_GetRxFifoSize(base)) && (--waitTimes != 0U))
#else
while (LPSPI_GetTxFifoCount(base) == LPSPI_GetRxFifoSize(base))
#endif
{
}
#if SPI_RETRY_TIMES
if (waitTimes == 0U)
{
return false;
}
#endif
base->TCR = (base->TCR & ~(LPSPI_TCR_CONTC_MASK | LPSPI_TCR_CONT_MASK));
return true;
}
static bool LPSPI_MasterTransferReadDataInFifo(LPSPI_Type *base,
lpspi_transfer_t *transfer,
lpspi_transfer_blocking_param_t *stateParams)
{
uint32_t readData;
bool isByteSwap = ((transfer->configFlags & (uint32_t)kLPSPI_MasterByteSwap) != 0U);
while ((stateParams->rxRemainingByteCount) > 0U)
{
#if SPI_RETRY_TIMES
uint32_t waitTimes = SPI_RETRY_TIMES;
while ((LPSPI_GetRxFifoCount(base) != 0U) && (--waitTimes != 0U))
#else
while (LPSPI_GetRxFifoCount(base) != 0U)
#endif
{
readData = LPSPI_ReadData(base);
if ((stateParams->rxRemainingByteCount) < (stateParams->bytesEachRead))
{
(stateParams->bytesEachRead) = (uint8_t)(stateParams->rxRemainingByteCount);
}
LPSPI_SeparateReadData((stateParams->rxData), readData, (stateParams->bytesEachRead), isByteSwap);
(stateParams->rxData) += (stateParams->bytesEachRead);
(stateParams->rxRemainingByteCount) -= (stateParams->bytesEachRead);
}
#if SPI_RETRY_TIMES
if (waitTimes == 0U)
{
return false;
}
#endif
}
return true;
}
static bool LPSPI_MasterTransferReadDataInFifoNoBuf(LPSPI_Type *base, lpspi_transfer_blocking_param_t *stateParams)
{
#if SPI_RETRY_TIMES
uint32_t waitTimes = SPI_RETRY_TIMES;
while (((LPSPI_GetStatusFlags(base) & (uint32_t)kLPSPI_TransferCompleteFlag) == 0U) && (--waitTimes != 0U))
#else
while ((LPSPI_GetStatusFlags(base) & (uint32_t)kLPSPI_TransferCompleteFlag) == 0U)
#endif
{
}
#if SPI_RETRY_TIMES
if (waitTimes == 0U)
{
return false;
}
#endif
return true;
}
/*!
* brief LPSPI master transfer data using a polling method.
*
* This function transfers data using a polling method. This is a blocking function, which does not return until all
* transfers have been completed.
*
* Note:
* The transfer data size should be integer multiples of bytesPerFrame if bytesPerFrame is less than or equal to 4.
* For bytesPerFrame greater than 4:
* The transfer data size should be equal to bytesPerFrame if the bytesPerFrame is not integer multiples of 4.
* Otherwise, the transfer data size can be an integer multiple of bytesPerFrame.
*
* param base LPSPI peripheral address.
* param transfer pointer to lpspi_transfer_t structure.
* return status of status_t.
*/
status_t LPSPI_MasterTransferBlocking(LPSPI_Type *base, lpspi_transfer_t *transfer)
{
assert(transfer != NULL);
/* Check that LPSPI is not busy.*/
/*
* $Branch Coverage Justification$
* MBF state setting and clearing is done by hardware, the state is too fast to be overwritten.(will improve)
*/
if ((LPSPI_GetStatusFlags(base) & (uint32_t)kLPSPI_ModuleBusyFlag) != 0U)
{
return kStatus_LPSPI_Busy;
}
LPSPI_Enable(base, false);
/* Check arguements */
if (!LPSPI_CheckTransferArgument(base, transfer, false))
{
return kStatus_InvalidArgument;
}
LPSPI_FlushFifo(base, true, true);
LPSPI_ClearStatusFlags(base, (uint32_t)kLPSPI_AllStatusFlag);
/* Variables */
uint32_t whichPcs = (transfer->configFlags & LPSPI_MASTER_PCS_MASK) >> LPSPI_MASTER_PCS_SHIFT;
#if !(defined(FSL_FEATURE_LPSPI_HAS_NO_MULTI_WIDTH) && FSL_FEATURE_LPSPI_HAS_NO_MULTI_WIDTH)
uint32_t width = (transfer->configFlags & LPSPI_MASTER_WIDTH_MASK) >> LPSPI_MASTER_WIDTH_SHIFT;
#endif
uint32_t temp = (base->CFGR1 & LPSPI_CFGR1_PINCFG_MASK);
lpspi_transfer_blocking_param_t stateParams;
(void)memset(&stateParams, 0, sizeof(stateParams));
stateParams.txData = transfer->txData;
stateParams.rxData = transfer->rxData;
stateParams.isTxMask = false;
stateParams.rxRemainingByteCount = transfer->dataSize;
/*The TX and RX FIFO sizes are always the same*/
uint32_t bytesPerFrame = ((base->TCR & LPSPI_TCR_FRAMESZ_MASK) >> LPSPI_TCR_FRAMESZ_SHIFT) / 8U + 1U;
/* No need to configure PCS continous if the transfer byte count is smaller than frame size */
stateParams.isPcsContinuous = (((transfer->configFlags & (uint32_t)kLPSPI_MasterPcsContinuous) != 0U) &&
(bytesPerFrame < transfer->dataSize));
/* Mask tx data in half duplex mode */
if (((temp == LPSPI_CFGR1_PINCFG(kLPSPI_SdiInSdiOut)) || (temp == LPSPI_CFGR1_PINCFG(kLPSPI_SdoInSdoOut))) &&
(stateParams.txData == NULL))
{
stateParams.isTxMask = true;
}
base->CFGR1 &= (~LPSPI_CFGR1_NOSTALL_MASK);
LPSPI_Enable(base, true);
/* Configure transfer control register. */
base->TCR = (base->TCR & ~(LPSPI_TCR_CONT_MASK | LPSPI_TCR_CONTC_MASK | LPSPI_TCR_RXMSK_MASK |
LPSPI_TCR_TXMSK_MASK | LPSPI_TCR_PCS_MASK)) |
#if !(defined(FSL_FEATURE_LPSPI_HAS_NO_MULTI_WIDTH) && FSL_FEATURE_LPSPI_HAS_NO_MULTI_WIDTH)
LPSPI_TCR_WIDTH(width) |
#endif
LPSPI_TCR_PCS(whichPcs);
/*TCR is also shared the FIFO, so wait for TCR written.*/
/*
* $Branch Coverage Justification$
* $ref fsl_lpspi_c_ref_2$
*/
if (!LPSPI_WaitTxFifoEmpty(base))
{
return kStatus_LPSPI_Timeout;
}
/* PCS should be configured separately from the other bits, otherwise it will not take effect. */
base->TCR |= LPSPI_TCR_CONT(stateParams.isPcsContinuous) | LPSPI_TCR_CONTC(stateParams.isPcsContinuous) |
LPSPI_TCR_RXMSK(NULL == stateParams.rxData);
/*TCR is also shared the FIFO, so wait for TCR written.*/
/*
* $Branch Coverage Justification$
* $ref fsl_lpspi_c_ref_2$
*/
if (!LPSPI_WaitTxFifoEmpty(base))
{
return kStatus_LPSPI_Timeout;
}
if (bytesPerFrame <= 4U)
{
stateParams.bytesEachWrite = (uint8_t)bytesPerFrame;
stateParams.bytesEachRead = (uint8_t)bytesPerFrame;
}
else
{
stateParams.bytesEachWrite = 4U;
stateParams.bytesEachRead = 4U;
}
/*
* $Branch Coverage Justification$
* $ref fsl_lpspi_c_ref_2$
*/
if (false == LPSPI_MasterTransferWriteAllTxData(base, transfer, &stateParams))
{
return kStatus_LPSPI_Timeout;
}
if (stateParams.isPcsContinuous && !stateParams.isTxMask)
{
/* In PCS continous mode(TCR[CONT]), after write all the data in TX FIFO, TCR[CONTC] and TCR[CONT] should be
cleared to de-assert the PCS. Note that TCR register also use the TX FIFO. Also CONTC should be cleared when
tx is not masked, otherwise written to TCR register with TXMSK bit set will initiate a new transfer. */
/*
* $Branch Coverage Justification$
* $ref fsl_lpspi_c_ref_2$
*/
if (false == LPSPI_MasterTransferClearTCR(base, &stateParams))
{
return kStatus_LPSPI_Timeout;
}
}
/*Read out the RX data in FIFO*/
if (stateParams.rxData != NULL)
{
/*
* $Branch Coverage Justification$
* $ref fsl_lpspi_c_ref_2$
*/
if (false == LPSPI_MasterTransferReadDataInFifo(base, transfer, &stateParams))
{
return kStatus_LPSPI_Timeout;
}
}
else
{
/* If no RX buffer, then transfer is not complete until transfer complete flag sets */
/*
* $Branch Coverage Justification$
* $ref fsl_lpspi_c_ref_2$
*/
if (false == LPSPI_MasterTransferReadDataInFifoNoBuf(base, &stateParams))
{
return kStatus_LPSPI_Timeout;
}
}
return kStatus_Success;
}
/*!
* brief LPSPI master transfer data using an interrupt method.
*
* This function transfers data using an interrupt method. This is a non-blocking function, which returns right away.
* When all data is transferred, the callback function is called.
*
* Note:
* The transfer data size should be integer multiples of bytesPerFrame if bytesPerFrame is less than or equal to 4.
* For bytesPerFrame greater than 4:
* The transfer data size should be equal to bytesPerFrame if the bytesPerFrame is not integer multiples of 4.
* Otherwise, the transfer data size can be an integer multiple of bytesPerFrame.
*
* param base LPSPI peripheral address.
* param handle pointer to lpspi_master_handle_t structure which stores the transfer state.
* param transfer pointer to lpspi_transfer_t structure.
* return status of status_t.
*/
status_t LPSPI_MasterTransferNonBlocking(LPSPI_Type *base, lpspi_master_handle_t *handle, lpspi_transfer_t *transfer)
{
assert(handle != NULL);
assert(transfer != NULL);
/* Check that we're not busy.*/
if (handle->state == (uint8_t)kLPSPI_Busy)
{
return kStatus_LPSPI_Busy;
}
LPSPI_Enable(base, false);
/* Check arguements */
if (!LPSPI_CheckTransferArgument(base, transfer, false))
{
return kStatus_InvalidArgument;
}
/* Flush FIFO, clear status, disable all the interrupts. */
LPSPI_FlushFifo(base, true, true);
LPSPI_ClearStatusFlags(base, (uint32_t)kLPSPI_AllStatusFlag);
LPSPI_DisableInterrupts(base, (uint32_t)kLPSPI_AllInterruptEnable);
/* Variables */
bool isRxMask = false;
handle->isTxMask = false;
uint8_t txWatermark;
uint8_t dummyData = g_lpspiDummyData[LPSPI_GetInstance(base)];
uint32_t tmpTimes;
uint32_t whichPcs = (transfer->configFlags & LPSPI_MASTER_PCS_MASK) >> LPSPI_MASTER_PCS_SHIFT;
uint32_t temp = (base->CFGR1 & LPSPI_CFGR1_PINCFG_MASK);
/* Assign the original value for members of transfer handle. */
handle->state = (uint8_t)kLPSPI_Busy;
handle->txData = transfer->txData;
handle->rxData = transfer->rxData;
handle->txRemainingByteCount = transfer->dataSize;
handle->rxRemainingByteCount = transfer->dataSize;
handle->totalByteCount = transfer->dataSize;
handle->writeTcrInIsr = false;
handle->bytesPerFrame = (uint16_t)((base->TCR & LPSPI_TCR_FRAMESZ_MASK) >> LPSPI_TCR_FRAMESZ_SHIFT) / 8U + 1U;
/* No need to configure PCS continous if the transfer byte count is smaller than frame size */
bool isPcsContinuous = (((transfer->configFlags & (uint32_t)kLPSPI_MasterPcsContinuous) != 0U) &&
(transfer->dataSize > handle->bytesPerFrame));
handle->writeRegRemainingTimes =
(transfer->dataSize / (uint32_t)handle->bytesPerFrame) * (((uint32_t)handle->bytesPerFrame + 3U) / 4U);
handle->readRegRemainingTimes = handle->writeRegRemainingTimes;
handle->txBuffIfNull =
((uint32_t)dummyData) | ((uint32_t)dummyData << 8) | ((uint32_t)dummyData << 16) | ((uint32_t)dummyData << 24);
/*The TX and RX FIFO sizes are always the same*/
handle->fifoSize = LPSPI_GetRxFifoSize(base);
handle->isPcsContinuous = isPcsContinuous;
handle->isByteSwap = ((transfer->configFlags & (uint32_t)kLPSPI_MasterByteSwap) != 0U);
/*Calculate the bytes for write/read the TX/RX register each time*/
if (handle->bytesPerFrame <= 4U)
{
handle->bytesEachWrite = (uint8_t)handle->bytesPerFrame;
handle->bytesEachRead = (uint8_t)handle->bytesPerFrame;
}
else
{
handle->bytesEachWrite = 4U;
handle->bytesEachRead = 4U;
}
/*Set the RX and TX watermarks to reduce the ISR times.*/
/*
* $Branch Coverage Justification$
* $ref fsl_lpspi_c_ref_3$
*/
if (handle->fifoSize > 1U)
{
txWatermark = 1U;
handle->rxWatermark = handle->fifoSize - 2U;
}
else
{
txWatermark = 0U;
handle->rxWatermark = 0U;
}
LPSPI_SetFifoWatermarks(base, txWatermark, handle->rxWatermark);
/* If there is no rxData, mask the receive data so that receive data is not stored in receive FIFO. */
if (handle->rxData == NULL)
{
isRxMask = true;
handle->rxRemainingByteCount = 0;
}
/* Mask tx data in half duplex mode since the tx/rx share the same pin, so that the data received from slave is not
* interfered. */
if (((temp == LPSPI_CFGR1_PINCFG(kLPSPI_SdiInSdiOut)) || (temp == LPSPI_CFGR1_PINCFG(kLPSPI_SdoInSdoOut))) &&
(handle->txData == NULL))
{
handle->isTxMask = true;
}
/*Transfers will stall when transmit FIFO is empty or receive FIFO is full. */
base->CFGR1 &= (~LPSPI_CFGR1_NOSTALL_MASK);
/* Enable module for following configuration of TCR to take effect. */
LPSPI_Enable(base, true);
/* Configure transfer control register. */
base->TCR = (base->TCR & ~(LPSPI_TCR_CONT_MASK | LPSPI_TCR_CONTC_MASK | LPSPI_TCR_RXMSK_MASK |
LPSPI_TCR_TXMSK_MASK | LPSPI_TCR_PCS_MASK)) |
LPSPI_TCR_PCS(whichPcs);
/*TCR is also shared the FIFO , so wait for TCR written.*/
/*
* $Branch Coverage Justification$
* $ref fsl_lpspi_c_ref_2$
*/
if (!LPSPI_WaitTxFifoEmpty(base))
{
return kStatus_LPSPI_Timeout;
}
/* PCS should be configured separately from the other bits, otherwise it will not take effect. */
base->TCR |= LPSPI_TCR_CONT(isPcsContinuous) | LPSPI_TCR_CONTC(isPcsContinuous) | LPSPI_TCR_RXMSK(isRxMask);
/* Enable the NVIC for LPSPI peripheral. Note that below code is useless if the LPSPI interrupt is in INTMUX ,
* and you should also enable the INTMUX interupt in your application.
*/
(void)EnableIRQ(s_lpspiIRQ[LPSPI_GetInstance(base)]);
/*TCR is also shared the FIFO , so wait for TCR written.*/
/*
* $Branch Coverage Justification$
* $ref fsl_lpspi_c_ref_2$
*/
if (!LPSPI_WaitTxFifoEmpty(base))
{
return kStatus_LPSPI_Timeout;
}
if (handle->isTxMask)
{
/* When TCR[TXMSK]=1, transfer is initiate by writting a new command word to TCR. TCR[TXMSK] is cleared by
hardware every time when TCR[FRAMESZ] bit of data is transfered. In this case TCR[TXMSK] should be set to
initiate each transfer. */
base->TCR |= LPSPI_TCR_TXMSK_MASK;
handle->txRemainingByteCount -= (uint32_t)handle->bytesPerFrame;
if (!LPSPI_WaitTxFifoEmpty(base))
{
return kStatus_LPSPI_Timeout;
}
}
else
{
/* Fill up the TX data in FIFO to initiate transfer */
LPSPI_MasterTransferFillUpTxFifo(base, handle);
}
/* Since SPI is a synchronous interface, we only need to enable the RX interrupt if there is RX data.
* The IRQ handler will get the status of RX and TX interrupt flags.
*/
if (handle->rxData != NULL)
{
if (handle->isTxMask)
{
/* if tx data is masked, transfer is initiated by writing 1 to TCR[TXMSK] and TCR[FRMESZ] bits of data is
read. If rx water mark is set larger than TCR[FRMESZ], rx interrupt will not be generated. Lower the rx
water mark setting */
if ((handle->bytesPerFrame / 4U) < (uint16_t)handle->rxWatermark)
{
handle->rxWatermark =
(uint8_t)(handle->bytesPerFrame / 4U) > 0U ? (uint8_t)(handle->bytesPerFrame / 4U - 1U) : 0U;
base->FCR = (base->FCR & (~LPSPI_FCR_RXWATER_MASK)) | LPSPI_FCR_RXWATER(handle->rxWatermark);
}
}
else
{
/*Set rxWatermark to (readRegRemainingTimes-1) if readRegRemainingTimes less than rxWatermark. Otherwise
*there is not RX interrupt for the last datas because the RX count is not greater than rxWatermark.
*/
tmpTimes = handle->readRegRemainingTimes;
if (tmpTimes <= handle->rxWatermark)
{
base->FCR = (base->FCR & (~LPSPI_FCR_RXWATER_MASK)) | LPSPI_FCR_RXWATER(tmpTimes - 1U);
}
}
LPSPI_EnableInterrupts(base, (uint32_t)kLPSPI_RxInterruptEnable);
}
else
{
LPSPI_EnableInterrupts(base, (uint32_t)kLPSPI_TxInterruptEnable);
}
return kStatus_Success;
}
static void LPSPI_MasterTransferFillUpTxFifo(LPSPI_Type *base, lpspi_master_handle_t *handle)
{
assert(handle != NULL);
uint32_t wordToSend = 0;
uint8_t fifoSize = handle->fifoSize;
uint32_t writeRegRemainingTimes = handle->writeRegRemainingTimes;
uint32_t readRegRemainingTimes = handle->readRegRemainingTimes;
size_t txRemainingByteCount = handle->txRemainingByteCount;
uint8_t bytesEachWrite = handle->bytesEachWrite;
bool isByteSwap = handle->isByteSwap;
/* Make sure the difference in remaining TX and RX byte counts does not exceed FIFO depth
* and that the number of TX FIFO entries does not exceed the FIFO depth.
* But no need to make the protection if there is no rxData.
*/
while ((LPSPI_GetTxFifoCount(base) < fifoSize) &&
(((readRegRemainingTimes - writeRegRemainingTimes) < (uint32_t)fifoSize) || (handle->rxData == NULL)))
{
if (txRemainingByteCount < (size_t)bytesEachWrite)
{
handle->bytesEachWrite = (uint8_t)txRemainingByteCount;
bytesEachWrite = handle->bytesEachWrite;
}
if (handle->txData != NULL)
{
wordToSend = LPSPI_CombineWriteData(handle->txData, bytesEachWrite, isByteSwap);
handle->txData += bytesEachWrite;
}
else
{
wordToSend = handle->txBuffIfNull;
}
/*Write the word to TX register*/
LPSPI_WriteData(base, wordToSend);
/*Decrease the write TX register times.*/
--handle->writeRegRemainingTimes;
writeRegRemainingTimes = handle->writeRegRemainingTimes;
/*Decrease the remaining TX byte count.*/
handle->txRemainingByteCount -= (size_t)bytesEachWrite;
txRemainingByteCount = handle->txRemainingByteCount;
if (handle->txRemainingByteCount == 0U)
{
/* If PCS is continuous, update TCR to de-assert PCS */
if (handle->isPcsContinuous)
{
/* Only write to the TCR if the FIFO has room */
if (LPSPI_GetTxFifoCount(base) < fifoSize)
{
base->TCR = (base->TCR & ~(LPSPI_TCR_CONTC_MASK));
handle->writeTcrInIsr = false;
}
/* Else, set a global flag to tell the ISR to do write to the TCR */
else
{
handle->writeTcrInIsr = true;
}
}
break;
}
}
}
static void LPSPI_MasterTransferComplete(LPSPI_Type *base, lpspi_master_handle_t *handle)
{
assert(handle != NULL);
/* Disable interrupt requests*/
LPSPI_DisableInterrupts(base, (uint32_t)kLPSPI_AllInterruptEnable);
handle->state = (uint8_t)kLPSPI_Idle;
if (handle->callback != NULL)
{
handle->callback(base, handle, kStatus_Success, handle->userData);
}
}
/*!
* brief Gets the master transfer remaining bytes.
*
* This function gets the master transfer remaining bytes.
*
* param base LPSPI peripheral address.
* param handle pointer to lpspi_master_handle_t structure which stores the transfer state.
* param count Number of bytes transferred so far by the non-blocking transaction.
* return status of status_t.
*/
status_t LPSPI_MasterTransferGetCount(LPSPI_Type *base, lpspi_master_handle_t *handle, size_t *count)
{
assert(handle != NULL);
if (NULL == count)
{
return kStatus_InvalidArgument;
}
/* Catch when there is not an active transfer. */
if (handle->state != (uint8_t)kLPSPI_Busy)
{
*count = 0;
return kStatus_NoTransferInProgress;
}
size_t remainingByte;
if (handle->rxData != NULL)
{
remainingByte = handle->rxRemainingByteCount;
}
else
{
remainingByte = handle->txRemainingByteCount;
}
*count = handle->totalByteCount - remainingByte;
return kStatus_Success;
}
/*!
* brief LPSPI master abort transfer which uses an interrupt method.
*
* This function aborts a transfer which uses an interrupt method.
*
* param base LPSPI peripheral address.
* param handle pointer to lpspi_master_handle_t structure which stores the transfer state.
*/
void LPSPI_MasterTransferAbort(LPSPI_Type *base, lpspi_master_handle_t *handle)
{
assert(handle != NULL);
/* Disable interrupt requests*/
LPSPI_DisableInterrupts(base, (uint32_t)kLPSPI_AllInterruptEnable);
LPSPI_Reset(base);
handle->state = (uint8_t)kLPSPI_Idle;
handle->txRemainingByteCount = 0;
handle->rxRemainingByteCount = 0;
}
/*!
* brief LPSPI Master IRQ handler function.
*
* This function processes the LPSPI transmit and receive IRQ.
*
* param base LPSPI peripheral address.
* param handle pointer to lpspi_master_handle_t structure which stores the transfer state.
*/
void LPSPI_MasterTransferHandleIRQ(LPSPI_Type *base, lpspi_master_handle_t *handle)
{
assert(handle != NULL);
uint32_t readData;
uint8_t bytesEachRead = handle->bytesEachRead;
bool isByteSwap = handle->isByteSwap;
uint32_t readRegRemainingTimes = handle->readRegRemainingTimes;
if (handle->rxData != NULL)
{
if (handle->rxRemainingByteCount != 0U)
{
/* First, disable the interrupts to avoid potentially triggering another interrupt
* while reading out the RX FIFO as more data may be coming into the RX FIFO. We'll
* re-enable the interrupts based on the LPSPI state after reading out the FIFO.
*/
LPSPI_DisableInterrupts(base, (uint32_t)kLPSPI_RxInterruptEnable);
/*
* $Branch Coverage Justification$
* If the remaining number is 0, the FIFO must be 0, and the condition after will not be judged.(will
* improve)
*/
while ((LPSPI_GetRxFifoCount(base) != 0U) && (handle->rxRemainingByteCount != 0U))
{
/*Read out the data*/
readData = LPSPI_ReadData(base);
/*Decrease the read RX register times.*/
--handle->readRegRemainingTimes;
readRegRemainingTimes = handle->readRegRemainingTimes;
if (handle->rxRemainingByteCount < (size_t)bytesEachRead)
{
handle->bytesEachRead = (uint8_t)(handle->rxRemainingByteCount);
bytesEachRead = handle->bytesEachRead;
}
LPSPI_SeparateReadData(handle->rxData, readData, bytesEachRead, isByteSwap);
handle->rxData += bytesEachRead;
/*Decrease the remaining RX byte count.*/
handle->rxRemainingByteCount -= (size_t)bytesEachRead;
}
/* Re-enable the interrupts only if rxCount indicates there is more data to receive,
* else we may get a spurious interrupt.
* */
if (handle->rxRemainingByteCount != 0U)
{
/* Set the TDF and RDF interrupt enables simultaneously to avoid race conditions */
LPSPI_EnableInterrupts(base, (uint32_t)kLPSPI_RxInterruptEnable);
}
}
/*Set rxWatermark to (readRegRemainingTimes-1) if readRegRemainingTimes less than rxWatermark. Otherwise there
*is not RX interrupt for the last datas because the RX count is not greater than rxWatermark.
*/
if (readRegRemainingTimes <= (uint32_t)handle->rxWatermark)
{
base->FCR = (base->FCR & (~LPSPI_FCR_RXWATER_MASK)) |
LPSPI_FCR_RXWATER((readRegRemainingTimes > 1U) ? (readRegRemainingTimes - 1U) : (0U));
}
}
if (handle->txRemainingByteCount != 0U)
{
if (handle->isTxMask)
{
/* When TCR[TXMSK]=1, transfer is initiate by writting a new command word to TCR. TCR[TXMSK] is cleared by
hardware every time when TCR[FRAMESZ] bit of data is transfered.
In this case TCR[TXMSK] should be set to initiate each transfer. */
base->TCR |= LPSPI_TCR_TXMSK_MASK;
if ((handle->txRemainingByteCount == (uint32_t)handle->bytesPerFrame) && (handle->isPcsContinuous))
{
/* For the last piece of frame size of data, if is PCS continous mode(TCR[CONT]), TCR[CONTC] should
* be cleared to de-assert the PCS. Be sure to clear the TXMSK as well otherwise another FRAMESZ
* of data will be received. */
base->TCR &= ~(LPSPI_TCR_CONTC_MASK | LPSPI_TCR_CONT_MASK | LPSPI_TCR_TXMSK_MASK);
}
else
{
if (!LPSPI_WaitTxFifoEmpty(base))
{
return;
}
}
handle->txRemainingByteCount -= (uint32_t)handle->bytesPerFrame;
}
else
{
LPSPI_MasterTransferFillUpTxFifo(base, handle);
}
}
else
{
if ((LPSPI_GetTxFifoCount(base) < (handle->fifoSize)))
{
if ((handle->isPcsContinuous) && (handle->writeTcrInIsr) && (!handle->isTxMask))
{
base->TCR = (base->TCR & ~(LPSPI_TCR_CONTC_MASK));
handle->writeTcrInIsr = false;
}
}
}
if ((handle->txRemainingByteCount == 0U) && (handle->rxRemainingByteCount == 0U) && (!handle->writeTcrInIsr))
{
/* If no RX buffer, then transfer is not complete until transfer complete flag sets */
if (handle->rxData == NULL)
{
if ((LPSPI_GetStatusFlags(base) & (uint32_t)kLPSPI_TransferCompleteFlag) != 0U)
{
LPSPI_ClearStatusFlags(base, (uint32_t)kLPSPI_TransferCompleteFlag);
/* Complete the transfer and disable the interrupts */
LPSPI_MasterTransferComplete(base, handle);
}
else
{
LPSPI_EnableInterrupts(base, (uint32_t)kLPSPI_TransferCompleteInterruptEnable);
LPSPI_DisableInterrupts(base, (uint32_t)kLPSPI_TxInterruptEnable | (uint32_t)kLPSPI_RxInterruptEnable);
}
}
else
{
/* Complete the transfer and disable the interrupts */
LPSPI_MasterTransferComplete(base, handle);
}
}
}
/*Transactional APIs -- Slave*/
/*!
* brief Initializes the LPSPI slave handle.
*
* This function initializes the LPSPI handle, which can be used for other LPSPI transactional APIs. Usually, for a
* specified LPSPI instance, call this API once to get the initialized handle.
*
* param base LPSPI peripheral address.
* param handle LPSPI handle pointer to lpspi_slave_handle_t.
* param callback DSPI callback.
* param userData callback function parameter.
*/
void LPSPI_SlaveTransferCreateHandle(LPSPI_Type *base,
lpspi_slave_handle_t *handle,
lpspi_slave_transfer_callback_t callback,
void *userData)
{
assert(handle != NULL);
/* Zero the handle. */
(void)memset(handle, 0, sizeof(*handle));
s_lpspiHandle[LPSPI_GetInstance(base)] = handle;
/* Set irq handler. */
s_lpspiSlaveIsr = LPSPI_SlaveTransferHandleIRQ;
handle->callback = callback;
handle->userData = userData;
}
/*!
* brief LPSPI slave transfer data using an interrupt method.
*
* This function transfer data using an interrupt method. This is a non-blocking function, which returns right away.
* When all data is transferred, the callback function is called.
*
* Note:
* The transfer data size should be integer multiples of bytesPerFrame if bytesPerFrame is less than or equal to 4.
* For bytesPerFrame greater than 4:
* The transfer data size should be equal to bytesPerFrame if the bytesPerFrame is not an integer multiple of 4.
* Otherwise, the transfer data size can be an integer multiple of bytesPerFrame.
*
* param base LPSPI peripheral address.
* param handle pointer to lpspi_slave_handle_t structure which stores the transfer state.
* param transfer pointer to lpspi_transfer_t structure.
* return status of status_t.
*/
status_t LPSPI_SlaveTransferNonBlocking(LPSPI_Type *base, lpspi_slave_handle_t *handle, lpspi_transfer_t *transfer)
{
assert(handle != NULL);
assert(transfer != NULL);
/* Check that we're not busy.*/
if (handle->state == (uint8_t)kLPSPI_Busy)
{
return kStatus_LPSPI_Busy;
}
LPSPI_Enable(base, false);
/* Check arguements */
if (!LPSPI_CheckTransferArgument(base, transfer, false))
{
return kStatus_InvalidArgument;
}
/* Flush FIFO, clear status, disable all the inerrupts. */
LPSPI_FlushFifo(base, true, true);
LPSPI_ClearStatusFlags(base, (uint32_t)kLPSPI_AllStatusFlag);
LPSPI_DisableInterrupts(base, (uint32_t)kLPSPI_AllInterruptEnable);
/* Variables */
bool isRxMask = false;
bool isTxMask = false;
uint8_t txWatermark;
uint32_t readRegRemainingTimes;
uint32_t whichPcs = (transfer->configFlags & LPSPI_SLAVE_PCS_MASK) >> LPSPI_SLAVE_PCS_SHIFT;
uint32_t bytesPerFrame = ((base->TCR & LPSPI_TCR_FRAMESZ_MASK) >> LPSPI_TCR_FRAMESZ_SHIFT) / 8U + 1U;
/* Assign the original value for members of transfer handle. */
handle->state = (uint8_t)kLPSPI_Busy;
handle->txData = transfer->txData;
handle->rxData = transfer->rxData;
handle->txRemainingByteCount = transfer->dataSize;
handle->rxRemainingByteCount = transfer->dataSize;
handle->totalByteCount = transfer->dataSize;
handle->writeRegRemainingTimes = (transfer->dataSize / bytesPerFrame) * ((bytesPerFrame + 3U) / 4U);
handle->readRegRemainingTimes = handle->writeRegRemainingTimes;
/*The TX and RX FIFO sizes are always the same*/
handle->fifoSize = LPSPI_GetRxFifoSize(base);
handle->isByteSwap = ((transfer->configFlags & (uint32_t)kLPSPI_SlaveByteSwap) != 0U);
/*Calculate the bytes for write/read the TX/RX register each time*/
if (bytesPerFrame <= 4U)
{
handle->bytesEachWrite = (uint8_t)bytesPerFrame;
handle->bytesEachRead = (uint8_t)bytesPerFrame;
}
else
{
handle->bytesEachWrite = 4U;
handle->bytesEachRead = 4U;
}
/* Set proper RX and TX watermarks to reduce the ISR response times. */
/*
* $Branch Coverage Justification$
* $ref fsl_lpspi_c_ref_3$
*/
if (handle->fifoSize > 1U)
{
txWatermark = 1U;
handle->rxWatermark = handle->fifoSize / 2U;
}
else
{
txWatermark = 0U;
handle->rxWatermark = 0U;
}
LPSPI_SetFifoWatermarks(base, txWatermark, handle->rxWatermark);
/* If there is no rxData, mask the receive data so that receive data is not stored in receive FIFO. */
if (handle->rxData == NULL)
{
isRxMask = true;
handle->rxRemainingByteCount = 0U;
}
/* If there is no txData, mask the transmit data so that no data is loaded from transmit FIFO and output pin
* is tristated. */
if (handle->txData == NULL)
{
isTxMask = true;
handle->txRemainingByteCount = 0U;
}
/* Enable module for following configuration of TCR to take effect. */
LPSPI_Enable(base, true);
base->TCR = (base->TCR & ~(LPSPI_TCR_CONT_MASK | LPSPI_TCR_CONTC_MASK | LPSPI_TCR_RXMSK_MASK |
LPSPI_TCR_TXMSK_MASK | LPSPI_TCR_PCS_MASK)) |
LPSPI_TCR_RXMSK(isRxMask) | LPSPI_TCR_TXMSK(isTxMask) | LPSPI_TCR_PCS(whichPcs);
/* Enable the NVIC for LPSPI peripheral. Note that below code is useless if the LPSPI interrupt is in INTMUX ,
* and you should also enable the INTMUX interupt in your application.
*/
(void)EnableIRQ(s_lpspiIRQ[LPSPI_GetInstance(base)]);
/*TCR is also shared the FIFO, so wait for TCR written.*/
/*
* $Branch Coverage Justification$
* $ref fsl_lpspi_c_ref_3$
*/
if (!LPSPI_WaitTxFifoEmpty(base))
{
return kStatus_LPSPI_Timeout;
}
/* Fill up the TX data in FIFO */
if (handle->txData != NULL)
{
LPSPI_SlaveTransferFillUpTxFifo(base, handle);
}
/* Since SPI is a synchronous interface, we only need to enable the RX interrupt if there is RX data.
* The IRQ handler will get the status of RX and TX interrupt flags.
*/
if (handle->rxData != NULL)
{
/*Set rxWatermark to (readRegRemainingTimes-1) if readRegRemainingTimes less than rxWatermark. Otherwise there
*is not RX interrupt for the last datas because the RX count is not greater than rxWatermark.
*/
readRegRemainingTimes = handle->readRegRemainingTimes;
if (readRegRemainingTimes <= (uint32_t)handle->rxWatermark)
{
base->FCR = (base->FCR & (~LPSPI_FCR_RXWATER_MASK)) | LPSPI_FCR_RXWATER(readRegRemainingTimes - 1U);
}
/* RX request and FIFO overflow request enable */
LPSPI_EnableInterrupts(base, (uint32_t)kLPSPI_RxInterruptEnable | (uint32_t)kLPSPI_ReceiveErrorInterruptEnable);
}
else
{
LPSPI_EnableInterrupts(base, (uint32_t)kLPSPI_TxInterruptEnable);
}
if (handle->txData != NULL)
{
/* TX FIFO underflow request enable */
LPSPI_EnableInterrupts(base, (uint32_t)kLPSPI_TransmitErrorInterruptEnable);
}
return kStatus_Success;
}
static void LPSPI_SlaveTransferFillUpTxFifo(LPSPI_Type *base, lpspi_slave_handle_t *handle)
{
assert(handle != NULL);
uint32_t wordToSend = 0U;
uint8_t bytesEachWrite = handle->bytesEachWrite;
bool isByteSwap = handle->isByteSwap;
while (LPSPI_GetTxFifoCount(base) < (handle->fifoSize))
{
if (handle->txRemainingByteCount < (size_t)bytesEachWrite)
{
handle->bytesEachWrite = (uint8_t)handle->txRemainingByteCount;
bytesEachWrite = handle->bytesEachWrite;
}
wordToSend = LPSPI_CombineWriteData(handle->txData, bytesEachWrite, isByteSwap);
handle->txData += bytesEachWrite;
/*Decrease the remaining TX byte count.*/
handle->txRemainingByteCount -= (size_t)bytesEachWrite;
/*Write the word to TX register*/
LPSPI_WriteData(base, wordToSend);
if (handle->txRemainingByteCount == 0U)
{
break;
}
}
}
static void LPSPI_SlaveTransferComplete(LPSPI_Type *base, lpspi_slave_handle_t *handle)
{
assert(handle != NULL);
status_t status = kStatus_Success;
/* Disable interrupt requests*/
LPSPI_DisableInterrupts(base, (uint32_t)kLPSPI_AllInterruptEnable);
if (handle->state == (uint8_t)kLPSPI_Error)
{
status = kStatus_LPSPI_Error;
}
else
{
status = kStatus_Success;
}
handle->state = (uint8_t)kLPSPI_Idle;
if (handle->callback != NULL)
{
handle->callback(base, handle, status, handle->userData);
}
}
/*!
* brief Gets the slave transfer remaining bytes.
*
* This function gets the slave transfer remaining bytes.
*
* param base LPSPI peripheral address.
* param handle pointer to lpspi_slave_handle_t structure which stores the transfer state.
* param count Number of bytes transferred so far by the non-blocking transaction.
* return status of status_t.
*/
status_t LPSPI_SlaveTransferGetCount(LPSPI_Type *base, lpspi_slave_handle_t *handle, size_t *count)
{
assert(handle != NULL);
if (NULL == count)
{
return kStatus_InvalidArgument;
}
/* Catch when there is not an active transfer. */
if (handle->state != (uint8_t)kLPSPI_Busy)
{
*count = 0;
return kStatus_NoTransferInProgress;
}
size_t remainingByte;
if (handle->rxData != NULL)
{
remainingByte = handle->rxRemainingByteCount;
}
else
{
remainingByte = handle->txRemainingByteCount;
}
*count = handle->totalByteCount - remainingByte;
return kStatus_Success;
}
/*!
* brief LPSPI slave aborts a transfer which uses an interrupt method.
*
* This function aborts a transfer which uses an interrupt method.
*
* param base LPSPI peripheral address.
* param handle pointer to lpspi_slave_handle_t structure which stores the transfer state.
*/
void LPSPI_SlaveTransferAbort(LPSPI_Type *base, lpspi_slave_handle_t *handle)
{
assert(handle != NULL);
/* Disable interrupt requests*/
LPSPI_DisableInterrupts(base, (uint32_t)kLPSPI_TxInterruptEnable | (uint32_t)kLPSPI_RxInterruptEnable);
LPSPI_Reset(base);
handle->state = (uint8_t)kLPSPI_Idle;
handle->txRemainingByteCount = 0U;
handle->rxRemainingByteCount = 0U;
}
/*!
* brief LPSPI Slave IRQ handler function.
*
* This function processes the LPSPI transmit and receives an IRQ.
*
* param base LPSPI peripheral address.
* param handle pointer to lpspi_slave_handle_t structure which stores the transfer state.
*/
void LPSPI_SlaveTransferHandleIRQ(LPSPI_Type *base, lpspi_slave_handle_t *handle)
{
assert(handle != NULL);
uint32_t readData; /* variable to store word read from RX FIFO */
uint8_t bytesEachRead = handle->bytesEachRead;
bool isByteSwap = handle->isByteSwap;
uint32_t readRegRemainingTimes;
if (handle->rxData != NULL)
{
if (handle->rxRemainingByteCount > 0U)
{
while (LPSPI_GetRxFifoCount(base) != 0U)
{
/*Read out the data*/
readData = LPSPI_ReadData(base);
/*Decrease the read RX register times.*/
--handle->readRegRemainingTimes;
if (handle->rxRemainingByteCount < (size_t)bytesEachRead)
{
handle->bytesEachRead = (uint8_t)handle->rxRemainingByteCount;
bytesEachRead = handle->bytesEachRead;
}
LPSPI_SeparateReadData(handle->rxData, readData, bytesEachRead, isByteSwap);
handle->rxData += bytesEachRead;
/*Decrease the remaining RX byte count.*/
handle->rxRemainingByteCount -= (size_t)bytesEachRead;
if ((handle->txRemainingByteCount > 0U) && (handle->txData != NULL))
{
LPSPI_SlaveTransferFillUpTxFifo(base, handle);
}
if (handle->rxRemainingByteCount == 0U)
{
break;
}
}
}
/*Set rxWatermark to (readRegRemainingTimes-1) if readRegRemainingTimes less than rxWatermark. Otherwise there
*is not RX interrupt for the last datas because the RX count is not greater than rxWatermark.
*/
readRegRemainingTimes = handle->readRegRemainingTimes;
if (readRegRemainingTimes <= (uint32_t)handle->rxWatermark)
{
base->FCR = (base->FCR & (~LPSPI_FCR_RXWATER_MASK)) |
LPSPI_FCR_RXWATER((readRegRemainingTimes > 1U) ? (readRegRemainingTimes - 1U) : (0U));
}
}
if ((handle->rxData == NULL) && (handle->txRemainingByteCount != 0U) && (handle->txData != NULL))
{
LPSPI_SlaveTransferFillUpTxFifo(base, handle);
}
if ((handle->txRemainingByteCount == 0U) && (handle->rxRemainingByteCount == 0U))
{
/* If no RX buffer, then transfer is not complete until transfer complete flag sets and the TX FIFO empty*/
if (handle->rxData == NULL)
{
if (((LPSPI_GetStatusFlags(base) & (uint32_t)kLPSPI_FrameCompleteFlag) != 0U) &&
(LPSPI_GetTxFifoCount(base) == 0U))
{
LPSPI_ClearStatusFlags(base, (uint32_t)kLPSPI_FrameCompleteFlag);
/* Complete the transfer and disable the interrupts */
LPSPI_SlaveTransferComplete(base, handle);
}
else
{
LPSPI_ClearStatusFlags(base, (uint32_t)kLPSPI_FrameCompleteFlag);
LPSPI_EnableInterrupts(base, (uint32_t)kLPSPI_FrameCompleteInterruptEnable);
LPSPI_DisableInterrupts(base, (uint32_t)kLPSPI_TxInterruptEnable | (uint32_t)kLPSPI_RxInterruptEnable);
}
}
else
{
/* Complete the transfer and disable the interrupts */
LPSPI_SlaveTransferComplete(base, handle);
}
}
/* Catch tx fifo underflow conditions, service only if tx under flow interrupt enabled */
/*
* $Branch Coverage Justification$
* $ref fsl_lpspi_c_ref_4$
*/
if (((LPSPI_GetStatusFlags(base) & (uint32_t)kLPSPI_TransmitErrorFlag) != 0U) &&
((base->IER & LPSPI_IER_TEIE_MASK) != 0U))
{
LPSPI_ClearStatusFlags(base, (uint32_t)kLPSPI_TransmitErrorFlag);
/* Change state to error and clear flag */
if (handle->txData != NULL)
{
handle->state = (uint8_t)kLPSPI_Error;
}
handle->errorCount++;
/* ERR051588: Clear FIFO after underrun occurs */
LPSPI_FlushFifo(base, true, false);
}
/* Catch rx fifo overflow conditions, service only if rx over flow interrupt enabled */
/*
* $Branch Coverage Justification$
* $ref fsl_lpspi_c_ref_4$
*/
if (((LPSPI_GetStatusFlags(base) & (uint32_t)kLPSPI_ReceiveErrorFlag) != 0U) &&
((base->IER & LPSPI_IER_REIE_MASK) != 0U))
{
LPSPI_ClearStatusFlags(base, (uint32_t)kLPSPI_ReceiveErrorFlag);
/* Change state to error and clear flag */
/*
* $Branch Coverage Justification$
* This error happens on receive, so rxData won't be empty.(will improve)
*/
if (handle->rxData != NULL)
{
handle->state = (uint8_t)kLPSPI_Error;
}
handle->errorCount++;
}
}
static uint32_t LPSPI_CombineWriteData(uint8_t *txData, uint8_t bytesEachWrite, bool isByteSwap)
{
assert(txData != NULL);
uint32_t wordToSend = 0U;
/*
* $Branch Coverage Justification$
* $ref fsl_lpspi_c_ref_1$
*/
switch (bytesEachWrite)
{
case 1:
wordToSend = *txData;
++txData;
break;
case 2:
if (!isByteSwap)
{
wordToSend = *txData;
++txData;
wordToSend |= (unsigned)(*txData) << 8U;
++txData;
}
else
{
wordToSend = (unsigned)(*txData) << 8U;
++txData;
wordToSend |= *txData;
++txData;
}
break;
case 3:
if (!isByteSwap)
{
wordToSend = *txData;
++txData;
wordToSend |= (unsigned)(*txData) << 8U;
++txData;
wordToSend |= (unsigned)(*txData) << 16U;
++txData;
}
else
{
wordToSend = (unsigned)(*txData) << 16U;
++txData;
wordToSend |= (unsigned)(*txData) << 8U;
++txData;
wordToSend |= *txData;
++txData;
}
break;
case 4:
if (!isByteSwap)
{
wordToSend = *txData;
++txData;
wordToSend |= (unsigned)(*txData) << 8U;
++txData;
wordToSend |= (unsigned)(*txData) << 16U;
++txData;
wordToSend |= (unsigned)(*txData) << 24U;
++txData;
}
else
{
wordToSend = (unsigned)(*txData) << 24U;
++txData;
wordToSend |= (unsigned)(*txData) << 16U;
++txData;
wordToSend |= (unsigned)(*txData) << 8U;
++txData;
wordToSend |= *txData;
++txData;
}
break;
default:
assert(false);
break;
}
return wordToSend;
}
static void LPSPI_SeparateReadData(uint8_t *rxData, uint32_t readData, uint8_t bytesEachRead, bool isByteSwap)
{
assert(rxData != NULL);
/*
* $Branch Coverage Justification$
* $ref fsl_lpspi_c_ref_1$
*/
switch (bytesEachRead)
{
case 1:
*rxData = (uint8_t)readData;
++rxData;
break;
case 2:
if (!isByteSwap)
{
*rxData = (uint8_t)readData;
++rxData;
*rxData = (uint8_t)(readData >> 8);
++rxData;
}
else
{
*rxData = (uint8_t)(readData >> 8);
++rxData;
*rxData = (uint8_t)readData;
++rxData;
}
break;
case 3:
if (!isByteSwap)
{
*rxData = (uint8_t)readData;
++rxData;
*rxData = (uint8_t)(readData >> 8);
++rxData;
*rxData = (uint8_t)(readData >> 16);
++rxData;
}
else
{
*rxData = (uint8_t)(readData >> 16);
++rxData;
*rxData = (uint8_t)(readData >> 8);
++rxData;
*rxData = (uint8_t)readData;
++rxData;
}
break;
case 4:
if (!isByteSwap)
{
*rxData = (uint8_t)readData;
++rxData;
*rxData = (uint8_t)(readData >> 8);
++rxData;
*rxData = (uint8_t)(readData >> 16);
++rxData;
*rxData = (uint8_t)(readData >> 24);
++rxData;
}
else
{
*rxData = (uint8_t)(readData >> 24);
++rxData;
*rxData = (uint8_t)(readData >> 16);
++rxData;
*rxData = (uint8_t)(readData >> 8);
++rxData;
*rxData = (uint8_t)readData;
++rxData;
}
break;
default:
assert(false);
break;
}
}
/*!
* brief Wait for tx FIFO to be empty.
* Wait the tx fifo empty when set TCR register
* param base LPSPI peripheral address.
* return true for the tx FIFO is ready, false is not.
*/
bool LPSPI_WaitTxFifoEmpty(LPSPI_Type *base)
{
#if SPI_RETRY_TIMES
uint32_t waitTimes = SPI_RETRY_TIMES;
while (((uint8_t)LPSPI_GetTxFifoCount(base) != 0U) && (--waitTimes != 0U))
#else
while ((uint8_t)LPSPI_GetTxFifoCount(base) != 0U)
#endif
{
}
#if SPI_RETRY_TIMES
if (waitTimes == 0U)
{
return false;
}
#endif
return true;
}
static void LPSPI_CommonIRQHandler(LPSPI_Type *base, void *param)
{
if (LPSPI_IsMaster(base))
{
s_lpspiMasterIsr(base, (lpspi_master_handle_t *)param);
}
else
{
s_lpspiSlaveIsr(base, (lpspi_slave_handle_t *)param);
}
SDK_ISR_EXIT_BARRIER;
}
#if defined(LPSPI0)
void LPSPI0_DriverIRQHandler(void);
void LPSPI0_DriverIRQHandler(void)
{
assert(s_lpspiHandle[0] != NULL);
LPSPI_CommonIRQHandler(LPSPI0, s_lpspiHandle[0]);
}
#endif
#if defined(LPSPI1)
void LPSPI1_DriverIRQHandler(void);
void LPSPI1_DriverIRQHandler(void)
{
assert(s_lpspiHandle[1] != NULL);
LPSPI_CommonIRQHandler(LPSPI1, s_lpspiHandle[1]);
}
#endif
#if defined(LPSPI2)
void LPSPI2_DriverIRQHandler(void);
void LPSPI2_DriverIRQHandler(void)
{
assert(s_lpspiHandle[2] != NULL);
LPSPI_CommonIRQHandler(LPSPI2, s_lpspiHandle[2]);
}
#endif
#if defined(LPSPI3)
void LPSPI3_DriverIRQHandler(void);
void LPSPI3_DriverIRQHandler(void)
{
assert(s_lpspiHandle[3] != NULL);
LPSPI_CommonIRQHandler(LPSPI3, s_lpspiHandle[3]);
}
#endif
#if defined(LPSPI4)
void LPSPI4_DriverIRQHandler(void);
void LPSPI4_DriverIRQHandler(void)
{
assert(s_lpspiHandle[4] != NULL);
LPSPI_CommonIRQHandler(LPSPI4, s_lpspiHandle[4]);
}
#endif
#if defined(LPSPI5)
void LPSPI5_DriverIRQHandler(void);
void LPSPI5_DriverIRQHandler(void)
{
assert(s_lpspiHandle[5] != NULL);
LPSPI_CommonIRQHandler(LPSPI5, s_lpspiHandle[5]);
}
#endif
#if defined(LPSPI6)
void LPSPI6_DriverIRQHandler(void);
void LPSPI6_DriverIRQHandler(void)
{
assert(s_lpspiHandle[6] != NULL);
LPSPI_CommonIRQHandler(LPSPI6, s_lpspiHandle[6]);
}
#endif
#if defined(DMA__LPSPI0)
void DMA_SPI0_INT_DriverIRQHandler(void);
void DMA_SPI0_INT_DriverIRQHandler(void)
{
assert(s_lpspiHandle[LPSPI_GetInstance(DMA__LPSPI0)] != NULL);
LPSPI_CommonIRQHandler(DMA__LPSPI0, s_lpspiHandle[LPSPI_GetInstance(DMA__LPSPI0)]);
}
#endif
#if defined(DMA__LPSPI1)
void DMA_SPI1_INT_DriverIRQHandler(void);
void DMA_SPI1_INT_DriverIRQHandler(void)
{
assert(s_lpspiHandle[LPSPI_GetInstance(DMA__LPSPI1)] != NULL);
LPSPI_CommonIRQHandler(DMA__LPSPI1, s_lpspiHandle[LPSPI_GetInstance(DMA__LPSPI1)]);
}
#endif
#if defined(DMA__LPSPI2)
void DMA_SPI2_INT_DriverIRQHandler(void);
void DMA_SPI2_INT_DriverIRQHandler(void)
{
assert(s_lpspiHandle[LPSPI_GetInstance(DMA__LPSPI2)] != NULL);
LPSPI_CommonIRQHandler(DMA__LPSPI2, s_lpspiHandle[LPSPI_GetInstance(DMA__LPSPI2)]);
}
#endif
#if defined(DMA__LPSPI3)
void DMA_SPI3_INT_DriverIRQHandler(void);
void DMA_SPI3_INT_DriverIRQHandler(void)
{
assert(s_lpspiHandle[LPSPI_GetInstance(DMA__LPSPI3)] != NULL);
LPSPI_CommonIRQHandler(DMA__LPSPI3, s_lpspiHandle[LPSPI_GetInstance(DMA__LPSPI3)]);
}
#endif
#if defined(ADMA__LPSPI0)
void ADMA_SPI0_INT_DriverIRQHandler(void);
void ADMA_SPI0_INT_DriverIRQHandler(void)
{
assert(s_lpspiHandle[LPSPI_GetInstance(ADMA__LPSPI0)] != NULL);
LPSPI_CommonIRQHandler(ADMA__LPSPI0, s_lpspiHandle[LPSPI_GetInstance(ADMA__LPSPI0)]);
}
#endif
#if defined(ADMA__LPSPI1)
void ADMA_SPI1_INT_DriverIRQHandler(void);
void ADMA_SPI1_INT_DriverIRQHandler(void)
{
assert(s_lpspiHandle[LPSPI_GetInstance(ADMA__LPSPI1)] != NULL);
LPSPI_CommonIRQHandler(ADMA__LPSPI1, s_lpspiHandle[LPSPI_GetInstance(ADMA__LPSPI1)]);
}
#endif
#if defined(ADMA__LPSPI2)
void ADMA_SPI2_INT_DriverIRQHandler(void);
void ADMA_SPI2_INT_DriverIRQHandler(void)
{
assert(s_lpspiHandle[LPSPI_GetInstance(ADMA__LPSPI2)] != NULL);
LPSPI_CommonIRQHandler(ADMA__LPSPI2, s_lpspiHandle[LPSPI_GetInstance(ADMA__LPSPI2)]);
}
#endif
#if defined(ADMA__LPSPI3)
void ADMA_SPI3_INT_DriverIRQHandler(void);
void ADMA_SPI3_INT_DriverIRQHandler(void)
{
assert(s_lpspiHandle[LPSPI_GetInstance(ADMA__LPSPI3)] != NULL);
LPSPI_CommonIRQHandler(ADMA__LPSPI3, s_lpspiHandle[LPSPI_GetInstance(ADMA__LPSPI3)]);
}
#endif