Faster method than analogRead?

I’m having trouble reading samples as fast as I need to. I’m aware that analogRead is not optimized for speed in any way, more so for convenience. When I time analogRead on my Artemis Nano I get approximately 80 microseconds per read, or a frequency around 12 kHz. My goal is to be able to read two channels at 44 kHz each (audio sampling for FFT). I can live with lower frequencies and a single channel if needed, but the documentation for the Artemis and Apollo3 core seem to say this shouldn’t be any problem.

On the ATMEGA chips I was pretty good at sorting through the datasheets to access low level behavior, but these Apollo3 cores are so much more complex and I’m drowning…

I searched the forms here and found the post “High Speed Analog Acquisition” but there’s so much more going on in that example than just an analogRead replacement that I can’t follow what’s going on (timers, sleep, DMA, etc). And the example seems specific to the Artemis Red Board and doesn’t work on my Artemis Nano . I’m making the assumptions that it’s the board difference that breaks it, but regardless of whether that’s true or not the program does not run on my board and attempts at debugging are getting me nowhere.

SO…

Does anyone have an example of an analogRead replacement that works faster? Eventually I’d like to figure out how to sync it with a timer and possibly use DMA, but for now I just want to tackle one issue at a time.

Help?

Try library version 1.2.3 instead of an 2.x.x for the Artemis ( tools / board / boards manager and downgrade Apollo3 ). The V1 library is MUCH faster as it does not include Mbed. Maybe that is enough for you.

I had Arduino 1.8.13 installed and when checking the SparkFun Apollo3 Boards version I had v1.2.1 installed and as far as I can tell this is the latest version. I don’t know if there’s another way to get to the 2.x.x versions you mention, but I’m not running that.

Just to double check, I installed the latest 1.8.16 Arduino IDE and checked the board manager again to confirm that 1.2.1 was still the latest version.

Just to be sure you didn’t have a digit shift in your version numbers, I downgraded SparkFun Apollo3 Boards to 1.1.2 and ran my speed test again and found no appreciable difference in speed.

Here’s the code for my speed test, it’s really bare bones and the only overhead is in the analogRead function itself.

void setup() {
    pinMode(A2, INPUT);
    
    Serial.begin(115200);
    Serial.println("Analog Read Speed Test");
  
    int loop_count = 0;
    unsigned long now = micros();
    
    while (loop_count < 1000) {
        analogRead(A2);
        loop_count++;
    }
    unsigned long elapsed_time = micros() - now;

    Serial.printf("Read %d samples in %d microseconds\n", loop_count, elapsed_time);
    Serial.printf("Averaged %d microseconds per sample\n", elapsed_time / loop_count);
    Serial.printf("%d Hz sampling rate\n\n", loop_count * 1000000 / elapsed_time);
}

void loop() {}

Any the serial output was

Analog Read Speed Test

Read 1000 samples in 76678 microseconds

Averaged 76 microseconds per sample

13041 Hz sampling rate

The different versions of the boards resulted in less than a 100 Hz sampling rate difference.

hi

I get the same speed, using library version V1.2.3. With a little modification in the printout only, I run the sketch on different platforms.

Arduino Uno

 1000 samples in 112112 microseconds
 Averaged 112.11 microseconds per sample
 8919.65 Hz sampling rate

Arduino MEGA2560

 1000 samples in 112100 microseconds
 Averaged 112.10 microseconds per sample
 8920.61 Hz sampling rate

on an Arduino DUO

 1000 samples in 4411 microseconds
 Averaged 4.41 microseconds per sample
 226705.97 Hz sampling rate
 
on an ATP with library  V1.2.3

 1000 samples in 78624 microseconds
 Averaged 78.62 microseconds per sample
 12718.76 Hz sampling rate
 
on an ATP with library  V2.1.1
 1000 samples in 120548 microseconds
 Averaged 120.55 microseconds per sample
 8295.45 Hz sampling rate

The DUO is very fast (same sketch).

Also notice the difference between an Artemis ATP on V1 ( 12718HZ) and same ATP, same sketch, but now with library V2.1.1 is 8295 hz (largely due to Mbed overhead)

The issue is with “only” 12Khz is the number of checks and settings that happen with each and every analogRead. Something could have changed since the last analogRead(), maybe setting the pin digital or what. So in order to be stable, and “better be save than sorry” the pin is re-init is performed for analog reading with each call.

Just for the fun I have made a special version (analogReadFast()) for version 1.2.3 where I removed the checks. It will only initialize the first time the pin and assumes only ONE and ONLY ONE ADC is used. I have then run the same sketch on ATP with library V1.2.3 and analogReadFast()

 1000 samples in 7227 microseconds
 Averaged 7.23 microseconds per sample
 138370.00 Hz sampling rate

Bang… 138Khz !!

You can try it as well. Attached is a zip file with the installation instructions for V1.x only. It also includes more information & options about the analogReadFast() and the sketch that I have used. I don’t expect this to become part of the product, but it shows the hardware can provide good speed.

AnalogReadFast.zip (11.6 KB)

Thanks for the detailed reply! And thanks for verifying my results.

I’ll try out your analogReadFast() next time I get a chance, but sounds like it will be perfect for what I need.

This worked perfectly for me, thanks!

It took a bit to figure out where to put the files you shared, but once I did it works flawlessly out if the box. For those who might be reading later, I found the existing ap3_analog.h and ap3_analog.cpp files (installed via board manager) and replaced them with the files paulvha provided. For me on Windows 10, the files were located in

\AppData\Local\Arduino15\packages\SparkFun\hardware\apollo3\1.2.1\cores\arduino\ard_sup\

for ap3_analog.h and the subfolder “analog” for ap3_analog.cpp

Is there a way to use this AnalogReadFast function with apollo3 2.2.1? :?:

This kind of Analog reading will be very helpful, but I am actually not using 1.2.3 as commented on the README file.

I have tried that.

As stated in an earlier response with V2.2.1 we can achieve around 8295Hz. Made changes so that the initialization of the pin is happening only once. It can now achieve max. 31Khz on an ATP. Mbed has a huge impact, every clock tick there are background administration tasks performed (whether you want it or need it). Nothing I can do about that.

Be aware this proof_of_concept only works for ONE single ADC pin.

In case you want to try it.

In ..... apollo3/2.2.1/cores/arduino/sdk/core-implement (sdk NOT mbed-bridge!!)
  make a safety copy of CommonAnalog.cpp
  replace it with the attached CommonAnalog.cpp in the zip-file.

Also in the zip file the sketch I used.

fast_analog_2_x_x.zip (7.71 KB)

Is there a way for all the ADC pins (on version 2.2.1) to have this faster functionality?

it would mean a major rewrite of the analog driver.

Although the Apollo3 chip has 7 circuits to measure Analog signal, only 1 is used. Every time an analogRead is performed the analogcircuit and pin are ‘connected’. All my change does is check whether current pin of the analogRead() is the same as the previous one. If so it will skip performing the ‘connection.’

Anyone try this on the SparkFun IoT RedBoard - ESP32 Development Board? It has the same 11 KHz limit on the A/D conversion.

Thank you paulvha, your mods to the CommonAnalog.cpp using lib 2.2.1 work great. Saved 75us off an ADC read! This seems like a reasonable mod for future library releases. Here is my file to conditional compile the mods…

CommonAnalog.cpp (30 KB)