Double Buffered ADC to DMA

I am trying to convert SAMD51 code written by MartinL which stores ADC values in 2 buffers using DMA to work on a SAMD21 microcontroller

His example code is below

// Use SAMD51's DMAC to read the ADC on A4 and alternately store results in two memory arrays
#define NO_RESULTS 256

volatile boolean results0Ready = false;
volatile boolean results1Ready = false;
uint16_t adcResults0[NO_RESULTS];                                  // ADC results array 0
uint16_t adcResults1[NO_RESULTS];                                  // ADC results array 1

typedef struct           // DMAC descriptor structure
{
  uint16_t btctrl;
  uint16_t btcnt;
  uint32_t srcaddr;
  uint32_t dstaddr;
  uint32_t descaddr;
} dmacdescriptor ;

volatile dmacdescriptor wrb[DMAC_CH_NUM] __attribute__ ((aligned (16)));          // Write-back DMAC descriptors
dmacdescriptor descriptor_section[DMAC_CH_NUM] __attribute__ ((aligned (16)));    // DMAC channel descriptors
dmacdescriptor descriptor __attribute__ ((aligned (16)));                         // Place holder descriptor

void setup() {
  Serial.begin(115200);                                                       // Start the native USB port
  while(!Serial);                                                             // Wait for the console to open

  PORT->Group[PORTA].DIRSET.reg = PORT_PA20;                                  // Initialise the output on D9 for debug purposes
  
  DMAC->BASEADDR.reg = (uint32_t)descriptor_section;                          // Specify the location of the descriptors
  DMAC->WRBADDR.reg = (uint32_t)wrb;                                          // Specify the location of the write back descriptors
  DMAC->CTRL.reg = DMAC_CTRL_DMAENABLE | DMAC_CTRL_LVLEN(0xf);                // Enable the DMAC peripheral
   
  DMAC->Channel[0].CHCTRLA.reg = DMAC_CHCTRLA_TRIGSRC(ADC0_DMAC_ID_RESRDY) |  // Set DMAC to trigger when ADC0 result is ready
                                 DMAC_CHCTRLA_TRIGACT_BURST;                  // DMAC burst transfer
  descriptor.descaddr = (uint32_t)&descriptor_section[1];                     // Set up a circular descriptor
  descriptor.srcaddr = (uint32_t)&ADC0->RESULT.reg;                           // Take the result from the ADC0 RESULT register
  descriptor.dstaddr = (uint32_t)adcResults0 + sizeof(uint16_t) * NO_RESULTS; // Place it in the adcResults0 array
  descriptor.btcnt = NO_RESULTS;                                              // Beat count
  descriptor.btctrl = DMAC_BTCTRL_BEATSIZE_HWORD |                            // Beat size is HWORD (16-bits)
                      DMAC_BTCTRL_DSTINC |                                    // Increment the destination address
                      DMAC_BTCTRL_VALID |                                     // Descriptor is valid
                      DMAC_BTCTRL_BLOCKACT_SUSPEND;                           // Suspend DMAC channel 0 after block transfer
  memcpy(&descriptor_section[0], &descriptor, sizeof(descriptor));            // Copy the descriptor to the descriptor section
  descriptor.descaddr = (uint32_t)&descriptor_section[0];                     // Set up a circular descriptor
  descriptor.srcaddr = (uint32_t)&ADC0->RESULT.reg;                           // Take the result from the ADC0 RESULT register
  descriptor.dstaddr = (uint32_t)adcResults1 + sizeof(uint16_t) * NO_RESULTS; // Place it in the adcResults1 array
  descriptor.btcnt = NO_RESULTS;                                              // Beat count
  descriptor.btctrl = DMAC_BTCTRL_BEATSIZE_HWORD |                            // Beat size is HWORD (16-bits)
                      DMAC_BTCTRL_DSTINC |                                    // Increment the destination address
                      DMAC_BTCTRL_VALID |                                     // Descriptor is valid
                      DMAC_BTCTRL_BLOCKACT_SUSPEND;                           // Suspend DMAC channel 0 after block transfer
  memcpy(&descriptor_section[1], &descriptor, sizeof(descriptor));            // Copy the descriptor to the descriptor section
  
  NVIC_SetPriority(DMAC_0_IRQn, 0);    // Set the Nested Vector Interrupt Controller (NVIC) priority for TCC1 OVF to 0 (highest) 
  NVIC_EnableIRQ(DMAC_0_IRQn);         // Connect TCC1 to Nested Vector Interrupt Controller (NVIC)
  
  DMAC->Channel[0].CHINTENSET.reg = DMAC_CHINTENSET_SUSP;                     // Activate the suspend (SUSP) interrupt on DMAC channel 0
  
  ADC0->INPUTCTRL.bit.MUXPOS = ADC_INPUTCTRL_MUXPOS_AIN2_Val;     // Set the analog input to A4
  while(ADC0->SYNCBUSY.bit.INPUTCTRL);                // Wait for synchronization
  ADC0->SAMPCTRL.bit.SAMPLEN = 0x00;                  // Set max Sampling Time Length to half divided ADC clock pulse (2.66us)
  while(ADC0->SYNCBUSY.bit.SAMPCTRL);                 // Wait for synchronization  
  ADC0->CTRLA.reg = ADC_CTRLA_PRESCALER_DIV256;       // Divide Clock ADC GCLK by 256 (48MHz/256 = 187.5kHz)
  ADC0->CTRLB.reg = ADC_CTRLB_RESSEL_12BIT |         // Set ADC resolution to 12 bits 
                    ADC_CTRLB_FREERUN;               // Set ADC to free run mode        
  while(ADC0->SYNCBUSY.bit.CTRLB);                    // Wait for synchronization
  ADC0->CTRLA.bit.ENABLE = 1;                         // Enable the ADC
  while(ADC0->SYNCBUSY.bit.ENABLE);                   // Wait for synchronization
  ADC0->SWTRIG.bit.START = 1;                         // Initiate a software trigger to start an ADC conversion
  while(ADC0->SYNCBUSY.bit.SWTRIG);                   // Wait for synchronization
  DMAC->Channel[0].CHCTRLA.bit.ENABLE = 1;            // Enable DMAC ADC on channel 1
}

