Problem with DMA interrupt

Status
Not open for further replies.
Hi!
I would like to use the direct memory access (DMA) controller to transfer some values between two arrays and then run a interrupt on end-of-major loop. The data copying works fine but I can't get the interrupt to run. Below is an example sketch to demonstrate my problem. The B buffer fills up as expected but the interrupt never fires. (It should have fired 4 times for these 16 transfers)

Code:
const uint16_t N = 16;

uint16_t bufferA[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
uint16_t bufferB[N];

uint16_t i = 0;

void setup() {
	Serial.begin(9600);
	while (!Serial);

	dmaInit();
	
	while (i < N) {
		DMA_TCD1_CSR = DMA_TCD_CSR_START;
		delay(10);
		printBuffer();
		i++;
	}
}

void loop() {}

void dmaInit() {
	// Enable DMA clock
	SIM_SCGC7 |= SIM_SCGC7_DMA;

	// Use default configuration
	DMA_CR = 0;
	
	// Source address
	DMA_TCD1_SADDR = bufferA;
	DMA_TCD1_SOFF = 2;
	DMA_TCD1_SLAST = 0;
	
	// Destination address
	DMA_TCD1_DADDR = bufferB;
	DMA_TCD1_DOFF = 2;
	DMA_TCD1_DLASTSGA = 0;
	
	// Source and destination size 16 bit
	DMA_TCD1_ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1);
	// Number of bytes to transfer (in each service request)
	DMA_TCD1_NBYTES_MLNO = 2;
	
	// Set loop counts
	DMA_TCD1_CITER_ELINKNO = 4;
	DMA_TCD1_BITER_ELINKNO = 4;
	
	// Enable interrupt (end-of-major loop)
	DMA_TCD1_CSR = DMA_TCD_CSR_INTMAJOR;
	
	// Enable interrupt request
	NVIC_ENABLE_IRQ(IRQ_DMA_CH1);
}


void dma_ch1_isr() {
	Serial.println("ISR!");
	// Clear interrupt request for channel 1
	DMA_CINT = 1;
}


void printBuffer() {
	Serial.print("B: ");
	for (uint32_t i = 0; i < N; i++) {
		if (i != 0) Serial.print(", ");
		Serial.print(bufferB[i]);
	}
	Serial.println(" ");
}

The DMA interrupts have been working for me with the transfers being triggered by the ADC (see http://forum.pjrc.com/threads/24492-Using-the-PDB-on-Teensy-3#post_message_37659). I think the only difference now is that I need to trigger the transfers from software.

Any ideas are welcome!
 
Last edited:
I have still not figured out how to get the interrupt to trigger, but now I am using a synchronous workaround. You can wait for the DONE bit of the channel to get set and then you know the transfer is complete.

Code:
// Start transfer
DMA_TCD1_CSR = DMA_TCD_CSR_START;
// Then wait
while (!(DMA_TCD1_CSR & DMA_TCD_CSR_DONE));
// Do stuff!

This works fine since the DMA is typically really quick. With this approach it is also best to do the whole transfer in one request.

Code:
// Number of bytes to transfer (in each service request)
DMA_TCD1_NBYTES_MLNO = N * sizeof(uint16_t);
// Set loop counts
DMA_TCD1_CITER_ELINKNO = 1;
DMA_TCD1_BITER_ELINKNO = 1;

See also http://forum.pjrc.com/threads/18237-teensy-3-0-memory-to-memory-DMA-help
 
Hi!
I would like to use the direct memory access (DMA) controller to transfer some values between two arrays and then run a interrupt on end-of-major loop. The data copying works fine but I can't get the interrupt to run. Below is an example sketch to demonstrate my problem. The B buffer fills up as expected but the interrupt never fires. (It should have fired 4 times for these 16 transfers)

Any ideas are welcome!

Here you go....

Code:
const uint16_t N = 16;

uint16_t bufferA[] = {
  1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
uint16_t bufferB[N];

uint16_t i = 0;

void setup() {
  Serial.begin(9600);
  while (!Serial);

  dmaInit();

  while (i < N) {

    DMA_TCD1_CSR = DMA_TCD_CSR_START;
    // Enable interrupt (end-of-major loop)
    DMA_TCD1_CSR = DMA_TCD_CSR_INTMAJOR;
    delay(10);
    printBuffer();
    i++;
  }
}

void loop() {
}

void dmaInit() {
  // Enable DMA clock
  //SIM_SCGC7 |= SIM_SCGC7_DMA;
  //SIM_SCGC6 |= SIM_SCGC6_DMAMUX;

  // Enable interrupt request
  NVIC_ENABLE_IRQ(IRQ_DMA_CH1);

  // Use default configuration
  DMA_CR = 0;

  // Source address
  DMA_TCD1_SADDR = bufferA;
  DMA_TCD1_SOFF = 2;
  DMA_TCD1_SLAST = 0;

  // Destination address
  DMA_TCD1_DADDR = bufferB;
  DMA_TCD1_DOFF = 2;
  DMA_TCD1_DLASTSGA = 0;

  // Source and destination size 16 bit
  DMA_TCD1_ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1);
  // Number of bytes to transfer (in each service request)
  DMA_TCD1_NBYTES_MLNO = 2;

  // Set loop counts
  DMA_TCD1_CITER_ELINKNO = 4;
  DMA_TCD1_BITER_ELINKNO = 4;
}


void dma_ch1_isr() {
  Serial.println("ISR!");
  // Clear interrupt request for channel 1
  DMA_CINT = 1;
}


void printBuffer() {
  Serial.print("B: ");
  for (uint32_t i = 0; i < N; i++) {
    if (i != 0) Serial.print(", ");
    Serial.print(bufferB[i]);
  }
  Serial.println(" ");
}

OUTPUT:
B: 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
B: 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
B: 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
ISR!
B: 1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
B: 1, 2, 3, 4, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
B: 1, 2, 3, 4, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
B: 1, 2, 3, 4, 5, 6, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0
ISR!
B: 1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 0, 0, 0, 0, 0, 0
B: 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0
B: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0, 0, 0, 0, 0
B: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0, 0, 0, 0, 0
ISR!
B: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 0, 0, 0
B: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 0, 0, 0
B: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 0, 0
B: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0
ISR!
B: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16
 
Thank you duff! That works great!

My mistake was overwriting the whole control and status register, where the interrupt setting lives. To keep any other bits intact as well it might be even better to change the line to

Code:
DMA_TCD1_CSR |= DMA_TCD_CSR_START;
 
Thank you duff! That works great!

My mistake was overwriting the whole control and status register, where the interrupt setting lives. To keep any other bits intact as well it might be even better to change the line to

Code:
DMA_TCD1_CSR |= DMA_TCD_CSR_START;

oh ya missed that, thanks!
 
Status
Not open for further replies.
Back
Top