/*FUNCTION********************************************************************** * * Function Name : DSPI_DRV_DmaTxCallback * Description : Callback function, this called when DMA transmitting data * completed * *END**************************************************************************/ static void DSPI_DRV_DmaTxCallback(void *param, dma_channel_status_t status) { uint32_t instance = (uint32_t)param; SPI_Type *base = g_dspiBase[instance]; dspi_dma_master_state_t * dspiDmaState = (dspi_dma_master_state_t *)g_dspiStatePtr[instance]; uint32_t dmaChannel; DMAMUX_Type *dmamuxbase; dmaChannel = dspiDmaState->dmaTxDataChannel.channel; dmamuxbase = g_dmamuxBase[dmaChannel/FSL_FEATURE_DMAMUX_MODULE_CHANNEL]; // DMA_DRV_ClearStatus(&dspiDmaState->dmaTxDataChannel); DMAMUX_HAL_SetChannelCmd(dmamuxbase, dmaChannel, false); DMAMUX_HAL_SetChannelCmd(dmamuxbase, dmaChannel, true); /* Stop DMA Tx channel */ DMA_DRV_StopChannel(&dspiDmaState->dmaTxDataChannel); /* Setup DMA to transmit the last frame */ /* Have one more byte, config DMA Rx channel to receive this byte */ DMA_DRV_ConfigTransfer(&dspiDmaState->dmaTxDataChannel, kDmaPeripheralToPeripheral, 4u, (uint32_t)(&s_lastCmdData), /* src is data register */ DSPI_HAL_GetMasterPushrRegAddr(base), /* dest is temporary location */ 4u); /* Register callback */ DMA_DRV_RegisterCallback(&dspiDmaState->dmaTxDataChannel, DSPI_DRV_DmaTxLastCallback, (void *)instance); /* Enable the DMA peripheral request */ DMA_DRV_StartChannel(&dspiDmaState->dmaTxDataChannel); }
/*FUNCTION********************************************************************** * * Function Name : LPSCI_DRV_DmaStartReceiveData * Description : Initiate (start) a receive by beginning the process of * receiving data and enabling the interrupt. * This is not a public API as it is called from other driver functions. * *END**************************************************************************/ static lpsci_status_t LPSCI_DRV_DmaStartReceiveData(uint32_t instance, uint8_t * rxBuff, uint32_t rxSize) { assert(instance < UART0_INSTANCE_COUNT); /* Get current runtime structure. */ lpsci_dma_state_t * lpsciDmaState = (lpsci_dma_state_t *)g_lpsciStatePtr[instance]; dma_channel_t *chn = &lpsciDmaState->dmaLpsciRx; DMA_Type * dmaBase = g_dmaBase[chn->channel/FSL_FEATURE_DMA_DMAMUX_CHANNELS]; /* Check that we're not busy already receiving data from a previous function call. */ if (lpsciDmaState->isRxBusy) { return kStatus_LPSCI_RxBusy; } /* Update LPSCI DMA run-time structure. */ lpsciDmaState->isRxBusy = true; DMA_HAL_SetDestAddr(dmaBase, chn->channel, (uint32_t)rxBuff); DMA_HAL_SetTransferCount(dmaBase, chn->channel, rxSize); DMA_DRV_StartChannel(chn); return kStatus_LPSCI_Success; }
/*FUNCTION********************************************************************** * * Function Name : UART_DRV_DmaStartReceiveData * Description : Initiate (start) a receive by beginning the process of * receiving data and enabling the interrupt. * This is not a public API as it is called from other driver functions. * *END**************************************************************************/ static uart_status_t UART_DRV_DmaStartReceiveData(uint32_t instance, uint8_t * rxBuff, uint32_t rxSize) { assert(instance < UART_INSTANCE_COUNT); UART_Type * base = g_uartBase[instance]; /* Get current runtime structure. */ uart_dma_state_t * uartDmaState = (uart_dma_state_t *)g_uartStatePtr[instance]; dma_channel_t *chn = &uartDmaState->dmaUartRx; /* Check that we're not busy already receiving data from a previous function call. */ if (uartDmaState->isRxBusy) { return kStatus_UART_RxBusy; } /* Update UART DMA run-time structure. */ uartDmaState->isRxBusy = true; DMA_DRV_ConfigTransfer(&uartDmaState->dmaUartRx, kDmaPeripheralToMemory, 1u, UART_HAL_GetDataRegAddr(base), (uint32_t)rxBuff, rxSize); DMA_DRV_StartChannel(chn); return kStatus_UART_Success; }
/*FUNCTION********************************************************************** * * Function Name : LPUART_DRV_DmaStartSendData * Description : This is not a public interface. * *END**************************************************************************/ static lpuart_status_t LPUART_DRV_DmaStartSendData(uint32_t instance, const uint8_t * txBuff, uint32_t txSize) { assert(instance < LPUART_INSTANCE_COUNT); LPUART_Type * base = g_lpuartBase[instance]; /* Get current runtime structure. */ lpuart_dma_state_t * lpuartDmaState = (lpuart_dma_state_t *)g_lpuartStatePtr[instance]; dma_channel_t *chn = &lpuartDmaState->dmaLpuartTx; /* Check that we're not busy already transmitting data from a previous function call. */ if (lpuartDmaState->isTxBusy) { return kStatus_LPUART_TxBusy; } /* Update LPUART DMA run-time structure. */ lpuartDmaState->isTxBusy = true; DMA_DRV_ConfigTransfer(&lpuartDmaState->dmaLpuartTx, kDmaMemoryToPeripheral, 1u, (uint32_t)txBuff, LPUART_HAL_GetDataRegAddr(base), txSize); DMA_DRV_StartChannel(chn); return kStatus_LPUART_Success; }
/*FUNCTION********************************************************************** * * Function Name : DSPI_DRV_DmaRxCallback * Description : Callback function, this called when DMA receiving data * completed * *END**************************************************************************/ static void DSPI_DRV_DmaRxCallback(void *param, dma_channel_status_t status) { uint32_t instance = (uint32_t)param; SPI_Type *base = g_dspiBase[instance]; dspi_dma_master_state_t * dspiDmaState = (dspi_dma_master_state_t *)g_dspiStatePtr[instance]; /* Stop DMA Rx channel */ DMA_DRV_StopChannel(&dspiDmaState->dmaRxChannel); if (dspiDmaState->extraByte != 0) { /* Have one more byte, config DMA Rx channel to receive this byte */ DMA_DRV_ConfigTransfer(&dspiDmaState->dmaRxChannel, kDmaPeripheralToPeripheral, 1u, DSPI_HAL_GetPoprRegAddr(base), /* src is data register */ (uint32_t)(&s_rxBuffIfNull), /* dest is temporary location */ (uint32_t)(1u)); /* Register callback */ DMA_DRV_RegisterCallback(&dspiDmaState->dmaRxChannel, DSPI_DRV_DmaRxCallback, (void *)instance); /* Enable the DMA peripheral request */ DMA_DRV_StartChannel(&dspiDmaState->dmaRxChannel); if (dspiDmaState->rxBuffer) { /* Update the receive pointer to extra byte */ dspiDmaState->rxBuffer = dspiDmaState->rxBuffer + dspiDmaState->rxTransferByteCnt; } dspiDmaState->rxTransferByteCnt = 0; dspiDmaState->extraByte = false; return; } /* If transmission completed, stop the transferring */ if ((dspiDmaState->isTransferInProgress) && (dspiDmaState->rxTransferByteCnt == 0)) { /* Extra byte completed */ if (dspiDmaState->rxBuffer) { /* Store the extra byte */ *dspiDmaState->rxBuffer = (uint8_t)s_rxBuffIfNull; } /* Stop DMA Rx channel */ DMA_DRV_StopChannel(&dspiDmaState->dmaRxChannel); DSPI_DRV_DmaMasterCompleteTransfer(instance); } else { /* Stop DMA Rx channel */ DMA_DRV_StopChannel(&dspiDmaState->dmaRxChannel); DSPI_DRV_DmaMasterCompleteTransfer(instance); } }
void led(uint8_t red, uint8_t green, uint8_t blue) { FLEXIO_Type *fiobase = g_flexioBase[0]; uint32_t color = (green << 16) | (red << 8) | blue; int i; for (i = 0; i < 24; ++i) { shift0_buf[i / 10] <<= 3; shift0_buf[i / 10] |= 4 | ((color >> (22 - i)) & 2); } shift0_buf[2] <<= 3 * 6; DMA_DRV_ConfigTransfer(&g_fioChan, kDmaMemoryToPeripheral, 4, (intptr_t)&shift0_buf, (intptr_t)&FLEXIO_SHIFTBUFBIS_REG(fiobase, 0), sizeof(shift0_buf)); DMA_DRV_StartChannel(&g_fioChan); }
/*! * @brief Initiate (start) a transfer using DMA. This is not a public API as it is called from * other driver functions */ static dspi_status_t DSPI_DRV_DmaMasterStartTransfer(uint32_t instance, const dspi_dma_device_t * device, const uint8_t * sendBuffer, uint8_t * receiveBuffer, size_t transferByteCount) { /* instantiate local variable of type dspi_dma_master_state_t and point to global state */ dspi_dma_master_state_t * dspiDmaState = (dspi_dma_master_state_t *)g_dspiStatePtr[instance]; /* For temporarily storing DMA instance and channel */ uint8_t transferSizeInBytes = 1U; uint32_t calculatedBaudRate; dspi_command_config_t command; /* create an instance of the data command struct*/ SPI_Type *base = g_dspiBase[instance]; uint32_t txTransferByteCnt = 0; uint32_t rxTransferByteCnt = 0; /* Initialize s_wordToSend */ s_wordToSend = 0; /* Check that we're not busy.*/ if (dspiDmaState->isTransferInProgress) { return kStatus_DSPI_Busy; } /* Calculate the transfer size on bits/frame */ if (dspiDmaState->bitsPerFrame <= 8) { transferSizeInBytes = 1U; } else if (dspiDmaState->bitsPerFrame <= 16) { transferSizeInBytes = 2U; } else { transferSizeInBytes = 4U; } /* Configure bus for this device. If NULL is passed, we assume the caller has * preconfigured the bus using DSPI_DRV_DmaMasterConfigureBus(). * Do nothing for calculatedBaudRate. If the user wants to know the calculatedBaudRate * then they can call this function separately. */ if (device) { DSPI_DRV_DmaMasterConfigureBus(instance, device, &calculatedBaudRate); dspiDmaState->bitsPerFrame = device->dataBusConfig.bitsPerFrame; /*update bits/frame*/ } /* 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 increase the TX transfer byte count * by one and assert a flag to indicate to the send functions that it will * need to handle an extra byte. * For receive, we actually round down the transfer count to the next lowest even number. * Then in the interrupt handler, we take care of geting this last byte. */ if ((dspiDmaState->bitsPerFrame > 8) && (transferByteCount & 1UL)) { dspiDmaState->extraByte = true; txTransferByteCnt = transferByteCount + 1U; /* Increment to next even byte count */ rxTransferByteCnt = transferByteCount & ~1U; /* Decrement to next even byte count */ } else { dspiDmaState->extraByte = false; txTransferByteCnt = transferByteCount; rxTransferByteCnt = transferByteCount; } /* Store the receiveBuffer pointer and receive byte count to the run-time state struct * for later use in the interrupt handler. */ dspiDmaState->rxBuffer = (uint8_t *)receiveBuffer; dspiDmaState->rxTransferByteCnt = rxTransferByteCnt; /* Save information about the transfer for use by the ISR.*/ dspiDmaState->isTransferInProgress = true; /* Reset the transfer counter to 0. Normally this is done via the PUSHR[CTCNT], but as we * are under DMA controller, we won't be able to change this bit dynamically after the * first word is transferred. */ DSPI_HAL_PresetTransferCount(base, 0); /* flush the fifos*/ DSPI_HAL_SetFlushFifoCmd(base, true, true); /* Clear status flags that may have been set from previous transfers */ DSPI_HAL_ClearStatusFlag(base, kDspiTxComplete); DSPI_HAL_ClearStatusFlag(base, kDspiEndOfQueue); DSPI_HAL_ClearStatusFlag(base, kDspiTxFifoUnderflow); DSPI_HAL_ClearStatusFlag(base, kDspiTxFifoFillRequest); DSPI_HAL_ClearStatusFlag(base, kDspiRxFifoOverflow); DSPI_HAL_ClearStatusFlag(base, kDspiRxFifoDrainRequest); /************************************************************************************ * Set up the RX DMA channel Transfer Control Descriptor (TCD) * Do this before filling the TX FIFO. * Note, if there is no remaining receive count, then bypass RX DMA set up. * This means we only have one byte to read of a 16-bit data word and will read this * in the complete transfer function. ***********************************************************************************/ /* If a receive buffer is used and if rxTransferByteCnt > 0 */ if (rxTransferByteCnt) { if (!receiveBuffer) { /* Set up this channel's control which includes enabling the DMA interrupt */ DMA_DRV_ConfigTransfer(&dspiDmaState->dmaRxChannel, kDmaPeripheralToPeripheral, transferSizeInBytes, DSPI_HAL_GetPoprRegAddr(base), /* src is data register */ (uint32_t)(&s_rxBuffIfNull), /* dest is temporary location */ (uint32_t)(rxTransferByteCnt)); } else { /* Set up this channel's control which includes enabling the DMA interrupt */ DMA_DRV_ConfigTransfer(&dspiDmaState->dmaRxChannel, kDmaPeripheralToMemory, transferSizeInBytes, DSPI_HAL_GetPoprRegAddr(base), /* src is data register */ (uint32_t)(receiveBuffer), /* dest is rx buffer */ (uint32_t)(rxTransferByteCnt)); /* Destination size is only 1 byte */ DMA_DRV_SetDestTransferSize(&dspiDmaState->dmaRxChannel, 1U); } /* Enable the DMA peripheral request */ DMA_DRV_StartChannel(&dspiDmaState->dmaRxChannel); } else if (dspiDmaState->extraByte) { /* Need to receive only one frame - extra byte by calling Rx callback function*/ DSPI_DRV_DmaRxCallback((void *)instance, kDmaIdle); } /************************************************************************************ * Set up the Last Command/data Word Intermediate Buffer and fill up the TX FIFO. ***********************************************************************************/ /* Before sending the data, we first need to initialize the data command struct * Configure the data command attributes for the desired PCS, CTAR, and continuous PCS * which are derived from the run-time state struct */ command.whichPcs = dspiDmaState->whichPcs; command.whichCtar = dspiDmaState->whichCtar; command.clearTransferCount = false; /* don't clear the transfer count */ /************************************************************************ * Next, set up the Last Command/data Word Intermediate Buffer before * filling up the TX FIFO * Create a 32-bit word with the final 16-bit command settings. This means * that EOQ = 1 and CONT = 0. * This 32-bit word will also be initialized with the final data byte/word * from the send source buffer and then the entire 32-bit word will be * transferred to the DSPI PUSHR. ************************************************************************/ /* Declare a variable for storing the last send data (either 8- or 16-bit) */ uint32_t lastWord = 0; /* If a send buffer was provided, the word comes from there. Otherwise we just send * a zero (initialized above). */ if (sendBuffer) { /* Store the last byte from the send buffer */ if (dspiDmaState->bitsPerFrame <= 8) { lastWord = sendBuffer[txTransferByteCnt-1]; /* Last byte */ } /* Store the last two bytes the send buffer */ else { /* If 16-bit transfers and odd transfer byte count then an extra byte was added * to the transfer byte count, but we cannot access more of the send buffer */ if(!dspiDmaState->extraByte) { lastWord = sendBuffer[txTransferByteCnt-1] ; /* Save off the last byte */ lastWord = lastWord << 8U; /* Shift to MSB (separate step due to MISHA) */ } lastWord |= sendBuffer[txTransferByteCnt-2]; /* OR with next to last byte */ } } /* Now, build the last command/data word intermediate buffer */ command.isChipSelectContinuous = false; /* Always clear CONT for last data word */ command.isEndOfQueue = true; /* Set EOQ for last data word */ s_lastCmdData = DSPI_HAL_GetFormattedCommand(base, &command) | lastWord; /************************************************************************ * Begin TX DMA channels transfer control descriptor set up. * 1. First, set up intermediate buffers which contain 16-bit commands. * 2. Set up the DMA Software Transfer Control Descriptors (STCD) for various * scenarios: * - Channel for intermediate buffer to TX FIFO * - Channel for source buffer to intermediate buffer * - STCD for scatter/gather for end of previous channel to replace * intermediate buffer with last-command buffer. ************************************************************************/ /************************************************************************ * Intermediate Buffer * Create a 32-bit word with the 16-bit command settings. Data from * the send source buffer will be transferred here and then the entire * 32-bit word will be transferred to the DSPI PUSHR. * This buffer will be preloaded with the next data word in the send buffer ************************************************************************/ /* restore the isChipSelectContinuous setting to the original value as it was cleared above */ command.isChipSelectContinuous = dspiDmaState->isChipSelectContinuous; command.isEndOfQueue = false; /* Clear End of Queue (previously set for last cmd/data word)*/ s_cmdData = DSPI_HAL_GetFormattedCommand(base, &command); /* Place the next data from the send buffer into the intermediate buffer (preload it) * based on whether it is one byte or two. */ if (dspiDmaState->bitsPerFrame <= 8) { /* If a send buffer was provided, the word comes from there. Otherwise we just send * a zero (initialized above). */ if (sendBuffer) { s_wordToSend = *sendBuffer; /* queue up the next data */ ++sendBuffer; /* increment to next data word*/ } --txTransferByteCnt; /* decrement txTransferByteCnt*/ } else { /* If a send buffer was provided, the word comes from there. Otherwise we just send * a zero (initialized above). */ if (sendBuffer) { s_wordToSend = *sendBuffer; ++sendBuffer; /* increment to next data byte */ /* Note, if the extraByte flag is set and we're down to the last two bytes we can still * do this even though we're going past the sendBuffer size. We're just reading data we * don't care about anyways since it is dummy data to make up for the last byte. */ s_wordToSend |= (unsigned)(*sendBuffer) << 8U; ++sendBuffer; /* increment to next data byte */ } txTransferByteCnt -= 2; /* decrement txTransferByteCnt by 2 bytes */ } s_cmdData |= s_wordToSend; /* write s_wordToSend to intermediate buffer */ /* For Tx, DSPI need two DMA channel, one for calculate the command and data into one value * (dmaTxCmdChannel), and another for push the value into DSPI PUSHR register (dmaTxDataChannel). * The dmaTxCmdChannel DMA channel will be triggered by DSPI Tx FIFO, and the dmaTxDataChannel * DMA channel will be linked to calculate DMA channel and triggered by this channel also. */ /* If the transfer size is less than size of one frame, only transmit the last byte that calculated */ if (!txTransferByteCnt) { DMA_DRV_ConfigTransfer(&dspiDmaState->dmaTxDataChannel, kDmaMemoryToPeripheral, 4u, (uint32_t)(&s_lastCmdData), DSPI_HAL_GetMasterPushrRegAddr(base), 4u); /* Register callback for last byte transmitting */ DMA_DRV_RegisterCallback(&dspiDmaState->dmaTxDataChannel, DSPI_DRV_DmaTxLastCallback, (void *)instance); } else { /* Config DMA to copy data from generated cmd data to FIFO */ if (dspiDmaState->bitsPerFrame <= 8) { DMA_DRV_ConfigTransfer(&dspiDmaState->dmaTxDataChannel, kDmaPeripheralToPeripheral, 4u, (uint32_t)(&s_cmdData), DSPI_HAL_GetMasterPushrRegAddr(base), (uint32_t)((txTransferByteCnt) * 4)); } else { DMA_DRV_ConfigTransfer(&dspiDmaState->dmaTxDataChannel, kDmaPeripheralToPeripheral, 4u, (uint32_t)(&s_cmdData), DSPI_HAL_GetMasterPushrRegAddr(base), (uint32_t)((txTransferByteCnt) * 2)); } /* Register Tx callback function */ DMA_DRV_RegisterCallback(&dspiDmaState->dmaTxDataChannel, DSPI_DRV_DmaTxCallback, (void *)instance); /* Config dmaTxCmdChannel channel if send buffer is provided */ if (sendBuffer) { dma_channel_link_config_t config; DMA_DRV_ConfigTransfer(&dspiDmaState->dmaTxCmdChannel, kDmaMemoryToPeripheral, 1u, (uint32_t)(sendBuffer), (uint32_t)(&s_cmdData), (uint32_t)(txTransferByteCnt - transferSizeInBytes)); if (dspiDmaState->bitsPerFrame > 8) { DMA_DRV_SetDestTransferSize(&dspiDmaState->dmaTxCmdChannel, 2U); } /* Link dmaTxCmdChannel after dmaTxDataChannel */ config.channel1 = dspiDmaState->dmaTxCmdChannel.channel; config.channel2 = 0; config.linkType = kDmaChannelLinkChan1; DMA_DRV_ConfigChanLink(&dspiDmaState->dmaTxDataChannel, &config); /* start channel */ DMA_DRV_StartChannel(&dspiDmaState->dmaTxCmdChannel); } } /* Register callback */ DMA_DRV_RegisterCallback(&dspiDmaState->dmaRxChannel, DSPI_DRV_DmaRxCallback, (void *)instance); /* Enable the Receive FIFO Drain DMA Request */ DSPI_HAL_SetRxFifoDrainDmaIntMode(base, kDspiGenerateDmaReq, true); /* Enable TFFF DMA request */ DSPI_HAL_SetTxFifoFillDmaIntMode(base, kDspiGenerateDmaReq, true); /* Enable the DMA peripheral request */ DMA_DRV_StartChannel(&dspiDmaState->dmaTxDataChannel); return kStatus_DSPI_Success; }
/*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; }
/*! * @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; }