Supporting differential ADC (analogRead()) input?

1,712 views
Skip to first unread message

David Mellis

unread,
Jan 28, 2015, 2:20:54 PM1/28/15
to Arduino Developer's List
Many AVR's support differential analog-to-digital conversion (i.e. reading the difference between the voltage on two pins, possibly multiplied by a gain factor). We don't currently have an API in Arduino for this (AFAIK) but it seems like something that would be useful in various situations. (For example, it's used in the temperature and microphone examples from the Fab Lab: http://academy.cba.mit.edu/classes/input_devices/index.html

Does this seem useful?

What do you think about an API like:

analogReadGain(gain); // set gain, e.g. to GAIN1, GAIN10, GAIN20, etc.
analogRead(pinA, pinB); // difference between the voltage on pin A and pin B, times the gain

One complication is that some combinations of pins and gains might not be supported on the chip. Any thoughts on how to handle that?


David

Pierce Nichols

unread,
Jan 28, 2015, 2:48:36 PM1/28/15
to Arduino Developers
David,

First, I love this idea!

IIRC, the sets of pins that can be used for differential measurement
tend to be pretty limited -- each differential channel uses a specific
pair of pins and no others.

I think the simplest way would be to have something like
analogMode(<base pin>, <mode>, <gain>) to set the mode of each analog
channel (either single-ended or differential) and the gain and
document what happens with each mode. Then analogRead() should work as
per normal.

The defines should be arranged with enums and conditional compilation
or whatever else is available such that using an invalid pin and/or
gain combination should fail at compile time. That's a much easier
problem to diagnose then it perhaps going off and doing something
strange or not working at all.

-p
> --
> You received this message because you are subscribed to the Google Groups
> "Developers" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to developers+...@arduino.cc.



--
Pierce Nichols
Principal Engineer
Logos Electromechanical, LLC

Victor Aprea

unread,
Jan 28, 2015, 3:04:33 PM1/28/15
to Arduino Developers
Pierce,

Yes, ADC inputs are usually tightly bound in differential use, when differential mode is supported. In fact I've never seen otherwise (which is not to say I've seen everything). The analogMode approach seems pretty sane to me. On the bright side, as you noted, analogRead already returns a signed value so no signature change would be needed to that function. 

If this goes through, I'd also suggest / request adoption of an explicit define (e.g. via pins_arduino.h) for 3rd party boards to declare whether they support this feature set or not (i.e. #define HAS_DIFFERENTIAL_ADC_INPUTS) in a painless way for existing boards.

Kind Regards,

Vic

Victor Aprea // Wicked Device

Paul Stoffregen

unread,
Jan 28, 2015, 3:19:12 PM1/28/15
to devel...@arduino.cc
I like the idea of an API to support differential mode.

But I'd hope to avoid modal settings, like this:

> I think the simplest way would be to have something like
> analogMode(<base pin>, <mode>, <gain>) ....

Modal settings greatly increase the learning curve for novices.

The trouble isn't necessarily complexity, but rather discovery while
learning & experimenting.

Novices tend to discover and experiment with functions that seem to
promise the results they seek. For example, digitalWrite() works so
well, because it clearly advertises that you can write to something
digital. One of the more common pitfalls of the Arduino API is not
knowing to use pinMode() first.

Obviously pinMode can't be changed now, but going forward, we can strive
to define functions which clearly indicate what they will do, and do so
without the need for other functions to also be discovered and learned.

There are probably many great ways to support differential analog read.
Let's do it without creating modal settings and burdening novices with
the requirement to discover and learn a second function.

Tom Igoe

unread,
Jan 29, 2015, 11:19:50 AM1/29/15
to devel...@arduino.cc
Paul posts for me, to an almost letter-perfect level. It’s like he’s reading my mind.

I think this is an interesting idea, though.

t.

David Mellis

unread,
Jan 29, 2015, 11:55:38 AM1/29/15
to Arduino Developer's List
Would you guys prefer something like: analogRead(pinA, pinB, gain)? What about a separate name, like analogReadDifferential(pinA, pinB, gain)?

Pierce Nichols

unread,
Jan 29, 2015, 12:12:17 PM1/29/15
to Arduino Developers
The value of pinB is entirely a function of pinA, so I don't see any
value to including it. It implies a flexibility that the hardware does
not provide and that cannot be faked in software.
analogReadDifferential(pinA, [gain]) makes sense to me -- it answers
Paul's objection and keeps things nice and simple.

We should think about ways to make sure that calling it against
combinations of pins and gain that the hardware chosen does not
support will break at compile time.


-p

Matthijs Kooijman

unread,
Jan 29, 2015, 12:27:37 PM1/29/15
to devel...@arduino.cc
Hey Pierce,

