AT Mega 2560 adc read higher pins

Hello,

I have a problem with reading the pin numbers over 7 (8 - 15) on the Arduino Mega 2560. I understand I need to set the ADCSRB register to 1 (MUX5) to enable the higher pins, but I cannot get it to work.

I tried to set the bit by the following methods:

ADMUX &= 0xE0; // clear bits MUX0 - 4
ADMUX |= dacChan&0x07; // define ADC channel to be read

ADCSRB |= _BV(MUX5); // set MUX05 - to read channels above 5

Or:

ADCSRB = (ADCSRB | (1<<MUX5)); // set MUX05 - to read channels above 5

My test code is (based on the analogRead example and ADC oversampling samples), the channel selection is in function readADC(), the code runs fine for pins 0 - 7 but not for any pin above 7:

/*
  Analog Input
 Demonstrates analog input by reading an analog sensor on analog pin 0 and
 turning on and off a light emitting diode(LED)  connected to digital pin 13.
 The amount of time the LED will be on and off depends on
 the value obtained by analogRead().

 The circuit:
 * Potentiometer attached to analog input 0
 * center pin of the potentiometer to the analog pin
 * one side pin (either one) to ground
 * the other side pin to +5V
 * LED anode (long leg) attached to digital output 13
 * LED cathode (short leg) attached to ground

 * Note: because most Arduinos have a built-in LED attached
 to pin 13 on the board, the LED is optional.


 Created by David Cuartielles
 modified 30 Aug 2011
 By Tom Igoe

 This example code is in the public domain.

 http://www.arduino.cc/en/Tutorial/AnalogInput

 */

int sensorPin = 2;    // select the input pin for the potentiometer
int ledPin = 13;      // select the pin for the LED
int sensorValue = 0;  // variable to store the value coming from the sensor
int i;
uint8_t skipopt, tscale, faultCode, overSamp, rightShift, bitRes=10, cnt, sleepMode;

// used for easy reset to PS_128
const unsigned char PS_128 = (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0);

void setup() {
  Serial.begin(57600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for Leonardo only
  }
  bitRes = 12;
  bitRes = abs(bitRes) - 10;
  if(bitRes <= 0) bitRes = 0;   // minimum ADC resolution, 8 bits not supported in this version
  if(bitRes > 2) bitRes = 2;    // maximum ADC resolution 12 bits
  
  overSamp = ipow(4, bitRes);
  rightShift = ipow(2, bitRes) / 2; // when overSamp = 0 this will be 0,5 which truncates to 0 (and that is fine, 10 bits -> no right shift needed)
  Serial.println(String(overSamp)+ " " + String(rightShift));
  // Define various ADC prescaler, just as a reminder/note of different options:
   const unsigned char PS_16 = (1 << ADPS2);
   const unsigned char PS_32 = (1 << ADPS2) | (1 << ADPS0);
   const unsigned char PS_64 = (1 << ADPS2) | (1 << ADPS1);
   const unsigned char PS_128 = (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0);
  
  // set prescaler to (32) - seems OK value with 16x oversampling
  // ADCSRA =(1 << ADPS2) | (1 << ADPS0);
  // clear any bits set by default
  ADCSRA &=~PS_128;
  ADCSRA |= PS_32; // add prescale of 16 (1MHz)
}

void loop() {
  // read the value from the sensor:
  // sensorValue = analogRead(sensorPin);
  for(i=0;i<=14;i++) {
    sensorValue = readADC(overSamp, rightShift,i );
    Serial.println(String(i) + " " + String(sensorValue));
    delay(500);
  }
  

}

uint16_t readADC(int overSamp, int rightShift, int dacChan)
{
  volatile  uint16_t rawVal = 0;
  uint16_t rawSum = 0;


  ADMUX &= 0xE0;            // clear bits MUX0 - 4
  ADMUX |= dacChan&0x07;    // define ADC channel to be read
  // ADCSRB |= _BV(MUX5);  // set MUX05 - to read channels above 5
  // ADCSRB = (ADCSRB | (1<<MUX5));
  // ADMUX = dacChan;          // Select channel
  
  if (dacChan >= 8) 
      ADCSRB |= _BV(MUX5);
  else
      ADCSRB &= ~_BV(MUX5);
  
   
  ADMUX |= (1 << REFS0);    // use Avcc as reference (REFS1=0, REFS0=1)
  ADMUX &= ~(1 << ADLAR);   // clear for 10 bit resolution (& because we clear: & where one is 0 -> 0)
  ADCSRA |= (1 << ADEN);    // Enable the ADC

  for (int i = 0; i < overSamp; i++) {
    ADCSRA |= (1 << ADSC);    // Start the ADC conversion
    while (ADCSRA & (1 << ADSC)); //wait for conversion to finish
    rawVal = (ADCL | (ADCH << 8));    // ADCH is read so ADC can be updated again
    rawSum += (rawVal);
  }
 
  ADCSRA |= (0 << ADEN);    // Disable the ADC
  // return the rawSum right shifted by rightShift
  return rawSum >> rightShift; 
}

int ipow(int base, int expo)
{
  int result = 1;
  expo = abs(expo);
  base = abs(base);
  while (expo)
  {
    if (expo & 1) result *= base;
    expo >>= 1;
    base *= base;
  }
  return result;
}

Sorry if there is a silly mistake - but I hope you can help!

Henk

Note: ADCSRA |= (0 << ADEN);    // Disable the ADC
Using ‘OR’ with a zero value does nothing.

Note:     rawVal = (ADCL | (ADCH << 8));    // ADCH is read so ADC can be updated again
The compiler knows what order to read the I/O registers so “rawVal = ADC;” should work.

I don’t see anything that would cause it to work properly with A0-A7 but not A8-A15. :frowning:

Thanks for your suggestions, I will delve a bit deeper in bitwise logic. I just copied those statements (I understand them more or less but have not calculated what happens).

Still, the code runs fine on pins 0 - 7 and not at all on the higher pins. To test I have a simple pot meter hooked up to 5V and GND and it runs smoothly through 0 - 4092 (12 bits) on the first 7 pins.

I also tried the following (and other hex codes) to set the bits directly:

ADMUX = 0b01000000 | (dacChan & 0b00000111);
ADCSRB = dacChan & 0b00001000;
ADCSRA = 0b11000001; // Bit 7: Enable ADC

And also:

ADMUX=0x00;
ADCSRB=0x08; //channel 15

But no joy.

I did find some remarks about the MUX5 bit not set properly, but no arduino code example.

http://www.mcselec.com/index.php/category/music-business/index2.php?option=com_forum&Itemid=59&page=viewtopic&p=70244&sid=59eb47cc18963996fb2dbc9b0e5127b8:

when you use a numeric value, mux5 bit is not set properly. this is a bug.
you can use a variable too: w=getadc(someChannel) . this will set the proper bit.

But this is not arduino code....

FOUND IT, I was getting crazy. I copied the code from wiring_analog and started testing with that - with strange results. So I started measuring the actual volts on the circuit. Turned out that on moving from the office this afternoon to home the GND wire was damaged (broken pin). I redid all the wiring on the test setup and with code

if (adcPin>= 8 )
ADCSRB |= _BV(MUX5);
else
ADCSRB &= ~_BV(MUX5);

to set the ADVCRSB bit to 1 or 0 depending on the pin number it now runs.

So - it was a silly mistake!

(Always check the wiring).