Problem reading two sensors with analogRead()

Post here about your Arduino projects, get help - for Adafruit customers!

Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.
User avatar
adafruit_support_rick
 
Posts: 35092
Joined: Tue Mar 15, 2011 11:42 am

Re: Problem reading two sensors with analogRead()

Post by adafruit_support_rick »

FWIW - here's a single read ADC routine I'm using on an ATmega32U4 in another (non-arduino) project. I've also got an auto-triggered conversion on a different channel going on at about 4kHz. I can call this single-read routine at any time, and they don't seem to interfere with each other.

Code: Select all

typedef int16_t	ADC_t;			//ADC sample type.

 ADC_t ReadADC(uint8_t channel)
 {
	ADC_t result;
	uint8_t save_adcsra = ADCSRA;		//save current setup
	uint8_t save_adcsrb = ADCSRB;		//save current setup
	uint8_t save_admux = ADMUX;		//save current setup

	while (ADCSRA & mADIF);		//wait for conversion
	ADCSRA = mADCCLK_DIV128;			//disable ADC
	
	ADCSRB	= (ADCSRB & ~0x20) | (channel & 0x20);		//select channel
	ADMUX = mADC_AVCC | (channel & 0x1F);		//select channel
	
	ADCSRA = mADEN | mADSC | mADCCLK_DIV128;			//start conversion
	while (0 == (ADCSRA & mADIF));						//wait for conversion
	result = (ADCL | (((ADC_t)ADCH) << 8));
	
	ADCSRA	|=  mADIF;					//clear interrupt flag (yes, write a '1' to clear it!)

	ADMUX = save_admux;		//restore setup
	ADCSRB = save_adcsrb;		//restore setup
	ADCSRA = save_adcsra;		//restore setup

	return result;
 }

pavlii
 
Posts: 5
Joined: Sun Sep 22, 2013 5:13 pm

Re: Problem reading two sensors with analogRead()

Post by pavlii »

Do you think, that this

Code: Select all

ADMUX = mADC_AVCC | (channel & 0x1F);      //select channel
resp.
ADMUX = (analog_reference << 6) | (pin & 0x07);
switches the muxer immediately?

Second option is, that this value is used when starting the conversion. In that case would not be possible to switch the muxer only.

User avatar
adafruit_support_rick
 
Posts: 35092
Joined: Tue Mar 15, 2011 11:42 am

Re: Problem reading two sensors with analogRead()

Post by adafruit_support_rick »

Those two instructions ought to switch the mux immediately.

The datasheet makes the following note:
Note that the conversion starts on the following rising ADC clock edge after ADSC is written. The user is thus advised not to write new channel or reference selection values to ADMUX until one ADC clock cycle after ADSC is written.

The principal difference between my routine and the arduino routine is that I am disabling the ADC before I set the mux. The arduino code does not appear to do that. Looking at the datasheet, there is no particular reason for Arduino to disable the ADC, so I can't say that they're wrong.

Anyway, my sequence is:
- wait for any current conversions to complete, (since I have the auto-triggered conversions running)
- disable the ADC
- switch the mux
- enable the ADC
- wait for conversion to complete

It can't hurt to try it. You can just use my routine as is - it will peacefully coexist with the Arduino library.

You're using a Uno? The channel number is the same as the Analog pin number.

pavlii
 
Posts: 5
Joined: Sun Sep 22, 2013 5:13 pm

Re: Problem reading two sensors with analogRead()

Post by pavlii »

I use Arduino Ethernet board.

There are other interesting things in the datasheet:
According to page 255 it should be possible discharge the mux cap directly to ground by selecting "ADC input" number 15 (1111 bin). That is better, than my solution with the temp sensor.
Also a 1.1 V reference can be selected as an input on virtual input channel 14 (1110 bin).

But you cannot select inputs > 7 with the Arduino library analogRead function, you have to use your own.

EDIT:

I have tested it and it works:

Code: Select all

//setup ADC, this can be done in the setup section
  ADCSRB = 0;
  ADCSRA &= B10000111; // Set Start disable, Auto trigger disable, Interrupts disable, Do not modify Prescaler

//this may go to a DischargeMuxCap subroutine - it should connect the mux to GND
  ADMUX = B01001111; // Uref=Vcc, ADC input = GND

