Monday, May 12, 2014

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


Subscribe by Email!
By Gabriel Staples Written: 13 May 2014
Last Updated: 16 Sept. 2015

History (newest on top):
-16 Sept 2015 - changed title from "10-bit to 21-bit ADC..." to "10-bit to 16+-bit ADC..."
-25 July 2015 - added a brief update below, updated download links
-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 16+-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 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 

Update: 25 July 2015: The library works. It flat out works. You do get extra precision. How much? A few bits. I still need to do more testing, and I have plans to further refine the library, but the point is, if you only need a few extra bits of precision, this library can replace the need for buying a higher-resolution external ADC. I've received several emails from people (and some comments below) telling me their success stories in using this library. 
------------------------------------------------------------------------------------------------------------

Download:

UPDATE 17 Feb 2015: Version 2.1 beta available now!

  • Click the above link, optionally donate (just set the price you want to pay to $0 if you like), & download the library. You may also click here, then choose the appropriate download. 
    • For the email address field, you may enter none@gmail.com if you like. Or, enter your real email if you want to receive an email with the download link, and if you don't mind if I send you an occasional email with something I'm working on (perhaps a few times a year or so).
  • Install the library into the Arduino IDE (using Sketch --> Include Library --> Add .ZIP Library), then run the examples to see how to use the library.
  • GitHub: https://github.com/ElectricRCAircraftGuy/eRCaGuy_analogReadXXbit

