예제 #1
0
void SpiFormat(Spi_t *obj, int8_t bits, int8_t cpol, int8_t cpha, int8_t slave)
{
    spi_clock_polarity_t clockPolarity;
    spi_clock_phase_t clockPhase;
    spi_data_bitcount_mode_t bitCount;

    /* Disable Spi module */
    SPI_HAL_Disable(obj->Spi);

    clockPolarity = ((cpol != 1) ? kSpiClockPolarity_ActiveHigh : kSpiClockPolarity_ActiveLow);
    clockPhase = ((cpha != 1) ? kSpiClockPhase_FirstEdge : kSpiClockPhase_SecondEdge);
    bitCount = ((bits != 8) ? kSpi16BitMode : kSpi8BitMode);

    if (obj->isSlave) {
        /* Set SPI to master mode */
        SPI_HAL_SetMasterSlave(obj->Spi, kSpiSlave);
    } else {
        /* Set SPI to master mode */
        SPI_HAL_SetMasterSlave(obj->Spi, kSpiMaster);
        // Set slave select to automatic output mode
        SPI_HAL_SetSlaveSelectOutputMode(obj->Spi, kSpiSlaveSelect_AutomaticOutput);
    }

    SPI_HAL_SetDataFormat(obj->Spi, clockPolarity, clockPhase, kSpiMsbFirst);
    SPI_HAL_Set8or16BitMode(obj->Spi, bitCount);

    /* Enable Spi module */
    SPI_HAL_Enable(obj->Spi);
}
예제 #2
0
void SpiFrequency(Spi_t *obj, uint32_t hz)
{
    uint32_t spiSourceClock;

    spiSourceClock = CLOCK_SYS_GetSpiFreq(obj->instance);

    /* Disable Spi module */
    SPI_HAL_Disable(obj->Spi);

    SPI_HAL_SetBaud(obj->Spi, hz, spiSourceClock);

    /* Enable Spi module */
    SPI_HAL_Enable(obj->Spi);
}
예제 #3
0
/*FUNCTION**********************************************************************
 *
 * Function Name : SPI_DRV_MasterInit
 * Description   : Initialize a SPI instance for master mode operation.
 * This function uses a CPU interrupt driven method for transferring data.
 * This function initializes the run-time state structure to track the ongoing
 * transfers, ungates the clock to the SPI module, resets and initializes the module
 * to default settings, configures the IRQ state structure, enables
 * the module-level interrupt to the core, and enables the SPI module.
 *
 *END**************************************************************************/
spi_status_t SPI_DRV_MasterInit(uint32_t instance, spi_master_state_t * spiState)
{
    SPI_Type *base = g_spiBase[instance];

    /* Clear the state for this instance.*/
    memset(spiState, 0, sizeof(* spiState));

    /* Enable clock for SPI*/
    CLOCK_SYS_EnableSpiClock(instance);

    /* configure the run-time state struct with the source clock value */
    spiState->spiSourceClock = CLOCK_SYS_GetSpiFreq(instance);

    /* Reset the SPI module to it's default state, which includes SPI disabled */
    SPI_HAL_Init(base);

    /* Init the interrupt sync object.*/
    OSA_SemaCreate(&spiState->irqSync, 0);

    /* Set SPI to master mode */
    SPI_HAL_SetMasterSlave(base, kSpiMaster);

    /* Set slave select to automatic output mode */
    SPI_HAL_SetSlaveSelectOutputMode(base, kSpiSlaveSelect_AutomaticOutput);

    /* Set the SPI pin mode to normal mode */
    SPI_HAL_SetPinMode(base, kSpiPinMode_Normal);

#if FSL_FEATURE_SPI_FIFO_SIZE
    if (g_spiFifoSize[instance] != 0)
    {
        /* If SPI module contains a FIFO, enable it and set watermarks to half full/empty */
        SPI_HAL_SetFifoMode(base, true, kSpiTxFifoOneHalfEmpty, kSpiRxFifoOneHalfFull);
    }
#endif
    /* Save runtime structure pointers to irq handler can point to the correct state structure*/
    g_spiStatePtr[instance] = spiState;

    /* Enable SPI interrupt.*/
    INT_SYS_EnableIRQ(g_spiIrqId[instance]);

    /* SPI system Enable*/
    SPI_HAL_Enable(base);

    return kStatus_SPI_Success;
}
예제 #4
0
void SpiInit(Spi_t *obj, PinNames mosi, PinNames miso, PinNames sclk, PinNames nss)
{
    /* Check if a proper channel was selected */
    if (g_spiBase[obj->instance] == NULL) return;

    obj->Spi = g_spiBase[obj->instance];

    GpioInit(&obj->Mosi, mosi, PIN_ALTERNATE_FCT, PIN_PUSH_PULL, PIN_PULL_DOWN, 0);
    GpioInit(&obj->Miso, miso, PIN_ALTERNATE_FCT, PIN_PUSH_PULL, PIN_PULL_DOWN, 0);
    GpioInit(&obj->Sclk, sclk, PIN_ALTERNATE_FCT, PIN_PUSH_PULL, PIN_PULL_DOWN, 0);
    if (nss != NC) GpioInit(&obj->Nss, nss, PIN_ALTERNATE_FCT, PIN_PUSH_PULL, PIN_PULL_DOWN, 1);

    /* Enable clock for SPI.*/
    CLOCK_SYS_EnableSpiClock(obj->instance);

    // Initialize the SPI module registers to default value, which disables the module
    SPI_HAL_Init(obj->Spi);

    // Set the SPI pin mode to normal mode
    SPI_HAL_SetPinMode(obj->Spi, kSpiPinMode_Normal);

#if FSL_FEATURE_SPI_FIFO_SIZE
    if (g_spiFifoSize[obj->instance] != 0)
    {
        // If SPI module contains a FIFO, disable it and set watermarks to half full/empty
        SPI_HAL_SetFifoMode(obj->Spi, false, kSpiTxFifoOneHalfEmpty, kSpiRxFifoOneHalfFull);
    }
#endif

    if (!obj->isSlave) {
        // 8 bits, CPOL = 0, CPHA = 0, MASTER
        SpiFormat(obj, 8, 0, 0, 0);
    } else {
        // 8 bits, CPOL = 0, CPHA = 0, SLAVE
        SpiFormat(obj, 8, 0, 0, 1);
    }
    SpiFrequency(obj, 10000000);

    /* Enable Spi module */
    SPI_HAL_Enable(obj->Spi);
}
예제 #5
0
uint16_t SpiInOut(Spi_t *obj, uint16_t outData)
{
    uint16_t data;

    if ((obj == NULL) || (obj->Spi) == NULL) {
        while (1) {
        }
    }

    // Disable module to clear the shift register
    SPI_HAL_Disable(obj->Spi);
    SPI_HAL_Enable(obj->Spi);

    SPI_HAL_WriteDataBlocking(obj->Spi, kSpi8BitMode, 0, (uint8_t)(outData & 0xFF));

    // Wait for slave send data back
    while (SPI_HAL_IsReadBuffFullPending(obj->Spi) == 0) {
    }
    data = SPI_HAL_ReadDataLow(obj->Spi);

    return data;
}
/*FUNCTION**********************************************************************
 *
 * Function Name : SPI_DRV_DmaSlaveStartTransfer
 * Description   : Starts the transfer with information passed.
 *
 *END**************************************************************************/