void loop() 
{  
  if (results0Ready)                                  // Display the results in results0 array
  {
    Serial.println(F("Results0"));
    for (uint32_t i = 0; i < NO_RESULTS; i++)
    {
      Serial.print(i);
      Serial.print(F(": "));
      Serial.println(adcResults0[i]);
    }
    Serial.println();
    results0Ready = false;                            // Clear the results0 ready flag
  }
  if (results1Ready)                                  // Display the results in results1 array
  {
    Serial.println(F("Results1"));
    for (uint32_t i = 0; i < NO_RESULTS; i++)
    {
      Serial.print(i);
      Serial.print(F(": "));
      Serial.println(adcResults0[i]);
    }
    Serial.println();
    results1Ready = false;                            // Clear the results1 ready flag
  }
}

void DMAC_0_Handler()                                           // Interrupt handler for DMAC channel 0
{
  static uint8_t count = 0;                                     // Initialise the count 
  if (DMAC->Channel[0].CHINTFLAG.bit.SUSP)                      // Check if DMAC channel 0 has been suspended (SUSP) 
  {  
    DMAC->Channel[0].CHCTRLB.reg = DMAC_CHCTRLB_CMD_RESUME;     // Restart the DMAC on channel 0
    DMAC->Channel[0].CHINTFLAG.bit.SUSP = 1;                    // Clear the suspend (SUSP)interrupt flag
    if (count)                                                  // Test if the count is 1
    {
      results1Ready = true;                                     // Set the results 1 ready flag
    }
    else
    {
      results0Ready = true;                                     // Set the results 0 ready flag
    }
    count = (count + 1) % 2;                                    // Toggle the count between 0 and 1 
    PORT->Group[PORTA].OUTSET.reg = PORT_PA20;                  // Toggle the output high then low on D9 for debug purposes
    PORT->Group[PORTA].OUTCLR.reg = PORT_PA20; 
  }
}

@MartinL To give credit where credit is due, I found this code on this thread

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.