Arduino Oversampling: How to Get More ADC Bits for Free! This Technique lets you Add ADC bits to your ADC module with no Extra Hardware! Whaaaaatttt...! How does this Arcane Magic Work?

Arduino oversampling and Decimation (O & D) is a method you can use to increase the resolution of any ADC. Fundamental equations [1] show that if you increase the number of samples by a factor of four, then the bit resolution of the ADC increases by an extra bit!

You can take the humble Arduino 10-bit ADC and turn it into a 14-bit ADC (or more!) just by sampling the analogue input 256 times and shifting the result right by 4 bits! this does sound like a bit of magic but it does work. Of course you don't get these extra bits for free; The bandwidth of the input signal is reduced and it takes more time to gather the samples.

Arduino Oversampling: Number of Bits

The mathematics [1] show that for n bits of extra resolution, you sample the input 4n times, and for decimation (the output) you shift the result right by n bits. The following table shows what you can do with the Arduino ADC:

New Bits
Extra samples
Decimation (right shift)
Bit resolution achieved
1
4
1
11
2
16
2
12
3
64
3
13
4
256
4
14
5
512
5
15
6
1024
6
16


Note: Every multiple of 4 samples gets you one more bit of resolution.


It is really a fancy way to do some averaging with the mathematics showing that you get the benefit of an extra bit (or more bits depending on how many samples you read). However you don't divide by the number of samples taken - you decimate instead (or shift right).

The method does not average the samples for noise reduction. You can still do that on top of this method - by averaging.

It does sound a bit too good to be true but there is a cost and that cost comes in the form of:

  1. Increased measurement time,
  2. Reduced bandwidth.

Every factor of four increase in the number of samples reduces the bandwidth that is readable by four (due to the Nyquist theorem - since the sampling rate is reduced by a factor of 4), so Arduino ADC oversampling does not come for free. The benefit is you can increase the resolution of any ADC without adding any more hardware!

Arduino Oversampling: Resolution

The following table shows the Nyquist frequency (usable bandwidth) and voltage resolution achievable with the Arduino ADC running at 125kHz (ADC clock see the Arduino ADC) - the normal ADC Sample rate is 9.615kHz See this page :

New Bits
Voltage resolution
Bandwidth
Bit resolution achieved
0
4.883mV 4810Hz
10
1
2.441mV 1202.5Hz 11
2
1.221mV 300.63Hz
12
3
610.352uV 75.16Hz
13
4
305.176uV 18.79Hz
14
5
152.588uV 4.697Hz
15
6
76.294uV 1.174Hz
16
The table is created for reference voltage of 5V and ADC clock 125kHz, Sample rate=9.615kHz.

Warning: The more oversampling you do the less bandwidth you have.

Arduino Oversampling: Gaussian Noise

The other requirement is quite strange and it is...

    ...Gaussian noise is required for the method to work.

In fact, to work properly, you need electronic noise on the input signal entering the ADC chip (it is needed for toggling the lowest bit) - without it the average around the lowest bit can not be seen. Adding an artificial noise signal is known as dithering.

TIP: Arduino boards probably have enough noise!

If you have made a really good system with very low noise then you will be in trouble! You have to have noise and there are recommended circuits to inject noise into the ADC input signal! (its probably not a problem working with Arduino on the bench!).

The noise required is between:

    0.5 LSB and 2 LSB and should be at least 1 LSB [2].

TIP: Tip add noise into the ADC signal to ensure correct operation

Arduino Oversampling: Decimated Range

The output values can not be of the same range as the original ADC output. You don't get an Oversampled and Decimated reading returned as a 10bit value that you started with; instead you have:

New Bits
Voltage resolution Bit resolution achieved
Decimated output
0
4.883mV 10
0 ~ 1023
1
2.441mV 11
0 ~ 2047
2
1.221mV 12
0 ~ 4095
3
610.352uV 13
0 ~ 8191
4
305.176uV 14
0 ~ 16383
5
152.588uV
15
0 ~ 32767
6
76.294uV
16
0 ~ 65535 
Table is created for reference voltage of 5V and ADC clock 125kHz, Sample rate=9.615kHz.

In the decimated output, one is subtracted from the output range to account for representation of zero.

Arduino Oversampling: Practical

Since this does sound "too good to be true" lets do some testing to find out.

For the tests, you need to setup the Arduino to make measurements using the internal voltage reference - The tests below use an Arduino Nano and a DAC (the MCP4725).

Note the internal reference has a large offset (due to manufacturing) but is fairly stable - find out more here - if you want ~1% accuracy then you can calibrate it as described in the link. However, the tests below do not require you to calibrate the reference since you only need to see the difference between decimated and undecimated output. 

The idea is to generate a voltage from the MCP4725 that is fed into the Arduino ADC for testing. You can compare the undecimated output to the n-decimation output and check that the method works by viewing the output on a spreadsheet.

NOTE: My two Nanos have lots of noise so allowing good decimation!

Arduino Oversampling: Hardware setup

An external DAC is used to generate the input signal to A0 and the internal bandgap reference used for more accurate measurement. Both decimated and raw ADC values are output by the program.

Connections

For testing Arduino Oversampling use an Arduino Nano (or Uno) and connect it as follows:


Arduino MCP4725
5V
VDD
GND
GND
A5
SCL
A4
SDA
GND
A0 _ this the MCP4725 address
A0 - Arduino
Analogue input
Output to the A0 input.

Example Sketch

This sketch outputs DAC values from 2500 to 2559 which are read by the Arduino analog input pin A0.

// ADC oversampling

#include <Adafruit_MCP4725.h>

Adafruit_MCP4725 dac;