static spi_status_t SPI_DRV_DmaSlaveStartTransfer(uint32_t instance)
{
    spi_dma_slave_state_t * spiState = (spi_dma_slave_state_t *)g_spiStatePtr[instance];

    /* For temporarily storing DMA register channel */
    void * param;
    SPI_Type *base = g_spiBase[instance];
    uint32_t transferSizeInBytes;  /* DMA transfer size in bytes */

    /* Initialize s_byteToSend */
    s_byteToSend = spiState->dummyPattern;

    /* If the transfer count is zero, then return immediately. */
    if (spiState->remainingSendByteCount == 0)
    {
        /* Signal the synchronous completion object if the transfer wasn't async.
         * Otherwise, when we return the the sync function we'll get stuck in the sync wait loop.
         */
        if (spiState->isSync)
        {
            /* Signal the synchronous completion object */
            OSA_EventSet(&spiState->event, kSpiDmaTransferDone);
        }
        return kStatus_SPI_Success;
    }

    /* In order to flush any remaining data in the shift register, disable then enable the SPI */
    SPI_HAL_Disable(base);
    SPI_HAL_Enable(base);

    /* First, set the DMA transfer size in bytes */
#if FSL_FEATURE_SPI_16BIT_TRANSFERS
    if (SPI_HAL_Get8or16BitMode(base) == kSpi16BitMode)
    {
        transferSizeInBytes = 2;
        /* If bits/frame > 8, meaning 2 bytes, then the transfer byte count must not be an odd
         * count. If so, drop the last odd byte. This odd byte will be transferred in when dma
         * completed
         */
        if (spiState->remainingSendByteCount % 2 != 0)
        {
            spiState->remainingSendByteCount ++;
            spiState->remainingReceiveByteCount --;
            spiState->hasExtraByte = true;
        }
        else
        {
            spiState->hasExtraByte = false;
        }
    }
    else
    {
        transferSizeInBytes = 1;
    }
#else
    transferSizeInBytes = 1;
#endif /* FSL_FEATURE_SPI_16BIT_TRANSFERS */

    param = (void *)(instance);     /* For DMA callback, set "param" as the SPI instance number */

    /* Save information about the transfer for use by the ISR. */
    spiState->isTransferInProgress = true;

    /************************************************************************************
     * Set up the RX DMA channel Transfer Control Descriptor (TCD)
     * Note, if there is no receive buffer (if user passes in NULL), then bypass RX DMA
     * set up.
     ************************************************************************************/
    /* If no receive buffer then disable incrementing the destination and set the destination
     * to a temporary location
     */
    if ((spiState->remainingReceiveByteCount > 0) || (spiState->hasExtraByte))
    {
        uint32_t receiveSize = spiState->remainingReceiveByteCount;
        if ((!spiState->receiveBuffer) || (!spiState->remainingReceiveByteCount))
        {
            if (!spiState->remainingReceiveByteCount)
            {
                /* If receive count is 0, always receive 1 frame (2 bytes) */
                receiveSize = 2;
            }
            /* Set up this channel's control which includes enabling the DMA interrupt */
            DMA_DRV_ConfigTransfer(&spiState->dmaReceive,
                                   kDmaPeripheralToPeripheral,
                                   transferSizeInBytes,
                                   SPI_HAL_GetDataRegAddr(base), /* src is data register */
                                   (uint32_t)(&s_rxBuffIfNull), /* dest is temporary location */
                                   (uint32_t)(receiveSize));
        }
        else
        {
            /* Set up this channel's control which includes enabling the DMA interrupt */
            DMA_DRV_ConfigTransfer(&spiState->dmaReceive,
                                   kDmaPeripheralToMemory,
                                   transferSizeInBytes,
                                   SPI_HAL_GetDataRegAddr(base), /* src is data register */
                                   (uint32_t)(spiState->receiveBuffer), /* dest is rx buffer */
                                   (uint32_t)(receiveSize));
        }
        /* Destination size is only one byte */
        DMA_DRV_SetDestTransferSize(&spiState->dmaReceive, 1U);

        /* Enable the DMA peripheral request */
        DMA_DRV_StartChannel(&spiState->dmaReceive);

        /* Register callback for DMA interrupt */
        DMA_DRV_RegisterCallback(&spiState->dmaReceive, SPI_DRV_DmaSlaveCallback, param);
    }

    /************************************************************************************
     * Set up the TX DMA channel Transfer Control Descriptor (TCD)
     * Note, if there is no source buffer (if user passes in NULL), then send zeros
     ************************************************************************************/
    /* Per the reference manual, before enabling the SPI transmit DMA request, we first need
     * to read the status register and then write to the SPI data register.  Afterwards, we need
     * to decrement the sendByteCount and perform other driver maintenance functions.
     */
    /* Read the SPI Status register */
    SPI_HAL_IsTxBuffEmptyPending(base);

    /* Start the transfer by writing the first byte/word to the SPI data register.
     * If a send buffer was provided, the byte/word comes from there. Otherwise we just send zeros.
     */
#if FSL_FEATURE_SPI_16BIT_TRANSFERS
    if (transferSizeInBytes == 2) /* 16-bit transfers for SPI16 module */
    {
        if (spiState->sendBuffer)
        {
            s_byteToSend = *(spiState->sendBuffer);
            SPI_HAL_WriteDataLow(base, s_byteToSend);
            ++spiState->sendBuffer;

            s_byteToSend = *(spiState->sendBuffer);
            SPI_HAL_WriteDataHigh(base, s_byteToSend);
            ++spiState->sendBuffer;
        }
        else  /* Else, if no send buffer, write zeros */
        {
            SPI_HAL_WriteDataLow(base, s_byteToSend);
            SPI_HAL_WriteDataHigh(base, s_byteToSend);
        }
        spiState->remainingSendByteCount -= 2;  /* Decrement the send byte count by 2 */
    }
    else /* 8-bit transfers for SPI16 module */
    {
        if (spiState->sendBuffer)
        {
            s_byteToSend = *(spiState->sendBuffer);
            ++spiState->sendBuffer;
        }
        SPI_HAL_WriteDataLow(base, s_byteToSend);  /* If no send buffer, s_byteToSend=0 */
        --spiState->remainingSendByteCount; /* Decrement the send byte count for use in DMA setup */
    }
#else
    /* For SPI modules that do not support 16-bit transfers */
    if (spiState->sendBuffer)
    {
        s_byteToSend = *(spiState->sendBuffer);
        ++spiState->sendBuffer;
    }
    SPI_HAL_WriteData(base, s_byteToSend); /* If no send buffer, s_byteToSend=0 */
    --spiState->remainingSendByteCount; /* Decrement the send byte count for use in DMA setup */
#endif /* FSL_FEATURE_SPI_16BIT_TRANSFERS */

    /* If there are no more bytes to send then return without setting up the TX DMA channel.
     * Else, set up the TX DMA channel and enable the TX DMA request.
     */
    if (!spiState->remainingSendByteCount) /* No more bytes to send */
    {
        if ((spiState->remainingReceiveByteCount) || (spiState->hasExtraByte))
        {
            /* Enable the RX DMA channel request now */
            SPI_HAL_SetRxDmaCmd(base, true);
            return kStatus_SPI_Success;
        }
        else    /* If RX DMA chan not setup then enable the interrupt to get the received byte */
        {
            SPI_HAL_SetIntMode(base, kSpiTxEmptyInt, true);
            return kStatus_SPI_Success;
        }
    }
    else  /* Since there are more bytes to send, set up the TX DMA channel */
    {
        /* If there is a send buffer, data comes from there, else send 0 */
        if (spiState->sendBuffer)
        {
            /* Set up this channel's control which includes enabling the DMA interrupt */
            DMA_DRV_ConfigTransfer(&spiState->dmaTransmit, kDmaMemoryToPeripheral,
                                   transferSizeInBytes,
                                   (uint32_t)(spiState->sendBuffer),
                                   SPI_HAL_GetDataRegAddr(base),
                                   (uint32_t)(spiState->remainingSendByteCount));
        }
        else /* Configure TX DMA channel to send zeros */
        {
            /* Set up this channel's control which includes enabling the DMA interrupt */
            DMA_DRV_ConfigTransfer(&spiState->dmaTransmit, kDmaPeripheralToPeripheral,
                                   transferSizeInBytes,
                                   (uint32_t)(&s_byteToSend),
                                   SPI_HAL_GetDataRegAddr(base),
                                   (uint32_t)(spiState->remainingSendByteCount));
        }
        /* Source size is only one byte */
        DMA_DRV_SetSourceTransferSize(&spiState->dmaTransmit, 1U);

        /* Enable the SPI TX DMA Request */
        SPI_HAL_SetTxDmaCmd(base, true);

        /* Enable the SPI RX DMA request also. */
        SPI_HAL_SetRxDmaCmd(base, true);

        /* Enable the DMA peripheral request */
        DMA_DRV_StartChannel(&spiState->dmaTransmit);
    }

    return kStatus_SPI_Success;
}
/*FUNCTION**********************************************************************
 *
 * Function Name : SPI_DRV_DmaSlaveInit
 * Description   : Initializes the SPI module for slave mode.
 * Saves the application callback info, turns on the clock to the module,
 * enables the device, and enables interrupts. Sets the SPI to a slave mode.
 *
 *END**************************************************************************/