V2.1 changes: this is a major overhaul of the 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. If you still have V1.0 of the library, from previous downloads, I HIGHLY RECOMMEND THIS 2.1 VERSION OVER THE ORIGINAL eRCaGuy_analogReadXXbit V1.0 alpha LIBRARY. More examples to come.

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 [25 July 2015 Update: Though this table is still useful to get the point across, I need to make significant updates to the table to bring it up to speed with the new performance of the V2.1 library. This table is no longer correct, but you'd still be wise to read and understand it].


==========================================================
OLD LIBRARY VERSION EXAMPLE CODE:
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, getting the avg. of [num_samples] 16-bit readings 
  float analog_reading = adc.analogReadXXbit(pin, bits_of_precision, num_samples); 
  
  //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!***

100 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. 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
  4. 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
  5. 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
  6. 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
  7. 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
  8. 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
  9. 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
  10. 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
  11. 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
  12. 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
  13. Version 2.0 alpha release is now available. Link is in the article above, currently it is highlighted in yellow.

    ReplyDelete
  14. 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
  15. 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
  16. 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
  17. 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
  18. 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
  19. 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
  20. 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
  21. 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
  22. 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
    6. I want to measure the voltage and current of 220V using MCP3911 2-channel analog input 3.3V. How measuring circuit? thank you

      Delete
    7. Viet Bui, the Arduino Forum and Electronics Stack Exchange are two great places to look for project guidance. Also Google is your friend. I'm assuming you mean 220V AC voltage. AC is tricky. For starters though, I recommend a multi-stage voltage divider to divide down the voltage to reasonable levels (0 to 3.3V), and either clip off the negative component first with a diode, or positive bias the voltage-divided AC voltage so that it doesn't go negative. For current, look into using a hall-effect current sensor, such as the ACS712 (on Ebay, datasheet). They come in 5A, 20A, and 30A varieties on Ebay. Be cautious though whenever dealing with potentially lethal voltages like this, however, as a mistake can mean that your Arduino gets a full 220V and you shock (and possibly kill) yourself. Also, for voltage dividing calcs, keep in mind that 220V AC actually has +/- peaks of 220V x sqrt(2) = +/-311V, and is a sine wave. To get true RMS voltage and current samples you'll have to sample an entire sine wave period then take the RMS (Root Mean Square) of your data.

      Delete
  23. 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
  24. 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
  25. 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
  26. 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
    2. Update: license for eRCaGuy_NewAnalogRead is GPLv3 or later, unless you purchase an alternate license agreement.

      Delete
  27. For this library to correctly work, the input analog signal has to have some noise that would render different ADC readings for one certain value. If your signal source is very stable, this one might not work well. Anyway, it is a great tool for use when a better ADC is not available. Thanks

    ReplyDelete
    Replies
    1. True. Thanks. And I hope you can find a useful case for it where it works out for you. I still have many more ideas for it left to implement and test.

      Delete
  28. I am looking to use this library in a commercial product. What are the licensing terms and price for a license to redistribute as a embedded controller? I am at the R&D phase and need 10 copies, and as my boss will not look here, $5 doesn't seem fair to you for a product that HOPEFULLY in the future ship 10,000 or so units. I DO need to know the licensing before using the libs. Please let me know. Thanks, Paul

    ReplyDelete
    Replies
    1. Paul, thanks for contacting me. I'll respond to your email.

      Delete
  29. Oh I love this library.
    Thank you so much.

    ReplyDelete
    Replies
    1. Gabriel,
      Great library (but I've said that already). I'm using it to try to monitor 3 analogue inputs on an Arduino Uno. Do you have any examples hanging around where I can get the library working across all three analogue ports?
      I'm fairly new to Arduino and I'm trying to monitor an experiment for a Hydrogen producing catalyst.
      Any help or pointers world be very welcome.

      Delete
    2. Martin, thanks for the support and donation! It is much appreciated. Just call "adc.newAnalogRead(pin)" for each analog pin you want to read. In place of "pin" just put A0, A1, A2, A3, etc. Store the result into a variable, or use it directly. Assuming you are using a 5V Arduino, you can get the voltage at a given pin by doing this, for example: "float V = 5.0*adc.newAnalogRead(pin)/adc.getMaxPossibleReading();"

      For examples of reading multiple pins, see the included "eight_analog_reads.ino" example. This reads up to 8 analog pins (ex: on the Nano) or 6 analog pins (Uno), and measures how long they take, for speed-test purposes.

      Basically, just follow any Arduino example that uses analogRead(pin), and replace analogRead(pin) with adc.newAnalogRead(pin). When they divide by 1023 or 1024 to get the voltage, divide by adc.getMaxPossibleReading() instead.

      For help getting started in Arduino, see the links at the bottom of my article here: The Power of Arduino. Esp. see these two links, which are under the “Beginner” links section of my article just mentioned (Adafruit learn Arduino) and here (Arduino built-in examples).

      Delete
    3. Well you could knock me down with a feather.
      Your library seems to be working on an Arduino Mega 2560 as well as the Uno I was using.
      I followed your pointers and, just for fun, now have it running over all 16 analogue ports of a Mega at 16 bit accuracy.
      I don't need all 16 ports but it was fun getting it going.

      Delete
  30. This comment has been removed by the author.

    ReplyDelete
  31. Dear Gabriel,
    I can't thank you enough. Using your library and guidance I've put together for one of my projects a 4 port 16 bit voltage and for port temperature data logger. Analogue inputs tied and protected with 5.1volt Zener diodes and it runs like a dream. Really stable. It is for a wet-cell electrolysis project.

    However it has not ended there. A colleague has seen what what I've put together and has asked if he could supply a kit of parts for a similar project to enthusiasts.

    Of course it utilises some of your code so I'd like to ask you what your licensing terms are in relation to your library for distribution within a programmed finished article - not for the library itself to be distributed - but as part of the compiled and programmed board.

    Please contact me through my private email address.

    Regards,

    Martin

    ReplyDelete
  32. Martin, could you please let me know the part number for your 5.1 V zener?

    ReplyDelete
  33. Hi Ranbir,
    It is 5.1 volt @ 5 watts - type#: 1N5338B
    I am using a 2M Ohm multi-turn potentiometer at a potential divider so that I can get accurate readings and useful voltages up to 50 volts DC.
    Not blown up yet!
    Hope this helps you.

    Regards,

    Martin

    ReplyDelete
  34. Hi Martin, yes that helps. Thanks. My application is very similar to what you describe.

    ReplyDelete
  35. Hi Gabriel, will this library work on an Arduino Mega (ATMEGA1280) the same or its just for Arduino Uno?

    ReplyDelete
    Replies
    1. In its current state I believe it will work for any ATmega AVR mcu (microcontroller), though I have not tested this. Give it a shot and let me know. It should work.

      Delete
    2. Works like a dream with the MEGA. I've had it running with 16 channels just for fun!

      Delete
    3. Thanks for the info, Martin. What speed did you have the ADC running at, just out of curiosity?

      Delete
    4. Well I've had it a time ticking away at 1 second for a week at a time monitoring an experiment. Thats a lot of data points! The fastest I've run was .5 seconds but that developed a stupid volume of data.

      Delete
  36. This comment has been removed by the author.

    ReplyDelete
    Replies
    1. Check your serial baudrate. It should match what's inside Serial.begin(), so set it to 115200 in the serial monitor.

      Delete
  37. thanks for replay
    problem solved
    again thanks

    ReplyDelete
  38. Hi,

    I would like to use your library, but first I need to know some characteristics before:

    1) Maximum resolution of the library.
    2) samples per second it can handle.

    I'm making a project that needs 16 bits minimun ADC and needs to handle signals with frequency from 0 to 5kHz. So I need at least to sample at more than 10KHz Nyquist).

    Thank you very much.

    Regards,
    Marcelo

    ReplyDelete
  39. Max resolution you can *command* is 21-bits, but the max it will actually do is probably closer to 16-bits, depending on your particular setup. Oversampling is interesting in that it *requires* certain low levels of random white noise to even work, so the additional resolution you get for any given circuit can vary, regardless of what you are commanding in my library. Having said that, I think 16-bits.

    Samples per second is a function of resolution. At 10-bits an Arduino can do ~50kHz in fast ADC mode. A rule of thumb for the Arduino is to divide 50kHz by 4 each time you increase resolution by 1-bit using oversampling, so at 12-bits, you have 2 extra bits of resolution, so expect ~50kHz/(4^2) = 3125Hz, and at 16-bits you have an extra 6 bits so expect ~50kHz/(4^6) = 12.2Hz.

    No, you can't sample at 10KHz and 16-bits with this library, on an ATmega328. Not even close. PS: default max ADC sample rate of any standard Arduino, at 10-bits resolution, and not using my library isn't even 10Khz as it is...it's a little less than that. I'm speeding up the ADC clock in my library to get the higher sample rates.

    PS, from one of my comments above, you'll see the following specs from actual tests I did at 12-bits:

    ADC Clock speed / 12-bit sample time(us) / 12-bit sample rate(Hz)
    1MHz / 390us / 2565Hz
    2MHz / 275us / 3636Hz

    ReplyDelete
    Replies
    1. Oh, that's bad for me. Thanks for the fast reply. I'll have to buy a fast IC ADC.

      Thanks again.

      Delete
  40. Hi, I'm an engineer who's worked a lot with oversampling. I know that O/S increases the apparent resolution of an ADC conversion, when sufficient (a few LSBs pp) noise is present. However, it won't improve the linearity of the converter, and though it will disguise missing codes and interference from internal activity (the latter one of the reasons you're advised by the Arduino docs to sleep the processor while taking an ADC reading to get lower noise) the residual error from these will tend to stay at the native resolution level rather than the enhanced resolution level. Have you made any critical tests of these other measures of ADC goodness? One test I use as a 'hello world' of ADCs is to use a large capacitor discharging into a parallel resistor to generate a very slowly changing low noise low impedance voltage source. There are others I can suggest if you're interested in testing. I found your page while looking for things like the Adafruit 1115 for my Arduino!

    ReplyDelete
    Replies
    1. Hi Neil, that sounds like a great test to run. No, I haven't done any extensive testing, but the test you recommend would definitely help clarify what the true resolution increase really is when using my library. Basically, I did the research and wrote the library over several weeks, a long time ago, then got sidetracked with other projects before I had time to do any serious testing. Meanwhile, I shared it and some people heckled while others downloaded it and responded favorably. Based on the several plots and applications and results I've seen from many of those who have actually used it, I'm convinced it is working. How well it is working, however, is not yet quantified. If you don't mind and are inclined to do so, I'd be happy if you ran the test and sent me the data and plots for me to post, so we can really know how well my library is working.

      Also, I'd be happy to hear of other tests you have in mind, but for now I'll just have to add them to my to-do list. I'm in the process of changing jobs and need to focus on other things. Unless I really needed my own oversampling library for a money-generating project, and thereby had a financial incentive to improve it, I just don't have the time to properly dedicate to it.

      Delete
  41. This comment has been removed by the author.

    ReplyDelete
  42. hmmmm, at 10 bits I'm getting the full 1024 values, at 11 bit I'm getting only 2047 values instead of 2048... and at 12 bit(which is what I need for this particular project) I'm only getting 4093 instead of 4096

    ReplyDelete
    Replies
    1. This is normal for oversampling. That's just how oversampling works. Think about it this way: 10-bits is 1024 values, which range from 0 to 1023. Each new bit of resolution essentially sums 4 samples at the previous resolution and divides by 2 in order to get the new value. So, the largest possible 4 samples at 10-bits is (1023 + 1023 + 1023 + 1023)/2 = 1023*4/2 = 1023*2 = 2046. Therefore, at 11-bits oversampled resolution, your max value is 2046, instead of the expected 2047. 2047 just isn't possible when oversampling, as you can now see. So, 0 to 2046 is 2047 values, instead of the expected 2048...that's how oversampling works.

      Delete
    2. Is there any way I can get my 0-4095 values to work with something else instead of over sampling?

      Delete
    3. You'd have to buy a 12-bit ADC. Example: ADS1015 12-Bit ADC - 4 Channel with Programmable Gain Amplifier, $9.95. But, is there really a measurable difference between 0 to 4093 and 0 to 4095? No, not if the oversampling is truly working, based on the necessary random noise levels and bit toggling generated by the measured signal.

      So, to test the oversampling, you could do a very gradual and slow sweep of your analog input from one extreme to the other, while sampling it. Then plot it. If you see that you were able to get samples at every value, 0 to 4093, you are getting a true 12-bit oversampled reading, and there's really no point in buying a 12-bit ADC. If, however, you see steps in the plot that *skip* values, rather than hitting every single value, you know you are getting less than 12-bit resolution, and you can determine your actual resolution you are getting based on the size of the skipped values.

      Ex: 0, 0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 4, 4, 5, 5, 5, etc up to 4093 indicates the 12-bit oversampling is spot-on and performing perfectly, whereas 0, 0, 0, 3, 3, 3, 5, 5, 5, 8, 8, 8, 8, etc indicates you are *not* getting the full 12-bit resolution, and instead are getting approximately 10.5-bit or 11-bit resolution. This is because for 12-bit resolution, you'd expect 9 steps from 0 to 8 (0,1,2,3,4,5,6,7,8), for 11-bit resolution you'd expect 5 steps (0,2,4,6,8), for 10-bit resolution you'd expect 3 steps (0,4,8), and you got 4 steps (0,3,5,8), which is between 10 and 11-bits.

      Delete
    4. Thank you very much Gabriel :)

      Delete
  43. Hi Gabriel,
    thanks for your excellent work. However, according to AVR121 (and common sense, as well) it's essential for oversampling (dithering in this case) to add some (at least +- 1 LSB) noise either to measured signal or to ADC reference voltage. In your code as well as in comments I can see nothing about noise adding. Question is how do you do it?

    Regards,
    Piotr

    ReplyDelete
  44. This only works if there is 6 bits of noise on the signal. It's not really going to give you cd audio quality adc input. Read the original article on oversampling to see what I mean. You might be able to get a true higher resolution by varying the vref signals, and doing it in 6 samples doing a binary search on each bit. This way you might be able to doing it in 6 samples instead of 4096 samples, and without noise.

    ReplyDelete
  45. This comment has been removed by the author.

    ReplyDelete
  46. I support the fact that this works. I wanted a bit more precision out of the 10 bit internal DAC without having to go to an external DAC. I decided to try this code and go for 12 bit accuracy...and it worked perfectly.

    ReplyDelete
  47. Replies
    1. The concept would work but the ADC-tweaking code would need to be modified for that chip. I haven't tried my library with that chip.

      Delete

Thanks for your comment or question! If it is a question, I will try to get back to you quickly. Notice to spammers: I personally remove all spam promptly and report spammers to Google, so please don't do it.

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).