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:

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:


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

On the arduino forum user "Totv" 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:
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() {        
  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();
    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.print("Average time per conversion: ");

  Serial.println("Values: ");
  for(i=0;i<1000;i++) {

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 was made to help others with their projects. Connect with Franci on Google+