Higher prescaler ADC readings

I set up a test to look at what happens with higher prescaler values for the ADC and got some strange results. Can anybody explain what I am seeing?

With prescaler values from 128 to 16, both reading methods track the same.
Prescaler of 8, the full scale input, A0, doesn't read full value and A1 begins to wonder.
Prescaler of 4 and 2, all the inputs begin to wonder.

However, the readings using the register control seem to be solid.

Setup:

  • Mega
  • target - 4 resistors in series between +5 and gnd on a breadboard
  • inputs are at the 5 junctions - A0 5v to A4 gnd

Readings on left are using the analogRead() calls. Readings on right are done using registers to start and stop. I threw in some time markers to see how long the samples took.

//receive single char

char receivedChar;
boolean newData = false;
boolean takesample = false;

unsigned long timestart;
unsigned long timestop0;
unsigned long timestop0a;
unsigned long timestop1;
unsigned long timestop2;
unsigned long timestop3;
unsigned long timestop4;

int sensorValue0 = 0;        // value read from the pot
int sensorValue1 = 0;        // value read from the pot
int sensorValue2 = 0;        // value read from the pot
int sensorValue3 = 0;        // value read from the pot
int sensorValue4 = 0;        // value read from the pot


void setup() 
{
  Serial.begin(9600);
  Serial.println("<Arduino is ready>");
  DIDR0 = 0xFF; //diable digital inputs
  
  }

void loop() 
{
  recvOneChar();
  setSampleRate();
  takereadings();
}

void recvOneChar() 
{
  if (Serial.available() > 0) 
  {
    receivedChar = Serial.read();
    newData = true;
  }
}

void setSampleRate() 
{
  if (newData == true) 
  {
    switch (receivedChar)
    {
        case '7':
        Serial.print("128 prescalar - ");
        bitWrite(ADCSRA,2,1);
        bitWrite(ADCSRA,1,1);
        bitWrite(ADCSRA,0,1);
        Serial.println(ADCSRA);
        takesample = true;
        break;
        
        case '6':
        Serial.print("64 prescalar - ");
        bitWrite(ADCSRA,2,1);
        bitWrite(ADCSRA,1,1);
        bitWrite(ADCSRA,0,0);
        Serial.println(ADCSRA);
        takesample = true;
        break;
        
        case '5':
        Serial.print("32 prescalar - ");
        bitWrite(ADCSRA,2,1);
        bitWrite(ADCSRA,1,0);
        bitWrite(ADCSRA,0,1);
        Serial.println(ADCSRA);
        takesample = true;
        break;
        
        case '4':
        Serial.print("16 prescalar - ");
        bitWrite(ADCSRA,2,1);
        bitWrite(ADCSRA,1,0);
        bitWrite(ADCSRA,0,0);
        Serial.println(ADCSRA);
        takesample = true;
        break;
        
        case '3':
        Serial.print("8 prescalar - ");
        bitWrite(ADCSRA,2,0);
        bitWrite(ADCSRA,1,1);
        bitWrite(ADCSRA,0,1);
        Serial.println(ADCSRA);
        takesample = true;
        break;
        
        case '2':
        Serial.print("4 prescalar - ");
        bitWrite(ADCSRA,2,0);
        bitWrite(ADCSRA,1,1);
        bitWrite(ADCSRA,0,0);
        Serial.println(ADCSRA);
        takesample = true;
        break;
        
        case '1':
        Serial.print("2 prescalar - ");
        bitWrite(ADCSRA,2,0);
        bitWrite(ADCSRA,1,0);
        bitWrite(ADCSRA,0,1);
        Serial.println(ADCSRA);
        takesample = true;
        break;
        
        case '0':
        Serial.print("0 prescalar - ");
        bitWrite(ADCSRA,2,0);
        bitWrite(ADCSRA,1,0);
        bitWrite(ADCSRA,0,0);
        Serial.println(ADCSRA);
        takesample = true;
        break;
    }
    
    newData = false;
  }
}

