I need to send some Data over SPI really fast. Goal is an continuous data stream with 5 Mbit data rate. Data is 64 frames with 32 bytes each. The data I need to send repeats itself, after I reach frame 64, we start with frame 0 again. After each frame, Chip Select has to be deasserted.
First I got the SPI up and running, just using interrupts. I used the SPI Interrupt example as base. The result was a 5-10µSec gap after each 16 bit sent. Also there was an event longer 30-50µSec gap after each frame while the SPI interrupt triggered and check for the "mstidle" state, waiting to send the next frame.
Next I tried SPI with DMA. This went far better. There was no gap between the bytes inside a frame. But with DMA i get a really big 250µSec gap after each frame. I guess this is caused by preparing the next frame for DMA.
At last I tried to chain up all frames as dma_descriptor_t:
dma_transfer_config_t masterTxDmaConfig;
lastData = spi0_user_data.cycles[spi0_user_data.cycle][DRIVER_COUNT] | kSPI_EndOfTransfer | (SPI0->TXCTL & 0xFFFF0000);
DMA_CreateDescriptor(&txDescriptor[0], &dma_cfg_end, &lastData, (void *)&SPI0->TXDATCTL, &txDescriptor[1]);
DMA_CreateDescriptor(&txDescriptor[1], &dma_cfg_base, spi0_user_data.cycles[spi0_user_data.cycle+1], (void *)&SPI0->TXDATCTL, &txDescriptor[2]);
DMA_CreateDescriptor(&txDescriptor[2], &dma_cfg_end, &lastData, (void *)&SPI0->TXDATCTL, &txDescriptor[3]);
DMA_CreateDescriptor(&txDescriptor[3], &dma_cfg_base, spi0_user_data.cycles[spi0_user_data.cycle+1], (void *)&SPI0->TXDATCTL, &txDescriptor[4]);
DMA_CreateDescriptor(&txDescriptor[4], &dma_cfg_end, &lastData, (void *)&SPI0->TXDATCTL, &txDescriptor[0]);
DMA_PrepareTransfer(&masterTxDmaConfig, spi0_user_data.cycles[spi0_user_data.cycle], (void *)&SPI0->TXDAT, sizeof(uint16_t), DRIVER_COUNT - 1, kDMA_MemoryToPeripheral, &txDescriptor[0]);
masterTxDmaConfig.xfercfg.intA = false;
masterTxDmaConfig.xfercfg.intB = false;
DMA_SubmitTransfer(&masterTxHandle, &masterTxDmaConfig);
DMA_StartTransfer(&masterTxHandle);
Config:
/* DMA init */
DMA_Init(DMA0);
/* Enable channel and Create handle for TX channel. */
DMA_EnableChannel(DMA0, DMA_SPI_MASTER_TX_CHANNEL);
DMA_CreateHandle(&masterTxHandle, DMA0, DMA_SPI_MASTER_TX_CHANNEL);
DMA_SetCallback(&masterTxHandle, spi_dma_finished, NULL);
/* Set the channel priority. */
DMA_SetChannelPriority(DMA0, DMA_SPI_MASTER_TX_CHANNEL, kDMA_ChannelPriority3);
dma_cfg_end.valid = true;
dma_cfg_end.swtrig = true;
dma_cfg_end.intA = true;
dma_cfg_end.byteWidth = sizeof(uint32_t);
dma_cfg_end.srcInc = 0;
dma_cfg_end.dstInc = 0;
dma_cfg_end.transferCount = 1;
dma_cfg_base.valid = true;
dma_cfg_base.swtrig = true;
dma_cfg_base.intA = false;
dma_cfg_base.byteWidth = sizeof(uint16_t);
dma_cfg_base.srcInc = 1;
dma_cfg_base.dstInc = 0;
dma_cfg_base.transferCount = DRIVER_COUNT;
But in this case the interrupts never fired and chip selct (SSEL) was never deasserted.
Any Idea how to close this gap between frames?
Ok... Tried some more stuff. Came up with following example:
dma_cfg_end.valid = true;
dma_cfg_end.swtrig = true;
dma_cfg_end.intA = true;
dma_cfg_end.byteWidth = sizeof(uint32_t);
dma_cfg_end.srcInc = 0;
dma_cfg_end.dstInc = 0;
dma_cfg_end.transferCount = 1;
dma_cfg_base.valid = true;
dma_cfg_base.reload = true;
dma_cfg_base.swtrig = true;
dma_cfg_base.clrtrig = false;
dma_cfg_base.intA = false;
dma_cfg_base.intB = false;
dma_cfg_base.byteWidth = sizeof(uint16_t);
dma_cfg_base.srcInc = 1;
dma_cfg_base.dstInc = 0;
dma_cfg_base.transferCount = 2;
SDK_ALIGN(uint32_t lastData, 4) = 0U;
SDK_ALIGN(uint16_t text16Data[2], 4) = {0U};
extern dma_descriptor_t txDescriptor;
extern dma_descriptor_t txDescriptor2;
extern dma_xfercfg_t dma_cfg_base;
extern dma_xfercfg_t dma_cfg_end;
void setup_dma () {
dma_transfer_config_t masterTxDmaConfig;
text16Data[1] = 0xFFFF;
lastData = 0x0 | kSPI_EndOfTransfer | (SPI0->TXCTL & 0xFFFF0000);
DMA_CreateDescriptor(&txDescriptor, &dma_cfg_base, text16Data, (void *)&SPI0->TXDATCTL, &txDescriptor2);
DMA_CreateDescriptor(&txDescriptor2, &dma_cfg_end, &lastData, (void *)&SPI0->TXDATCTL, NULL);
DMA_PrepareTransfer(&masterTxDmaConfig, spi0_user_data.cycles[spi0_user_data.cycle], (void *)&SPI0->TXDAT, sizeof(uint16_t), BYTE_PER_CYCLE-2, kDMA_MemoryToPeripheral, &txDescriptor);
masterTxDmaConfig.xfercfg.intA = false;
masterTxDmaConfig.xfercfg.intB = false;
DMA_SubmitTransfer(&masterTxHandle, &masterTxDmaConfig);
DMA_StartTransfer(&masterTxHandle);
If I try "dma_cfg_base.transferCount = 1" it works. anything higher than this will crash the driver, causing it to immediately stopping all output and never start up again.
Hello,
Are you still having issues setting DMA? We also have an SPI demo using DMA in the SDK that you may find useful.
Best regards,
Felipe
Found my mistake. I tried to write just 2 byte to TXDATCTL, I changed the target register to TXDAT and now it works fine and fast :smileywink:
I am glad to hear that. Thanks for sharing the solution with the community!
Regards,
Felipe