0

Problem reading two sensors with analogRead()
Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.

Re: Problem reading two sensors with analogRead()

by pavlii on Sun Sep 22, 2013 5:54 pm

Well, I am trying to read many sensors at once with Arduino Ethernet board, better said all my AD inputs are connected: AD0-4 to AMPLOC 25 current sensors (quite low output resistance, directly connected to AD inputs) and AD5 is a voltage divider, scanning solar array voltage (0-40V).

I do a continuous loop through all AD inputs as fast as possible and my server is reading summary values each minute over the ethernet interface. It simply reads sum of all AD reads on each AD input and a loop counter. The server divides the sum by a counter and gets an average value on the AD input for the finished minute. The goal was to measure power consumptions precisely.

Problem was, that the AMPLOC's are very sensitive and I needed to get a long term precision of one tenth of AD digit, that seemed unrealistic with Arduino. The zero value (sensor has a zero current value at half of the range, that is 2.5V when Vcc is taken from the +5V Arduino source and Arduino reference is set to Vcc), varied +-1AD digit minimum through the day. That is not much, but makes quite a difference in the measurement.

I started to read through the forums and soon I found this topic and tried the doubled AD reads (one blind to switch the mux and second to get the data) but I still got the AD reads influencing each other. Not much, but still... Please note, that I do not want to insert big delays, because I need as much measurements per minute, as possible. The code was like this:

Code: Select all | TOGGLE FULL SIZE
    AD0 = analogRead(0); 
    analogRead(1);  //switch mux to the next input to stabilize, discard this read 
    ADD0 = ADD0+AD0;  // some math to remember max, min and sum values. It gives some time to the mux cap to charge         
    MAX0 = max (AD0,MAX0);
    MIN0 = min (AD0,MIN0);

    AD1 = analogRead(1);  //read A1 value
    analogRead(2);  //switch mux to the next input to stabilize, discard this read 
    .....


At the end I found some reply here, that someone discharged the mux capacitor between the reads by switching the mux cap to a grounded AD input. I had no free AD input to test it, but at this moment I realized, that the only AD input, which does not vary a lot, is the internal thermal sensor at AD8. So I tried to insert an analogRead of the thermal sensor to (dis)charge the mux capacitor to a stable value before all reads, something like:

(please note that the following code is an example, you cannot use analogRead(8) with the Arduino Ethernet board, you have to use alternative analogRead routine, the Arduino internal will make analogRead(0) instead of analogRead(8), it does not take input numbers 8 and above)

Code: Select all | TOGGLE FULL SIZE
    AD0 = analogRead(0); // read the A0 value
    analogRead(8);  // discharge the mux cap to stable temp value (+-10 digits do not matter)
    analogRead(1);  //switch mux to the next input to charge the mux cap (discard this read)
    ADD0 = ADD0+AD0;  // remember values       
    MAX0 = max (AD0,MAX0);
    MIN0 = min (AD0,MIN0);

    AD1 = analogRead(1); 
    analogRead(8);  // discard the mux cap to stable temp value (+-10 digits do not matter)
    analogRead(2);  //switch mux to the next input to charge the mux cap, discard this read
    etc...


This is much better! The long term AMPLOC sensor zero value precision is really in the range of 0.1 AD digits, that is perfect. Running this loop for all 6 inputs gives me a loop frequency around 260Hz. Not much but at least something with stable values.

Maybe this helps someone. If you have any further suggestions, please keep me posted.
Last edited by pavlii on Tue Sep 24, 2013 1:44 am, edited 1 time in total.
pavlii
 
Posts: 5
Joined: Sun Sep 22, 2013 5:13 pm

Re: Problem reading two sensors with analogRead()

by adafruit_support_bill on Mon Sep 23, 2013 5:40 am

Clever solution! Thanks for posting it. This is a problem that comes up often and I'm sure someone will find this helpful.

adafruit_support_bill
 
Posts: 57436
Joined: Sat Feb 07, 2009 10:11 am

Re: Problem reading two sensors with analogRead()

by pavlii on Mon Sep 23, 2013 10:13 am

Maybe it's useless to perform the whole analogRead command, if we want to switch the muxer only. Does anyone know the low level command to switch the muxer only (to another AD input) and not to do the whole ADC read? That would be faster and will reduce power consumption of the atmega.
pavlii
 
Posts: 5
Joined: Sun Sep 22, 2013 5:13 pm

Re: Problem reading two sensors with analogRead()

by adafruit_support_bill on Mon Sep 23, 2013 10:33 am

Here is the source code for analogRead (from wiring_analog.c)
Code: Select all | TOGGLE FULL SIZE