void takereadings()
{
  if(takesample)
  {
      // read the analog in value:
      timestart = micros();
      sensorValue0 = analogRead(A0);
      timestop0 = micros() - timestart;

      timestart = micros();
      bitWrite(ADCSRA,6,1); // start ADC
      while(!(ADCSRA & 0x10)); // wait for adc to be ready
      byte m = ADCL; // fetch adc data
      byte j = ADCH;
      int a = (j << 8) | m; // form into an int
      timestop0a = micros() - timestart;
      
      timestart = micros();
      sensorValue1 = analogRead(A1);
      timestop1 = micros() - timestart;

      //timestart = micros();
      bitWrite(ADCSRA,6,1); // start ADC
      while(!(ADCSRA & 0x10)); // wait for adc to be ready
      m = ADCL; // fetch adc data
      j = ADCH;
      int b = (j << 8) | m; // form into an int
      //timestop0a = micros() - timestart;
      

      timestart = micros();
      sensorValue2 = analogRead(A2);
      timestop2 = micros() - timestart;

      //timestart = micros();
      bitWrite(ADCSRA,6,1); // start ADC
      while(!(ADCSRA & 0x10)); // wait for adc to be ready
      m = ADCL; // fetch adc data
      j = ADCH;
      int c = (j << 8) | m; // form into an int
      //timestop0a = micros() - timestart;
      
      
      timestart = micros();
      sensorValue3 = analogRead(A3);
      timestop3 = micros() - timestart;

      //timestart = micros();
      bitWrite(ADCSRA,6,1); // start ADC
      while(!(ADCSRA & 0x10)); // wait for adc to be ready
      m = ADCL; // fetch adc data
      j = ADCH;
      int d = (j << 8) | m; // form into an int
      //timestop0a = micros() - timestart;
            
      timestart = micros();
      sensorValue4 = analogRead(A4);
      timestop4 = micros() - timestart;
      
      //timestart = micros();
      bitWrite(ADCSRA,6,1); // start ADC
      while(!(ADCSRA & 0x10)); // wait for adc to be ready
      m = ADCL; // fetch adc data
      j = ADCH;
      int e = (j << 8) | m; // form into an int
      //timestop0a = micros() - timestart;
            
      //Serial.print("ADMUX = ");
      //Serial.println(ADMUX);
      Serial.print("A0 = " );
      Serial.print(sensorValue0);
      Serial.print("\t ");
      Serial.println(a);
      Serial.print("t = ");
      Serial.print(timestop0);
      Serial.print("\t\t ");
      Serial.println(timestop0a);
      Serial.println("");
     
      Serial.print("A1 = " );
      Serial.print(sensorValue1);
      Serial.print("\t ");
      Serial.println(b);
      Serial.print("t = ");
      Serial.println(timestop1);
      Serial.println("");
      
      Serial.print("A2 = " );
      Serial.print(sensorValue2);
      Serial.print("\t ");
      Serial.println(c);
      Serial.print("t = ");
      Serial.println(timestop2);
      Serial.println("");
      
      Serial.print("A3 = " );
      Serial.print(sensorValue3);
      Serial.print("\t ");
      Serial.println(d);
      Serial.print("t = ");
      Serial.println(timestop3);
      Serial.println("");
      
      Serial.print("A4 = " );
      Serial.print(sensorValue4);
      Serial.print("\t\t ");
      Serial.println(e);
      Serial.print("t = ");
      Serial.println(timestop4);
      Serial.println("");

      takesample = false;
  }
}

Well, for anybody interested, I think I know what the problem is.

It has to do with letting the ADC mux channel settling down before making a measurement. I will look at the data sheet to see if there is any info about it.

The only difference I could see between the two methods of reading was that I didn't change the mux when I took the second reading. I assumed the analogRead() call wouldn't reset the mux when it was done, and it appears that it doesn't. I changed the code to have two analogReads() back-to-back, keeping the second reading. All the strange readings are gone.

Yes you need to let the system settle after a mux change or take a couple of readings and discard the first.

If you look here at #10 you will see the readings remain consistent right up to ADPS4 that is off by a small amount then ADPS2 is way off.

That is what I am finding out.

I found out that putting just a 1 uS delay between setting the mux and starting conversion cleaned everything up.

My application will not require me to make back-to-back readings. I can take a reading and then get around to it when I am ready, but I wanted to know how fast that could be. I did not want to use analogRead() and pay the price of it's blocking nature.

// Read ADC with different prescalers
// set by keyboard commands
// 0-7

char receivedChar;
boolean newData = false;
boolean takesample = false;

