-1

I have a project where I need to store data (~16 Mo) on a memory and be able to fetch the data fast (<36000 bits/s) with DMA because I have other signals to take care of.

Currently I use an SD card with the SD library, but it doesn't seems to use DMA because of the delay between sending.

Many people told me it shouldn't be that hard and I suppose so. My guess is that I can't configure the DMA and/or the SPI correctly.

I tried this a long time ago.

Can someone show me an example of SPI with DMA, or tips on how to configure both?

1 Answer 1

0
#include <dmac.h>
#include <SPI.h>
#include <Arduino.h>
#include <sam.h>

uint8_t sourceBuffer[256];

void setup() {
  SPI.begin();
  SPI.setClockDivider(21);  // 4MHz clock (assuming 84MHz system clock)
  setupDMA();
}

void loop() {

  for(int i = 0; i < sizeof(sourceBuffer); i++) {
    sourceBuffer[i] = i;
  }
  startDMATransfer();
  delay(1000);
}

void setupDMA() {
  // Enable the DMA controller
  PMC->PMC_PCER1 |= PMC_PCER1_PID34;  // DMA ID for the Due is 34

  // Disable SPI DMA TX and RX
  SPI0->SPI_PTCR = SPI_PTCR_RXTDIS | SPI_PTCR_TXTDIS;

  // Setup a DMA channel for the transfer (channel 1 for this example)
  DmacChannel* channel = &(DMAC->DMAC_CH_NUM[1]);

  // Disable the DMA channel
  channel->DMAC_CHDR = DMAC_CHDR_DIS;

  // Clear any flags and reset the status
  channel->DMAC_CHDR = DMAC_CHDR_RES;

  // Set up source and destination pointers
  channel->DMAC_SADDR = (uint32_t) sourceBuffer;
  channel->DMAC_DADDR = (uint32_t) &(SPI0->SPI_TDR);

  // Configure source and destination pointers: source increments, destination does not
  channel->DMAC_CTRLA = DMAC_CTRLA_SRC_WIDTH_BYTE | DMAC_CTRLA_DST_WIDTH_BYTE;

  // Set source and destination handshake (related to the SPI0 Tx)
  channel->DMAC_CTRLB = DMAC_CTRLB_SRC_DSCR_FETCH_DISABLE | DMAC_CTRLB_DST_DSCR_FETCH_DISABLE |
                        DMAC_CTRLB_FC_MEM2PER_DMA_FC | DMAC_CTRLB_SRC_INCR_INCREMENTING |
                        DMAC_CTRLB_DST_INCR_FIXED;

  channel->DMAC_CFG = DMAC_CFG_SRC_PER(1) | DMAC_CFG_DST_PER(1) | DMAC_CFG_SOD | DMAC_CFG_FIFOCFG_ALAP_CFG;
}

void startDMATransfer() {
  // Assuming channel 1 is used
  DmacChannel* channel = &(DMAC->DMAC_CH_NUM[1]);

  // Set up the buffer size
  channel->DMAC_CUBC = sizeof(sourceBuffer);

  // Enable SPI DMA for TX
  SPI0->SPI_PTCR = SPI_PTCR_TXTEN;

  // Enable the DMA channel to start the transfer
  channel->DMAC_CHER = DMAC_CHER_ENA;
}

When you want to send data via SPI using DMA, you can fill the sourceBuffer and call startDMATransfer().

Remember to always refer to the Atmel SAM3X8E datasheet and the Arduino Due's schematic when you are working at this level, as it provides in-depth details on how the hardware functions. I am unable to run the code or test it. But I believe that the code logic is proper. Error handling is up to you.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.