Fast analogRead with Arduino Due

Posted on by

Since the day that I got my Arduino Due I had an idea to use it as a cheap oscillosope.
84MHz main clock was very promising but when I tried analogRead I was dissapointed to see that it needs 40uS for one conversion.

I guess It could be useful for signals up to 1kHz:

arduino due analogread

I knew there must be a better way so I went digging...

First I found this great article: http://www.djerickson.com/arduino/

The due's default 0xC value in the ADC Mode Register sets ADC STARTUP to 768. 768 / 21MHz = 36.6uS startup time.
So he changed that value to 2 with this line of code:

REG_ADC_MR = (REG_ADC_MR & 0xFFF0FFFF) | 0x00020000;

Conversion time went down to 3.97uS. That is 10x improvement.

I went digging into SA3X datasheet and found FREERUN flag which puts ADC into continuous mode.
Enabling it made the conversion time go down to 3.12uS.

REG_ADC_MR = (REG_ADC_MR & 0xFFFFFF0F) | 0x00000080; //enable FREERUN mode

After testing all sorts of configurations I found another optimization:

analogReadResolution(12);

This lowered conversion time to 3.11uS and increased resolution to 12bits.

On the arduino forum user "Totv" http://goo.gl/jdRjc0 suggested commenting out line 164 in file \arduino-1.5.1r2\hardware\arduino\sam\cores\arduino\wiring_analog.c

//adc_disable_channel(ADC, ulChannel);

Beacuse there is no need to disable ADC after every conversion.

With this improvement conversion time dropped to 1.79uS.

I could now measure 10kHz signal with no problem:

arduino due 10kHz and 50kHz

But then I found great piece of code from arduino forum user "stimmer" which ignores analogRead by using more direct approach: http://goo.gl/Bn8Z4P
With this solution single conversion takes only 1uS. And now we have a cheap 1MSa/s oscilloscope. ;)

unsigned long start_time;
unsigned long stop_time;
unsigned long values[1000];

void setup() {        
  Serial.begin(9600);  
  ADC->ADC_MR |= 0x80;  //set free running mode on ADC
  ADC->ADC_CHER = 0x80; //enable ADC on pin A0
}

void loop() {
  unsigned int i;
    
  start_time = micros();
  for(i=0;i<1000;i++){
    while((ADC->ADC_ISR & 0x80)==0); // wait for conversion
    values[i]=ADC->ADC_CDR[7]; //get values
  }
  stop_time = micros();

  Serial.print("Total time: ");
  Serial.println(stop_time-start_time); 
  Serial.print("Average time per conversion: ");
  Serial.println((float)(stop_time-start_time)/1000);

  Serial.println("Values: ");
  for(i=0;i<1000;i++) {
    Serial.println(values[i]);
  }
  
  delay(2000);
}

Measured signals:

arduino due 1MSPS adc

arduino due 100kHz

I hope you will find this "quest" for fast Arduino Due ADC usefull :)
Franci KapelFranci is an experienced web developer who spends most of his free time hacking gadgets. Site frenki.net was made to help others with their projects. Connect with Franci on Google+