spi_status_t SPI_DRV_DmaSlaveInit(uint32_t instance, spi_dma_slave_state_t * spiState,
                       const spi_dma_slave_user_config_t * slaveConfig)
{
    SPI_Type *base = g_spiBase[instance];
    assert(slaveConfig);
    assert(instance < SPI_INSTANCE_COUNT);
    assert(spiState);

#if FSL_FEATURE_SPI_16BIT_TRANSFERS
    if (slaveConfig->bitCount > kSpi16BitMode)
    {
        /* bits/frame larger than hardware support */
        return kStatus_SPI_InvalidParameter;
    }
#endif /* FSL_FEATURE_SPI_16BIT_TRANSFERS */

    /* Check if the slave already initialized */
    if (g_spiStatePtr[instance])
    {
        return kStatus_SPI_AlreadyInitialized;
    }

    /* Clear the state for this instance. */
    memset(spiState, 0, sizeof(* spiState));

    spiState->hasExtraByte = false;

    /* Update dummy pattern value */
    spiState->dummyPattern = slaveConfig->dummyPattern;

    /* Enable clock for SPI */
    CLOCK_SYS_EnableSpiClock(instance);

    /* Reset the SPI module to its default settings including disabling SPI */
    SPI_HAL_Init(base);

    /* Initialize the event structure */
    if (OSA_EventCreate(&spiState->event, kEventAutoClear) != kStatus_OSA_Success)
    {
        return kStatus_SPI_Error;
    }

    /* Set SPI to slave mode */
    SPI_HAL_SetMasterSlave(base, kSpiSlave);

    /* Configure the slave clock polarity, phase and data direction */
    SPI_HAL_SetDataFormat(base, slaveConfig->polarity, slaveConfig->phase,
            slaveConfig->direction);

    /* Set the SPI pin mode to normal mode */
    SPI_HAL_SetPinMode(base, kSpiPinMode_Normal);

#if FSL_FEATURE_SPI_16BIT_TRANSFERS
    SPI_HAL_Set8or16BitMode(base, slaveConfig->bitCount);
#endif /* FSL_FEATURE_SPI_16BIT_TRANSFERS */

#if FSL_FEATURE_SPI_FIFO_SIZE
    if (g_spiFifoSize[instance] != 0)
    {
        /* If SPI module contains a FIFO, enable it and set watermarks to half full/empty */
        SPI_HAL_SetFifoMode(base, true, kSpiTxFifoOneHalfEmpty, kSpiRxFifoOneHalfFull);

        /* Set the interrupt clearing mechansim select for later use in driver to clear
         * status flags
         */
        SPI_HAL_SetIntClearCmd(base, true);
    }
#endif /* FSL_FEATURE_SPI_FIFO_SIZE */

    /*****************************************
     * Request DMA channel for RX and TX FIFO
     *****************************************/
    /* This channel transfers data from RX FIFO to receiveBuffer */
    if (instance == 0)
    {
        /* Request DMA channel for RX FIFO */
        if (kDmaInvalidChannel == DMA_DRV_RequestChannel(kDmaAnyChannel,
                                                         kDmaRequestMux0SPI0Rx,
                                                         &spiState->dmaReceive))
        {
            return kStatus_SPI_DMAChannelInvalid;
        }
        /* Request DMA channel for TX FIFO */
        if (kDmaInvalidChannel == DMA_DRV_RequestChannel(kDmaAnyChannel,
                                                         kDmaRequestMux0SPI0Tx,
                                                         &spiState->dmaTransmit))
        {
            return kStatus_SPI_DMAChannelInvalid;
        }
    }
#if (SPI_INSTANCE_COUNT > 1)
    else if (instance == 1)
    {
        /* Request DMA channel for RX FIFO */
        if (kDmaInvalidChannel == DMA_DRV_RequestChannel(kDmaAnyChannel,
                                                         kDmaRequestMux0SPI1Rx,
                                                         &spiState->dmaReceive))
        {
            return kStatus_SPI_DMAChannelInvalid;
        }
        /* Request DMA channel for TX FIFO */
        if (kDmaInvalidChannel == DMA_DRV_RequestChannel(kDmaAnyChannel,
                                                         kDmaRequestMux0SPI1Tx,
                                                         &spiState->dmaTransmit))
        {
            return kStatus_SPI_DMAChannelInvalid;
        }
    }
    else
    {
        return kStatus_SPI_OutOfRange;
    }
#endif

    /* Save runtime structure pointers to irq handler can point to the correct state structure */
    g_spiStatePtr[instance] = spiState;

    /* Enable SPI interrupt. The transmit interrupt should immediately cause an interrupt
     * which will fill in the transmit buffer and will be ready to send once the slave initiates
     * transmission.
     */
    INT_SYS_EnableIRQ(g_spiIrqId[instance]);

    /* SPI module enable */
    SPI_HAL_Enable(base);

    return kStatus_SPI_Success;
}
/*!
 * @brief Initiate (start) a transfer using DMA. This is not a public API as it is called from
 *  other driver functions
 */