> The value of pinB is entirely a function of pinA, so I don't see any
> value to including it. It implies a flexibility that the hardware does
> not provide and that cannot be faked in software.
I don't follow you here. You are saying that there's only a fixed number
of differential combinations, _and_ that each analog pin only appears in
at most one of these combinations on the positive side?

That doesn't seem true, looking at e.g. the 2560:

For example, here's an excerpt from that datasheet, that lists MUX,
positive input, negative inmput and gain.

010000 ADC0 ADC1 1×
010001 ADC1 ADC1 1×
010010 ADC2 ADC1 1×
010011 ADC3 ADC1 1×
010100 ADC4 ADC1 1×
010101 ADC5 ADC1 1×
010110 ADC6 ADC1 1×
010111 ADC7 ADC1 1×
011000 ADC0 ADC2 1×
011001 ADC1 ADC2 1×
011010 ADC2 ADC2 1×
011011 ADC3 ADC2 1×
011100 ADC4 ADC2 1×
011101 ADC5 ADC2 1×

As you can see, I couldn't figure out what configuration to use given
jujst one pin number?

Or am I totally misunderstanding you here?

In either case, I'd be against adding an API that includes (too many)
assumptions about the hardware, and rather have an API that semantically
expresses what is meant.

> We should think about ways to make sure that calling it against
> combinations of pins and gain that the hardware chosen does not
> support will break at compile time.
I'd like to support that, but I'm not entirely sure how (other than
doing fancy template tricks, but I can only think of ways where the
template syntax shines through).

Gr.

Matthijs
signature.asc

Adrian Godwin

unread,
Jan 29, 2015, 12:27:48 PM1/29/15
to devel...@arduino.cc
Or you could have names for Analog1, Analog2, Analog3,. . Analog1ref2, Analog3ref4 etc. So it would self-describe the pins in use, require only one interface etc.

David Mellis

unread,
Jan 29, 2015, 12:33:27 PM1/29/15
to Arduino Developer's List
Some processors seem to have more flexibility in the choice of pins. For example, the ATmega32U4 allows either ADC0 or ADC1 to serve as the negative input, with ADC4 to ADC7 as the positive. In general, though, you're right that not all combinations are supported. I'm not sure how we'd detect it at compile time, aside from using a single constant to specify the entire setup, e.g. analogReadDifferential(A5_A1_GAIN1). That seems unwieldy, though. Potentially, we could have separate compile time constants that were concatenated by a macro (e.g. analogReadDifferential(A5, A1, GAIN1) would get translated by the pre-processor into analogReadDifferential(A5_A1_GAIN1), but I'm not sure if there's a way to do that and provide reasonable error messages. (And, of course, if the pin number was specified at run time, you wouldn't be able to detect the problem at compile time.)

In general, it seems like we're going to want a way to signal an error at run-time (instead of or in addition to at compile time). Maybe returning a special value? (Adding another function, like analogReadDifferentialSupported() seems clunky.)

Tom Igoe

unread,
Jan 29, 2015, 1:02:22 PM1/29/15
to devel...@arduino.cc
How does the user know that the other pin they should connect to is pinB, in this case?

t.

Paul Stoffregen

unread,
Jan 29, 2015, 5:48:26 PM1/29/15
to devel...@arduino.cc

The SAM D21 chip used on the upcoming Arduino Zero appears to have
tremendous flexibility in analog differential input configuration. The
datasheet says the negative input can be one of 8 analog signals and the
positive can be any of the 20 analog signals, plus some internal signals.

I'm not necessarily saying the API needs to have 2 parameters. Names of
all the valid combinations could be a good alternative, and it might
lead to very readable code is the names are something like A3_MINUS_A6.
For Arduino Zero, it seems such a list would need to define 152 names,
if all the signals mentioned in the datasheet are available to the user.





David Mellis

unread,
Jan 29, 2015, 5:58:07 PM1/29/15
to Arduino Developer's List
A3_MINUS_A6 is relatively readable, but it seems inconsistent with the typical Arduino approach to pin numbers. Also, it would make it hard (impossible?) to specify the pins at run-time. 

With a function like analogRead(pinA, pinB, gain), could we do compile time checking on the arguments if they're compile-time constants (e.g. using __builtin_constant_p())? Is there any advantage to a syntax like A3_MINUS_A6 besides compile-time checking for valid pin combinations?

On Thu, Jan 29, 2015 at 5:48 PM, Paul Stoffregen <pa...@pjrc.com> wrote:

The SAM D21 chip used on the upcoming Arduino Zero appears to have tremendous flexibility in analog differential input configuration. The datasheet says the negative input can be one of 8 analog signals and the positive can be any of the 20 analog signals, plus some internal signals.

