Hi geryuu123,
I don't think that the SAMD21 will be able to meet your requirements.
The first issue is that two ADC channels are running at 50kHz, means that using the DMA to transfer the data is your only option, as an ISR operating at this frequency would overwhelm the CPU.
The second is that the SAMD21's DMA has no means of alternating the analog input channels (A0, A1, etc...) during operation. You could change analog channels using the DMA transfer complete (TCMPL) interrupt, but again we're back to the first issue, in that calling the ISR at 100kHz would overwhelm the CPU.
One possible solution however could be to use the SAMD21's cousin, the SAMD51.
The SAMD51 contains two ADC peripherals: ADC0 and ADC1, but also has a DMAC sequencing capability. The DMAC sequencing capability allows the ADC's control registers to be changed during operation. This means input channels can be alternately switched on a single ADC peripheral, for instance ADC0.
Here's some example code that uses the SAMD51's DMAC sequencing to switch between A0 and A1 on ADC0 during operation. DMAC channel 0 switches input channels, while channel 1 reads the data:
// Use SAMD51's DMA Sequencing to read both A0 and A1 without CPU intervention
uint16_t adcResult[2]; // A0 and A1 result array
uint32_t inputCtrl[] = { ADC_INPUTCTRL_MUXPOS_AIN0, // ADC0 INPUTCTRL register MUXPOS settings AIN0 = A0, AIN5 = A1
ADC_INPUTCTRL_MUXPOS_AIN5 };
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
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_SEQ) | // Set DMAC to trigger on ADC0 DMAC sequence
DMAC_CHCTRLA_TRIGACT_BURST; // DMAC burst transfer
descriptor.descaddr = (uint32_t)&descriptor_section[0]; // Set up a circular descriptor
descriptor.srcaddr = (uint32_t)inputCtrl + sizeof(uint32_t) * 2; // Configure the DMAC to set the
descriptor.dstaddr = (uint32_t)&ADC0->DSEQDATA.reg; // Write the INPUT CTRL
descriptor.btcnt = 2; // Beat count is 2
descriptor.btctrl = DMAC_BTCTRL_BEATSIZE_WORD | // Beat size is WORD (32-bits)
DMAC_BTCTRL_SRCINC | // Increment the source address
DMAC_BTCTRL_VALID; // Descriptor is valid
memcpy(&descriptor_section[0], &descriptor, sizeof(descriptor)); // Copy the descriptor to the descriptor section
DMAC->Channel[1].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)adcResult + sizeof(uint16_t) * 2; // Place it in the adcResult array
descriptor.btcnt = 2; // Beat count is 1
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
memcpy(&descriptor_section[1], &descriptor, sizeof(descriptor)); // Copy the descriptor to the descriptor section
ADC0->INPUTCTRL.bit.MUXPOS = 0x0; // Set the analog input to A0
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->DSEQCTRL.reg = ADC_DSEQCTRL_AUTOSTART | // Auto start a DMAC conversion upon ADC0 DMAC sequence completion
ADC_DSEQCTRL_INPUTCTRL; // Change the ADC0 INPUTCTRL register on DMAC sequence
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
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 Sequencing on channel 0
DMAC->Channel[1].CHCTRLA.bit.ENABLE = 1; // Enable DMAC ADC on channel1
delay(1); // Wait a millisecond
}
void loop()
{
Serial.println(adcResult[0]); // Display the A0 result
Serial.println(adcResult[1]); // Display the A1 result
Serial.println(); // Display newline
delay(1000); // Wait for 1 second
}