spi_status_t SPI_DRV_DmaMasterStartTransfer(uint32_t instance,
                                            const spi_dma_master_user_config_t * device)
{
    /* instantiate local variable of type spi_dma_master_state_t and point to global state */
    spi_dma_master_state_t * spiDmaState = (spi_dma_master_state_t *)g_spiStatePtr[instance];

    /* For temporarily storing DMA register channel */
    void * param;
    uint32_t calculatedBaudRate;
    SPI_Type *base = g_spiBase[instance];
    uint32_t transferSizeInBytes;  /* DMA transfer size in bytes */

    /* Initialize s_byteToSend */
    s_byteToSend = 0;

    /* If the transfer count is zero, then return immediately.*/
    if (spiDmaState->remainingSendByteCount == 0)
    {
        /* Signal the synchronous completion object if the transfer wasn't async.
         * Otherwise, when we return the the sync function we'll get stuck in the sync wait loop.
         */
        if (spiDmaState->isTransferBlocking)
        {
            OSA_SemaPost(&spiDmaState->irqSync);
        }

        return kStatus_SPI_Success;
    }

    /* Configure bus for this device. If NULL is passed, we assume the caller has
     * preconfigured the bus using SPI_DRV_DmaMasterConfigureBus().
     * Do nothing for calculatedBaudRate. If the user wants to know the calculatedBaudRate
     * then they can call this function separately.
     */
    if (device)
    {
        SPI_DRV_DmaMasterConfigureBus(instance, device, &calculatedBaudRate);
    }

    /* In order to flush any remaining data in the shift register, disable then enable the SPI */
    SPI_HAL_Disable(base);
    SPI_HAL_Enable(base);

#if FSL_FEATURE_SPI_16BIT_TRANSFERS
    /* Check the transfer byte count. If bits/frame > 8, meaning 2 bytes, and if
     * the transfer byte count is an odd count we'll have to round down the RX transfer byte count
     * to the next lowest even number by one and assert a flag to indicate in the interrupt handler
     * that we take care of sending and receiving this last byte.  We'll round up TX byte count.
     */
    if (SPI_HAL_Get8or16BitMode(base) == kSpi16BitMode) /* Applies to 16-bit transfers */
    {
        /* Odd byte count for 16-bit transfers, set the extraByte flag */
        if (spiDmaState->remainingSendByteCount & 1UL) /* If odd byte count */
        {
            transferSizeInBytes = 2; /* Set transfer size to two bytes for the DMA operation */
            spiDmaState->extraByte = true; /* Set the extraByte flag */

            /* Round up TX byte count so when DMA completes, all data would've been sent */
            spiDmaState->remainingSendByteCount += 1U;

            /* Round down RX byte count which means at the end of the RX DMA transfer, we'll need
             * to set up an interrupt to get the last byte.
             */
            spiDmaState->remainingReceiveByteCount &= ~1U;

            /* Store the transfer byte count to the run-time state struct
             * for later use in the interrupt handler.
             */
            spiDmaState->transferByteCnt = spiDmaState->remainingSendByteCount;
        }
        /* Even byte count for 16-bit transfers, clear the extraByte flag */
        else
        {
            transferSizeInBytes = 2; /* Set transfer size to two bytes for the DMA operation */
            spiDmaState->extraByte = false; /* Clear the extraByte flag */
        }
    }
    else /* For 8-bit transfers */
    {
        transferSizeInBytes = 1;
        spiDmaState->extraByte = false;
    }
#else
    transferSizeInBytes = 1;
#endif

    param = (void *)(instance); /* For DMA callback, set "param" as the SPI instance number */

    /* Check that we're not busy.*/
    if (spiDmaState->isTransferInProgress)
    {
        return kStatus_SPI_Busy;
    }

    /* Save information about the transfer for use by the ISR.*/
    spiDmaState->isTransferInProgress = true;

    /************************************************************************************
     * Set up the RX DMA channel Transfer Control Descriptor (TCD)
     * Note, if there is no receive byte count, then bypass RX DMA set up.
     ***********************************************************************************/
    if (spiDmaState->remainingReceiveByteCount)
    {
        /* If no receive buffer then disable incrementing the destination and set the destination
         * to a temporary location
         */
        if (!spiDmaState->receiveBuffer)
        {
            /* Set up this channel's control which includes enabling the DMA interrupt */
            DMA_DRV_ConfigTransfer(&spiDmaState->dmaReceive,
                                   kDmaPeripheralToPeripheral,
                                   transferSizeInBytes,
                                   SPI_HAL_GetDataRegAddr(base), /* src is data register */
                                   (uint32_t)(&s_rxBuffIfNull), /* dest is temporary location */
                                   (uint32_t)(spiDmaState->remainingReceiveByteCount));
        }
        else
        {
            /* Set up this channel's control which includes enabling the DMA interrupt */
            DMA_DRV_ConfigTransfer(&spiDmaState->dmaReceive,
                                   kDmaPeripheralToMemory,
                                   transferSizeInBytes,
                                   SPI_HAL_GetDataRegAddr(base), /* src is data register */
                                   (uint32_t)(spiDmaState->receiveBuffer),/* dest is rx buffer */
                                   (uint32_t)(spiDmaState->remainingReceiveByteCount));
        }

        /* Dest size is always 1 byte on each transfer */
        DMA_DRV_SetDestTransferSize(&spiDmaState->dmaReceive, 1U);

        /* Enable the DMA peripheral request */
        DMA_DRV_StartChannel(&spiDmaState->dmaReceive);

        /* Register callback for DMA interrupt */
        DMA_DRV_RegisterCallback(&spiDmaState->dmaReceive, SPI_DRV_DmaMasterCallback, param);
    }

    /************************************************************************************
     * Set up the TX DMA channel Transfer Control Descriptor (TCD)
     * Note, if there is no source buffer (if user passes in NULL), then send zeros
     ***********************************************************************************/
    /* Per the reference manual, before enabling the SPI transmit DMA request, we first need
     * to read the status register and then write to the SPI data register.  Afterwards, we need
     * to decrement the sendByteCount and perform other driver maintenance functions.
     */

    /* Read the SPI Status register */
    SPI_HAL_IsTxBuffEmptyPending(base);

    /* Start the transfer by writing the first byte/word to the SPI data register.
     * If a send buffer was provided, the byte/word comes from there. Otherwise we just send zeros.
     * This will cause an immeidate transfer which in some cases may cause the RX DMA channel to
     * complete before having the chance to completely set up the TX DMA channel. As such, we'll
     * enable the RX DMA channel last.
     */
#if FSL_FEATURE_SPI_16BIT_TRANSFERS
    if (transferSizeInBytes == 2) /* 16-bit transfers for SPI16 module */
    {
        if (spiDmaState->sendBuffer)
        {
            s_byteToSend = *(spiDmaState->sendBuffer);
            SPI_HAL_WriteDataLow(base, s_byteToSend);
            ++spiDmaState->sendBuffer;

            s_byteToSend = *(spiDmaState->sendBuffer);
            SPI_HAL_WriteDataHigh(base, s_byteToSend);
            ++spiDmaState->sendBuffer;
        }
        else  /* Else, if no send buffer, write zeros */
        {
            SPI_HAL_WriteDataLow(base, s_byteToSend);
            SPI_HAL_WriteDataHigh(base, s_byteToSend);
        }
        spiDmaState->remainingSendByteCount -= 2;  /* Decrement the send byte count by 2 */
    }
    else /* 8-bit transfers for SPI16 module */
    {
        if (spiDmaState->sendBuffer)
        {
            s_byteToSend = *(spiDmaState->sendBuffer);
            ++spiDmaState->sendBuffer;
        }
        SPI_HAL_WriteDataLow(base, s_byteToSend); /* If no send buffer, s_byteToSend=0 */
        --spiDmaState->remainingSendByteCount; /* Decrement the send byte count */
    }
#else
    /* For SPI modules that do not support 16-bit transfers */
    if (spiDmaState->sendBuffer)
    {
        s_byteToSend = *(spiDmaState->sendBuffer);
        ++spiDmaState->sendBuffer;
    }
    SPI_HAL_WriteData(base, s_byteToSend); /* If no send buffer, s_byteToSend=0 */
    --spiDmaState->remainingSendByteCount; /* Decrement the send byte count */
#endif

    /* If there are no more bytes to send then return without setting up the TX DMA channel
     * and let the receive DMA channel complete the transfer if the RX DMA channel was setup.
     * If the RX DMA channel was not set up (due to odd byte count of 1 in 16-bit mode), enable
     * the interrupt to get the received byte.
     */
    if (!spiDmaState->remainingSendByteCount) /* No more bytes to send */
    {
        if (spiDmaState->remainingReceiveByteCount)
        {
            /* Enable the RX DMA channel request now  */
            SPI_HAL_SetRxDmaCmd(base, true);
            return kStatus_SPI_Success;
        }
        else /* If RX DMA chan not setup then enable the interrupt to get the received byte */
        {
            SPI_HAL_SetIntMode(base, kSpiTxEmptyInt, true);
            return kStatus_SPI_Success;
        }
    }
    /* Else, since there are more bytes to send, go ahead and set up the TX DMA channel */
    else
    {
        /* If there is a send buffer, data comes from there, else send 0 */
        if (spiDmaState->sendBuffer)
        {
            /* Set up this channel's control which includes enabling the DMA interrupt */
            DMA_DRV_ConfigTransfer(&spiDmaState->dmaTransmit, kDmaMemoryToPeripheral,
                                   transferSizeInBytes,
                                   (uint32_t)(spiDmaState->sendBuffer),
                                   SPI_HAL_GetDataRegAddr(base),
                                   (uint32_t)(spiDmaState->remainingSendByteCount));
        }
        else /* Configure TX DMA channel to send zeros */
        {
            /* Set up this channel's control which includes enabling the DMA interrupt */
            DMA_DRV_ConfigTransfer(&spiDmaState->dmaTransmit, kDmaPeripheralToPeripheral,
                                   transferSizeInBytes,
                                   (uint32_t)(&s_byteToSend),
                                   SPI_HAL_GetDataRegAddr(base),
                                   (uint32_t)(spiDmaState->remainingSendByteCount));
        }

        /* Source size is only one byte on each transfer */
        DMA_DRV_SetSourceTransferSize(&spiDmaState->dmaTransmit, 1U);

        /* Enable the SPI TX DMA Request */
        SPI_HAL_SetTxDmaCmd(base, true);

        /* Enable the SPI RX DMA Request after the TX DMA request is enabled.  This is done to
         * make sure that the RX DMA channel does not end prematurely before we've completely set
         * up the TX DMA channel since part of the TX DMA set up involves placing 1 or 2 bytes of
         * data into the send data register which causes an immediate transfer.
         */
        SPI_HAL_SetRxDmaCmd(base, true);

        /* Enable the DMA peripheral request */
        DMA_DRV_StartChannel(&spiDmaState->dmaTransmit);
    }

    return kStatus_SPI_Success;
}
/*FUNCTION**********************************************************************
 *
 * Function Name : SPI_DRV_DmaMasterInit
 * Description   : Initializes a SPI instance for master mode operation to work with DMA.
 * This function uses a dma driven method for transferring data.
 * this function initializes the run-time state structure to track the ongoing
 * transfers, ungates the clock to the SPI module, resets the SPI module, initializes the module
 * to user defined settings and default settings, configures the IRQ state structure, enables
 * the module-level interrupt to the core, and enables the SPI module.
 *
 * This initialization function also configures the DMA module by requesting channels for DMA
 * operation.
 *
 *END**************************************************************************/
