| /* |
| * Copyright (c) 2015, Freescale Semiconductor, Inc. |
| * Copyright 2016-2017 NXP |
| * |
| * Redistribution and use in source and binary forms, with or without modification, |
| * are permitted provided that the following conditions are met: |
| * |
| * o Redistributions of source code must retain the above copyright notice, this list |
| * of conditions and the following disclaimer. |
| * |
| * o Redistributions in binary form must reproduce the above copyright notice, this |
| * list of conditions and the following disclaimer in the documentation and/or |
| * other materials provided with the distribution. |
| * |
| * o Neither the name of the copyright holder nor the names of its |
| * contributors may be used to endorse or promote products derived from this |
| * software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
| * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR |
| * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON |
| * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "fsl_lpspi.h" |
| |
| /******************************************************************************* |
| * Definitions |
| ******************************************************************************/ |
| /*! |
| * @brief Default watermark values. |
| * |
| * The default watermarks are set to zero. |
| */ |
| enum _lpspi_default_watermarks |
| { |
| kLpspiDefaultTxWatermark = 0, |
| kLpspiDefaultRxWatermark = 0, |
| }; |
| |
| /*! @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 Get instance number for LPSPI module. |
| * |
| * @param base LPSPI peripheral base address. |
| */ |
| uint32_t LPSPI_GetInstance(LPSPI_Type *base); |
| |
| /*! |
| * @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, uint32_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, uint32_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 Check the argument for transfer . |
| * This is not a public API. Not static because lpspi_edma.c will use this API. |
| */ |
| bool LPSPI_CheckTransferArgument(lpspi_transfer_t *transfer, uint32_t bitsPerFrame, uint32_t bytesPerFrame); |
| |
| /*! |
| * @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); |
| |
| /******************************************************************************* |
| * 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 */ |
| |
| /*! @brief Pointers to lpspi handles for each instance. */ |
| static void *s_lpspiHandle[ARRAY_SIZE(s_lpspiBases)] = {NULL}; |
| |
| /*! @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 s_dummyData[ARRAY_SIZE(s_lpspiBases)] = {0}; |
| /********************************************************************************************************************** |
| * Code |
| *********************************************************************************************************************/ |
| 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; |
| } |
| |
| void LPSPI_SetDummyData(LPSPI_Type *base, uint8_t dummyData) |
| { |
| uint32_t instance = LPSPI_GetInstance(base); |
| s_dummyData[instance] = dummyData; |
| } |
| |
| void LPSPI_MasterInit(LPSPI_Type *base, const lpspi_master_config_t *masterConfig, uint32_t srcClock_Hz) |
| { |
| assert(masterConfig); |
| |
| 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 */ |
| CLOCK_EnableClock(s_lpspiClocks[instance]); |
| |
| #if defined(LPSPI_PERIPH_CLOCKS) |
| CLOCK_EnableClock(s_LpspiPeriphClocks[instance]); |
| #endif |
| |
| #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ |
| |
| /* Reset to known status */ |
| LPSPI_Reset(base); |
| |
| /* 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_OUTCFG(masterConfig->dataOutConfig) | LPSPI_CFGR1_PINCFG(masterConfig->pinCfg) | |
| LPSPI_CFGR1_NOSTALL(0); |
| |
| /* Set baudrate and delay times*/ |
| LPSPI_MasterSetBaudRate(base, masterConfig->baudRate, srcClock_Hz, &tcrPrescaleValue); |
| |
| /* Set default watermarks */ |
| LPSPI_SetFifoWatermarks(base, kLpspiDefaultTxWatermark, 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 - 1) | |
| LPSPI_TCR_PRESCALE(tcrPrescaleValue) | LPSPI_TCR_PCS(masterConfig->whichPcs); |
| |
| LPSPI_Enable(base, true); |
| |
| LPSPI_MasterSetDelayTimes(base, masterConfig->pcsToSckDelayInNanoSec, kLPSPI_PcsToSck, srcClock_Hz); |
| LPSPI_MasterSetDelayTimes(base, masterConfig->lastSckToPcsDelayInNanoSec, kLPSPI_LastSckToPcs, srcClock_Hz); |
| LPSPI_MasterSetDelayTimes(base, masterConfig->betweenTransferDelayInNanoSec, kLPSPI_BetweenTransfer, srcClock_Hz); |
| |
| LPSPI_SetDummyData(base, LPSPI_DUMMY_DATA); |
| } |
| |
| void LPSPI_MasterGetDefaultConfig(lpspi_master_config_t *masterConfig) |
| { |
| assert(masterConfig); |
| |
| masterConfig->baudRate = 500000; |
| masterConfig->bitsPerFrame = 8; |
| masterConfig->cpol = kLPSPI_ClockPolarityActiveHigh; |
| masterConfig->cpha = kLPSPI_ClockPhaseFirstEdge; |
| masterConfig->direction = kLPSPI_MsbFirst; |
| |
| masterConfig->pcsToSckDelayInNanoSec = 1000000000 / masterConfig->baudRate * 2; |
| masterConfig->lastSckToPcsDelayInNanoSec = 1000000000 / masterConfig->baudRate * 2; |
| masterConfig->betweenTransferDelayInNanoSec = 1000000000 / masterConfig->baudRate * 2; |
| |
| masterConfig->whichPcs = kLPSPI_Pcs0; |
| masterConfig->pcsActiveHighOrLow = kLPSPI_PcsActiveLow; |
| |
| masterConfig->pinCfg = kLPSPI_SdiInSdoOut; |
| masterConfig->dataOutConfig = kLpspiDataOutRetained; |
| } |
| |
| void LPSPI_SlaveInit(LPSPI_Type *base, const lpspi_slave_config_t *slaveConfig) |
| { |
| assert(slaveConfig); |
| |
| #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) |
| |
| uint32_t instance = LPSPI_GetInstance(base); |
| /* Enable LPSPI clock */ |
| CLOCK_EnableClock(s_lpspiClocks[instance]); |
| |
| #if defined(LPSPI_PERIPH_CLOCKS) |
| CLOCK_EnableClock(s_LpspiPeriphClocks[instance]); |
| #endif |
| |
| #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ |
| |
| /* Reset to known status */ |
| LPSPI_Reset(base); |
| |
| 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); |
| |
| LPSPI_SetFifoWatermarks(base, kLpspiDefaultTxWatermark, kLpspiDefaultRxWatermark); |
| |
| base->TCR = LPSPI_TCR_CPOL(slaveConfig->cpol) | LPSPI_TCR_CPHA(slaveConfig->cpha) | |
| LPSPI_TCR_LSBF(slaveConfig->direction) | LPSPI_TCR_FRAMESZ(slaveConfig->bitsPerFrame - 1); |
| |
| /* 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); |
| } |
| |
| void LPSPI_SlaveGetDefaultConfig(lpspi_slave_config_t *slaveConfig) |
| { |
| assert(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; |
| } |
| |
| 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; |
| } |
| |
| 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 */ |
| CLOCK_DisableClock(s_lpspiClocks[instance]); |
| |
| #if defined(LPSPI_PERIPH_CLOCKS) |
| 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 & ~(1U << (LPSPI_CFGR1_PCSPOL_SHIFT + pcs)); |
| |
| /* Configure the PCS polarity bit according to the activeLowOrHigh setting */ |
| base->CFGR1 = cfgr1Value | ((uint32_t)activeLowOrHigh << (LPSPI_CFGR1_PCSPOL_SHIFT + pcs)); |
| } |
| |
| uint32_t LPSPI_MasterSetBaudRate(LPSPI_Type *base, |
| uint32_t baudRate_Bps, |
| uint32_t srcClock_Hz, |
| uint32_t *tcrPrescaleValue) |
| { |
| assert(tcrPrescaleValue); |
| |
| /* 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)) |
| { |
| return 0; |
| } |
| |
| 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 = 0; (prescaler < 8) && min_diff; prescaler++) |
| { |
| for (scaler = 0; (scaler < 256) && min_diff; scaler++) |
| { |
| 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. |
| */ |
| base->CCR = (base->CCR & ~LPSPI_CCR_SCKDIV_MASK) | LPSPI_CCR_SCKDIV(bestScaler); |
| |
| /* return the best prescaler value for user to use later */ |
| *tcrPrescaleValue = bestPrescaler; |
| |
| /* return the actual calculated baud rate */ |
| return bestBaudrate; |
| } |
| |
| void LPSPI_MasterSetDelayScaler(LPSPI_Type *base, uint32_t scaler, lpspi_delay_type_t whichDelay) |
| { |
| /*These settings are only relevant in master mode */ |
| 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); |
| |
| break; |
| default: |
| assert(false); |
| break; |
| } |
| } |
| |
| 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 = 0; |
| } |
| |
| /* 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 initialDelayNanoSec; |
| } |
| |
| /* If min_diff = 0, the exit for loop */ |
| for (scaler = 0; (scaler < 256U) && min_diff; scaler++) |
| { |
| /* 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 *= (scaler + 1 + 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 = realDelay - 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 bestDelay; |
| } |
| |
| /*Transactional APIs -- Master*/ |
| |
| void LPSPI_MasterTransferCreateHandle(LPSPI_Type *base, |
| lpspi_master_handle_t *handle, |
| lpspi_master_transfer_callback_t callback, |
| void *userData) |
| { |
| assert(handle); |
| |
| /* Zero the handle. */ |
| memset(handle, 0, sizeof(*handle)); |
| |
| s_lpspiHandle[LPSPI_GetInstance(base)] = handle; |
| |
| /* Set irq handler. */ |
| s_lpspiMasterIsr = LPSPI_MasterTransferHandleIRQ; |
| |
| handle->callback = callback; |
| handle->userData = userData; |
| } |
| |
| bool LPSPI_CheckTransferArgument(lpspi_transfer_t *transfer, uint32_t bitsPerFrame, uint32_t bytesPerFrame) |
| { |
| assert(transfer); |
| |
| /* If the transfer count is zero, then return immediately.*/ |
| if (transfer->dataSize == 0) |
| { |
| return false; |
| } |
| |
| /* If both send buffer and receive buffer is null */ |
| if ((!(transfer->txData)) && (!(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 <= 4) |
| { |
| if ((transfer->dataSize % bytesPerFrame) != 0) |
| { |
| return false; |
| } |
| } |
| else |
| { |
| if ((bytesPerFrame % 4U) != 0) |
| { |
| if (transfer->dataSize != bytesPerFrame) |
| { |
| return false; |
| } |
| } |
| else |
| { |
| if ((transfer->dataSize % bytesPerFrame) != 0) |
| { |
| return false; |
| } |
| } |
| } |
| |
| return true; |
| } |
| |
| status_t LPSPI_MasterTransferBlocking(LPSPI_Type *base, lpspi_transfer_t *transfer) |
| { |
| assert(transfer); |
| |
| uint32_t bitsPerFrame = ((base->TCR & LPSPI_TCR_FRAMESZ_MASK) >> LPSPI_TCR_FRAMESZ_SHIFT) + 1; |
| uint32_t bytesPerFrame = (bitsPerFrame + 7) / 8; |
| uint32_t temp = 0U; |
| uint8_t dummyData = s_dummyData[LPSPI_GetInstance(base)]; |
| |
| if (!LPSPI_CheckTransferArgument(transfer, bitsPerFrame, bytesPerFrame)) |
| { |
| return kStatus_InvalidArgument; |
| } |
| |
| /* Check that LPSPI is not busy.*/ |
| if (LPSPI_GetStatusFlags(base) & kLPSPI_ModuleBusyFlag) |
| { |
| return kStatus_LPSPI_Busy; |
| } |
| |
| uint8_t *txData = transfer->txData; |
| uint8_t *rxData = transfer->rxData; |
| uint32_t txRemainingByteCount = transfer->dataSize; |
| uint32_t rxRemainingByteCount = transfer->dataSize; |
| |
| uint8_t bytesEachWrite; |
| uint8_t bytesEachRead; |
| |
| uint32_t readData = 0; |
| uint32_t wordToSend = |
| ((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*/ |
| uint32_t fifoSize = LPSPI_GetRxFifoSize(base); |
| |
| uint32_t whichPcs = (transfer->configFlags & LPSPI_MASTER_PCS_MASK) >> LPSPI_MASTER_PCS_SHIFT; |
| |
| bool isPcsContinuous = (bool)(transfer->configFlags & kLPSPI_MasterPcsContinuous); |
| bool isRxMask = false; |
| bool isByteSwap = (bool)(transfer->configFlags & kLPSPI_MasterByteSwap); |
| |
| LPSPI_FlushFifo(base, true, true); |
| LPSPI_ClearStatusFlags(base, kLPSPI_AllStatusFlag); |
| |
| if (!rxData) |
| { |
| isRxMask = true; |
| } |
| |
| LPSPI_Enable(base, false); |
| base->CFGR1 &= (~LPSPI_CFGR1_NOSTALL_MASK); |
| /* Check if using 3-wire mode and the txData is NULL, set the output pin to tristated. */ |
| temp = base->CFGR1; |
| temp &= LPSPI_CFGR1_PINCFG_MASK; |
| if ((temp == LPSPI_CFGR1_PINCFG(kLPSPI_SdiInSdiOut)) || (temp == LPSPI_CFGR1_PINCFG(kLPSPI_SdoInSdoOut))) |
| { |
| if (!txData) |
| { |
| base->CFGR1 |= LPSPI_CFGR1_OUTCFG_MASK; |
| } |
| /* The 3-wire mode can't send and receive data at the same time. */ |
| if ((txData) && (rxData)) |
| { |
| return kStatus_InvalidArgument; |
| } |
| } |
| LPSPI_Enable(base, true); |
| |
| base->TCR = |
| (base->TCR & ~(LPSPI_TCR_CONT_MASK | LPSPI_TCR_CONTC_MASK | LPSPI_TCR_RXMSK_MASK | LPSPI_TCR_PCS_MASK)) | |
| LPSPI_TCR_CONT(isPcsContinuous) | LPSPI_TCR_CONTC(0) | LPSPI_TCR_RXMSK(isRxMask) | LPSPI_TCR_PCS(whichPcs); |
| |
| if (bytesPerFrame <= 4) |
| { |
| bytesEachWrite = bytesPerFrame; |
| bytesEachRead = bytesPerFrame; |
| } |
| else |
| { |
| bytesEachWrite = 4; |
| bytesEachRead = 4; |
| } |
| |
| /*Write the TX data until txRemainingByteCount is equal to 0 */ |
| while (txRemainingByteCount > 0) |
| { |
| if (txRemainingByteCount < bytesEachWrite) |
| { |
| bytesEachWrite = txRemainingByteCount; |
| } |
| |
| /*Wait until TX FIFO is not full*/ |
| while (LPSPI_GetTxFifoCount(base) == fifoSize) |
| { |
| } |
| |
| if (txData) |
| { |
| wordToSend = LPSPI_CombineWriteData(txData, bytesEachWrite, isByteSwap); |
| txData += bytesEachWrite; |
| } |
| |
| LPSPI_WriteData(base, wordToSend); |
| txRemainingByteCount -= bytesEachWrite; |
| |
| /*Check whether there is RX data in RX FIFO . Read out the RX data so that the RX FIFO would not overrun.*/ |
| if (rxData) |
| { |
| while (LPSPI_GetRxFifoCount(base)) |
| { |
| readData = LPSPI_ReadData(base); |
| if (rxRemainingByteCount < bytesEachRead) |
| { |
| bytesEachRead = rxRemainingByteCount; |
| } |
| |
| LPSPI_SeparateReadData(rxData, readData, bytesEachRead, isByteSwap); |
| rxData += bytesEachRead; |
| |
| rxRemainingByteCount -= bytesEachRead; |
| } |
| } |
| } |
| |
| /* After write all the data in TX FIFO , should write the TCR_CONTC to 0 to de-assert the PCS. Note that TCR |
| * register also use the TX FIFO. |
| */ |
| while ((LPSPI_GetTxFifoCount(base) == fifoSize)) |
| { |
| } |
| base->TCR = (base->TCR & ~(LPSPI_TCR_CONTC_MASK)); |
| |
| /*Read out the RX data in FIFO*/ |
| if (rxData) |
| { |
| while (rxRemainingByteCount > 0) |
| { |
| while (LPSPI_GetRxFifoCount(base)) |
| { |
| readData = LPSPI_ReadData(base); |
| |
| if (rxRemainingByteCount < bytesEachRead) |
| { |
| bytesEachRead = rxRemainingByteCount; |
| } |
| |
| LPSPI_SeparateReadData(rxData, readData, bytesEachRead, isByteSwap); |
| rxData += bytesEachRead; |
| |
| rxRemainingByteCount -= bytesEachRead; |
| } |
| } |
| } |
| else |
| { |
| /* If no RX buffer, then transfer is not complete until transfer complete flag sets */ |
| while (!(LPSPI_GetStatusFlags(base) & kLPSPI_TransferCompleteFlag)) |
| { |
| } |
| } |
| |
| return kStatus_Success; |
| } |
| |
| status_t LPSPI_MasterTransferNonBlocking(LPSPI_Type *base, lpspi_master_handle_t *handle, lpspi_transfer_t *transfer) |
| { |
| assert(handle); |
| assert(transfer); |
| |
| uint32_t bitsPerFrame = ((base->TCR & LPSPI_TCR_FRAMESZ_MASK) >> LPSPI_TCR_FRAMESZ_SHIFT) + 1; |
| uint32_t bytesPerFrame = (bitsPerFrame + 7) / 8; |
| uint32_t temp = 0U; |
| uint8_t dummyData = s_dummyData[LPSPI_GetInstance(base)]; |
| |
| if (!LPSPI_CheckTransferArgument(transfer, bitsPerFrame, bytesPerFrame)) |
| { |
| return kStatus_InvalidArgument; |
| } |
| |
| /* Check that we're not busy.*/ |
| if (handle->state == kLPSPI_Busy) |
| { |
| return kStatus_LPSPI_Busy; |
| } |
| |
| handle->state = kLPSPI_Busy; |
| |
| bool isRxMask = false; |
| |
| uint8_t txWatermark; |
| |
| uint32_t whichPcs = (transfer->configFlags & LPSPI_MASTER_PCS_MASK) >> LPSPI_MASTER_PCS_SHIFT; |
| |
| handle->txData = transfer->txData; |
| handle->rxData = transfer->rxData; |
| handle->txRemainingByteCount = transfer->dataSize; |
| handle->rxRemainingByteCount = transfer->dataSize; |
| handle->totalByteCount = transfer->dataSize; |
| |
| handle->writeTcrInIsr = false; |
| |
| handle->writeRegRemainingTimes = (transfer->dataSize / bytesPerFrame) * ((bytesPerFrame + 3) / 4); |
| 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 = (bool)(transfer->configFlags & kLPSPI_MasterPcsContinuous); |
| handle->isByteSwap = (bool)(transfer->configFlags & kLPSPI_MasterByteSwap); |
| |
| /*Set the RX and TX watermarks to reduce the ISR times.*/ |
| if (handle->fifoSize > 1) |
| { |
| txWatermark = 1; |
| handle->rxWatermark = handle->fifoSize - 2; |
| } |
| else |
| { |
| txWatermark = 0; |
| handle->rxWatermark = 0; |
| } |
| |
| LPSPI_SetFifoWatermarks(base, txWatermark, handle->rxWatermark); |
| |
| LPSPI_Enable(base, false); |
| /*Transfers will stall when transmit FIFO is empty or receive FIFO is full. */ |
| base->CFGR1 &= (~LPSPI_CFGR1_NOSTALL_MASK); |
| /* Check if using 3-wire mode and the txData is NULL, set the output pin to tristated. */ |
| temp = base->CFGR1; |
| temp &= LPSPI_CFGR1_PINCFG_MASK; |
| if ((temp == LPSPI_CFGR1_PINCFG(kLPSPI_SdiInSdiOut)) || (temp == LPSPI_CFGR1_PINCFG(kLPSPI_SdoInSdoOut))) |
| { |
| if (!handle->txData) |
| { |
| base->CFGR1 |= LPSPI_CFGR1_OUTCFG_MASK; |
| } |
| /* The 3-wire mode can't send and receive data at the same time. */ |
| if ((handle->txData) && (handle->rxData)) |
| { |
| return kStatus_InvalidArgument; |
| } |
| } |
| LPSPI_Enable(base, true); |
| |
| /*Flush FIFO , clear status , disable all the inerrupts.*/ |
| LPSPI_FlushFifo(base, true, true); |
| LPSPI_ClearStatusFlags(base, kLPSPI_AllStatusFlag); |
| LPSPI_DisableInterrupts(base, kLPSPI_AllInterruptEnable); |
| |
| /* If there is not rxData , can mask the receive data (receive data is not stored in receive FIFO). |
| * For master transfer , we'd better not masked the transmit data in TCR since the transfer flow is hard to |
| * controlled by software.*/ |
| if (handle->rxData == NULL) |
| { |
| isRxMask = true; |
| handle->rxRemainingByteCount = 0; |
| } |
| |
| base->TCR = |
| (base->TCR & ~(LPSPI_TCR_CONT_MASK | LPSPI_TCR_CONTC_MASK | LPSPI_TCR_RXMSK_MASK | LPSPI_TCR_PCS_MASK)) | |
| LPSPI_TCR_CONT(handle->isPcsContinuous) | LPSPI_TCR_CONTC(0) | LPSPI_TCR_RXMSK(isRxMask) | |
| LPSPI_TCR_PCS(whichPcs); |
| |
| /*Calculate the bytes for write/read the TX/RX register each time*/ |
| if (bytesPerFrame <= 4) |
| { |
| handle->bytesEachWrite = bytesPerFrame; |
| handle->bytesEachRead = bytesPerFrame; |
| } |
| else |
| { |
| handle->bytesEachWrite = 4; |
| handle->bytesEachRead = 4; |
| } |
| |
| /* 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. |
| */ |
| EnableIRQ(s_lpspiIRQ[LPSPI_GetInstance(base)]); |
| |
| /*TCR is also shared the FIFO , so wait for TCR written.*/ |
| while (LPSPI_GetTxFifoCount(base) != 0) |
| { |
| } |
| /*Fill up the TX data in FIFO */ |
| 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) |
| { |
| /*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 ((handle->readRegRemainingTimes) <= handle->rxWatermark) |
| { |
| base->FCR = (base->FCR & (~LPSPI_FCR_RXWATER_MASK)) | LPSPI_FCR_RXWATER(handle->readRegRemainingTimes - 1); |
| } |
| |
| LPSPI_EnableInterrupts(base, kLPSPI_RxInterruptEnable); |
| } |
| else |
| { |
| LPSPI_EnableInterrupts(base, kLPSPI_TxInterruptEnable); |
| } |
| |
| return kStatus_Success; |
| } |
| |
| static void LPSPI_MasterTransferFillUpTxFifo(LPSPI_Type *base, lpspi_master_handle_t *handle) |
| { |
| assert(handle); |
| |
| uint32_t wordToSend = 0; |
| |
| /* 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) < (handle->fifoSize)) && |
| (((handle->readRegRemainingTimes - handle->writeRegRemainingTimes) < handle->fifoSize) || |
| (handle->rxData == NULL))) |
| { |
| if (handle->txRemainingByteCount < handle->bytesEachWrite) |
| { |
| handle->bytesEachWrite = handle->txRemainingByteCount; |
| } |
| |
| if (handle->txData) |
| { |
| wordToSend = LPSPI_CombineWriteData(handle->txData, handle->bytesEachWrite, handle->isByteSwap); |
| handle->txData += handle->bytesEachWrite; |
| } |
| else |
| { |
| wordToSend = handle->txBuffIfNull; |
| } |
| |
| /*Write the word to TX register*/ |
| LPSPI_WriteData(base, wordToSend); |
| |
| /*Decrease the write TX register times.*/ |
| --handle->writeRegRemainingTimes; |
| |
| /*Decrease the remaining TX byte count.*/ |
| handle->txRemainingByteCount -= handle->bytesEachWrite; |
| |
| if (handle->txRemainingByteCount == 0) |
| { |
| /* 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) < (handle->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); |
| |
| /* Disable interrupt requests*/ |
| LPSPI_DisableInterrupts(base, kLPSPI_AllInterruptEnable); |
| |
| handle->state = kLPSPI_Idle; |
| |
| if (handle->callback) |
| { |
| handle->callback(base, handle, kStatus_Success, handle->userData); |
| } |
| } |
| |
| status_t LPSPI_MasterTransferGetCount(LPSPI_Type *base, lpspi_master_handle_t *handle, size_t *count) |
| { |
| assert(handle); |
| |
| if (!count) |
| { |
| return kStatus_InvalidArgument; |
| } |
| |
| /* Catch when there is not an active transfer. */ |
| if (handle->state != kLPSPI_Busy) |
| { |
| *count = 0; |
| return kStatus_NoTransferInProgress; |
| } |
| |
| size_t remainingByte; |
| |
| if (handle->rxData) |
| { |
| remainingByte = handle->rxRemainingByteCount; |
| } |
| else |
| { |
| remainingByte = handle->txRemainingByteCount; |
| } |
| |
| *count = handle->totalByteCount - remainingByte; |
| |
| return kStatus_Success; |
| } |
| |
| void LPSPI_MasterTransferAbort(LPSPI_Type *base, lpspi_master_handle_t *handle) |
| { |
| assert(handle); |
| |
| /* Disable interrupt requests*/ |
| LPSPI_DisableInterrupts(base, kLPSPI_AllInterruptEnable); |
| |
| LPSPI_Reset(base); |
| |
| handle->state = kLPSPI_Idle; |
| handle->txRemainingByteCount = 0; |
| handle->rxRemainingByteCount = 0; |
| } |
| |
| void LPSPI_MasterTransferHandleIRQ(LPSPI_Type *base, lpspi_master_handle_t *handle) |
| { |
| assert(handle); |
| |
| uint32_t readData; |
| |
| if (handle->rxData != NULL) |
| { |
| if (handle->rxRemainingByteCount) |
| { |
| /* 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, kLPSPI_RxInterruptEnable); |
| |
| while ((LPSPI_GetRxFifoCount(base)) && (handle->rxRemainingByteCount)) |
| { |
| /*Read out the data*/ |
| readData = LPSPI_ReadData(base); |
| |
| /*Decrease the read RX register times.*/ |
| --handle->readRegRemainingTimes; |
| |
| if (handle->rxRemainingByteCount < handle->bytesEachRead) |
| { |
| handle->bytesEachRead = handle->rxRemainingByteCount; |
| } |
| |
| LPSPI_SeparateReadData(handle->rxData, readData, handle->bytesEachRead, handle->isByteSwap); |
| handle->rxData += handle->bytesEachRead; |
| |
| /*Decrease the remaining RX byte count.*/ |
| handle->rxRemainingByteCount -= handle->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) |
| { |
| /* Set the TDF and RDF interrupt enables simultaneously to avoid race conditions */ |
| LPSPI_EnableInterrupts(base, 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 ((handle->readRegRemainingTimes) <= (handle->rxWatermark)) |
| { |
| base->FCR = |
| (base->FCR & (~LPSPI_FCR_RXWATER_MASK)) | |
| LPSPI_FCR_RXWATER((handle->readRegRemainingTimes > 1) ? (handle->readRegRemainingTimes - 1U) : (0U)); |
| } |
| } |
| |
| if (handle->txRemainingByteCount) |
| { |
| LPSPI_MasterTransferFillUpTxFifo(base, handle); |
| } |
| else |
| { |
| if ((LPSPI_GetTxFifoCount(base) < (handle->fifoSize))) |
| { |
| if ((handle->isPcsContinuous) && (handle->writeTcrInIsr)) |
| { |
| base->TCR = (base->TCR & ~(LPSPI_TCR_CONTC_MASK)); |
| handle->writeTcrInIsr = false; |
| } |
| } |
| } |
| |
| if ((handle->txRemainingByteCount == 0) && (handle->rxRemainingByteCount == 0) && (!handle->writeTcrInIsr)) |
| { |
| /* If no RX buffer, then transfer is not complete until transfer complete flag sets */ |
| if (handle->rxData == NULL) |
| { |
| if (LPSPI_GetStatusFlags(base) & kLPSPI_TransferCompleteFlag) |
| { |
| /* Complete the transfer and disable the interrupts */ |
| LPSPI_MasterTransferComplete(base, handle); |
| } |
| else |
| { |
| LPSPI_EnableInterrupts(base, kLPSPI_TransferCompleteInterruptEnable); |
| LPSPI_DisableInterrupts(base, kLPSPI_TxInterruptEnable | kLPSPI_RxInterruptEnable); |
| } |
| } |
| else |
| { |
| /* Complete the transfer and disable the interrupts */ |
| LPSPI_MasterTransferComplete(base, handle); |
| } |
| } |
| } |
| |
| /*Transactional APIs -- Slave*/ |
| void LPSPI_SlaveTransferCreateHandle(LPSPI_Type *base, |
| lpspi_slave_handle_t *handle, |
| lpspi_slave_transfer_callback_t callback, |
| void *userData) |
| { |
| assert(handle); |
| |
| /* Zero the handle. */ |
| memset(handle, 0, sizeof(*handle)); |
| |
| s_lpspiHandle[LPSPI_GetInstance(base)] = handle; |
| |
| /* Set irq handler. */ |
| s_lpspiSlaveIsr = LPSPI_SlaveTransferHandleIRQ; |
| |
| handle->callback = callback; |
| handle->userData = userData; |
| } |
| |
| status_t LPSPI_SlaveTransferNonBlocking(LPSPI_Type *base, lpspi_slave_handle_t *handle, lpspi_transfer_t *transfer) |
| { |
| assert(handle); |
| assert(transfer); |
| |
| uint32_t bitsPerFrame = ((base->TCR & LPSPI_TCR_FRAMESZ_MASK) >> LPSPI_TCR_FRAMESZ_SHIFT) + 1; |
| uint32_t bytesPerFrame = (bitsPerFrame + 7) / 8; |
| uint32_t temp = 0U; |
| |
| if (!LPSPI_CheckTransferArgument(transfer, bitsPerFrame, bytesPerFrame)) |
| { |
| return kStatus_InvalidArgument; |
| } |
| |
| /* Check that we're not busy.*/ |
| if (handle->state == kLPSPI_Busy) |
| { |
| return kStatus_LPSPI_Busy; |
| } |
| handle->state = kLPSPI_Busy; |
| |
| bool isRxMask = false; |
| bool isTxMask = false; |
| |
| uint32_t whichPcs = (transfer->configFlags & LPSPI_SLAVE_PCS_MASK) >> LPSPI_SLAVE_PCS_SHIFT; |
| |
| 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 + 3) / 4); |
| handle->readRegRemainingTimes = handle->writeRegRemainingTimes; |
| |
| /*The TX and RX FIFO sizes are always the same*/ |
| handle->fifoSize = LPSPI_GetRxFifoSize(base); |
| |
| handle->isByteSwap = (bool)(transfer->configFlags & kLPSPI_SlaveByteSwap); |
| |
| /*Set the RX and TX watermarks to reduce the ISR times.*/ |
| uint8_t txWatermark; |
| if (handle->fifoSize > 1) |
| { |
| txWatermark = 1; |
| handle->rxWatermark = handle->fifoSize - 2; |
| } |
| else |
| { |
| txWatermark = 0; |
| handle->rxWatermark = 0; |
| } |
| LPSPI_SetFifoWatermarks(base, txWatermark, handle->rxWatermark); |
| |
| /* Check if using 3-wire mode and the txData is NULL, set the output pin to tristated. */ |
| temp = base->CFGR1; |
| temp &= LPSPI_CFGR1_PINCFG_MASK; |
| if ((temp == LPSPI_CFGR1_PINCFG(kLPSPI_SdiInSdiOut)) || (temp == LPSPI_CFGR1_PINCFG(kLPSPI_SdoInSdoOut))) |
| { |
| if (!handle->txData) |
| { |
| LPSPI_Enable(base, false); |
| base->CFGR1 |= LPSPI_CFGR1_OUTCFG_MASK; |
| LPSPI_Enable(base, true); |
| } |
| /* The 3-wire mode can't send and receive data at the same time. */ |
| if ((handle->txData) && (handle->rxData)) |
| { |
| return kStatus_InvalidArgument; |
| } |
| } |
| |
| /*Flush FIFO , clear status , disable all the inerrupts.*/ |
| LPSPI_FlushFifo(base, true, true); |
| LPSPI_ClearStatusFlags(base, kLPSPI_AllStatusFlag); |
| LPSPI_DisableInterrupts(base, kLPSPI_AllInterruptEnable); |
| |
| /*If there is not rxData , can mask the receive data (receive data is not stored in receive FIFO).*/ |
| if (handle->rxData == NULL) |
| { |
| isRxMask = true; |
| handle->rxRemainingByteCount = 0; |
| } |
| |
| /*If there is not txData , can mask the transmit data (no data is loaded from transmit FIFO and output pin |
| * is tristated). |
| */ |
| if (handle->txData == NULL) |
| { |
| isTxMask = true; |
| handle->txRemainingByteCount = 0; |
| } |
| |
| 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_CONT(0) | LPSPI_TCR_CONTC(0) | LPSPI_TCR_RXMSK(isRxMask) | LPSPI_TCR_TXMSK(isTxMask) | |
| LPSPI_TCR_PCS(whichPcs); |
| |
| /*Calculate the bytes for write/read the TX/RX register each time*/ |
| if (bytesPerFrame <= 4) |
| { |
| handle->bytesEachWrite = bytesPerFrame; |
| handle->bytesEachRead = bytesPerFrame; |
| } |
| else |
| { |
| handle->bytesEachWrite = 4; |
| handle->bytesEachRead = 4; |
| } |
| |
| /* 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. |
| */ |
| EnableIRQ(s_lpspiIRQ[LPSPI_GetInstance(base)]); |
| |
| /*TCR is also shared the FIFO , so wait for TCR written.*/ |
| while (LPSPI_GetTxFifoCount(base) != 0) |
| { |
| } |
| |
| /*Fill up the TX data in FIFO */ |
| if (handle->txData) |
| { |
| 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) |
| { |
| /*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 ((handle->readRegRemainingTimes) <= handle->rxWatermark) |
| { |
| base->FCR = (base->FCR & (~LPSPI_FCR_RXWATER_MASK)) | LPSPI_FCR_RXWATER(handle->readRegRemainingTimes - 1); |
| } |
| |
| LPSPI_EnableInterrupts(base, kLPSPI_RxInterruptEnable); |
| } |
| else |
| { |
| LPSPI_EnableInterrupts(base, kLPSPI_TxInterruptEnable); |
| } |
| |
| if (handle->rxData) |
| { |
| /* RX FIFO overflow request enable */ |
| LPSPI_EnableInterrupts(base, kLPSPI_ReceiveErrorInterruptEnable); |
| } |
| if (handle->txData) |
| { |
| /* TX FIFO underflow request enable */ |
| LPSPI_EnableInterrupts(base, kLPSPI_TransmitErrorInterruptEnable); |
| } |
| |
| return kStatus_Success; |
| } |
| |
| static void LPSPI_SlaveTransferFillUpTxFifo(LPSPI_Type *base, lpspi_slave_handle_t *handle) |
| { |
| assert(handle); |
| |
| uint32_t wordToSend = 0; |
| |
| while (LPSPI_GetTxFifoCount(base) < (handle->fifoSize)) |
| { |
| if (handle->txRemainingByteCount < handle->bytesEachWrite) |
| { |
| handle->bytesEachWrite = handle->txRemainingByteCount; |
| } |
| |
| wordToSend = LPSPI_CombineWriteData(handle->txData, handle->bytesEachWrite, handle->isByteSwap); |
| handle->txData += handle->bytesEachWrite; |
| |
| /*Decrease the remaining TX byte count.*/ |
| handle->txRemainingByteCount -= handle->bytesEachWrite; |
| |
| /*Write the word to TX register*/ |
| LPSPI_WriteData(base, wordToSend); |
| |
| if (handle->txRemainingByteCount == 0) |
| { |
| break; |
| } |
| } |
| } |
| |
| static void LPSPI_SlaveTransferComplete(LPSPI_Type *base, lpspi_slave_handle_t *handle) |
| { |
| assert(handle); |
| |
| status_t status = 0; |
| |
| /* Disable interrupt requests*/ |
| LPSPI_DisableInterrupts(base, kLPSPI_AllInterruptEnable); |
| |
| if (handle->state == kLPSPI_Error) |
| { |
| status = kStatus_LPSPI_Error; |
| } |
| else |
| { |
| status = kStatus_Success; |
| } |
| |
| handle->state = kLPSPI_Idle; |
| |
| if (handle->callback) |
| { |
| handle->callback(base, handle, status, handle->userData); |
| } |
| } |
| |
| status_t LPSPI_SlaveTransferGetCount(LPSPI_Type *base, lpspi_slave_handle_t *handle, size_t *count) |
| { |
| assert(handle); |
| |
| if (!count) |
| { |
| return kStatus_InvalidArgument; |
| } |
| |
| /* Catch when there is not an active transfer. */ |
| if (handle->state != kLPSPI_Busy) |
| { |
| *count = 0; |
| return kStatus_NoTransferInProgress; |
| } |
| |
| size_t remainingByte; |
| |
| if (handle->rxData) |
| { |
| remainingByte = handle->rxRemainingByteCount; |
| } |
| else |
| { |
| remainingByte = handle->txRemainingByteCount; |
| } |
| |
| *count = handle->totalByteCount - remainingByte; |
| |
| return kStatus_Success; |
| } |
| |
| void LPSPI_SlaveTransferAbort(LPSPI_Type *base, lpspi_slave_handle_t *handle) |
| { |
| assert(handle); |
| |
| /* Disable interrupt requests*/ |
| LPSPI_DisableInterrupts(base, kLPSPI_TxInterruptEnable | kLPSPI_RxInterruptEnable); |
| |
| LPSPI_Reset(base); |
| |
| handle->state = kLPSPI_Idle; |
| handle->txRemainingByteCount = 0; |
| handle->rxRemainingByteCount = 0; |
| } |
| |
| void LPSPI_SlaveTransferHandleIRQ(LPSPI_Type *base, lpspi_slave_handle_t *handle) |
| { |
| assert(handle); |
| |
| uint32_t readData; /* variable to store word read from RX FIFO */ |
| uint32_t wordToSend; /* variable to store word to write to TX FIFO */ |
| |
| if (handle->rxData != NULL) |
| { |
| if (handle->rxRemainingByteCount > 0) |
| { |
| while (LPSPI_GetRxFifoCount(base)) |
| { |
| /*Read out the data*/ |
| readData = LPSPI_ReadData(base); |
| |
| /*Decrease the read RX register times.*/ |
| --handle->readRegRemainingTimes; |
| |
| if (handle->rxRemainingByteCount < handle->bytesEachRead) |
| { |
| handle->bytesEachRead = handle->rxRemainingByteCount; |
| } |
| |
| LPSPI_SeparateReadData(handle->rxData, readData, handle->bytesEachRead, handle->isByteSwap); |
| handle->rxData += handle->bytesEachRead; |
| |
| /*Decrease the remaining RX byte count.*/ |
| handle->rxRemainingByteCount -= handle->bytesEachRead; |
| |
| if ((handle->txRemainingByteCount > 0) && (handle->txData != NULL)) |
| { |
| if (handle->txRemainingByteCount < handle->bytesEachWrite) |
| { |
| handle->bytesEachWrite = handle->txRemainingByteCount; |
| } |
| |
| wordToSend = LPSPI_CombineWriteData(handle->txData, handle->bytesEachWrite, handle->isByteSwap); |
| handle->txData += handle->bytesEachWrite; |
| |
| /*Decrease the remaining TX byte count.*/ |
| handle->txRemainingByteCount -= handle->bytesEachWrite; |
| |
| /*Write the word to TX register*/ |
| LPSPI_WriteData(base, wordToSend); |
| } |
| |
| if (handle->rxRemainingByteCount == 0) |
| { |
| 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. |
| */ |
| if ((handle->readRegRemainingTimes) <= (handle->rxWatermark)) |
| { |
| base->FCR = |
| (base->FCR & (~LPSPI_FCR_RXWATER_MASK)) | |
| LPSPI_FCR_RXWATER((handle->readRegRemainingTimes > 1) ? (handle->readRegRemainingTimes - 1U) : (0U)); |
| } |
| } |
| else if ((handle->txRemainingByteCount) && (handle->txData != NULL)) |
| { |
| LPSPI_SlaveTransferFillUpTxFifo(base, handle); |
| } |
| else |
| { |
| __NOP(); |
| } |
| |
| if ((handle->txRemainingByteCount == 0) && (handle->rxRemainingByteCount == 0)) |
| { |
| /* 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) & kLPSPI_FrameCompleteFlag) && (LPSPI_GetTxFifoCount(base) == 0)) |
| { |
| /* Complete the transfer and disable the interrupts */ |
| LPSPI_SlaveTransferComplete(base, handle); |
| } |
| else |
| { |
| LPSPI_ClearStatusFlags(base, kLPSPI_FrameCompleteFlag); |
| LPSPI_EnableInterrupts(base, kLPSPI_FrameCompleteInterruptEnable); |
| LPSPI_DisableInterrupts(base, kLPSPI_TxInterruptEnable | 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 */ |
| if ((LPSPI_GetStatusFlags(base) & kLPSPI_TransmitErrorFlag) && (base->IER & LPSPI_IER_TEIE_MASK)) |
| { |
| LPSPI_ClearStatusFlags(base, kLPSPI_TransmitErrorFlag); |
| /* Change state to error and clear flag */ |
| if (handle->txData) |
| { |
| handle->state = kLPSPI_Error; |
| } |
| handle->errorCount++; |
| } |
| /* Catch rx fifo overflow conditions, service only if rx over flow interrupt enabled */ |
| if ((LPSPI_GetStatusFlags(base) & kLPSPI_ReceiveErrorFlag) && (base->IER & LPSPI_IER_REIE_MASK)) |
| { |
| LPSPI_ClearStatusFlags(base, kLPSPI_ReceiveErrorFlag); |
| /* Change state to error and clear flag */ |
| if (handle->txData) |
| { |
| handle->state = kLPSPI_Error; |
| } |
| handle->errorCount++; |
| } |
| } |
| |
| static uint32_t LPSPI_CombineWriteData(uint8_t *txData, uint32_t bytesEachWrite, bool isByteSwap) |
| { |
| assert(txData); |
| |
| uint32_t wordToSend = 0; |
| |
| 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, uint32_t bytesEachRead, bool isByteSwap) |
| { |
| assert(rxData); |
| |
| switch (bytesEachRead) |
| { |
| case 1: |
| *rxData = readData; |
| ++rxData; |
| break; |
| |
| case 2: |
| if (!isByteSwap) |
| { |
| *rxData = readData; |
| ++rxData; |
| *rxData = readData >> 8; |
| ++rxData; |
| } |
| else |
| { |
| *rxData = readData >> 8; |
| ++rxData; |
| *rxData = readData; |
| ++rxData; |
| } |
| break; |
| |
| case 3: |
| if (!isByteSwap) |
| { |
| *rxData = readData; |
| ++rxData; |
| *rxData = readData >> 8; |
| ++rxData; |
| *rxData = readData >> 16; |
| ++rxData; |
| } |
| else |
| { |
| *rxData = readData >> 16; |
| ++rxData; |
| *rxData = readData >> 8; |
| ++rxData; |
| *rxData = readData; |
| ++rxData; |
| } |
| break; |
| |
| case 4: |
| if (!isByteSwap) |
| { |
| *rxData = readData; |
| ++rxData; |
| *rxData = readData >> 8; |
| ++rxData; |
| *rxData = readData >> 16; |
| ++rxData; |
| *rxData = readData >> 24; |
| ++rxData; |
| } |
| else |
| { |
| *rxData = readData >> 24; |
| ++rxData; |
| *rxData = readData >> 16; |
| ++rxData; |
| *rxData = readData >> 8; |
| ++rxData; |
| *rxData = readData; |
| ++rxData; |
| } |
| break; |
| |
| default: |
| assert(false); |
| break; |
| } |
| } |
| |
| 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); |
| } |
| /* Add for ARM errata 838869, affects Cortex-M4, Cortex-M4F Store immediate overlapping |
| exception return operation might vector to incorrect interrupt */ |
| #if defined __CORTEX_M && (__CORTEX_M == 4U) |
| __DSB(); |
| #endif |
| } |
| |
| #if defined(LPSPI0) |
| void LPSPI0_DriverIRQHandler(void) |
| { |
| assert(s_lpspiHandle[0]); |
| LPSPI_CommonIRQHandler(LPSPI0, s_lpspiHandle[0]); |
| } |
| #endif |
| |
| #if defined(LPSPI1) |
| void LPSPI1_DriverIRQHandler(void) |
| { |
| assert(s_lpspiHandle[1]); |
| LPSPI_CommonIRQHandler(LPSPI1, s_lpspiHandle[1]); |
| } |
| #endif |
| |
| #if defined(LPSPI2) |
| void LPSPI2_DriverIRQHandler(void) |
| { |
| assert(s_lpspiHandle[2]); |
| LPSPI_CommonIRQHandler(LPSPI2, s_lpspiHandle[2]); |
| } |
| #endif |
| |
| #if defined(LPSPI3) |
| void LPSPI3_DriverIRQHandler(void) |
| { |
| assert(s_lpspiHandle[3]); |
| LPSPI_CommonIRQHandler(LPSPI3, s_lpspiHandle[3]); |
| } |
| #endif |
| |
| #if defined(LPSPI4) |
| void LPSPI4_DriverIRQHandler(void) |
| { |
| assert(s_lpspiHandle[4]); |
| LPSPI_CommonIRQHandler(LPSPI4, s_lpspiHandle[4]); |
| } |
| #endif |
| |
| #if defined(LPSPI5) |
| void LPSPI5_DriverIRQHandler(void) |
| { |
| assert(s_lpspiHandle[5]); |
| LPSPI_CommonIRQHandler(LPSPI5, s_lpspiHandle[5]); |
| } |
| #endif |
| |
| #if defined(DMA__LPSPI0) |
| void DMA_SPI0_INT_DriverIRQHandler(void) |
| { |
| assert(s_lpspiHandle[LPSPI_GetInstance(DMA__LPSPI0)]); |
| LPSPI_CommonIRQHandler(DMA__LPSPI0, s_lpspiHandle[LPSPI_GetInstance(DMA__LPSPI0)]); |
| } |
| #endif |
| |
| #if defined(DMA__LPSPI1) |
| void DMA_SPI1_INT_DriverIRQHandler(void) |
| { |
| assert(s_lpspiHandle[LPSPI_GetInstance(DMA__LPSPI1)]); |
| LPSPI_CommonIRQHandler(DMA__LPSPI1, s_lpspiHandle[LPSPI_GetInstance(DMA__LPSPI1)]); |
| } |
| #endif |
| #if defined(DMA__LPSPI2) |
| void DMA_SPI2_INT_DriverIRQHandler(void) |
| { |
| assert(s_lpspiHandle[LPSPI_GetInstance(DMA__LPSPI2)]); |
| LPSPI_CommonIRQHandler(DMA__LPSPI2, s_lpspiHandle[LPSPI_GetInstance(DMA__LPSPI2)]); |
| } |
| #endif |
| |
| #if defined(DMA__LPSPI3) |
| void DMA_SPI3_INT_DriverIRQHandler(void) |
| { |
| assert(s_lpspiHandle[LPSPI_GetInstance(DMA__LPSPI3)]); |
| LPSPI_CommonIRQHandler(DMA__LPSPI3, s_lpspiHandle[LPSPI_GetInstance(DMA__LPSPI3)]); |
| } |
| #endif |