Learning about DMA and interrupts

Status
Not open for further replies.

bilal33

Member
Hello

I'm playing with a modified version of code I found here.

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 < 1) 
  { 
    DMA_TCD1_CSR |= DMA_TCD_CSR_START;
    delay(10);
    printBuffer();
    i++;
  }

  if(!(DMA_TCD1_CSR & DMA_TCD_CSR_DONE))
  {
    Serial.println("DMA not complete");
  }
  else
  {
      Serial.println("DMA complete");
  }

}

void loop() {
}

void dmaInit() {
  // Enable DMA clock
  // system clock gating control register 7
  // bit 1 controls the DMA clock.
  SIM_SCGC7 |= SIM_SCGC7_DMA;

  // bit 1 controls the DMA Mux clock 
  SIM_SCGC6 |= SIM_SCGC6_DMAMUX;

  // Enable interrupt (end-of-major loop)
  DMA_TCD1_CSR |= DMA_TCD_CSR_INTMAJOR;
  // Enable interrupt request
  // for DMA channel 1
  NVIC_ENABLE_IRQ(IRQ_DMA_CH1);

  // Use default configuration
  // 21.3.1, kinetis manual, the DMA control register
  // Use normal operation mode
  DMA_CR = 0;

  // Source address
  DMA_TCD1_SADDR = bufferA;
  // source address to increment after
  // each dma tranfer completes, basically
  // after each minor loop
  DMA_TCD1_SOFF = 2;
  // source address to increment after a major
  // loop completes
  DMA_TCD1_SLAST = 0;

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

  // Source and destination size 16 bit
  // 000 (0) 8-bit
  // 001 (1) 16-bit
  // 010 (2) 32-bit
  DMA_TCD1_ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1);
  // Number of bytes to transfer (in each service request)
  // Minor byte transfer count
  DMA_TCD1_NBYTES_MLNO = N*sizeof(uint16_t);

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

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

void dma_error_isr()
{
    Serial.println("DMA error");
}


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

It's essentially the same code, with some more comments as I looked stuff up.
I've modified some of the parameters so that DMA_TCD1_NBYTES_MLNO is set to transfer all 32 bytes in one block instead of transferring 2 bytes at a time.

In my setup(), I have a while loop that is set to trigger the dma operation once. The DMA operation continues and by looking at the serial monitor output, I can see that bufferB has been correctly populated with all the data.

I've noticed that if I only trigger the dma once, I do not get the DMA ISR (no "ISR!" printed in the serial monitor). I also get the "DMA complete" message printed, but no interrupt.
So the DMA operation completed, but it's corresponding interrupt was never raised.

If I re-trigger the dma by adding the following line below the while loop

DMA_TCD1_CSR |= DMA_TCD_CSR_START;

Then I get the ISR message.
I had assumed that once the major loop iteration completes and the DMA has transferred everything over, I would get the ISR callback.

I looks like I need trigger the DMA operation and if the system detects that the loop count conditions have been met, then the interrupt is raised.

Am I understanding this correctly?

Thanks
 
Status
Not open for further replies.
Back
Top