spi_status_t SPI_DRV_DmaMasterInit(uint32_t instance, spi_dma_master_state_t * spiDmaState)
{
    SPI_Type *base = g_spiBase[instance];

    /* Clear the state for this instance.*/
    memset(spiDmaState, 0, sizeof(* spiDmaState));

    /* Enable clock for SPI*/
    CLOCK_SYS_EnableSpiClock(instance);

    /* configure the run-time state struct with the source clock value */
    spiDmaState->spiSourceClock = CLOCK_SYS_GetSpiFreq(instance);

    /* Reset the SPI module to it's default state, which includes SPI disabled */
    SPI_HAL_Init(base);

    /* Init the interrupt sync object.*/
    if (OSA_SemaCreate(&spiDmaState->irqSync, 0) != kStatus_OSA_Success)
    {
        return kStatus_SPI_Error;
    }

    /* Set SPI to master mode */
    SPI_HAL_SetMasterSlave(base, kSpiMaster);

    /* Set slave select to automatic output mode */
    SPI_HAL_SetSlaveSelectOutputMode(base, kSpiSlaveSelect_AutomaticOutput);

    /* Set the SPI pin mode to normal mode */
    SPI_HAL_SetPinMode(base, kSpiPinMode_Normal);

#if FSL_FEATURE_SPI_FIFO_SIZE
    if (g_spiFifoSize[instance] != 0)
    {
        /* If SPI module contains a FIFO, enable it and set watermarks to half full/empty */
        SPI_HAL_SetFifoMode(base, true, kSpiTxFifoOneHalfEmpty, kSpiRxFifoOneHalfFull);

        /* Set the interrupt clearing mechansim select for later use in driver to clear
         * status flags
         */
        SPI_HAL_SetIntClearCmd(base, true);
    }
#endif
    /* Save runtime structure pointers to irq handler can point to the correct state structure*/
    g_spiStatePtr[instance] = spiDmaState;

    /*****************************************
     * Request DMA channel for RX and TX FIFO
     *****************************************/
    /* This channel transfers data from RX FIFO to receiveBuffer */
    if (instance == 0)
    {
        /* Request DMA channel for RX FIFO */
        DMA_DRV_RequestChannel(kDmaAnyChannel, kDmaRequestMux0SPI0Rx, &spiDmaState->dmaReceive);
        /* Request DMA channel for TX FIFO */
        DMA_DRV_RequestChannel(kDmaAnyChannel, kDmaRequestMux0SPI0Tx, &spiDmaState->dmaTransmit);
    }
#if (SPI_INSTANCE_COUNT > 1)
    else
    {
        /* Request DMA channel for RX FIFO */
        DMA_DRV_RequestChannel(kDmaAnyChannel, kDmaRequestMux0SPI1Rx, &spiDmaState->dmaReceive);
        /* Request DMA channel for TX FIFO */
        DMA_DRV_RequestChannel(kDmaAnyChannel, kDmaRequestMux0SPI1Tx, &spiDmaState->dmaTransmit);
    }
#endif

    /* Enable SPI interrupt.*/
    INT_SYS_EnableIRQ(g_spiIrqId[instance]);

    /* SPI system Enable */
    SPI_HAL_Enable(base);

    return kStatus_SPI_Success;
}
예제 #10
0
/*!
 * @brief Initiate (start) a transfer. This is not a public API as it is called from other
 *  driver functions
 */
