-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?

ocrdu
1,7953 gold badges12 silver badges24 bronze badges
asked Aug 28, 2023 at 7:58

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.

answered Sep 3, 2023 at 18:03

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.