/*! * @brief SPI master DMA non-blocking. * * Thid function uses SPI master to send an array to slave * and receive the array back from slave, * then compare whether the two buffers are the same. */ int main (void) { uint8_t loopCount = 0; uint32_t j; uint32_t failCount = 0; uint32_t calculatedBaudRate; spi_dma_master_state_t spiDmaMasterState; dma_state_t state; spi_dma_master_user_config_t userDmaConfig = { #if FSL_FEATURE_SPI_16BIT_TRANSFERS .bitCount = kSpi8BitMode, #endif .polarity = kSpiClockPolarity_ActiveHigh, .phase = kSpiClockPhase_FirstEdge, .direction = kSpiMsbFirst, .bitsPerSec = TRANSFER_BAUDRATE }; // init the hardware, this also sets up up the SPI pins for each specific SoC hardware_init(); // Init OSA layer. OSA_Init(); PRINTF("\r\nSPI board to board dma-non-blocking example"); PRINTF("\r\nThis example run on instance %d", (uint32_t)SPI_MASTER_INSTANCE); PRINTF("\r\nBe sure master's SPI%d and slave's SPI%d are connected\r\n", (uint32_t)SPI_MASTER_INSTANCE, (uint32_t)SPI_MASTER_INSTANCE); // Set up and init the master DMA_DRV_Init(&state); // Init the dspi module for eDMA operation SPI_DRV_DmaMasterInit(SPI_MASTER_INSTANCE, &spiDmaMasterState); SPI_DRV_DmaMasterConfigureBus(SPI_MASTER_INSTANCE, &userDmaConfig, &calculatedBaudRate); if (calculatedBaudRate > userDmaConfig.bitsPerSec) { PRINTF("\r\n**Something failed in the master bus config \r\n"); return -1; } else { PRINTF("\r\nBaud rate in Hz is: %d\r\n", calculatedBaudRate); } while(1) { // Initialize the source buffer for (j = 0; j < TRANSFER_SIZE; j++) { s_spiSourceBuffer[j] = j + loopCount; } // Reset the sink buffer for (j = 0; j < TRANSFER_SIZE; j++) { s_spiSinkBuffer[j] = 0; } // Start the transfer SPI_DRV_DmaMasterTransferBlocking(SPI_MASTER_INSTANCE, NULL, s_spiSourceBuffer, NULL, TRANSFER_SIZE, MASTER_TRANSFER_TIMEOUT); while (SPI_DRV_DmaMasterGetTransferStatus(SPI_MASTER_INSTANCE, NULL) == kStatus_SPI_Busy) { } // Delay sometime to wait slave receive and send back data OSA_TimeDelay(500U); //Receive data from slave SPI_DRV_DmaMasterTransfer(SPI_MASTER_INSTANCE, NULL, NULL, s_spiSinkBuffer, TRANSFER_SIZE); while (SPI_DRV_DmaMasterGetTransferStatus(SPI_MASTER_INSTANCE, NULL) == kStatus_SPI_Busy) { } // Verify the contents of the master sink buffer // refer to the slave driver for the expected data pattern failCount = 0; // reset failCount variable for (j = 0; j < TRANSFER_SIZE; j++) { if (s_spiSinkBuffer[j] != s_spiSourceBuffer[j]) { failCount++; } } // Print out transmit buffer. PRINTF("\r\nMaster transmit:"); for (j = 0; j < TRANSFER_SIZE; j++) { // Print 16 numbers in a line. if ((j & 0x0F) == 0) { PRINTF("\r\n "); } PRINTF(" %02X", s_spiSourceBuffer[j]); } // Print out receive buffer. PRINTF("\r\nMaster 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]); } if (failCount == 0) { PRINTF("\r\n Spi master transfer succeed! \r\n"); } else { PRINTF("\r\n **failures detected in Spi master transfer! \r\n"); } // Wait for press any key. PRINTF("\r\nPress any key to run again\r\n"); GETCHAR(); loopCount++; } }
/*! * @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; }
// Setup the board as a spi master int main (void) { uint8_t loopCount = 0; uint32_t j; uint32_t failCount = 0; uint8_t * pRxBuff; uint32_t calculatedBaudRate; spi_master_state_t spiMasterState; spi_dma_master_state_t spiDmaMasterState; dma_state_t state; spi_dma_master_user_config_t userDmaConfig = { #if FSL_FEATURE_SPI_16BIT_TRANSFERS .bitCount = kSpi8BitMode, #endif .polarity = kSpiClockPolarity_ActiveHigh, .phase = kSpiClockPhase_FirstEdge, .direction = kSpiMsbFirst, .bitsPerSec = TRANSFER_BAUDRATE }; // init the hardware, this also sets up up the SPI pins for each specific SoC hardware_init(); dbg_uart_init(); OSA_Init(); printf("\r\n SPI board to board dma-blocking example"); printf("\r\n This example run on instance 0 "); printf("\r\n Be sure master's SPI0 and slave's SPI0 are connected "); //USER CONFIGURABLE OPTION FOR SPI INSTANCE // Configure SPI pin configure_spi_pins(SPI_MASTER_INSTANCE); printf("\rIMPORTANT, MAKE SURE TO FIRST SET UP THE SPI SLAVE BOARD!\r\n"); //USER CONFIGURABLE OPTION FOR RXBUFF pRxBuff = s_spiSinkBuffer; // Set up and init the master DMA_DRV_Init(&state); // Init the dspi module for eDMA operation SPI_DRV_DmaMasterInit(SPI_MASTER_INSTANCE, &spiDmaMasterState); SPI_DRV_DmaMasterConfigureBus(SPI_MASTER_INSTANCE, &userDmaConfig, &calculatedBaudRate); if (calculatedBaudRate > userDmaConfig.bitsPerSec) { printf("\r**Something failed in the master bus config \r\n"); return -1; } printf("\r\nSpi master SYNC test with various baud rates \r\n"); printf("\r\nBaudRate set to %dHz\r\n", calculatedBaudRate); while(1) { // Initialize the source buffer for (j = 0; j < TRANSFER_SIZE; j++) { s_spiSourceBuffer[j] = j + loopCount; } // Reset the sink buffer for (j = 0; j < TRANSFER_SIZE; j++) { s_spiSinkBuffer[j] = 0; } /* Start the transfer */ if (SPI_DRV_DmaMasterTransferBlocking(SPI_MASTER_INSTANCE, NULL, s_spiSourceBuffer, NULL, TRANSFER_SIZE, MASTER_TRANSFER_TIMEOUT) == kStatus_SPI_Timeout) { printf("\r**Sync transfer timed-out \r\n"); } // Delay sometime to wait slave receive and send back data OSA_TimeDelay(500U); if (SPI_DRV_DmaMasterTransferBlocking(SPI_MASTER_INSTANCE, NULL, NULL, s_spiSinkBuffer, TRANSFER_SIZE, MASTER_TRANSFER_TIMEOUT) == kStatus_SPI_Timeout) { printf("\r**Sync transfer timed-out \r\n"); } // Verify the contents of the master sink buffer // refer to the slave driver for the expected data pattern failCount = 0; // reset failCount variable for (j = 0; j < TRANSFER_SIZE; j++) { if (s_spiSinkBuffer[j] != s_spiSourceBuffer[j]) { failCount++; } } if (failCount == 0) { printf("\r\nMaster transmit:"); for (j = 0; j < TRANSFER_SIZE; j++) { if (j%16 == 0) { printf("\r\n "); } printf(" %02X", s_spiSourceBuffer[j]); } printf("\r\nMaster receive:"); for (j = 0; j < TRANSFER_SIZE; j++) { if (j%16 == 0) { printf("\r\n "); } printf(" %02X", s_spiSinkBuffer[j]); } printf("\r\n"); printf("\r Spi master blocking transfer succeed! \r\n"); } else { printf("\r\nSource buffer:"); for (j = 0; j < TRANSFER_SIZE; j++) { if (j%16 == 0) { printf("\r\n "); } printf(" %02X", s_spiSourceBuffer[j]); } printf("\r\nSink buffer:"); for (j = 0; j < TRANSFER_SIZE; j++) { if (j%16 == 0) { printf("\r\n "); } printf(" %02X", s_spiSinkBuffer[j]); } printf("\r\n"); printf("\r **failures detected in Spi master blocking transfer! \r\n"); } // Wait for press any key. printf("\r\nPress any key to run again\r\n"); getchar(); loopCount++; } }