Tuesday, May 13, 2014

Using the Arduino Uno’s built-in 10-bit to 21-bit ADC (Analog to Digital Converter)

Advertisement

Subscribe by Email! (via FeedBurner)
By Gabriel Staples Written: 13 May 2014
Last Updated: 27 Feb. 2015

History (newest on top):
-17 Feb. 2015 - changed latest link to new release: V2.1 alpha, in yellow below
-24 Jan. 2015 - added link to Version 2.0 alpha below...allows sampling rates of ~50+ kHz, and fixed bug to allow >16-bit samples to not have computation errors

A Few Other Articles to Check Out:

This Article:

Using the Arduino Uno’s built-in 10-bit to 21-bit ADC (Analog to Digital Converter)???
--Wait, what did you say!? I thought that Arduinos only had a 10-bit ADC!  How can you get, for example, 16-bit resolution out of a 10-bit ADC?  Well, the answer is oversampling.  Atmel has written a really good article about it called "AVR121 Enhancing ADC resolution by oversampling."

Before I continue, I'd like to give a very special thanks to user "fat16lib," on the Adafruit Forums, who first made this technique known unto me by his post right here, thereby inspiring me to write this, my first ever, library.

Now on to the library:



------------------------------------------------------------------------------------------------------------
27 May 2014: Update: With a ton of help from Ray Benitez, of Hackscribble, I am still evaluating the practicality & legitimacy of oversampling, via experimental data collection & analysis, in order to see if it really is increasing the precision to the degree I am claiming/hoping.

