3

I need to send data as fast as possible from an Arduino DUE to an extern DAC. To do so I use DMA & SPI and I want DMA to fetch data from the memory and send it to the SPI which will just relay it via its Master Output Slave input. So far I did a DMA transfer from a variable to another, woked perfectly. I'm using the same code but change the address as SPI_TDR (Transmit Data Register), unfortunatly it's not working. I guess the address is not good but if so what should I do ?

Here is my code :

#include <dmac.h>
#include <SPI.h>

#define DMA_CH 0         //N° Canal du DMA
#define DMA_BUF_SIZE 32  //Taille mémoire DMA

uint32_t g_dma_buf2[DMA_BUF_SIZE];

void setup() {
  Serial.begin(9600);
  SPI.begin();

  SPI0->SPI_WPMR = 0x53504900;             //Protection key
  SPI0->SPI_IDR = 0x0000070F;              //Desactivation interrupts
  SPI0->SPI_MR = SPI_MR_MSTR | SPI_MR_PS;  //SPI master
}

void loop() {
  Serial.println("+++++");
  pmc_enable_periph_clk(ID_DMAC);

  uint32_t i;
  uint32_t cfg;
  dma_transfer_descriptor_t desc;

  for (i = 0; i < DMA_BUF_SIZE; i++) {
    g_dma_buf2[i] = i;
    Serial.print(g_dma_buf2[i]);
  }
   Serial.println();

  dmac_init(DMAC);
  dmac_set_priority_mode(DMAC, DMAC_PRIORITY_ROUND_ROBIN);
  dmac_enable(DMAC);
  cfg = DMAC_CFG_SOD_ENABLE | DMAC_CFG_AHB_PROT(1) | DMAC_CFG_FIFOCFG_ALAP_CFG;  //Config registre CFG
  dmac_channel_set_configuration(DMAC, DMA_CH, cfg);

  desc.ul_source_addr = (uint32_t)g_dma_buf2;
  desc.ul_destination_addr = SPI0->SPI_TDR;
  desc.ul_ctrlA = DMAC_CTRLA_BTSIZE(DMA_BUF_SIZE) | DMAC_CTRLA_SRC_WIDTH_WORD | DMAC_CTRLA_DST_WIDTH_WORD;
  desc.ul_ctrlB = DMAC_CTRLB_SRC_DSCR_FETCH_DISABLE | DMAC_CTRLB_DST_DSCR_FETCH_DISABLE | DMAC_CTRLB_FC_MEM2MEM_DMA_FC | DMAC_CTRLB_SRC_INCR_INCREMENTING | DMAC_CTRLB_DST_INCR_FIXED;
  desc.ul_descriptor_addr = 0;

  SPI_Enable(SPI0);
  dmac_channel_multi_buf_transfer_init(DMAC, DMA_CH, &desc);
  dmac_channel_enable(DMAC, DMA_CH);
  Serial.println("*****");

  while (!dmac_channel_is_transfer_done(DMAC, DMA_CH)) { Serial.print('X'); }
Se
  Serial.print("SR : "); Serial.println(SPI0->SPI_SR, HEX);
  Serial.print("TDR : "); Serial.println(SPI0->SPI_TDR, HEX);
  Serial.print("PSR : "); Serial.println(PIOA->PIO_PSR, HEX);   //PIO_SODR
  Serial.print("OSR : "); Serial.println(PIOA->PIO_OSR, HEX);
  Serial.println(DMAC->DMAC_CH_NUM[0].DMAC_SADDR , HEX);
  Serial.println(DMAC->DMAC_CH_NUM[0].DMAC_DADDR, HEX);
  Serial.println("-----");
}

I use mainly this example : https://ww1.microchip.com/downloads/en/Appnotes/Atmel-42291-SAM3A-3U-3X-4E-DMA-Controller-DMAC_ApplicationNote_AT07892.pdf#_OPENTOPIC_TOC_PROCESSING_d91e3076 And here is the datasheet of the µc : https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-11057-32-bit-Cortex-M3-Microcontroller-SAM3X-SAM3A_Datasheet.pdf

You can see at the bottom of my code sevral prints, from them I had many idea : does the PIO could block the data ? Does the address PIO_PA26A_SPI0_MOSI could work ? Could the SPI block the data because conditions are not met ?

Any idea is welcomed, I'm on this for some time now.

Edit : SPI is not an necessity, the idea is to send data without length limit (unlike UART). I'm considering using SSC.

1
  • I had a look at the block diagram (section 32.2) and I'm wondering if DMA really can address SPI or if I have to manage some layer in-between.
    – Vlad
    Apr 18 at 12:26

1 Answer 1

2

I managed to solve it but not with SPI, I use the SSC of the µcontroller (which does I²S and uses DMA automatically) :

uint32_t liste[] PROGMEM = { 0x8F66F1, 0x0, 0xAAAAAB };  //Data to send
#define DMA_BUF_SIZE (sizeof(liste) / sizeof(liste[0]))  //Size DMA buffer

uint32_t i;

void setup() {
  uint clk_div = 0x90;  //Clock divider wanted
  clk_div = (clk_div / 24) / DMA_BUF_SIZE;
  clk_div = floor(clk_div + 0.5);
  clk_div = clk_div * 24 * DMA_BUF_SIZE;  //Clock divider adjusted -> MCK/2*clk_div

  PMC->PMC_WPMR = 0x504D4300;                                    //Desactivation protection PMC
  PMC->PMC_PCER0 = (1 << ID_SSC);                                //Activation SSC clock
  PMC->PMC_SCER |= 0x100;                                        //Activation clock
  PIOA->PIO_WPMR = 0x50494F00;                                   //Desactivation protection Port I/O A
  PIOA->PIO_PDR = PIO_PDR_P14 | PIO_PDR_P15 | PIO_PDR_P16;       //I/O controlled by the peripheral
  PIOA->PIO_ABSR |= PIO_PA14B_TK | PIO_PA15B_TF | PIO_PA16B_TD;  //Assignation of I/O to SSC

  SSC->SSC_CR = SSC_CR_RXDIS | SSC_CR_TXDIS | SSC_CR_SWRST;                                                     //Desactivation and reset SSC
  SSC->SSC_WPMR = 0x53534300;                                                                                   //Desactivation protection SSC
  SSC->SSC_IDR = 0xFFFFFFFF;                                                                                    //Desactivation Interrupts
  SSC->SSC_IER = 0x00000000;                                                                                    //Desactivation Interrupts bis
  SSC->SSC_CMR = clk_div;                                                                                       //Clock management
  SSC->SSC_TFMR = SSC_TFMR_DATLEN(0x18) | SSC_TFMR_MSBF | SSC_TFMR_DATNB(0);                                    //Data transfert management
  SSC->SSC_TCMR = SSC_TCMR_CKS_MCK | SSC_TCMR_CKO_CONTINUOUS | SSC_TCMR_START_CONTINUOUS | SSC_TCMR_STTDLY(0);  //Clock during transfert management
  SSC->SSC_CR = SSC_CR_TXEN;                                                                                    //Activation transmission SSC
}

void loop() {
  for (i = 0; i < DMA_BUF_SIZE; i++) {
    ssc_write((Ssc*)SSC, (uint32_t)liste[i]);  //Transmission SSC
  }
}

In case someone try the same thing.

Your Answer

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

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