LPC845 - continous SPI with DMA

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

LPC845 - continous SPI with DMA

1,998 Views
DasKeks
Contributor II

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?

Labels (2)
0 Kudos
Reply
4 Replies

1,887 Views
DasKeks
Contributor II

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.

0 Kudos
Reply

1,887 Views
FelipeGarcia
NXP Employee
NXP Employee

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

0 Kudos
Reply

1,887 Views
DasKeks
Contributor II

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:

0 Kudos
Reply

1,887 Views
FelipeGarcia
NXP Employee
NXP Employee

I am glad to hear that. Thanks for sharing the solution with the community!

 

Regards,

Felipe

0 Kudos
Reply