SAMD51 DMA from memory to a SPI TX.

Go To Last Post
2 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi all, here is a little gift to the forum.  I know lots of people get lost setting up DMAs, I spent about 3 days hacking together this code that DMAs data from an array out to a SPI TX.  Lots can go wrong.

 

First, make sure your SPI interface is configured correct, test data output with:

 

while (!(SERCOM5 -> SPI.INTFLAG.bit.DRE)) {};
SERCOM5 ->SPI.DATA.reg = 0x55;

 

Then, using Atmel's Start page, configure a basic DMA setting, this helps get your clocks initialized, etc.

 

Initialize your descriptor structures and and example tx buffer:

 

//DMA Stuff
COMPILER_ALIGNED(16)  //Probably redundant with the below commands
static DmacDescriptor descriptor __attribute__((aligned(16)));
static DmacDescriptor wb_descriptor __attribute__((aligned(16)));

static const uint8_t buffer_tx[20] = {
	0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A,
	0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14,
};

 

Here is the setup routine in main():

 

  

	DMAC->CTRL.bit.DMAENABLE = 0;
	DMAC->BASEADDR.reg = (uint32_t)&descriptor;
	DMAC->WRBADDR.reg = (uint32_t)&wb_descriptor;	
	DMAC->CTRL.bit.LVLEN0 =1 ;  // Activate all levels
	DMAC->CTRL.bit.LVLEN1 =1 ;
	DMAC->CTRL.bit.LVLEN2 =1 ;
	DMAC->CTRL.bit.LVLEN3 =1 ;
	DMAC->CTRL.bit.DMAENABLE = 1;  
	DMAC ->Channel[0].CHCTRLA.bit.BURSTLEN =0;  //Single beat burst
	DMAC ->Channel[0].CHCTRLA.bit.TRIGACT = 0x2;   //Trigger per burst (beat)
	DMAC ->Channel[0].CHCTRLA.bit.TRIGSRC = 0xf;   //0xf = SERCOM5 TX
	DMAC ->Channel[0].CHPRILVL.bit.PRILVL = 0x0;   //Set channel priority level to 0.
	descriptor.BTCTRL.bit.VALID    = 0x1; //Its a valid channel
	descriptor.BTCTRL.bit.EVOSEL   = 0x0; //Event disable
	descriptor.BTCTRL.bit.BLOCKACT = 0x0; //No action at last block, channel disable
	descriptor.BTCTRL.bit.BEATSIZE = 0x0;  // 0 = Byte.
	descriptor.BTCTRL.bit.SRCINC   = 0x1;   //Source increment is enabled
	descriptor.BTCTRL.bit.DSTINC   = 0x0;   //Destination increment disabled
	descriptor.BTCTRL.bit.STEPSEL  = 0x1;   //Step applies to Source
	descriptor.BTCTRL.bit.STEPSIZE = 0x0;    //Step size is X1.
	descriptor.BTCNT.reg           = 20;   //Start with 20 transactions
	descriptor.SRCADDR.reg         = (uint32_t)&buffer_tx[0] + sizeof(buffer_rx);	  
	descriptor.DSTADDR.reg         = (uint32_t)&SERCOM5->SPI.DATA.reg;

 

And then each time you want to trigger the DMA to dump buffer_tx to your SPI interface:

 

DMAC ->Channel[0].CHCTRLA.bit.ENABLE = 0x1;

 

Wow, it looks so easy here, but it was not so easy for me to get this working.  Happy DMA'ing! 

 

-Troy

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Oops, cleanup on aisle 3.

 

Use this instead:

 

descriptor.BTCNT.reg           =  sizeof(buffer_tx);   
descriptor.SRCADDR.reg         = (uint32_t)&buffer_tx[0] + sizeof(buffer_tx);