I'm not necessarily saying the API needs to have 2 parameters. Names of all the valid combinations could be a good alternative, and it might lead to very readable code is the names are something like A3_MINUS_A6.  For Arduino Zero, it seems such a list would need to define 152 names, if all the signals mentioned in the datasheet are available to the user.
--
You received this message because you are subscribed to the Google Groups "Developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to developers+unsubscribe@arduino.cc.

Pierce Nichols

unread,
Jan 29, 2015, 6:20:53 PM1/29/15
to Arduino Developers
Could we use an enum that covers all of the valid combinations? That
would enlist the compiler in preventing the use of invalid
combinations and allow run-time specification of pin combinations.

-p
>> email to developers+...@arduino.cc.
>
>
> --
> You received this message because you are subscribed to the Google Groups
> "Developers" group.
> To unsubscribe from this group and stop receiving emails from it, send an

Victor Aprea

unread,
Jan 29, 2015, 6:35:19 PM1/29/15
to Arduino Developers

Is changing this at runtime a useful feature or realistic scenario? I have trouble envisioning a real world application that would do this.

Regards,
Vic

To unsubscribe from this group and stop receiving emails from it, send an email to developers+...@arduino.cc.

Paul Stoffregen

unread,
Jan 29, 2015, 6:39:56 PM1/29/15
to devel...@arduino.cc
On 01/29/2015 03:35 PM, Victor Aprea wrote:

Is changing this at runtime a useful feature or realistic scenario? I have trouble envisioning a real world application that would do this.


Libraries, like Firmata, might wish to generate the input parameters at runtime, rather than having to hard-code (and maintain over time) a fixed list for every board they support.

Pierce Nichols

unread,
Jan 29, 2015, 6:45:28 PM1/29/15
to Arduino Developers
Yes -- iterating over a bunch of analog inputs is a perfectly
reasonable thing to do.

-p
>>> email to developers+...@arduino.cc.
>>
>>
>> --
>> You received this message because you are subscribed to the Google Groups
>> "Developers" group.
>> To unsubscribe from this group and stop receiving emails from it, send an
>> email to developers+...@arduino.cc.
>
> --
> You received this message because you are subscribed to the Google Groups
> "Developers" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to developers+...@arduino.cc.



Tom Igoe

unread,
Jan 29, 2015, 6:49:07 PM1/29/15
to Arduino Developers

Throwing a precedent into the mix: analogWrite. Can take any pin number, but only works when the number's legit. Maybe Dave's original proposal could work similarly? Both could use a clear error message from the preprocessor...

Matthijs Kooijman

unread,
Jan 30, 2015, 5:37:55 AM1/30/15
to devel...@arduino.cc
Hi folks,

> A3_MINUS_A6 is relatively readable, but it seems inconsistent with the
> typical Arduino approach to pin numbers. Also, it would make it hard
> (impossible?) to specify the pins at run-time.
Indeed, libraries might want to compose these differential settings from
two separate pin numbers. You could perhaps do something similar to
the SPISettings class, where you can pass arbitrary settings to build a
settings value (and probably add a valid() method to check for
validity). Additionally, constants like A3_MINUS_A6 could be defined for
all valid combinations for when you do have fixed pin numbers.

> With a function like analogRead(pinA, pinB, gain), could we do compile time
> checking on the arguments if they're compile-time constants (e.g. using
> __builtin_constant_p())?
I'm not sure how to implement this. You can do stuff usihng
__builtin_constant_p(), but I'm not sure how to actually throw an error
when invalid values are passed (possibly by using a function marked as
deprecated, but I'm afraid that you'll always get the error then..)

> Is there any advantage to a syntax like A3_MINUS_A6 besides
> compile-time checking for valid pin combinations?
Not that I can see.


While we're on the subject of improving the analog API, there's one
issue of ambiguity that we might be able to fix (if we introduce a new
API and keep the old API around for compatibility only). Right now,
analogRead supports both analog channel numbers (0-5) and pin numbers
(A0 - A5, which map to 14-19 on the standard variant). This seems nice,
but requires that analog pins have "high" numbers. E.g. if pin A0 would
have pin number 5, then analogRead(5) is ambiguous - do you mean channel
5, or pin 5 == channel 0?).

This shows up on the leonardo, which has analog input capabilities on
some of the digital pins. Since most of those digital pins have numbers
that fall within the range of analog channels, this causes ambiguity.
This is now worked around by giving these pins _two_ pin numbers, e.g.
pin D4 is available as pin 4 as well as pin 24 (the latter is named A6).

See https://github.com/arduino/Arduino/issues/1883 for details.

