DMA on XmegaA3

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

Hi,

 

I was wondering if I can get some input on my DMA setup.

I'm trying to transfer data from one string called source to another string called destination.

Destination equals all zeros when it runs, so it's not quite there yet

 

Here are the global string variables and the DMA_Init

uint8_t source[10] = {0,1,2,3,4,5,6,7,8,9};
uint8_t destination[10];

void DMA_Init(void) {
	DMA.CTRL = DMA_ENABLE_bm ;
	DMA.CH0.CTRLA = DMA_CH_BURSTLEN0_bm | DMA_CH_BURSTLEN1_bm; // 8 byte burst
	DMA.CH0.ADDRCTRL = DMA_CH_SRCDIR0_bm | DMA_CH_DESTDIR1_bm; // Increment source and address
	DMA.CH0.TRIGSRC = 0x00; // Software triggered
	DMA.CH0.TRFCNT = 10; // Block size is 10 bytes

	DMA.CH0.SRCADDR0 = (( (uint32_t)&source) >> 0) & 0xFF;
	DMA.CH0.SRCADDR1 = (( (uint32_t)&source) >> 1*8) & 0xFF;
	DMA.CH0.SRCADDR2 = (( (uint32_t)&source) >> 2*8) & 0xFF;

	DMA.CH0.DESTADDR0 = (( (uint32_t)&destination) >> 0) & 0xFF;
	DMA.CH0.DESTADDR1 = (( (uint32_t)&destination) >> 1*8) & 0xFF;
	DMA.CH0.DESTADDR2 = (( (uint32_t)&destination) >> 2*8) & 0xFF;

	DMA.CH0.CTRLA |= DMA_CH_ENABLE_bm;
	DMA.CH0.CTRLA |= DMA_CH_TRFREQ_bm;
	while(!(DMA.INTFLAGS & DMA_CH0TRNIF_bm));
	DMA.INTFLAGS = DMA_CH0TRNIF_bm;
}

Thanks for any help

Last Edited: Fri. Aug 25, 2017 - 08:09 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
(( (uint32_t)&destination) >> 2*8)

what's the order of precedence? the >> or the * ? use brackets or 0,8,16....

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

What warnings do you get when you do a clean build?  I think you need to drop the ampersand.  source and destination are arrays, so the variable name alone (w/o &) refers to the address of the first element.

	DMA.CH0.SRCADDR0 = (( (uint32_t)&source) >> 0) & 0xFF;
	DMA.CH0.SRCADDR1 = (( (uint32_t)&source) >> 1*8) & 0xFF;
	DMA.CH0.SRCADDR2 = (( (uint32_t)&source) >> 2*8) & 0xFF;

	DMA.CH0.DESTADDR0 = (( (uint32_t)&destination) >> 0) & 0xFF;
	DMA.CH0.DESTADDR1 = (( (uint32_t)&destination) >> 1*8) & 0xFF;
	DMA.CH0.DESTADDR2 = (( (uint32_t)&destination) >> 2*8) & 0xFF;

 

Greg Muth

Portland, OR, US

Xplained Boards mostly

Atmel Studio 7.0 on Windows 10

 

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

what's the order of precedence? the >> or the * ? use brackets or 0,8,16....

 I think you need to drop the ampersand.

Good ideas. Made the changes, but it still doesn't work

What warnings do you get when you do a clean build?

I get a warning for the type-casts, saying "cast from pointer to integer of different size". For this code

DMA.CH0.SRCADDR0 = (( (uint32_t)source) >> 0) & 0xFF;
DMA.CH0.SRCADDR1 = (( (uint32_t)source) >> 8) & 0xFF;
DMA.CH0.SRCADDR2 = (( (uint32_t)source) >> 16) & 0xFF;

DMA.CH0.DESTADDR0 = (( (uint32_t)destination) >> 0) & 0xFF;
DMA.CH0.DESTADDR1 = (( (uint32_t)destination) >> 8) & 0xFF;
DMA.CH0.DESTADDR2 = (( (uint32_t)destination) >> 16) & 0xFF;

EDIT: Ok I got it. I'll post the changes that made it work tomorrow.

Last Edited: Fri. Aug 25, 2017 - 08:09 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I have a question about timing DMA reads and writes with the USART in MSPIM.

It's the same project where I'm interfacing an ADS8332 Evaluation Board with an Xmega256A3BU.

 

I have a version that works where the USART SPI write is manual, and the read is automatic (via DMA), saving it in a buffer.

For that version, this is the DMA Read init function, and the manual USART SPI write function in main that I use.

