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;
}
}