Here is a proof of concept sketch that uses DMA to collect ADC samples. With the ADC clock running at 48MHz/16, the DMA reads a sample every 2.0 us. I just did simple testing with DAC, and jumpering to ground or 3.3v. You can experiment with various ADC settings.
I can't get the sketch to work. I am using an M0 Pro.
When I compile the sketch it says "void ADCsync()' was declared 'extern' and later 'static' [-fpermissive]"
Any help would be greatly appreciated. I would really like to get this working.
Huh, I ran this code without modification on an Adafruit Feather M0 (Adafruit's smaller version of Arduino Zero), and I got conversion times of 2057 us too, not 2 us.
If you check at the beginning of the code you'll find a buffer of 1024 analog value :
#define HWORDS 1024
uint16_t adcbuf[HWORDS];
The printed time is the one needed to complete the bufffer. It means 1024 conversions. So if you have a printed time of 2056us, divide it by HWORDS to get the time needed for one sample...
void loop() {
uint32_t t;
t = micros();
adc_dma(adcbuf,HWORDS);
while(!dmadone); // await DMA done isr
t = micros() - t;
Serial.print(float(t/HWORDS)); Serial.print(" us per conversion ");
Serial.println(adcbuf[0]);
delay(2000);
}
I'm a little confused and would love if someone could clarify things for me. The last thing printed is adcbuf, which looks like it contains the analog values read, am I wrong about that? I applied a constant 2V to the pin and got readings that were: 0, 0, 1023, 0, 0, 1023, repeating. Also, is there any limit for how small or large hwords can be besides the memory limit? If I wanted to do only two conversion at a time would that create other issue? Thanks
I have tried your fast adcdma and it is truly impressive.
Is it possible to disable the free run and trigger sampling anew each time the routine is called? I have tried but without success. ( I am new to this and am not sure how to proceed.) I am aiming to use the pin scan facility of the chip.
Any pointer in the right direction will be truly appreciated.
Is there any way to change the sample rate? I'm trying to use this for an FFT which requires a very specific sample frequency, and I'm not entirely sure where to insert code... Thanks!
AloyseTech:
Hey guys,
If you check at the beginning of the code you'll find a buffer of 1024 analog value :
#define HWORDS 1024
uint16_t adcbuf[HWORDS];
The printed time is the one needed to complete the bufffer. It means 1024 conversions. So if you have a printed time of 2056us, divide it by HWORDS to get the time needed for one sample... :)
void loop() {
uint32_t t;
t = micros();
adc_dma(adcbuf,HWORDS);
while(!dmadone); // await DMA done isr
t = micros() - t;
Serial.print(float(t/HWORDS)); Serial.print(" us per conversion ");
Serial.println(adcbuf[0]);
delay(2000);
}
The ADC prescaler that divides down the generic clock.
The ADC resolution (default 10 bits).
The ADC input configuration (differential or single ended).
The ADC gain (default divide by 2) and mode (single shot or free running).
The ADC SAMPLEEN bitfield in the SAMPLECTRL register that adds a specfied number of half ADC clock cycles.
In mantoui's DMAC code the ADC is set as follows:
Generic clock = 48MHz
ADC prescaler = 16
ADC resolution = 10 bits
ADC input configuration = single ended
ADC gain = divide by 2 and mode = free running
ADC SAMPLEEN bitfield = 0
Using the formula for propagation delay (sample time) in the SAMD21 datasheet with the ADC set to single ended, free running, gain divide by 2 and 10-bit resolution:
where:
resolution = 10 bits
delay gain = 1 (single ended mode and gain divide by 2), taken from table 33-1 in the SAMD21 datasheet
ADC CLK frequency = 48MHz / 16 = 3MHz
Propagation delay = ((10 / 2) + 1) / 3MHz = 2.0us
That's 6 full ADC clock cycles.
Note however that the 3MHz ADC clock exceeds the maximum ADC clock frequency, specified in the ADC electrical characteristics in the SAMD21 datasheet at 2.1MHz. It might be better to use the divide by 32 prescaler that will generate a 1.5MHz ADC clock giving a 4.0us sample time.
The sample time can then be extended by half ADC clock cycles by increasing the SAMPLEEN bitfield in the ADC's SAMPLECTRL register.
I think I'm missing something with how this code works. Perhaps with regard to memory consumption or access? If I remove the delay from the loop, my program/device locks up in 0-5 seconds of running. Isn't this just overwriting adcbuf every loop? Is something else allocating memory and causing the crash?
I am having the same issue with the serial port stopping printing the times. It appears to be random, or at least I can't see the correlation to anything else. I have even tried to use a dual buffer and this makes no difference. Still looking for a solution as I would like this to sample continuously.
I ran into the same problem, and posted here about it. It seems that ADC->RESULT.reg is not returning true, which causes the while(!dmadone); loop to become endless. It seems to happen more frequently without an active voltage on the pin.