//test conversion if we are really connected to ground
  ADCSRA |= B01000000; // Fire the conversion
  while (bit_is_set(ADCSRA,ADSC)); // Wait until finished
  return (ADCL | (ADCH << 8)); // Conversion result, in our case should be 0, or result = (ADCL | (ADCH << 8)); if we are not in subroutine but directly in the loop 

//if we want to continue conversion on another input when we have the mux cap discharged
//(remove the return line above)

  ADMUX = B01000000 | input; // Uref=Vcc, ADC input = input, possible values (0-5 Arduino AD input pins, 6-7 not connected pins, 8=temp sensor, 14=internal bandgap reference, 15=GND)

//same conversion as above
  ADCSRA |= B01000000; // Fire the conversion
  while (bit_is_set(ADCSRA,ADSC)); // Wait until finished
  return (ADCL | (ADCH << 8)); // Conversion result , or result = (ADCL | (ADCH << 8)); if we are not in subroutine but directly in the loop
tested on Arduino Ethernet (ATMEGA 328 P)
Last edited by pavlii on Tue Sep 24, 2013 6:09 pm, edited 1 time in total.

User avatar
adafruit_support_rick
 
Posts: 35092
Joined: Tue Mar 15, 2011 11:42 am

Re: Problem reading two sensors with analogRead()

Post by adafruit_support_rick »

Interesting. Might want to submit that to Arduino...

pavlii
 
Posts: 5
Joined: Sun Sep 22, 2013 5:13 pm

Re: Problem reading two sensors with analogRead()

Post by pavlii »

Maybe :)
If you want to modify the Arduino library analogRead to be able to select pins 8-13, you can modify 0x07 to 0x0F in wiring_analog.c , subroutine analogRead (posted above). Better backup the original file first !

this:

Code: Select all

#if defined(ADMUX)
   ADMUX = (analog_reference << 6) | (pin & 0x07);
#endif
change to

Code: Select all

#if defined(ADMUX)
   ADMUX = (analog_reference << 6) | (pin & 0x0F);
#endif
When compiling your sketch next time, the updated analogRead will be active.

Unfortunately the pins 14 and 15 (reference and GND) still cannot be selected (due to other commands in the subroutine), but the temp sensor 8 and "prohibited pins" 9-13 can. I personally think, that there is another GND on one pin in range 9-13, in my Arduino it is pin 13. But this is not guaranteed, as those pins are marked as "reserved" in the datasheet and can give unpredictable results. You can test it in this sketch (this aRead function can read all inputs from 0 to 15, just change the number in aRead(0); ):

Code: Select all

int test;

void setup() {
  Serial.begin(115200); 
  ADCSRB = 0;
  ADCSRA &= B10000111;
}

int16_t aRead(byte pin){ 
  ADMUX = B01000000 | pin; 
  ADCSRA |= B01000000; // Convert
  while (bit_is_set(ADCSRA,ADSC)); 
  return (ADCL | (ADCH << 8));;
}

void loop() {
  test = aRead(0);            
  Serial.print("Value = ");                       
  Serial.println(test);  
  delay (100);   
}
I have also corrected one error in my previous post (the ADC will shutdown if the left bit is not set).
Right line is

Code: Select all

ADCSRA &= B10000111;
not

Code: Select all

ADCSRA &= B00000111;

User avatar
panther3001
 
Posts: 4
Joined: Wed Feb 05, 2014 11:17 pm

Re: Problem reading two sensors with analogRead()

Post by panther3001 »

adafruit wrote:yes the bandgap reference isnt stable, thats why the tutorial has changed. please go and reread the tutorial and adjust your code to match the new tutorial with 3.3v as the reference.
Adafruit, I know this post is old as dirt, but since I was still too young to use the internet when this post was written (just joking), I wasn't allowed to read the ATmega 328 datasheet and point something out. Nevertheless, I'd like to point this out now:

ATmega 328 datasheet, pg. 254, Table 24-3, last row, "Internal 1.1V Voltage Reference with external capacitor at AREF pin"
-they recommend using a capacitor to filter the internal 1.1V bandgap reference, in order to stabilize it for good readings.
-place it from the AREF pin to GND.
-read more on pg. 248, paragraph 24.5.2: "The internal 1.1V reference is generated from the internal bandgap reference (VBG) through an internal amplifier. In either case, the external AREF pin is directly connected to the ADC, and the reference voltage can be made more immune to noise by connecting a capacitor between the AREF pin and ground."