//unsigned long timestart;
//unsigned long timestop0;
//unsigned long timestop1;
//unsigned long timestop2;
//unsigned long timestop3;
//unsigned long timestop4;

byte sensorValue0 = 0;        // value read from the pot
byte sensorValue1 = 0;        // value read from the pot
byte sensorValue2 = 0;        // value read from the pot
byte sensorValue3 = 0;        // value read from the pot
byte sensorValue4 = 0;        // value read from the pot


void setup() 
{
  Serial.begin(9600);
  Serial.println("<Arduino is ready>");
  DIDR0 = 0xFF; // disable digital inputs A0-A7
  DIDR2 = 0XFF; // disable digital inputs A8-A15
  bitWrite(ADMUX,5,1); // ADLAR left adjust for 8 bit read
  bitWrite(ADMUX,6,1); // REFS0 - reference selsct VACC
  bitWrite(ADMUX,7,0); // REFS0 - reference selsct VACC
  bitWrite(ADCSRA,7,1); // Enable ADC
  bitWrite(ADCSRA,6,1); // start ADC to inialize
}

void loop() 
{
  recvOneChar();
  setSampleRate();
  takereadings();
}

void recvOneChar() // wait for keyboard input to set prescaler and begin ADC reads
{
  if (Serial.available() > 0) 
  {
    receivedChar = Serial.read();
    newData = true;
  }
}

void setSampleRate() 
{
  if (newData == true) //process new keyboard command
  {
    switch (receivedChar)
    {
        case '7':
        Serial.print("128 prescalar - ");
        bitWrite(ADCSRA,2,1);
        bitWrite(ADCSRA,1,1);
        bitWrite(ADCSRA,0,1);
        Serial.println(ADCSRA);
        takesample = true;
        break;
        
        case '6':
        Serial.print("64 prescalar - ");
        bitWrite(ADCSRA,2,1);
        bitWrite(ADCSRA,1,1);
        bitWrite(ADCSRA,0,0);
        Serial.println(ADCSRA);
        takesample = true;
        break;
        
        case '5':
        Serial.print("32 prescalar - ");
        bitWrite(ADCSRA,2,1);
        bitWrite(ADCSRA,1,0);
        bitWrite(ADCSRA,0,1);
        Serial.println(ADCSRA);
        takesample = true;
        break;
        
        case '4':
        Serial.print("16 prescalar - ");
        bitWrite(ADCSRA,2,1);
        bitWrite(ADCSRA,1,0);
        bitWrite(ADCSRA,0,0);
        Serial.println(ADCSRA);
        takesample = true;
        break;
        
        case '3':
        Serial.print("8 prescalar - ");
        bitWrite(ADCSRA,2,0);
        bitWrite(ADCSRA,1,1);
        bitWrite(ADCSRA,0,1);
        Serial.println(ADCSRA);
        takesample = true;
        break;
        
        case '2':
        Serial.print("4 prescalar - ");
        bitWrite(ADCSRA,2,0);
        bitWrite(ADCSRA,1,1);
        bitWrite(ADCSRA,0,0);
        Serial.println(ADCSRA);
        takesample = true;
        break;
        
        case '1':
        Serial.print("2 prescalar - ");
        bitWrite(ADCSRA,2,0);
        bitWrite(ADCSRA,1,0);
        bitWrite(ADCSRA,0,1);
        Serial.println(ADCSRA);
        takesample = true;
        break;
        
        case '0':
        Serial.print("0 prescalar - ");
        bitWrite(ADCSRA,2,0);
        bitWrite(ADCSRA,1,0);
        bitWrite(ADCSRA,0,0);
        Serial.println(ADCSRA);
        takesample = true;
        break;
    }    
    newData = false; // clear received data flag
  } // end of IF
}

