Pushing to 1.5 MHz the performance of analogRead() on Arduino Due

First, I'd like to say hi to everyone being my first post on this forum. Actually, I signed in to share a code snippet I wrote this morning with everyone since I found almost nothing around about this topic, besides the very well know blog post of Evaluating Arduino and Due ADCs.

It's about pushing to the limit the performance of analogRead() on the Arduino Due. I needed to record a waveform with a carrier frequency of 1 MHz so I asked myself: "Why not using the Due instead of an FPGA or a portable oscilloscope?" Well, after a profoundly religious morning messing up with the ATSAM3X8E manual from Atmel, testing several configurations of the ADC core, I came out with a rather satisfying solution.

analogRead() takes only 0.67 us to execute on my Arduino Due, without loosing too much precision. I didn't do any methodic testing about the resolution of the ADC at around 1.5Mhz, but I would expect it to be at least 8 bits, which is absolutely honest.
I used a sequence of NOP operations to adjust the ADC speed closer to 1 MHz.

Anyways, here's the code: it records 8192 data points via ADC at around 1 MHz every 10 seconds and sends the results via serial communication to the PC.

bool Valid = false;
bool Rec = false;
int Cur = 0;
const int DataSize = 8192;
int Data[DataSize];
int Val = 0;

#define READ(Pin) \
  Val = analogRead(Pin); \
  __asm__ __volatile__ ("nop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\n");

void setup() {
  // First thing: we want to know EXACTLY how much time it takes an analogRead to run
  // Tuned with a signal generation technique ;-)
  // This ADC configuration achieves a 1.4927 Mhz sample rate
  REG_ADC_MR = 0x10320300;   
  // initialize the serial communication:
  Serial.begin(57600);
}

/*
  This reading loop has been tested with a reference signal at 1 MHz and thus optimized to have:
    Delay = 1.018(1) us
    Sample rate = 0.9818(1) MHz
*/
void loop() {
  READ(A0);
  if (Rec && (Cur < DataSize)) {
    Data[Cur++] = Val;
  } else {
    if (Rec && (Cur >= DataSize)) {
       for (int i=0; i<DataSize; ++i) {
         Serial.println(Data[i]);  
       }
       Rec = false;
       Valid = true;
       Cur = 0;
       delay(10000);    
    }
    if (!Rec && (Val > 100)) {
      Rec = true;
      Cur = 0;
      Data[Cur++] = Val; 
    } 
  }
}

If you're curious about the resolution of such a readout, attached there's the spectrum of a 0.5 MHz recorded square wave.

if you assumption is correct that the resolution is 8 bits, why not reflect that in your code and use a byte array. That allows twice as much samples in your buffer!

uint16_t  Cur = 0;
const uint16_t DataSize = 8192;
uint8_t Data[DataSize];
uint8_t Val = 0;

Hi,
Thank'you for the interesting post. Which IDE version are you using? I don't get over ~200Ksps with your sketch on the IDE 1.5.2.
Anyway, the ADC of the SAM3X8E needs 1us to peform a conversion and I do not see how can you get 1.5Msps!
Your program is perhaps reading out the ADC channel data register every 0.67us but it doesn't mean at all that your ADC is sampling at that rate.
BR
iz4afl