Also another good note: "If no external voltage is applied to the AREF pin, the user may switch between AVCC and 1.1V as reference selection. The first ADC conversion result after switching reference voltage source may be inaccurate, and the user is advised to discard this result."

Reference:
ATmega 328 datasheet: http://www.atmel.com/Images/Atmel-8271- ... asheet.pdf

Conclusions:
I still need to test it out myself, but I think placing a cap on the AREF pin to GND may allow the internal 1.1V Band gap ref voltage to be nice and stable, for good readings.


Additionally, the Arduino ADC can be made to sample at resolutions up to 16-bit or higher, via oversampling (http://www.atmel.com/images/doc8003.pdf). I've already tested this out and written some code on it, and I plan to post a library on my website soon here: http://electricrcaircraftguy.blogspot.com/

User avatar
adafruit_support_mike
 
Posts: 67446
Joined: Thu Feb 11, 2010 2:51 pm

Re: Problem reading two sensors with analogRead()

Post by adafruit_support_mike »

It's true that oversampling and decimation can improve an ADC's resolution, but each bit of increased resolution costs four times as many readings. To get 16 bits from a 10-bit ADC, you'd need 4096 readings.

For that to be worth doing, you need a signal source that remains stable to within one 16-bit LSB for the whole oversampling period.. about half a second at the Arduino's default ADC sampling rate.

There are signals for which that will work, but you have to watch the tradeoff between gains in resolution and loss of Nyquist frequency.

User avatar
panther3001
 
Posts: 4
Joined: Wed Feb 05, 2014 11:17 pm

Re: Problem reading two sensors with analogRead()

Post by panther3001 »

True True. My library is complete. Here it is: http://electricrcaircraftguy.blogspot.c ... t-adc.html

It allows sampling with ADC resolutions anywhere from 10-bit to 21-bit. It also can do smoothing by returning the average of many samples, if desired, or it can simply return a single sample.

User avatar
panther3001
 
Posts: 4
Joined: Wed Feb 05, 2014 11:17 pm

Re: Problem reading two sensors with analogRead()

Post by panther3001 »

fat16lib wrote:Did you do two reads for each sensor? Do one read then delay and then do the one you keep.

Analog read has a lot of problems. You can often improve your results with oversampling.
http://www.atmel.com/dyn/resources/prod ... oc8003.pdf

here is an example:
http://forums.adafruit.com/viewtopic.ph ... p36#p76594

David Mellis, the key Arduino developer, seems to know about this and has an analog library with options to fix many of these problems.

http://dam.mellis.org/2010/06/sensor_li ... r_arduino/
fat16lib, I'd like to give you a special thanks for pointing out these links. Because of this post, and your link to the AVR121 application note about oversampling, I was able to quickly implement this technique, and it also inspired me to write my very first library (which was quite a challenge for me), and share it with others. Here is a link to my library, which implements AVR121: http://electricrcaircraftguy.blogspot.c ... t-adc.html

And here is a link to where I have recently shared it on the Arduino Forums too. On the Arduino forums, however, guys with lots of posts are rather skeptical about the whole thing.
http://forum.arduino.cc/index.php?topic=240509

User avatar
txbobs
 
Posts: 22
Joined: Wed Jan 30, 2013 3:11 pm

Re: Problem reading two sensors with analogRead()

Post by txbobs »

Sorry for the necro-post but I have run into an interesting (to me anyway) instance of this happening.

I bought the RasPiO LCD kit which is just a i2c interface for a LCD and it includes two TMP-36 sensors. I hooked it up and it works perfectly. The code reads a sample, discards it and then takes the average of the next 10 samples. It does this loop for each of the two sensors.

Then I thought I would move it over to my new Digikey Metro. Once there however I started experiencing these wild fluctuations and I cannot get the two sensors to work at all. Same USB power supply for both.

Could just the board layout/chip size be causing this? The RaspIO uses a 328 and I believe the Metro does as well.

Attached is the pic of the RasPiO setup.
Attachments
raspio.jpg
raspio.jpg (101.31 KiB) Viewed 2174 times

User avatar
adafruit_support_bill
 
Posts: 88093
Joined: Sat Feb 07, 2009 10:11 am

Re: Problem reading two sensors with analogRead()

Post by adafruit_support_bill »

Please post a photo of the one that doesn't work. And also post the code you are using to read the sensors.

User avatar
txbobs
 
Posts: 22
Joined: Wed Jan 30, 2013 3:11 pm

Re: Problem reading two sensors with analogRead()

Post by txbobs »

adafruit_support_bill wrote:Please post a photo of the one that doesn't work. And also post the code you are using to read the sensors.
Sure thing.

Code: Select all

 
      analogRead(ADCpin);    // first ADC reading discarded
      delay(20);
      reading = 0;           // read ADC pin 0 10 times
      for (int loop = 0; loop < iterations; loop++)
      {
         reading += analogRead(ADCpin);
         delay(20);
      }  
      analogRead(ADCpin2);
      delay(20);
      reading1 = 0;           // read ADC pin 1 10 times
      for (int loop = 0; loop < iterations; loop++)
      {
         reading1 += analogRead(ADCpin2);
         delay(20);
      } 

ADCpin is set to 0 and ADCpin2 is set to 1.
Attachments
metrotemp.jpg
metrotemp.jpg (105.13 KiB) Viewed 1200 times

User avatar
adafruit_support_bill
 
Posts: 88093
Joined: Sat Feb 07, 2009 10:11 am

Re: Problem reading two sensors with analogRead()

Post by adafruit_support_bill »

Add some serial output to your sampling loops and post the Serial Monitor output.

And please post the complete code so we can see what else may be going on there.

User avatar
txbobs
 
Posts: 22
Joined: Wed Jan 30, 2013 3:11 pm

Re: Problem reading two sensors with analogRead()

Post by txbobs »

Output from the A0 and A1 readings.

I just added a false read to A2 which is connected to a 5k resistor to ground. Same behavior as when it was reading A0 and A1.

Code: Select all

/* LCD Thermometer Sketch for RasPiO Duino 20x4 i2c LCD
Library version:1.1 from robot-r-us.com
Compatible with the Arduino IDE 1.0 and above
*/

#include <Wire.h> 
#include <LiquidCrystal_I2C.h>
int ADCpin = 0;                    // declare variables
int ADCpin2 = 1;
int iterations = 10;
float voltage = 3.3;
float voltage1 = 3.3;
int reading = 0;
int reading1 = 0;

// set LCD i2c address to 0x27 and 20 char x 4 line display
LiquidCrystal_I2C lcd(0x27,20,4);  

void setup()
{
  lcd.init();                      // initialize lcd 
  lcd.backlight();
  lcd.setCursor(0, 0);             // go to column 0 row 0
  lcd.print("    i2c 20x4 LCD");   // Print txt on LCD
  lcd.setCursor(0, 1);
  lcd.print("     powered by");
  lcd.setCursor(0, 3); 
  lcd.print("        Bob");
  delay(5000);
  lcd.clear();
  Serial.begin(9600);
}

void loop()
{
      analogRead(2);    // first ADC reading discarded
      delay(20);
      reading = 0;           // read ADC pin 0 10 times
      for (int loop = 0; loop < iterations; loop++)
      {
         reading += analogRead(ADCpin);
         delay(20);
      }  
      analogRead(2);
      delay(20);
      reading1 = 0;           // read ADC pin 1 10 times
      for (int loop = 0; loop < iterations; loop++)
      {
         reading1 += analogRead(ADCpin2);
         delay(20);
      } 

      lcd.setCursor(0, 0);
      lcd.print(reading / iterations);
      lcd.print("    ");      
      lcd.print(reading1 / iterations);      
      lcd.print("    AD raw");

      voltage = reading / 1023.0 / iterations * 3.3;
      voltage1 = reading1 / 1023.0 / iterations * 3.3;        

      lcd.setCursor(0, 1);
      lcd.print(voltage, 3);   // print voltage to 3dp
      lcd.print("  ");
      lcd.print(voltage1, 3);
      lcd.print("  Volts");

      lcd.setCursor(0, 2);
      int temperature = voltage * 100 - 50;
      lcd.print(temperature);
      lcd.print((char)223);    // print degree symbol
      lcd.print("C   ");
      int temperature1 = voltage1 * 100 - 50;      
      lcd.print(temperature1);
      lcd.print((char)223);
      lcd.print("C   ");
      
      Serial.print("A");
      Serial.print(ADCpin);
      Serial.print(" - ");
      Serial.print(analogRead(ADCpin));
      Serial.print("   A");
      Serial.print(ADCpin2);
      Serial.print(" - ");
      Serial.println(analogRead(ADCpin2));
      
      delay(500);
}

Locked
Please be positive and constructive with your questions and comments.

Return to “Arduino”