static spi_status_t SPI_DRV_MasterStartTransfer(uint32_t instance,
                                                const spi_master_user_config_t * device)
{
    /* instantiate local variable of type spi_master_state_t and point to global state */
    spi_master_state_t * spiState = (spi_master_state_t *)g_spiStatePtr[instance];

    uint32_t calculatedBaudRate;
    SPI_Type *base = g_spiBase[instance];

    /* Check that we're not busy.*/
    if (spiState->isTransferInProgress)
    {
        return kStatus_SPI_Busy;
    }

    /* Configure bus for this device. If NULL is passed, we assume the caller has
     * preconfigured the bus using SPI_DRV_MasterConfigureBus().
     * Do nothing for calculatedBaudRate. If the user wants to know the calculatedBaudRate
     * then they can call this function separately.
     */
    if (device)
    {
        SPI_DRV_MasterConfigureBus(instance, device, &calculatedBaudRate);
    }

    /* In order to flush any remaining data in the shift register, disable then enable the SPI */
    SPI_HAL_Disable(base);
    SPI_HAL_Enable(base);

#if FSL_FEATURE_SPI_16BIT_TRANSFERS
    spi_data_bitcount_mode_t bitCount;

    bitCount = SPI_HAL_Get8or16BitMode(base);

    /* Check the transfer byte count. If bits/frame > 8, meaning 2 bytes if bits/frame > 8, and if
     * the transfer byte count is an odd count we'll have to increase the transfer byte count
     * by one and assert a flag to indicate to the receive function that it will need to handle
     * an extra byte so that it does not inadverently over-write an another byte to the receive
     * buffer. For sending the extra byte, we don't care if we read past the send buffer since we
     * are only reading from it and the absolute last byte (that completes the final 16-bit word)
     * is a don't care byte anyway.
     */
    if ((bitCount == kSpi16BitMode) && (spiState->remainingSendByteCount & 1UL))
    {
        spiState->remainingSendByteCount += 1;
        spiState->remainingReceiveByteCount += 1;
        spiState->extraByte = true;
    }
    else
    {
        spiState->extraByte = false;
    }

#endif

    /* If the byte count is zero, then return immediately.*/
    if (spiState->remainingSendByteCount == 0)
    {
        SPI_DRV_MasterCompleteTransfer(instance);
        return kStatus_SPI_Success;
    }

    /* Save information about the transfer for use by the ISR.*/
    spiState->isTransferInProgress = true;
    spiState->transferredByteCount = 0;

    /* Make sure TX data register (or FIFO) is empty. If not, return appropriate
     * error status. This also causes a read of the status
     * register which is required before writing to the data register below.
     */
    if (SPI_HAL_IsTxBuffEmptyPending(base) != 1)
    {
        return kStatus_SPI_TxBufferNotEmpty;
    }


#if FSL_FEATURE_SPI_16BIT_TRANSFERS
    /* If the module/instance contains a FIFO (and if enabled), proceed with FIFO usage for either
     *  8- or 16-bit transfers, else bypass to non-FIFO usage.
     */
    if ((g_spiFifoSize[instance] != 0) && (SPI_HAL_GetFifoCmd(base)))
    {
        /* First fill the FIFO with data */
        SPI_DRV_MasterFillupTxFifo(instance);

        /* If the remaining RX byte count is less than the RX FIFO watermark, enable
         * the TX FIFO empty interrupt. Once the TX FIFO is empty, we are ensured
         * that the transmission is complete and can then drain the RX FIFO.
         * Else, enable the RX FIFO interrupt based on the watermark. In the IRQ
         * handler, another check will be made to see if the remaining RX byte count
         * is less than the RX FIFO watermark.
         */
        if (spiState->remainingReceiveByteCount < g_spiFifoSize[instance])
        {
            SPI_HAL_SetIntMode(base, kSpiTxEmptyInt, true); /* TX FIFO empty interrupt */
        }
        else
        {
            SPI_HAL_SetFifoIntCmd(base, kSpiRxFifoNearFullInt, true);
        }
    }
    /* Modules that support 16-bit transfers but without FIFO support */
    else
    {
        uint8_t byteToSend = 0;

        /* SPI configured for 16-bit transfers, no FIFO */
        if (bitCount == kSpi16BitMode)
        {
            uint8_t byteToSendLow = 0;
            uint8_t byteToSendHigh = 0;

            if (spiState->sendBuffer)
            {
                byteToSendLow = *(spiState->sendBuffer);
                ++spiState->sendBuffer;

                /* If the extraByte flag is set and these are the last 2 bytes, then skip this */
                if (!((spiState->extraByte) && (spiState->remainingSendByteCount == 2)))
                {
                    byteToSendHigh = *(spiState->sendBuffer);
                    ++spiState->sendBuffer;
                }
            }
            SPI_HAL_WriteDataLow(base, byteToSendLow);
            SPI_HAL_WriteDataHigh(base, byteToSendHigh);

            spiState->remainingSendByteCount -= 2;  /* decrement by 2 */
            spiState->transferredByteCount += 2;  /* increment by 2 */
        }
        /* SPI configured for 8-bit transfers, no FIFO */
        else
        {
            if (spiState->sendBuffer)
            {
                byteToSend = *(spiState->sendBuffer);
                ++spiState->sendBuffer;
            }
            SPI_HAL_WriteDataLow(base, byteToSend);

            --spiState->remainingSendByteCount;
            ++spiState->transferredByteCount;
        }

        /* Only enable the receive interrupt.  This should be ok since SPI is a synchronous
         * protocol, so every RX interrupt we get, we should be ready to send. However, since
         * the SPI has a shift register and data buffer (for transmit and receive), things may not
         * be cycle-to-cycle synchronous. To compensate for this, enabling the RX interrupt only
         * ensures that we do indeed receive data before sending out the next data word. In the
         * ISR we make sure to first check for the RX data buffer full before checking the TX
         * data register empty flag.
         */
        SPI_HAL_SetIntMode(base, kSpiRxFullAndModfInt, true);
    }

#else /* For SPI modules that do not support 16-bit transfers */

    /* Start the transfer by writing the first byte. If a send buffer was provided, the byte
     * comes from there. Otherwise we just send a zero byte. Note that before writing to the
     * data register, the status register must first be read, which was already performed above.
     */
    uint8_t byteToSend = 0;
    if (spiState->sendBuffer)
    {
        byteToSend = *(spiState->sendBuffer);
        ++spiState->sendBuffer;
    }
    SPI_HAL_WriteData(base, byteToSend);

    --spiState->remainingSendByteCount;
    ++spiState->transferredByteCount;

    /* Only enable the receive interrupt.  This should be ok since SPI is a synchronous
     * protocol, so every RX interrupt we get, we should be ready to send. However, since
     * the SPI has a shift register and data buffer (for transmit and receive), things may not
     * be cycle-to-cycle synchronous. To compensate for this, enabling the RX interrupt only
     * ensures that we do indeed receive data before sending out the next data word. In the
     * ISR we make sure to first check for the RX data buffer full before checking the TX
     * data register empty flag.
     */
    SPI_HAL_SetIntMode(base, kSpiRxFullAndModfInt, true);
#endif

    return kStatus_SPI_Success;
}
/*!
 * @brief SPI slave polling.
 *
 * This function sends back received buffer from master through SPI interface.
 */