void takereadings()
{
  if(takesample)
  {
      // read the analog0 in value:
      bitWrite(ADMUX,2,0); 
      bitWrite(ADMUX,1,0);
      bitWrite(ADMUX,0,0); // input 0
      delayMicroseconds(1);    
      bitWrite(ADCSRA,6,1); // start ADC
      while(!(ADCSRA & 0x10)); // wait for adc to be ready
      //timestart = micros();
      sensorValue0 = ADCH; // get 8 bit data
      //timestop0 = micros() - timestart;
      
      // read the analog1 in value:
      bitWrite(ADMUX,2,0); 
      bitWrite(ADMUX,1,0);
      bitWrite(ADMUX,0,1); // input 1
      delayMicroseconds(1);
      bitWrite(ADCSRA,6,1); // start ADC
      while(!(ADCSRA & 0x10)); // wait for adc to be ready
      //timestart = micros();
      sensorValue1 = ADCH; // get 8 bit data
      //timestop0 = micros() - timestart;
      
      // read the analog2 in value:
      bitWrite(ADMUX,2,0); 
      bitWrite(ADMUX,1,1);
      bitWrite(ADMUX,0,0); // input 2
      delayMicroseconds(1);
      bitWrite(ADCSRA,6,1); // start ADC
      while(!(ADCSRA & 0x10)); // wait for adc to be ready
      //timestart = micros();
      sensorValue2 = ADCH; // get 8 bit data
      //timestop0 = micros() - timestart;
      
      // read the analog3 in value:
      bitWrite(ADMUX,2,0); 
      bitWrite(ADMUX,1,1);
      bitWrite(ADMUX,0,1); // input 3
      delayMicroseconds(1);
      bitWrite(ADCSRA,6,1); // start ADC
      while(!(ADCSRA & 0x10)); // wait for adc to be ready
      //timestart = micros();
      sensorValue3 = ADCH; // get 8 bit data
      //timestop0 = micros() - timestart;
      
      // read the analog4 in value:
      bitWrite(ADMUX,2,1); 
      bitWrite(ADMUX,1,0);
      bitWrite(ADMUX,0,0); // input 4
      delayMicroseconds(1);
      bitWrite(ADCSRA,6,1); // start ADC
      while(!(ADCSRA & 0x10)); // wait for adc to be ready
      //timestart = micros();
      sensorValue4 = ADCH; // get 8 bit data
      //timestop0 = micros() - timestart;
      
      Serial.print("A0 = " );
      Serial.println(sensorValue0);
      //Serial.print("t = ");
      //Serial.print(timestop0);
      //Serial.println("");

      Serial.print("A1 = " );
      Serial.println(sensorValue1);
      //Serial.print("\tt = ");
      //Serial.println(timestop1);
      //Serial.println("");
      
      Serial.print("A2 = " );
      Serial.println(sensorValue2);
      //Serial.print("\tt = ");
      //Serial.println(timestop2);
      //Serial.println("");
      
      Serial.print("A3 = " );
      Serial.println(sensorValue3);
      //Serial.print("\tt = ");
      //Serial.println(timestop3);
      //Serial.println("");
      
      Serial.print("A4 = " );
      Serial.println(sensorValue4);
      //Serial.print("\t\tt = ");
      //Serial.println(timestop4);
      Serial.println("");

      takesample = false;
  }
}

Maybe a shotout to Nick Gammom about his ADC page. I saw that you ran into the same problem using analogRead() at the higer prescale values. Might want to alert readers that they need to delay some between setting the mux and starting a read.

With the standard 125kHz ADC clock 10k input resistance or less is needed for proper settling,
with faster clocking this impedance must drop in proportion, with slower clocking it can rise. The
sample/hold circuit has 1.5 ADC clock cycles for settling to occur (12us in the default case) if
the multiplexor has just been switched to a different pin. The sample/hold capacitor on the chip has
to charge to within less than one part in 1000 in that window, which is why the source impedance
is all important.

It is normal to drive an ADC from a much lower impedance than 10k.

What resistor values were used.
Did you try 100n caps from the analogue inputs to ground.
That should give the A/D a "solid" voltage to sample from.
Leo..

The resistors were 4 x 330 ohms in series between 5v and gnd.

From the way the readings were going, it looked like it was a function of the sample and hold not having enough time to fully charge. The input tied to +5v was the first one to show signs of things not being right. It had the longest to charge. And there was no series resistance to the input.

From what I can make out from the data sheet, it shows the timing between the mux being set and the sample and hold being 1 1/2 clocks, but no times are given. A min would be nice.

I think I read in the data sheet that the part was not "qualified" with a prescaler clock above 1 MHz. I can't find that reference right now, but maybe that is a back handed way of saying that there needs to be at least 1.5 uS between the mux being changed and the sample and hold.

As far as the 100 nf cap goes, I didn't add one. From the schematics of the board, it looks like there are three 100 nf to begin with, C4, C5, and C6, so I didn't add any more.