This issue seems unfixable right now, but perhaps we can resolve this in
a new API. The most obvious way would be to always just use pin numbers
and never analog channel numbers. This doesn't reduce expressiveness at
at all.

Additionally, it might be good to use a "pin" type (which would be a
struct with just a byte inside). This pin type can then be used to
explicitely require a pin number to be passed, and perhaps also use
function overloading to provide alternative versions. Having implicit
conversion to and from uint8_t would probably allow existing code to
also just pass plain numbers and allow the Ax (and perhaps new Dx
constants) to use the pin type.

Not sure if this is something to completely tackle now, but I want to
keep this in mind when thinking about the new API.

Gr.

Matthijs
signature.asc

David Mellis

unread,
Jan 30, 2015, 12:35:43 PM1/30/15
to Arduino Developer's List
Maybe we can separate some of these issues? While it would be nice to provide compile-time warning if you try to read from or write to a pin that doesn't exist and to resolve the analog input numbering issue (pins vs. channels), neither of those is necessary to supporting differential analog input. My suggestion would be to simply add something like analogRead(pinA, pinB, gain) or analogReadDifferential(pinA, pinB, gain), and leave the other issues to be addressed independently. What do you all think?

--
You received this message because you are subscribed to the Google Groups "Developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to developers+...@arduino.cc.

Matthijs Kooijman

unread,
Jan 30, 2015, 1:30:25 PM1/30/15
to devel...@arduino.cc
Hey David,

> Maybe we can separate some of these issues?
Yes, that's probably wise.

> My suggestion would be to simply add something like analogRead(pinA,
> pinB, gain) or analogReadDifferential(pinA, pinB, gain), and leave the
> other issues to be addressed independently. What do you all think?
I raised the pin number issue, since _if_ we're going to introduce a new
function, that could be a moment to change the interface since there is
no backward compatibility to take into account yet. However, in this
case I wonder if it would help to have an improved differential API if
the plain API isn't replaced as well...

Gr.

Matthijs
signature.asc

David Mellis

unread,
Jan 30, 2015, 2:22:49 PM1/30/15
to Arduino Developer's List
My feeling is that it would probably make sense if the new function allows people to refer to the analog inputs in the same way as the existing analogRead() -- that is, using either pin numbers or channels. It's clunky to have both, but it seems more confusing for analogRead() and the differential analog read to behave differently.


Gr.

Matthijs

Rich Obermeyer

unread,
Jan 30, 2015, 3:04:21 PM1/30/15
to devel...@arduino.cc
David,
It would seem to me the differential mode is a setup issue not an analog read issue.
From a previous life I used the pin config to describe the pin as an analog pin to measure with possible ground reference as well as any gain setup.  Which is consistent with setting requiring the primary analog pin now with an optional ground reference and optional gain.  With no ground reference provided its just single ended.
So setup the pin as a differential with the gain setting.  Then use the existing analogread() with the plus PIN number.  The ground pin has already been defined in setup.   It's compatible with today's code and error checking is easy enough.

IMHO the naming A4_MINUS_A8 for a pin name would be impossible to remember and to the rookie makes no sense.  You would have to have a cheat sheet to tell you what each pin name is and way to many choices.


Paul Stoffregen

unread,
Jan 30, 2015, 4:57:00 PM1/30/15
to devel...@arduino.cc
On 01/30/2015 10:30 AM, Matthijs Kooijman wrote:
> I raised the pin number issue, since _if_ we're going to introduce a
> new function, that could be a moment to change the interface since
> there is no backward compatibility to take into account yet. However,
> in this case I wonder if it would help to have an improved
> differential API if the plain API isn't replaced as well... Gr. Matthijs

Difficult as consensus can be, the API is probably the easiest part to
change.

Updating the silk screen printing on so many boards will be harder.

Hardest of all will probably be updating so many places on the Arduino
and 3rd party websites!

Chris Lilley

unread,
Jan 30, 2015, 6:52:00 PM1/30/15
to Pierce Nichols, Arduino Developers
Hello Pierce,

Thursday, January 29, 2015, 6:12:13 PM, you wrote:

> The value of pinB is entirely a function of pinA, so I don't see any
> value to including it.

Agreed that they are dependent. I can think of one value from
including it, though:

> We should think about ways to make sure that calling it against
> combinations of pins and gain that the hardware chosen does not
> support will break at compile time.

Putting both pins into the call makes it easier to check that the
combination is valid. Its also useful documentation.

--
Best regards,
Chris Lilley, Technical Director, W3C Interaction Domain

Vignesh Subramaniam

unread,
Mar 11, 2016, 5:28:27 AM3/11/16
to Developers
Has this been implemented already? I'm trying to read a differential analog input on arduino. Any pointers on how to go about it?
Reply all
Reply to author
Forward
0 new messages