void simpleTestA0withDacAndDecimate(byte n) {
char buf[20];
byte adsGain;

   analogReference(INTERNAL);   // 1V1 ref = less noise?

   for(int lp=0;lp<50;lp++)
      analogRead(A0); // Throw away 1st readings to allow ADC to settle, 10 too low.
   delay(2); // Let settle

   float fSamples=pow(4,(float) n);
   int samples = (int)(fSamples+0.5); // solves : when n = 3 get get pow(4,n) of 63

   Serial.print("Extra bits ");
   Serial.print(n);
   Serial.print(" samples ");
   Serial.println(samples);

   byte numPrint = 60;
   for(int i=2500;i<2500+numPrint;i++) {

      // Dac output voltage ~ mid range
      dac.setVoltage(i, false);
      Serial.print(itoa(i,buf,10));

      // Arduino ADC A0 normal reading, averaged.
      //
      long v = 0;
      for (byte j=0;j<10;j++) v+= analogRead(A0); // analogRead(A0);
      v/=10;

      Serial.print("\t");
      itoa(v,buf,10);
      Serial.print(buf);

      Serial.print("\t");

      // Decimation for Arduino ADC A0, averaged then decimated.
      long dv=0;
      for (byte avg=0;avg<10;avg++)
         for (int j=0;j<samples;j++) dv+= analogRead(A0); // analogRead(A0);
      dv=(dv/10);
      dv = dv>>n;
      ltoa(dv,buf,10);

      Serial.println(buf);

      if (Serial.available()) return; // Allow keyboard exit
   }
   analogReference(DEFAULT);
}



void setup(void) {
   Serial.begin(115200);
   dac.begin(0x61);

   simpleTestA0withDacAndDecimate(4);
}



void loop(void) {

}

Viewing output in a spreadsheet

I used OpenOffice for viewing the output from the program.

Simply copy the three lines of data into the spreadsheet - they are tab separated so are put into three columns.

The first column is the DAC value used. The second is the Arduino ADC without decimation and the third is the Arduino output with decimation.

The reference voltage (measured with an ADS1115) is : 1.070125V

The output for the first and last measurements is:

2500    751    12001
2501    750    12006
2502    750    12009
2503    749    12014
2504    751    12018
2505    751    12023
2506    751    12027
2507    751    12032
2508    751    12036
...
2552    764    12241
2553    764    12247
2554    765    12252
2555    765    12257
2556    766    12261
2557    766    12266
2558    766    12270
2559    767    12274

The data for the above results is in this file.

Arduino Oversampling: Theoretical outputs

From the table above (for 4 bit decimation) resulting in a 14 ADC output the maximum value ADC is 16383 pow(2,14)-1. and the resolution is

    Vref/(pow(2,14) = 1.070125/pow(2,14) = 65.3uV

This is different to the data in the table above since the table assumes Vcc as the reference.

The actual voltage output depends on the voltage source and the divider output (and associated errors in the DAC and ADC and resistor tolerance).

For the test setup the VCC source for the MCP4725 DAC is 2.5V (MOSFET level shifters were used) and the output is divided by a resistive divider of 10k:10k so the maximum output is 1.25V.

The output of the test program runs from 2500 to 2559 (keeping the output in the mid range of the DAC). So voltages range from

    DACMinV  = 1.25 * 2500/pow(2,12) = 0.762V
    DACMaxV = 1.25 * 2559/pow(2,12) = 0.781V

    Theoretical difference is 0.019V

Arduino Oversampling: Measured outputs

Taking the minimum decimated output gives

    1.070125 * (12001/pow(2,14)) = 0.783V
    1.070125 * (12274/pow(2,14)) = 0.801V

    Actual difference is 0.801-0.783 = 0.018V

The actual outputs are different to theoretical outputs because Arduino ADC and DAC have offsets which are not calibrated out by this procedure, also the resitive divider uses 10% resistors. The difference between the maximum and minimum is nearly identical showing range of voltage measured is correct.

From the graphs below you can see that decimation is working i.e the decimated output graph shows finer resolution capability.

Arduino Oversampling: Graphs showing results

Paste the your results (three columns of data) into an OpenOffice spreadheet, then select B-column and the choose Menu->Insert->Chart. This brings up a plot of the data which shows the (noisy) raw ADC value of ADC A0 (although this waas filtered using an averaging filter with averages of 10 samples.

You can see the stepped nature of the output which is defined by the "normal" resolution of the ADC.

Normal Arduino ADC operation

Arduino ADC oversampling normal A0 output

Select C-column and the choose Menu->Insert->Chart. This brings up a plot of the decimated data. Amazingly the steps have disappeared and a very nice linear line is shown. 

Depending on noise in the system you may get a more bumpy output. There are many noise sources including ,the power supply (USB is noisy), The Nano layout itself (not analogue friendly).

This shows that the ADC is now capable of finer resolution - and that is just by doing a bit of averaging and shifting!

Arduino Oversampling ADC operation

Arduino oversampling 14 bit resolution

Note: Your results may vary depending on noise in the system.

Too much noise can give a less linear output - but too little won't allow the Arduino oversampling method to work.

Note: On the Arduino Nano, since the analogue ground and analog Vcc (AVCC) are both connected to the normal GND, and VCC respectively, Arduino Nano analogue measurement is very noisy.

Arduino Oversampling: References

[1]    Silabs application note on oversampling.
        https://www.silabs.com/documents/public/application-notes/an118.pdf

[2]    Microchip application note AVR121
        http://ww1.microchip.com/downloads/en/AppNotes/doc8003.pdf



Comments

Have your say about what you just read! Leave me a comment in the box below.

Don’t see the comments box? Log in to your Facebook account, give Facebook consent, then return to this page and refresh it.




Privacy Policy | Contact | About Me

Site Map | Terms of Use