int analogRead(uint8_t pin)
{
   uint8_t low, high;

#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
   if (pin >= 54) pin -= 54; // allow for channel or pin numbers
#elif defined(__AVR_ATmega32U4__)
   if (pin >= 18) pin -= 18; // allow for channel or pin numbers
#elif defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega644__) || defined(__AVR_ATmega644A__) || defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644PA__)
   if (pin >= 24) pin -= 24; // allow for channel or pin numbers
#elif defined(analogPinToChannel) && (defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__))
   pin = analogPinToChannel(pin);
#else
   if (pin >= 14) pin -= 14; // allow for channel or pin numbers
#endif
   
#if defined(__AVR_ATmega32U4__)
   pin = analogPinToChannel(pin);
   ADCSRB = (ADCSRB & ~(1 << MUX5)) | (((pin >> 3) & 0x01) << MUX5);
#elif defined(ADCSRB) && defined(MUX5)
   // the MUX5 bit of ADCSRB selects whether we're reading from channels
   // 0 to 7 (MUX5 low) or 8 to 15 (MUX5 high).
   ADCSRB = (ADCSRB & ~(1 << MUX5)) | (((pin >> 3) & 0x01) << MUX5);
#endif
 
   // set the analog reference (high two bits of ADMUX) and select the
   // channel (low 4 bits).  this also sets ADLAR (left-adjust result)
   // to 0 (the default).
#if defined(ADMUX)
   ADMUX = (analog_reference << 6) | (pin & 0x07);
#endif

   // without a delay, we seem to read from the wrong channel
   //delay(1);

#if defined(ADCSRA) && defined(ADCL)
   // start the conversion
   sbi(ADCSRA, ADSC);

   // ADSC is cleared when the conversion finishes
   while (bit_is_set(ADCSRA, ADSC));

   // we have to read ADCL first; doing so locks both ADCL
   // and ADCH until ADCH is read.  reading ADCL second would
   // cause the results of each conversion to be discarded,
   // as ADCL and ADCH would be locked when it completed.
   low  = ADCL;
   high = ADCH;
#else
   // we dont have an ADC, return 0
   low  = 0;
   high = 0;
#endif

   // combine the two bytes
   return (high << 8) | low;
}

You may find this post useful as well:
http://www.marulaberry.co.za/index.php/ ... duino-adc/

adafruit_support_bill
 
Posts: 57436
Joined: Sat Feb 07, 2009 10:11 am

Re: Problem reading two sensors with analogRead()

by adafruit_support_rick on Mon Sep 23, 2013 11:15 am

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 | TOGGLE FULL SIZE
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;
 }

adafruit_support_rick
 
Posts: 32394
Joined: Tue Mar 15, 2011 11:42 am
Location: Buffalo, NY

Re: Problem reading two sensors with analogRead()

by pavlii on Mon Sep 23, 2013 11:36 am

Do you think, that this

Code: Select all | TOGGLE FULL SIZE
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.
pavlii
 
Posts: 5
Joined: Sun Sep 22, 2013 5:13 pm

Re: Problem reading two sensors with analogRead()

by adafruit_support_rick on Mon Sep 23, 2013 11:53 am

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.

adafruit_support_rick
 
Posts: 32394
Joined: Tue Mar 15, 2011 11:42 am
Location: Buffalo, NY

Re: Problem reading two sensors with analogRead()

by pavlii on Mon Sep 23, 2013 4:26 pm

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 | TOGGLE FULL SIZE
//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.
pavlii
 
Posts: 5
Joined: Sun Sep 22, 2013 5:13 pm

Re: Problem reading two sensors with analogRead()

by adafruit_support_rick on Tue Sep 24, 2013 2:24 pm

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

adafruit_support_rick
 
Posts: 32394
Joined: Tue Mar 15, 2011 11:42 am
Location: Buffalo, NY

Re: Problem reading two sensors with analogRead()

by pavlii on Tue Sep 24, 2013 6:05 pm

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 | TOGGLE FULL SIZE
#if defined(ADMUX)
   ADMUX = (analog_reference << 6) | (pin & 0x07);
#endif


change to

Code: Select all | TOGGLE FULL SIZE
#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 | TOGGLE FULL SIZE
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 | TOGGLE FULL SIZE
ADCSRA &= B10000111;
not
Code: Select all | TOGGLE FULL SIZE
ADCSRA &= B00000111;
pavlii
 
Posts: 5
Joined: Sun Sep 22, 2013 5:13 pm

Re: Problem reading two sensors with analogRead()

by panther3001 on Wed May 07, 2014 9:40 pm

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/

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

Re: Problem reading two sensors with analogRead()

by adafruit_support_mike on Thu May 08, 2014 3:22 am

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.

adafruit_support_mike
 
Posts: 37301
Joined: Thu Feb 11, 2010 2:51 pm

Re: Problem reading two sensors with analogRead()

by panther3001 on Tue May 13, 2014 3:13 am

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.

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

Re: Problem reading two sensors with analogRead()

by panther3001 on Sat May 17, 2014 9:35 pm

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_documents/doc8003.pdf

here is an example:
http://forums.adafruit.com/viewtopic.php?f=24&t=15506&p=76674&hilit=tmp36#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_library_for_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

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

Re: Problem reading two sensors with analogRead()

by txbobs on Mon Jan 04, 2016 12:36 pm

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 282 times

txbobs
 
Posts: 17
Joined: Wed Jan 30, 2013 3:11 pm

Please be positive and constructive with your questions and comments.