int main (void)
{
    uint32_t j;
    SPI_Type * spiBaseAddr = g_spiBase[SPI_SLAVE_INSTANCE];

    // init the hardware, this also sets up up the SPI pins for each specific SoC
    hardware_init();

    OSA_Init();

    PRINTF("\r\nSPI board to board polling example");
    PRINTF("\r\nThis example run on instance %d", (uint32_t)SPI_SLAVE_INSTANCE);
    PRINTF("\r\nBe sure master's SPI%d and slave's SPI%d are connected",
                    (uint32_t)SPI_SLAVE_INSTANCE, (uint32_t)SPI_SLAVE_INSTANCE);

    // Enable clock for SPI
    CLOCK_SYS_EnableSpiClock(SPI_SLAVE_INSTANCE);

    // Reset the SPI module to its default settings including disabling SPI
    SPI_HAL_Init(spiBaseAddr);

    // Set SPI to slave mode
    SPI_HAL_SetMasterSlave(spiBaseAddr, kSpiSlave);

    // Set the SPI pin mode to normal mode
    SPI_HAL_SetPinMode(spiBaseAddr, kSpiPinMode_Normal);

#if FSL_FEATURE_SPI_FIFO_SIZE
    if (g_spiFifoSize[SPI_SLAVE_INSTANCE] != 0)
    {
        // If SPI module contains a FIFO, disable it and set watermarks to half full/empty
        SPI_HAL_SetFifoMode(spiBaseAddr, false, kSpiTxFifoOneHalfEmpty, kSpiRxFifoOneHalfFull);
    }
#endif

    //USER CONFIGURABLE OPTION FOR 8 or 16-BIT MODE (if applicable)
#if FSL_FEATURE_SPI_16BIT_TRANSFERS
    SPI_HAL_Set8or16BitMode(spiBaseAddr, kSpi8BitMode);
#endif
    // SPI system Enable
    SPI_HAL_Enable(spiBaseAddr);
    // Setup format as same as master
    SPI_HAL_SetDataFormat(spiBaseAddr, kSpiClockPolarity_ActiveHigh, kSpiClockPhase_FirstEdge, kSpiMsbFirst);

    while(1)
    {
        PRINTF("\r\nSlave example is running...\r\n");

        // Reset the sink buffer
        for (j = 0; j < TRANSFER_SIZE; j++)
        {
            s_spiSinkBuffer[j] = 0;
        }

        SPI_HAL_Disable(spiBaseAddr);
        SPI_HAL_Enable(spiBaseAddr);

        // Disable module to clear shift register
        for (j = 0; j <TRANSFER_SIZE; j++)
        {
            // Check if data received
            while(SPI_HAL_IsReadBuffFullPending(spiBaseAddr)==0){}

#if FSL_FEATURE_SPI_16BIT_TRANSFERS
            // Read data from data register
            s_spiSinkBuffer[j] = SPI_HAL_ReadDataLow(spiBaseAddr);
#else
            s_spiSinkBuffer[j] = SPI_HAL_ReadData(spiBaseAddr);
#endif
        }

        // Disable module to clear shift register
        SPI_HAL_Disable(spiBaseAddr);
        SPI_HAL_Enable(spiBaseAddr);

        // Send data back to master
        for (j = 0; j <TRANSFER_SIZE; j++)
        {
#if FSL_FEATURE_SPI_16BIT_TRANSFERS
            SPI_HAL_WriteDataBlocking(spiBaseAddr, kSpi8BitMode, 0, s_spiSinkBuffer[j]);
#else
            SPI_HAL_WriteDataBlocking(spiBaseAddr, s_spiSinkBuffer[j]);
#endif
        }

        // Print out receive buffer
        PRINTF("\r\nSlave receive:");
        for (j = 0; j < TRANSFER_SIZE; j++)
        {
            // Print 16 numbers in a line.
            if ((j & 0x0F) == 0)
            {
                PRINTF("\r\n    ");
            }
            PRINTF(" %02X", s_spiSinkBuffer[j]);
        }
    }
}