Update: 11 July 2014: I still plan on doing much more testing with my library, when I get the chance, and working on it to refine and validate it a lot more.  I want to know for myself, with certainty, how well it really is or isn't working, and what its limitations are.  One of the refinements I will make, for instance, will be to speed up the Arduino ADC from ~8kHz max sample rate to ~54kHz max sample rate, by changing the ADCSRA register to have an ADC prescaler of 16 instead of 128 (thanks to Simon Monk, pg. 82 of "Going Further with Sketches" for teaching me about this).  This way, I can see if reading the ADC faster affects the results produced by oversampling.  It will also be nice to just not have to wait so long for high-bit ADC reads that require tons of 10-bit samples to get one high-bit sample.  Meanwhile, if you are concerned about whether or not my library truly produces higher-precision ADC reads, you might just consider buying a 12 or 16-bit ADC from Adafruit.  They look really nice.  I will be using these myself to test my library eventually, in conjunction with an LTC1650CN 16-bit DAC to produce a signal to test.  As part of my test, I will vary the reference pin source from a noisy voltage regulator to a clean, dedicated reference IC chip.  This way, I can see how the noise affects the results.  Also, as part of my testing, I'll modify my library to introduce random noise (via software) to the analog readings, to see how that affects oversampling (It's possible that introducing just the right magnitude of random noise will increase precision of the oversampling process).  
Anyway, here's the Adafruit ADCs!  
12-bit ($10): https://www.adafruit.com/products/1083
16-bit ($15): https://www.adafruit.com/products/1085 
------------------------------------------------------------------------------------------------------------
Advertisement

Download:

Download my Library (eRCaGuy_analogReadXXbit) V1.0 alpha on GitHub by clicking here --> then click the "Download Zip" link at the bottom of the right-hand pane.  Using my library, you can actually choose any precision you want between 10 and 21 bits, when using the analogReadXXbit() function!

UPDATE 17 Feb 2015: Version 2.1 alpha available here --> then click the "Download Zip" link at the bottom of the right-hand pane.  Major overhaul of library. You now have the option to sample at ~50+khz when doing 10-bit samples, and I resolved a bug which prevented >16-bit readings from outputting correctly. This version is still in the works, but is significantly better than V1.0.  There is one thorough example contained in the library, so read it to see how to use the new functions. I HIGHLY RECOMMEND THIS 2.1 VERSION OVER THE ORIGINAL eRCaGuy_analogReadXXbit V1.0 alpha LIBRARY ABOVE. More examples coming soon.

Keep reading below:


Basically, here's how oversampling works:

For every n extra bits of precision that you want, you have to take 4^n samples at the base 10-bit precision.  You then sum all of the readings, and divide the sum by 2^n, by doing a bit-shift on the sum, to the right, n places (ie: sum >> n).  10-bit precision has an analogRead() range of 0-1023.  11-bit precision has an analogRead() range of 0-2046, 12-bit has a range of 0-4092, etc.

Each time you increase the resolution by 1-bit, you double the precision.  So, if you want 11-bit precision, that's 1 bit more than the standard 10-bits, so you have to take 4^1, or 4 samples at the standard 10-bit resolution.  You then sum the samples and divide by 2^1, or 2.  This now gives you a maximum value of 1023*4/2 = 2046.  If you want 16-bit precision, n = 6, so you need 4^6 = 4096 samples at the standard 10-bit resolution.  You sum them and divide by 2^6 = 64.  Your analogRead() max reading possible is now 4096*1023/64 = 65472.

Here's a nice table to sum it up.  You can find this spreadsheet in my library folder when you download it.


My Library - eRCaGuy_analogReadXXbit V1.0 alpha:

Using this concept, I have written a nice little library to tidy it all up and demonstrate oversampling.  I call it "eRCaGuy_analogReadXXbit."  The main function is:

analogReadXXbit(uint8_t analogPin, uint8_t bits_of_precision, unsigned long num_samples_to_avg)

I named it after the standard Arduino function, analogRead(), but I added the "XXbit" to indicate that you can specify the # of bits of precision you want from the ADC when doing the read.  You can input a bits_of_precision value from 10 to 21!  

Here is a simple sample code:

/*
Circuit:
We need to read an analog voltage on A0, so place a pot with the outer legs going to 5V and GND, respectively, and the wiper (middle leg) going to A0
-make sure to set your Serial Monitor to 115200 baud rate
*/

//include the library
#include <eRCaGuy_analogReadXXbit.h>

//instantiate an object of this library class; call it "adc"
eRCaGuy_analogReadXXbit adc;

//Global constants
//constants required to determine the voltage at the pin
const float MAX_READING_10_bit = 1023.0;
const float MAX_READING_11_bit = 2046.0;
const float MAX_READING_12_bit = 4092.0;
const float MAX_READING_13_bit = 8184.0;
const float MAX_READING_14_bit = 16368.0;
const float MAX_READING_15_bit = 32736.0;
const float MAX_READING_16_bit = 65472.0;
const float MAX_READING_17_bit = 130944.0;
const float MAX_READING_18_bit = 261888.0;
const float MAX_READING_19_bit = 523776.0;
const float MAX_READING_20_bit = 1047552.0;
const float MAX_READING_21_bit = 2095104.0;

void setup() 
{
  Serial.begin(115200);
  Serial.println(F("Oversampling example to get 10-bit to 21-bit resolution using a 10-bit ADC on an Arduino"));
  Serial.println("Ultra Basic demo");
  Serial.println(""); //add a line space
}

void loop() 
{
  //local variables
  int pin = A0; //analogRead pin
  int bits_of_precision = 16; //must be a value between 10 and 21
  int num_samples = 1;
  
  //take a reading on pin A0
  float analog_reading = adc.analogReadXXbit(pin,bits_of_precision,num_samples); //get the avg. of [num_samples] 16-bit readings 
  
  //output results
  Serial.print("analog_reading = ");
  Serial.println(analog_reading);
  Serial.print("Voltage = ");
  Serial.print(5.0*analog_reading/MAX_READING_16_bit,5); //display up to 5 digits of precision
  Serial.println("V");
  Serial.println("");
  
  //wait a bit before taking another reading
  delay(500);
}

You will see several other examples in the library.

Happy coding!



***Subscribe by Email! (via FeedBurner)***

62 comments:

  1. Hi Gabriel, kudos to such an awesome article. I have learned many things from you so thank you very much for sharing your knowledge. I have a couple of questions - will or should your code run on the Arduino Yun? I monitor a fluid pressure sensor as part of a weight change system for a honeybee hive. I am very interested in the analog precision potential and came across your site as I was researching bits and mV's and I would like to add a few bits to my sensors precision.
    I am wondering what is better - oversampling to get to 16 bit or using one of these - ADS1115 16-Bit ADC - 4 Channel with Programmable Gain Amplifier chips from adafruit? I would be very interested if you could share some of your thoughts - regarding the debate or conversation from May 27 with Ray, that sounds very very interesting and maybe I can learn something from that also?

    Thank you again, and warm regards,
    Stephen
    stephen@stephensapiary.com

    ReplyDelete
  2. Stephen, I'm glad I can help! Thanks for the support. Yes, the library should run fine on the Yun, since all it requires is the analogRead() command, and does not access any ports or registers directly. However, I don't have a Yun to verify this. It is definitely better to go with the ADS1115 from Adafruit. It is going to be much more predictable and accurate.

    Regarding the debate on the Arduino forums, Ray is the only one that I can tell who is willing to research stuff instead of just criticizing me. Ray and I have been looking at the utility of my library, and I think we still need to keep looking at things, as so far, it seems that it may not be increasing the precision as predicted, when taking a single sample. However, when taking and averaging multiple samples, I think we may see more promising results. I haven't done any extensive testing personally yet, and at the moment it's not high on my priority list, but I think my library as it is now is simply the *start* of a final product, as I think it needs a lot more research to prove its value (or the lack thereof). Anyway, def. go with the ADS1115 from adafruit for now, though, as it will just work, without having to wonder if it is working properly or if oversampling isn't adequate for your given situation. Oversampling does have its limitations, and I am still discovering what those are. One of those, however, seems to be noise, and I hypothesize that if the signal is too clean, oversampling won't increase precision, but if the sample is nice and noisy, it will. Weird stuff.

    ReplyDelete
  3. This comment has been removed by a blog administrator.

    ReplyDelete
    Replies
    1. Noah Jackson, thanks for your comments…NOT! I don’t appreciate your spam, so I have deleted your comment and reported you to Google. Hopefully your Google+ account is disabled soon. Thanks for your time.

      Delete
  4. This comment has been removed by a blog administrator.

    ReplyDelete
  5. Also you'r library is object oriented for no reason why would you use an empty class with a single non static function anyway just makes it slower? either declare it as static and remove the constructor or get rid of the class

    ReplyDelete
    Replies
    1. I'm an aeronautical engineer, and I don't have a computer science background (but I'm learning, and I wish I did), and this library is my first time ever really using C++. It's my first library ever, and I have virtually no formal programming training. I don't know how to do it another way. I simply looked at some other libraries and was happy to even make it work. Feel free to fork the library on Github and demonstrate precisely what you mean, as I am certainly striving to improve.

      Delete
    2. I am familiar with the concept that static class functions are shared among all instantiated objects of the class, but how do you access a static function if not through an object? With this "-->" or something? And you're saying that using a static function accessed through the main class, rather than accessed through an object of the class, is faster? Also, if I remove the constructor and get rid of the class, how do I make a shareable Arduino library at all? Because you're right....originally, I just made it a stand-alone function in an Arduino .ino file, but how do I make the function universally accessible by any code I write (without copying the .ino file to every place I write a new sketch) without making it a class? Thanks for any help/info by the way. I'm a smart guy, but I am teaching myself a new field and need help along the way of course. Once I finish another dozen projects or so (who knows when that will be), I plan on buying Alex Allain's "Jumping Into C++" to help me learn a great deal more ([url="http://www.amazon.com/Jumping-into-C-Alex-Allain/dp/0988927802"]Jumping into C++[/url]). Meanwhile, I'm mostly self-taught, using forums and online tutorials & things.

      Delete
    3. FYI, for anyone else wondering: http://www.tutorialspoint.com/cplusplus/cpp_static_members.htm
      -I should have made the member function static, then called it like this: "eRCaGuy_analogReadXXbit::analogReadXXbit()". I would have been better off calling the function this way *instead of* calling it through a (constructed) object of this class.

      I need to get back to this code and do a ton of testing on it someday anyway....

      Delete
  6. Would you mind owerwrite the name eRCaGuy_analogReadXXbit-master to eRCaGuy_analogReadXXbit on Github download section. It can not be install directly, beacuse of the "-master" text. (Win7 - Arduino 1.0.5)

    ReplyDelete
    Replies
    1. Sorry, I can't do that. Github adds the "-master" name to the folder automatically, after I upload the files. All you have to do is manually fix the folder name. Ex, after I extract the files from my download I get the following directory, "\eRCaGuy_analogReadXXbit-master\eRCaGuy_analogReadXXbit-master." Simply move the inner directory up one folder, and rename it to remove "-master." Now I have the whole library under "\eRCaGuy_analogReadXXbit". Now move it into your Arduino "libraries" folder and you're good to go.

      Delete
  7. Thank You. I think someone who have reached to this point would be able to solve this problem. But again, it would be more elegant to install from the IDE with a few mouseclick.
    I use your library for measuring temperature with Pt100 resistance thermometer (RTD). This library's advantage (beside of the higher resolution) is a smoother line on the graph - and of course avoid an ADC chip.
    In my case 14 bit resolution is the best. 15bit is almost the same as 14bit; with 13bit the graph line is acceptable, but the temperature resolution is more than 0.1°C.

    ReplyDelete
    Replies
    1. Szőcs, thanks for your comment! I am very interested in your code and plots of your data results, if you don't mind sharing. Honestly, I have been involved in many projects simultaneously and have spent very little time myself using this library. Would you mind sharing your code and plots, so I can see how well the oversampling is working? If you are willing to share, please put on Google Drive or Dropbox, and post your link here, *or* email me the results at rcflyyer (email at sign) gmail. Thanks!

      Delete
  8. Your code works great upto 12 bit on 50 hz (as your table shows)
    Can you use direct register access? Its much faster.
    see http://www.billporter.info/2010/08/18/ready-set-oscillate-the-fastest-way-to-change-arduino-pins/
    I don't know how to do it.


    // Define various ADC prescaler
    const unsigned char PS_16 = (1 << ADPS2);

    ReplyDelete
    Replies
    1. kiwibird, great suggestion! I'll add it to my to-do list. I think I know how to use direct register access and that is a good idea. If I am wrong about knowing how to do it now, I can always figure it out, and I'll look at your link. Also, I'll see about adding a feature to speed up the ADC by a factor of 8, by changing the prescaler, since this is also possible. Hopefully I can come out with version 2 of this code and repost.

      Delete
    2. Question for you: how did you test my code at 12 bit and 50 Hz. Do you have a signal generator by chance? if so, I'd love for you to send me plots of your input signal and the values read by my library.

      Delete
  9. Im using ac sine wave with some noise on it.
    Would love your direct access code when you have it.

    My first prescaler code didn't work.
    however code below worked great.


    void setup()
    {
    #if FASTADC
    // set prescale to 16
    sbi(ADCSRA,ADPS2) ;
    cbi(ADCSRA,ADPS1) ;
    cbi(ADCSRA,ADPS0) ;
    #endif



    I used this code with good results, but (later) need to make some comments on it.
    looop=8;
    while (looop-- >0)
    {
    sampleI += analogRead(inPinV); //Read in rawac signal
    }
    sampleI=sampleI*multval;

    ReplyDelete
  10. I think this code uses direct access for analogue read
    https://courses.cit.cornell.edu/ee476/FinalProjects/s2008/cj72_xg37/cj72_xg37/index.html
    Also they point out that fixed point as faster than floating point
    pre-scaler is set very fast. Not sure if accuracy is good at this level
    . see line 297

    ReplyDelete
  11. Can you also tell me conceptually if the c++ is much faster than standard arduno code?

    ReplyDelete
    Replies
    1. kiwibird, I still haven't been able to get around to doing this, but I can answer this question quickly. There is a common misconception that Arduino has or is its own language. This is just not true. The "Arduino language" is simply a collection of C++ libraries, written in C++. As far as I can tell (my only wondering is whether or not the Arduino code may use Assembly language in parts of it), *all* Arduino code is just C++ (or C) code. However, in order to maximize simplicity and the ability to universally apply the Arduino code, it oftentimes exchanges efficiency and speed for ease of use and broad application. This means that if you get into the 660 pg ATmega328 datasheet and learn to access ports and pins and registers and things directly, you will nearly always be able to make your code run faster by *not* using some of the Arduino functions or libraries directly. For example, according to Simon Monk's "Going Further With Sketches" book on pg. 79 [see links at bottom of my post here: http://electricrcaircraftguy.blogspot.com/2014/01/the-power-of-arduino.html#.VHjPA4vF_6d]), using direct port access (ex: via "PORTB") is 46 times faster than the Arduino function "digitalWrite". Similarly, digital input can be sped up dramatically by accessing the input ports (ex: PINB), rather than using "digitalRead", and analog input can be sped up by at least 6.5 times as well. If you open up the Arduino source code and look at how digitalRead, for example, is implemented, you'll see that each time you call it it has to determine how to access the correct register that corresponds to the pin you want. This is where the slow-down occurs. By hard-coding in which register you want to access, knowing the pin before-hand rather than having the code determine the register during run-time, you get dramatic speed improvements. Having said this, I still intend to expand this library and add the functionality to optionally improve speed ~10x (hopefully), but I still haven't done it....

      Delete
  12. Hi Gabriel,
    thank you for your reply. Its well documented that direct port access is much faster. Whats not documented is If there is difference in general arduino code compared to C. I would love to see some tests on this.
    Im keen to see the 10x speed improvement as the library has been very useful to me, but I still prefer more speed.

    ReplyDelete
    Replies
    1. kiwibird, can you elaborate on this statement please? "Whats not documented is If there is difference in general arduino code compared to C. I would love to see some tests on this."
      --I personally don't understand what you mean, as Arduino code is C.

      Delete
  13. Sorry for the delay. I missed the email notification.
    What im asking is in effect.
    Is adc.analogReadXXbit(1,10,1) going to be faster than analogRead(1) ?
    Is there any update on direct register read for this code?
    Will adc low power mode help? It will increase accuracy and decrease noise.
    However noise might help Decimation.
    Is noise + averaging* number of samples the same as decimation? Im not clear by reading the docs.
    What im thinking is if say 12 bits (16 samples ) is too slow to read a 60 hz signal (51.8 was max recommended)
    , can I use say 14 samples?

    ReplyDelete
  14. Replies
    1. I'm sorry; not yet! I'm in the middle of a big move. It will be weeks before I'm back into a normal routine. I've got this library high on my list though when I get back to my hobby stuff.

      Delete
    2. kiwibird, I've substantially overhauled the whole library and managed to speed up the library to be perhaps 50khz analogreads or so. Would you like an alpha copy now, or after I've had time to properly make examples and do some more testing?

      Delete
  15. Version 2.0 alpha release is now available. Link is in the article above, currently it is highlighted in yellow.

    ReplyDelete
  16. A big thank you. I hope to test it in the next week

    ReplyDelete
    Replies
    1. Sounds great. I look forward to hearing back from you about what you find out.

      Delete
  17. I get this error when I compile version 2. Help appreciated.











    In file included from eRCaGuy_analogReadXXbit_v2_practice1.ino:56:
    D:\arduino-1.0-windows\arduino-1.0\libraries\eRCaGuy_analogReadXXbit/eRCaGuy_analogReadXXbit.h:137: error: ISO C++ forbids initialization of member '_bits_of_resolution'
    D:\arduino-1.0-windows\arduino-1.0\libraries\eRCaGuy_analogReadXXbit/eRCaGuy_analogReadXXbit.h:137: error: making '_bits_of_resolution' static
    D:\arduino-1.0-windows\arduino-1.0\libraries\eRCaGuy_analogReadXXbit/eRCaGuy_analogReadXXbit.h:137: error: ISO C++ forbids in-class initialization of non-const static member '_bits_of_resolution'
    D:\arduino-1.0-windows\arduino-1.0\libraries\eRCaGuy_analogReadXXbit/eRCaGuy_analogReadXXbit.h:138: error: ISO C++ forbids initialization of member '_num_samples_to_avg'
    D:\arduino-1.0-windows\arduino-1.0\libraries\eRCaGuy_analogReadXXbit/eRCaGuy_analogReadXXbit.h:138: error: making '_num_samples_to_avg' static
    D:\arduino-1.0-windows\arduino-1.0\libraries\eRCaGuy_analogReadXXbit/eRCaGuy_analogReadXXbit.h:138: error: ISO C++ forbids in-class initialization of non-const static member '_num_samples_to_avg'
    D:\arduino-1.0-windows\arduino-1.0\libraries\eRCaGuy_analogReadXXbit/eRCaGuy_analogReadXXbit.h:139: error: ISO C++ forbids initialization of member '_ADC_speed'
    D:\arduino-1.0-windows\arduino-1.0\libraries\eRCaGuy_analogReadXXbit/eRCaGuy_analogReadXXbit.h:139: error: making '_ADC_speed' static
    D:\arduino-1.0-windows\arduino-1.0\libraries\eRCaGuy_analogReadXXbit/eRCaGuy_analogReadXXbit.h:139: error: ISO C++ forbids in-class initialization of non-const static member '_ADC_speed'

    ReplyDelete
    Replies
    1. Try using the latest Arduino beta version (1.5.8 beta) instead of 1.0.x. The Arduino beta version has many many improvements and additional features and my code compiled fine under the latest beta Arduino version. Nevertheless, I'll try to make a better fix on this bug for the next code iteration.

      Delete
    2. I'm assuming you got this error when you tried to run my example code I included with V2.0?

      Delete
  18. Im having no end of problems uploading to my dil 328p. I hope to have it resolved in the next week. Will keep you informed.

    ReplyDelete
    Replies
    1. Sounds good. I have no idea what problems you're having of course, but here's a few thoughts:

      First off: I'm guessing you at least got my library to compile?

      Next: I've noticed that sometimes the bootloader struggles with uploads--especially the Nano's bootloader. Try loading the Uno's bootloader onto the ATmega328 instead--in the event you were using the Nano's bootloader. I oftentimes load the Uno bootloader onto my Nanos too.

      Also, try uploading code via a programmer instead of using the bootloader. This has never failed me, even when uploads have failed repeatedly when trying to use the bootloader. To do this go to File --> Upload Using Programmer.

      Good luck.

      I'd also like to let you know I have been working on the library more, and have further refined it. I just have not had a good opportunity to sit down for another couple hours and write some good examples and do some testing is all. Once I get a solid several hours though I'd like to write a brand-new article on the new version of code, and get it posted. I'll post a link here of course to the new article when this happens.

      Delete
  19. Ive spend nearly 2 days on this.
    upload of blink via usb fails "avrdude: stk500_getsync(): not in sync: resp=0x00"

    In short a nano uploads via ftdi board, but not a 328p-dil
    I have 5 328-p from smiery, and one from element14. The element14 had a different chip id (known issue on the web with simple fix)
    Both have the same issue.
    I was using basic 328p circuit. 16 MHz crystal, 2 caps, reset cap & 10 k resistor.
    I then put the 328p in a very old uno pcb. Always same issue. 328p always fails, and nanos always go.
    serial out and serial in at 9600 baud always works. upload via isp always works.

    Well the plot thickens.
    My new UNO arrived today. Upload works fine via usb
    put in another 328p. serial test works fine.
    upload fails. same error.
    put in a brand new 328 (From smiery). burnt bootloader.
    load blink + serial output via isp
    blink works now on com port

    upload of blink via usb fails "avrdude: stk500_getsync(): not in sync: resp=0x00"
    It would make me think I have a bad batch of smiery 328p, but element14 328p had the same issue
    Im still in the dark.
    Comments welcome.

    ReplyDelete
    Replies
    1. Yeah I'm not sure what to tell you; I recommend you try asking for help on the Arduino forums if you haven't already done so (http://forum.arduino.cc/).

      Delete
  20. I have it running on nightly build 1.6.0
    Its a nice library to use:)
    I has assumed this would be a lot faster as it used direct register access. I assume this isn't done as its not significantly faster. Can you confirm if this is on the cards?
    You need to remove the print statements in the c# code
    //////////////FOR DEBUGGING/////////////
    //Serial.print(F("n = ")); Serial.println(n);
    //Serial.print(F("oversample_num = ")); Serial.println(oversample_num);
    //Serial.print(F("divisor = ")); Serial.println(divisor);
    ////////////////////////////////////////

    ReplyDelete
    Replies
    1. Thanks! Glad you got it working! I will comment out those lines. I am not using direct port access to do the analog reads at the moment, but I do intend to add that in. However, I only expect direct register access to increase analog read speed (over the built-in analogRead() function) by 15~30%. This is not like digital pin access, where direct port access can increase speeds by a factor of 50x or so (over the built-in Arduino digital pin functions), resulting in a speed increase of 49000%. The reason is relative overhead. Pin mapping to ports for *digital* pins creates a significant overhead compared to the time it actually takes to read a digital pin. However, reading an ADC is so much slower than reading a digital pin that the overhead required to map pins to the analog ports is almost insignificant compared to the time it takes to get the analog reading.

      Additionally, in my previous testing, I was able to do analogReads at 8300Hz. This is only 1315Hz shy of the theoretical maximum 9615Hz for that ADC clock frequency. Therefore, the maximum increase in speed for that ADC clock frequency is only 1315/8300 x 100 = 15.84%.

      If you want to speed up analog reads, direct port access will only help a little (~16% maximum in this case). Increasing the ADC clock speed, however, can help a LOT!

      In my new library, you can set the clock speed via adc.setADCSpeed(). Call "adc.setADCSpeed(ADC_FAST);" to set the ADC clock to 1Mhz. Call "adc.setADCSpeed(ADC_DEFAULT);" to set the ADC clock back to its Arduino-default of 125kHz. Note that changing the ADC clock affects the speed at which regular Arduino analogRead() calls function too.

      Normal analog conversions on the ADC take 13 clock cycles for these chips, so the max analog read rate with an ADC clock of 125kHz is 125000/13 = 9615Hz. The max analog read rate for an ADC clock of 1Mhz is 1000000/13 = 76923Hz, for a theoretical speed increase of 8x, or 7000%. In actuality, however, you might expect 10-bit analog reads to occur somewhere around 50kHz, due to standard overhead of storing the value and doing something with it. My library also has some overhead, since when you oversample (ie: when you command a resolution > 10-bits for my library) it has to do sums and division and things.

      In summary, I still need to write speed test code to verify, and I have more testing to do, but my original library took 10-bit samples at a max of 8.3kHz or so. This library should be more like 50kHz if you set the ADC clock to 1MHz by calling "adc.setADCSpeed(ADC_FAST);"

      Delete
    2. Correction: a speed increase of 50x is a 4900% increase, NOT 49000%, and 8x speed increase is 700% increase, NOT 7000%.

      Delete
  21. Hi Gabriel,
    your comments are useful and appreciated. Is adc noise reduction going to help?
    I have no idea on the speed, as cpu has to be put to sleep while adc conversion Is done.

    ReplyDelete
    Replies
    1. I'm glad my comments help. :)
      I'm not an oversampling expert yet, but my understanding is that some noise is actually *desired* on the line when doing oversampling, or else a true resolution increase cannot occur by oversampling. This means that if you want higher-bit resolution, noise reduction at the 10-bit level may make 10-bit readings better (more consistent), but oversampled 16-bit readings, for example, worse.

      Here is a hypothetical scenario:
      I like to think about it this way: the concept of oversampling is that if the actual 10-bit result wants to be a decimal value, ex; 991.75, and you do 12-bit oversampling by taking 16 10-bit samples, you might get 12 10-bit results of 992 and 4 10-bit results of 991. The sum would be 991x4 + 992x12 = 15868. Divide by 2 and round (which is what my library does) and you get your 12-bit result of 7934 (no rounding required in this case actually). To see where this falls as a floating-point 10-bit value, divide by 8 and you get 991.75. If you have zero noise on the line, however, an actual value of 991.75 would always return a 10-bit value of 992. It would *never* return 991. Since the actual value wants to be 991.75, this is *good* if you only care about getting the most accurate 10-bit sample possible, but bad if you want to do oversampling to get higher-resolution samples which could capture the decimal portion of the value. Your oversampled, so-called 12-bit sample would simply be 16 samples of 992, creating a 12-bit sample of 992*16/2 = 7936, which is exactly the equivalent of a 10-bit sample of 992. NO INCREASE IN RESOLUTION HAS OCCURED, DUE TO HAVING PERFECT, NOISE-FREE 10-BIT SAMPLES, EVEN THOUGH YOU HAVE DONE OVERSAMPLING AND ATTEMPTED TO GET A 12-BIT SAMPLE. This is due to having NO noise on the line, which is BAD for oversampling.

      However, let's now assume that you have a randomly oscillating high-frequency "noise" of +/- 0.5 steps on the input line. This noise is what will *increase* the oversampled resolution as follows:
      the true signal is 991.75. The noise causes the signal to now randomly read as high as 992.25, and as low as 991.25. Any value from 991.25 to 991.49 will now be read as a 10-bit value of 991, and any value from 991.50 to 992.25 will be read as a 10-bit value of 992. This noise allows the first scenario where 4 10-bit values of 991 are returned, and 12 10-bit values of 992 are returned, creating the 12-bit value of 7934, in which the true 10-bit value of 991.75 has been captured, *including* the decimal component! It was the *noise* which allowed oversampling to have a positive effect, thereby getting a true 12-bit resolution on the reading!

      Delete
    2. In summary: if you completely eliminate the noise on the line, oversampling is ineffective. In our example, however, where the true 10-bit value wanted to be 991.75, an oscillating noise level of +/- 0.5 steps was sufficient to allow 12-bit oversampling to adequately capture the true value, including its decimal, and true 12-bit resolution was achieved. The +/- 0.5 step noise allowed the input value to the analog pin to drop as low as 991.25, which is read as a 10-bit value of 991, and as high as 992.25, which is read as a 10-bit value of 992. This noise is what allowed oversampling to improve resolution. No noise --> no increase in resolution due to oversampling.

      All-in-all, I don't see much of a reason to use the ADC noise reduction. I'd prefer not to make speed suffer too.

      Having said all that now, I've never used the adc noise resolution, so perhaps it could be an interesting academic exercise to at least add that trick to my bag of tools, and provide the option to the user of this library in the event that they do find it to improve their results for their particular test setup. *Perhaps* eliminating this internal ADC noise could be good, since their analog line could still have its own self-generated noise coming from the analog sensor itself, which might be sufficient. I don't know if I'll add this feature to the library or not, but probably not.

      NOTE TO SELF: I should prob. share this noise example with others in my next article on oversampling, once I release my new library. I think it makes sense.

      Delete
  22. as time permits, please look into why this wont compile on 1.06. Its fine on 1.6
    Arduino: 1.0.6 (Windows 7), Board: "Arduino Nano w/ ATmega328 pz 57600 version"
    C:\Program Files (x86)\Arduino\hardware\tools\avr\bin\avr-g++ -c -g -Os -Wall -fno-exceptions -ffunction-sections -fdata-sections -mmcu=atmega328p -DF_CPU=16000000L -MMD -DUSB_VID=null -DUSB_PID=null -DARDUINO=106 -IC:\Program Files (x86)\Arduino\hardware\arduino\cores\arduino -IC:\Program Files (x86)\Arduino\hardware\arduino\variants\eightanaloginputs -IC:\Program Files (x86)\Arduino\libraries\eRCaGuy_analogReadXXbit C:\Users\kzwag\AppData\Local\Temp\build4761200217612423850.tmp\spark_hellospary4f_160.cpp -o C:\Users\kzwag\AppData\Local\Temp\build4761200217612423850.tmp\spark_hellospary4f_160.cpp.o

    In file included from spark_hellospary4f_160.ino:56:
    C:\Program Files (x86)\Arduino\libraries\eRCaGuy_analogReadXXbit/eRCaGuy_analogReadXXbit.h:137: error: ISO C++ forbids initialization of member '_bits_of_resolution'
    C:\Program Files (x86)\Arduino\libraries\eRCaGuy_analogReadXXbit/eRCaGuy_analogReadXXbit.h:137: error: making '_bits_of_resolution' static
    C:\Program Files (x86)\Arduino\libraries\eRCaGuy_analogReadXXbit/eRCaGuy_analogReadXXbit.h:137: error: ISO C++ forbids in-class initialization of non-const static member '_bits_of_resolution'
    C:\Program Files (x86)\Arduino\libraries\eRCaGuy_analogReadXXbit/eRCaGuy_analogReadXXbit.h:138: error: ISO C++ forbids initialization of member '_num_samples_to_avg'
    C:\Program Files (x86)\Arduino\libraries\eRCaGuy_analogReadXXbit/eRCaGuy_analogReadXXbit.h:138: error: making '_num_samples_to_avg' static
    C:\Program Files (x86)\Arduino\libraries\eRCaGuy_analogReadXXbit/eRCaGuy_analogReadXXbit.h:138: error: ISO C++ forbids in-class initialization of non-const static member '_num_samples_to_avg'
    C:\Program Files (x86)\Arduino\libraries\eRCaGuy_analogReadXXbit/eRCaGuy_analogReadXXbit.h:139: error: ISO C++ forbids initialization of member '_ADC_speed'
    C:\Program Files (x86)\Arduino\libraries\eRCaGuy_analogReadXXbit/eRCaGuy_analogReadXXbit.h:139: error: making '_ADC_speed' static
    C:\Program Files (x86)\Arduino\libraries\eRCaGuy_analogReadXXbit/eRCaGuy_analogReadXXbit.h:139: error: ISO C++ forbids in-class initialization of non-const static member '_ADC_speed'
    spark_hellospary4f_160.ino: In function 'void setup()':
    spark_hellospary4f_160.ino:61: warning: only initialized variables can be placed into program memory area
    spark_hellospary4f_160.ino:62: warning: only initialized variables can be placed into program memory area

    ReplyDelete
    Replies
    1. I believe I've already fixed this error in the next iteration which I haven't released yet. The problem is that I declared and initalized the values (_ADC_speed, _num_samples_to_avg, etc) without explicitly making them static. The newer gcc compiler automatically makes them static for me, whereas the old gcc expects them to be explicitly defined as such. I corrected the error by moving my initializations (ie: giving the local member variables a value) in the class contructor method rather than in the .h file.

      Delete
  23. V2.1 has now been released. See the link in yellow in the article. I highly recommend people switch from the original eRCaGuy_analogReadXXbit library to this new version 2.1, which is being name-changed to eRCaGuy_NewAnalogRead. Once I've done more testing still I plan on writing a new article dedicated to it, but for now updates to it will occur on this post.

    ReplyDelete
  24. Thanks for your great work.
    the issues around noise and adc is likely that the arduino noise is not the ideal type of noise recommended.
    I am stuck in that I need to read at least two, but preferable 5 analog inputs as very close in time as possible.
    I am measuring power factor/amps on 4 inputs, and voltage on the 5th

    The second issue for me is, I need to keep power consumption very low while doing the adc stuff.
    The reason is my arduino is running on mains via a rc network, and im low on milli amps to run it.
    Hence analogue noise reduction could be very useful too me.

    ReplyDelete
    Replies
    1. I see; I'll see what I can do. I'm curious about your application and setup. Would you be willing to elaborate or share diagrams or anything? If you need to email me to include attachments or whatever, my email is available under the "contact me" tab at the top of the site.

      Also, how are you doing the RC network to power the Arduino? That sounds very interesting to me, as I have several projects requiring mains power but I am currently just using cheapo 9V AC/DC power supplies from ebay. What's your circuit like?

      Delete
    2. FYI: I just uploaded a new example called "eight_analog_reads.ino" to Github. Check it out by downloaded the latest copy of the library. I showed how to take 8 Analog readings rapidly in succession, from all 8 analog pins of an Arduino Nano. I used two different ADC clock speeds: 1MHz and 2MHz. I commanded12-bit samples. At 1MHz, each 12-bit analog reading takes ~780us, for a sample rate of 1280Hz. At 2MHz, each analog reading takes ~550us, for a sample rate of ~1800Hz. Check it out. I don't know if this meets your needs, but that's what it's capable of doing at the moment.

      Delete
    3. Kiwibird: correction: I had a bug in my "eight_analog_reads.ino" file. I have fixed it, so please re-download. My specs above seemed awfully slow. The actual values are as follows:
      ADC Clock speed/12-bit sample time(us)/12-bit sample rate(Hz)
      1MHz/390us/2565Hz
      2MHz/275us/3636Hz

      Delete
    4. kiwibird, you might want to look into using MCP3304, 8 channel single ended or 4 channel differential 13 bit ADC with SPI interface. About $5 each from Digi-Key. To oversample you need a stable signal, 60 hz mains I don't think is stable enough for oversampling to work.

      Delete
    5. Another thought, the MCP3911 has dual ADC which can sample two inputs AT THE SAME TIME for measuring Current and Volts together. It is a 3.3V device though so requires level converters if using with 5V arduino. If measuring AC current you might want to think about using a current transformer. The SCT013-030 can measure up to 30 amps and is non intrusive.

      Delete
  25. here is a power supply, though different to mine. Beware if you use it as your arduino may be lethal to touch
    http://www.mikesmicromania.com/2013/03/kill-watt-circuit-analysis.html
    draft video of my project is here
    https://www.youtube.com/watch?v=qzOq3mUEzV8

    ReplyDelete
    Replies
    1. I looked at the links and watched the video. Is your name Peter? That's the name I saw in the video. Is this something you are going to sell?

      I've considered doing this for quite some time now, and I've already hacked into an outlet and turned it into a capacitive-touch-controlled outlet for my lamp. I've wanted to add AC voltage and current measurement to the outlet though for quite some time, so I've thought a lot about how to do so.

      Voltage:
      Are you planning on using a diode and voltage divider to measure Vrms by using only the top portion of the sine wave? That was my plan.

      Current
      For current, I planned on using an ACS712 current sensor module such as these shown here. They can measure up to 30A (raw, not RMS), and they provide a positive voltage output for both positive and negative currents, making them ideal for AC current measurements.

      Delete
  26. Hi, I have used your oversampling library "AnalogReadXXbit" in order to catch spectral information from an flame emission atomic spectrophotometer and the data obtained was really good. Thanks for your excellent library (see report in my blog, in spanish). regards
    http://hombrosdegigantes.blogspot.com/2015/03/aumento-virtual-de-la-resolucion-de-un.html

    ReplyDelete
    Replies
    1. Alberto, thanks for sharing your link to your ("Shoulders of Giants" blog!). I left a couple comments on your blog. I'd love to see your Arduino code which uses my library.

      Also, I downloaded your Processing program called "DatArduino 1.02", and am pretty impressed by it! I simply opened it up and ran it, but didn't connect it to an Arduino yet. I plan on learning to use Processing as well, so I can make interactive GUIs with my Arduino sketches, and I look forward to studying your code. I notice you built the GUI with the G4P Library and the G4P GUI Builder tool, but the GUI Builder tool you used is an older version and does not work with the latest version (V2.5). Which version of the GUI builder tool did you use? Is it possible for you to get your GUI to work with the latest version of the GUI Builder tool?

      Delete
  27. I have used the new AnalogReadXXbit v2.1 alpha in my post about oversampling (http://hombrosdegigantes.blogspot.com/2015/03/aumento-virtual-de-la-resolucion-de-un.html) and I repeated measurements for comparison from one version to another. As a result we can say that best results were obtained.

    ReplyDelete
    Replies
    1. Alberto, thanks for sharing. Here is a link to an English translation of your page. http://bit.ly/Virtual_Resolution_Increase_on_an_Arduino_Using_Oversampling_ENGLISH. I think your article is a great example of showing actual results using my library, so I appreciate you taking the time to write it. After all of your efforts, which setting in the library did you settle on (and what ADC speed are you using), and why?

      Delete
    2. Thanks to you Gabriel for write and share this excellent library.

      I only modify a little your example "NewAnalogRead_basic_demo". here you can download the code:

      https://www.dropbox.com/s/4hxekqu9zgro1hf/AumentarResolucion_AnalogReadXXbit_Version_2.rar?dl=0

      I change this parameters

      byte bitsOfResolution-------> 10,11,12,13 and 14
      unsigned long numSamplesToAvg------>1,4,16,64 and 256

      The ADC was

      ADC_prescaler_t ADCSpeed = ADC_FAST;

      and the reason, well, why not :) , only because was the default.

      Delete
  28. Hello Gabriel,
    My congratulations for sharing your library.
    Can you rewrite it using labview, makerHub LINX https://www.labviewmakerhub.com.

    ReplyDelete
    Replies
    1. Hi Djamelfadli, I looked at the makerHub link here. Sorry, I do not have LabVIEW at home to be able to test this. Also, I personally try to avoid using LabVIEW as much as possible in general as it is a very expensive license ($1000~$9200 base for the non-student) and it is not a standard programming language. I'd rather stick with mainstream languages like C, C++, Java, Python, etc. I'd even prefer to use MATLAB over LabVIEW whenever possible, since it more closely resembles mainstream languages. Nevertheless, if you want to write the library, I encourage you to do so. You can pretty easily modify the Arduino code to accept serial commands, then you can write a LabVIEW VI to send serial commands to the Arduino. You can also use any other language, including MATLAB, to send the same serial commands to the Arduino, to get it to take a sample and return the value. If you modify my library, make sure you follow the LGPL license by keeping your derived version open-source.

      Question: why are you using LabVIEW? What are you doing with this? Perhaps you can use something much cheaper such as MegunoLink or MakerPlot instead if you are just using the Arduino to take and record data for experiments.

      Delete

Thanks for your comment or question! If it is a question, I will try to get back to you quickly.

Note: some HTML tags are allowed in your comments. To learn how to add bold (<b>...</b>), italics (<i>...</i>), or hyperlinks (<a href="URL">NAME</a>) to your comments, read here.

~Sincerely, Gabriel Staples.

P.S. Yo hablo español también. Je parle français aussi. (I speak Spanish & French too).