Search code examples
arduinoarduino-unoadc

Arduino UNO ADC's ADCH returns a constant value in free running mode


I've stripped everything that I'm sure is not relevant out of my code below. Basically we're inputting a signal through pin A0 and sending samples over serial. It's a relatively simple project but because we want to maximize speed we're trying to avoid using analogRead(). However instead of getting a curve back we get a flatline that doesn't respond to any input, only to the prescaler values.

I've verified all of the register changes and they are all set correctly. If we run the system with the analogRead() code then it works so I know the circuit is working. I can not find any clear information around about why this might be happening. I've played around with the ADMUX channel select to see if maybe I was on the wrong channel but I'm not. Overall I'm very confused by this right now!

// defines for setting and clearing register bits
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif

void setup() {
// Opens up the serial port with a baud of 115200
  Serial.begin(115200, SERIAL_8E2);
  pinMode(A0, INPUT);
  digitalWrite(A0, HIGH);
  // Enable ADC Completion Interrupt
  sbi(ADCSRA,ADIE) ;
  sei() ;

  // Select the correct pin for the ADC
  cbi(ADMUX,MUX3);
  cbi(ADMUX,MUX2);
  cbi(ADMUX,MUX1);
  cbi(ADMUX,MUX0);

  // Set the ADC to left adjust so that MSB is in low Byte
  sbi(ADMUX,ADLAR) ;

 sbi(ADCSRA,ADEN) ;
 sbi(ADCSRA,ADSC) ;
}

void loop() 
{
}

ISR(ADC_vect)
{
  Serial.write(ADCH);
  cbi(ADCSRA,ADIF);
  sbi(ADCSRA,ADSC);
}

Solution

  • Sorry to thread dig, but i came across some information that may help others. Looks like @UncleO is on the right track there. Since you aren't setting the Prescaler ADPS[2:0], they default to 0 (division factor 2), which i too fast for the ADC to reliably approximate the value (http://www.gammon.com.au/adc):

    Note that the datasheet (Table 28-7. ADC Characteristics) mentions that the ADC clock frequency range is 50 kHz to 1000 kHz. Thus (at this CPU clock speed of 16 MHz) the smallest prescaler you are allowed to use is 16, which is born out by testing below.

    Prescaler 2
    
    Analog port = 0, average result = 1023
    Analog port = 1, average result = 1023
    Analog port = 2, average result = 1023
    Analog port = 3, average result = 1022
    Time taken = 26220
    
    Prescaler 16
    
    Analog port = 0, average result = 1022
    Analog port = 1, average result = 672
    Analog port = 2, average result = 509
    Analog port = 3, average result = 0
    Time taken = 73164
    

    The reason why it's working when you use the analogRead function is the prescalar is set to 128 within that library