// USART SPI Read via DMA
void DMA_Read_Init(uint8_t *destination) {
	
	// Enable DMA controller
	DMA.CTRL = DMA_CH_ENABLE_bm;
	DMA.CH0.REPCNT = REPEAT_COUNT; // Repeat_Count is a macro I have
	
	// Set burst length to 1 byte, single-shot, and repeat mode
	DMA.CH0.CTRLA = DMA_CH_BURSTLEN_1BYTE_gc | DMA_CH_SINGLE_bm | DMA_CH_REPEAT_bm;
	DMA.CH0.ADDRCTRL = (DMA_CH_SRCRELOAD_BURST_gc | DMA_CH_SRCDIR_INC_gc) |
	(DMA_CH_DESTRELOAD_TRANSACTION_gc | DMA_CH_DESTDIR_INC_gc);
	
	// Set the trigger source to USARTE0 Receive
	DMA.CH0.TRIGSRC = DMA_CH_TRIGSRC_USARTE0_RXC_gc;
	
	// Load the block transfer count register with the number of bytes in the block
	DMA.CH0.TRFCNT = 3;
	
	// Give the address of the USARTE0 data register
	DMA.CH0.SRCADDR0 = (( (uint32_t)&USARTE0_DATA) >> 0) & 0xFF;
	DMA.CH0.SRCADDR1 = (( (uint32_t)&USARTE0_DATA) >> 8) & 0xFF;
	DMA.CH0.SRCADDR2 = (( (uint32_t)&USARTE0_DATA) >> 16) & 0xFF;
	
	// Give the address of the destination array that holds the values
	DMA.CH0.DESTADDR0 = (( (uint32_t)destination) >> 0) & 0xFF;
	DMA.CH0.DESTADDR1 = (( (uint32_t)destination) >> 8) & 0xFF;
	DMA.CH0.DESTADDR2 = (( (uint32_t)destination) >> 16) & 0xFF;
}
// USART SPI Write
for (uint8_t i = 0; i < 10; i++) {
	Tug_CONVST(); // Start a conversion on the ADS8332
	PORTA.OUTCLR = PIN0_bm; // Bring CS low
	Interrupt_Check(); // Wait for a conversion to be complete
	SPI_Data_Transfer(0xd0); // Send a command to get data, 24 bits long
	SPI_Data_Transfer(0x00); // Dummy byte 1
	SPI_Data_Transfer(0x00); // Dummy byte 2
	PORTA.OUTSET = PIN0_bm; // Bring CS high
}

My question is about DMA reading and writing. I'd like to have a DMA-write channel that writes the get-data command, and then sends the extra two dummy bytes, while the other DMA channel reads the data.

 

I'm not sure though how to coordinate the chip select and convert-start pins with the DMA to initiate conversions.

What would that pseudo-code look like?

 

This is the DMA-write channel that I'm using which uses a source array of {0xd0, 0x00, 0x00} to write to the device

void DMA_Write_Init(uint8_t *source) {

	DMA.CH1.REPCNT = REPEAT_COUNT;

	// Set burst length to 1 byte, set to single-shot, and repeat mode
	DMA.CH1.CTRLA = DMA_CH_BURSTLEN_1BYTE_gc | DMA_CH_SINGLE_bm | DMA_CH_REPEAT_bm;
	DMA.CH1.ADDRCTRL = (DMA_CH_SRCRELOAD_BLOCK_gc | DMA_CH_SRCDIR_INC_gc) | 
	(DMA_CH_DESTRELOAD_BURST_gc | DMA_CH_DESTDIR_INC_gc); 

	// Set the trigger source to the SPIC slave
	DMA.CH1.TRIGSRC = DMA_CH_TRIGSRC_USARTE0_DRE_gc;

	// Load the block transfer count register with the number of bytes in the block
	DMA.CH1.TRFCNT = 3;

	// Give the address of the destination array that holds the values
	DMA.CH1.DESTADDR0 = (( (uint32_t)&USARTE0_DATA) >> 0) & 0xFF;
	DMA.CH1.DESTADDR1 = (( (uint32_t)&USARTE0_DATA) >> 8) & 0xFF;
	DMA.CH1.DESTADDR2 = (( (uint32_t)&USARTE0_DATA) >> 16) & 0xFF;

	// Give the address of the SPIC data register
	DMA.CH1.SRCADDR0 = (( (uint32_t) source) >> 0) & 0xFF;
	DMA.CH1.SRCADDR1 = (( (uint32_t) source) >> 8) & 0xFF;
	DMA.CH1.SRCADDR2 = (( (uint32_t) source) >> 16) & 0xFF;
}

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
PORTA.OUTCLR = PIN0_bm; // Bring CS low
DMA.CH0.CTRLA |= DMA_CH_ENABLE_bm; // Enable Read DMA channel
DMA.CH1.CTRLA |= DMA_CH_ENABLE_bm; // Enable Write DMA channel
_delay_ms(100);
PORTA.OUTSET = PIN0_bm; // Bring CS high

I have it kind of communicating with this setup (in main), but it only captures one reading

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

Is it really worth employing DMA when you just want to memcpy() 10 bytes? 

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

Good point. I should've mentioned that this program I posted is just a test. In actuality it's going to be about 8ksps hopefully.

I haven't been focusing on this problem, but in the back of my mind I've been thinking about using the interrupt pin that the external ADC pulses for each conversion-ready to trigger an event that pulls down the CS pin.. I'll try and post the code when I get there