ATmega32A, an external AREF, and differential inputs to the ADC

Go To Last Post
9 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

All:

 

I thought this would be a relatively straightforward research project this morning, but I've been unable to find some answers to what I thought were three pretty simple questions.

 

My design uses the ATmega32A, but the questions would apply to any AVR mega with support for differential ADC input.

 

1. The conversion formula is: ADC = (Vin * 1024) / Vref.

Table 26-3 tells us that there are four possible selections for the reference (of which three are valid): the full AVCC, the internal 2.56V reference (which many have suggested is not that good), and an external reference. The the internal 2.56 reference make sense and allows the hardware a fairly easy path to the binary division. I'm unsure how the division works when VCC is within the acceptable range.

 

If the external VREF is chosen and is 2.56 V, then presumably we gain the extra precision of conversion (assuming the external reference is "better" than the internal). So far, so good.

 

But what happens when the reference is not 2.56 V? I haven't seen any register that is set to the new denominator value (and I can't imagine that there is actually a hardware divider built-in for a generic division). Or, does this mean that only 2.56 V may be used as an external reference?

 

BTW: I've wondered if the solution to this is that the binary output of the ADC must itself be converted later by application-level code which "knows" the value of the Vref.

 

2. Differential ADC inputs.

And this brings me to the closely related question. I want to measure the (small) voltage drop across a shunt resistor. One of the advantages of the differential input is that, given an appropriate VREF value, the effective resolution of the conversion can be increased to handle closely the range of the desired differential inputs.

 

In the final design, there likely will be an opamp to buffer and scale the shunt resistor voltage. But the main question remains: what happens, say, if I select a 1.5 V reference, then scale the shunt voltage to something close to, but lower, than 1.5 V? I'm still left with question #1: how to "tell" the formula that Vref is not 2.56 V and is 1.5 V instead (as an example).

 

3. A schematic that actually shows an ATmegaXxx connected to a differential source.

This really surprised me. I found lots of articles telling me that I can perform differential ADC but not one schematic, or even code sample, that actually shows this being done.

 

The complete datasheet has not been helpful to answer these questions either.

 

So, anyone out there actually use an external reference that is not 2.56 V? Did you need downstream code to "complete" the value conversion? And, does anyone have a link to a (working!) schematic that uses differential ADC?

 

Thanks much!

Dave

 

 

 

The SiliconHacker  Plano, TX  www.siliconhacker.com

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Phoenix710 wrote:
Did you need downstream code to "complete" the value conversion?

I'm quite lost on this whole conversion-division thing.  The ADC (when appropriately configured) gives a 10-bit value of counts, that represent the level of the input signal to the reference.  There may be edge conditions, but let's just say that when the signal is 99/9% or more of the reference voltage, a full count 1023 0x3ff is returned.  If the voltage to be converted is less than the reference voltage tnen the ADC counts will be proportionally less.

 

So, do "they" say why the internal reference is bad?  Yes, the nominal value can vary by some percent.  Do the drift factors with respect to temperature, supply, and aging fit the needs of your application?  If so, then use it.  If not, use something else.

 

 

 

 

 

 

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Perhaps start with prior discussions:

http://www.avrfreaks.net/forum/d...

http://www.avrfreaks.net/comment... where I asked your same question

theusch wrote:
Anyone successfully using differential? On which AVR model(s)? Any snippets to share?

 

http://www.avrfreaks.net/forum/m...

http://www.avrfreaks.net/forum/c...

http://www.avrfreaks.net/forum/a...

...

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You can use any reference value that falls within the specs. It is common to perform scaling on the result in code.
As for differential mode, the table of mux settings outlines the gain and input options. You need to ensure your input voltages are within the specs. It should be easy to write some test code to set the required mode and output the adc results. Have some pots to set input voltages and have a play. It should start to make sense.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I've done pretty much this exact thing for a personal project, albeit on a Tiny85 using an LT1787 current sense amplifier and an external 2.5v reference.

 

Sadly the schematic and PCB were designed in gEDA a while ago and I now have no way to read them, having long-since switched to Kicad.

 

The code is still around though. The pin assignments and notes are here:

 In: Battery volts ADC0 1	8 Vcc
 In:     Reference ADC3 2	7 GPIO  Out: Tx Data
 In:     Sense Amp ADC2	3	6 GPIO  In/Out: 1-wire
		    Gnd	4	5 AREF  In: Reference

PB0 5	AREF, In	External reference (2.5V)
PB1 6	GPIO, In/Out	1-wire bus for DS18B20
PB2 7	GPIO  Out	Serial Tx data output
PB3 2	ADC3, In	External reference (diff -ve input)
PB4 3	ADC2, In	Current sense amplifier output
PB5 1	ADC0, In	Battery voltage

Shunt resistance of 0.00075 ohms
	100A = 0.07500 volts
	 50A = 0.03750 volts
	 10A = 0.00750 volts
	  1A = 0.00075 volts
	0.1A = 0.000075 volts

LT1787 sense amplifier with bias of 2.5v, External Rout 120K (48x gain)
	 69A = 2.50 volts	<--- Saturation
	 50A = 1.80 volts
	 10A = 0.3600 volts
       ~3.5A = 0.1250 volts	<--- 1x/20x threshold.
	  1A = 0.0360 volts
	0.1A = 0.0036 volts
       0.01A = 0.00036 volts	<--- Approx min resolvable at 20x gain


Current Measurement
~~~~~~~~~~~~~~~~~~~
AtTiny85 ADC configured in differential mode with AREF/ADC3 connected to
the 2.5v reference and the sense amplifier's output connnected to ADC2.
Usable range is +/-69A with ~50 mA resolution at 1x gain (currents between
3.5 amps and 69 amps. Below ~3.5A, MCU will switch input gain to 20x to
resolve lower currents with better granularity (in theory, resolution
will be about 2.5 mA but noise will become a significant factor, so
we'll assume 10 mA is our best resolvable current.

Note that the external reference is connected to both AREF and ADC3. The sense amplifier is connected to ADC2.

 

The relevant bit of the code is:

/* Current is differential ADC2/3, with external reference */
#define	BATT_AMPS_OFFSET_1X	((1u << REFS0) | 0x04)
#define	BATT_AMPS_OFFSET_20X	((1u << REFS0) | 0x05)
#define	BATT_AMPS_1X		((1u << REFS0) | 0x06)
#define	BATT_AMPS_20X		((1u << REFS0) | 0x07)
#define BATT_NSAMPLES           16

static int32_t
battery_current(void)
{
	uint16_t adcval;
	uint8_t binipr;
	int32_t rv;

	/*
	 * Start with a bipolar measurement at the highest gain
	 */
	adcval = adc_sample(1, BATT_AMPS_20X, 1 << BIN);

	/*
	 * If the result is negative, flip the polarity
	 */
	binipr = (adcval & (1u << 9)) ? (1u << IPR) : 0;

	/*
	 * Take a unipolar reading at 20x gain
	 */
	adcval = adc_sample(BATT_NSAMPLES, BATT_AMPS_20X, binipr);
	if (adcval <= (1008 * BATT_NSAMPLES)) {
		/*
		 * Looks like we're within range at 20x.
		 */
		rv = (int32_t)adcval;
	} else {
		/*
		 * Assume we're saturated. Take another reading at 1x gain
		 */
		adcval = adc_sample(BATT_NSAMPLES, BATT_AMPS_1X, binipr);
		rv = (int32_t)adcval * 20;
	}

	/*
	 * Compute final signed result.
	 * Proper scaling will be done by the master unit - we don't
	 * do any complex power-hungry calculations here.
	 */
	if (binipr)
		rv = -rv;

	return (rv);
}

Hope this is of some help.

 

Steve

 

Maverick Embedded Technologies Ltd. Home of the wAVR: WiFi AVR ISP/PDI/uPDI Programmer. http://www.maverick-embedded.co.uk/

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Theusch:

 

Thank much for the info and links (and I'm replying to both your posts here). From some of the other replies, and my own read of the datasheet, it appears that the ADC knows only to produce a 0x000 output value for 0 input and a 0x3FF  output (10 bits) for full input (single ended) as compared to VREF. So, that means the app code must apply its own conversion to the binary value returned by the ADC before a meaningful value can be obtained.

 

So, for example, if VREF is 2.56 volts, then an output of 0x3FF means that 2.56 V has been applied to that input (in a perfect system).

 

If, rather, VREF is VCC (assumed here to be 4.7 V as an arbitrary value as an example), then the same 2.56 V input would produce a value of ~0x22D. The ATmega48A has no way of "knowing" that VCC is 4.7, only that it has been told that VCC is used as VREF. Therefore, the app must "complete" the conversion to a meaningful value. This is what I assumed, and at least some of the replies below appear to validate that assumption.

 

The SiliconHacker  Plano, TX  www.siliconhacker.com

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Steve:

 

Thanks for the code; I'm still reviewing it.

 

I was thrown by your comment "Note that the external reference is connected to both AREF and ADC3." 

 

Looking at the LT1787 sense amp typical application schematic on the datasheet: it isn't obvious to me why you needed to route AREF also to ADC3. I would have thought that inputting the reference into AREF would have been enough.

 

But, I want to spend some more time with your code to see if I can understand this better - before pestering you any more. :-)

 

Thanks!

Dave

The SiliconHacker  Plano, TX  www.siliconhacker.com

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Phoenix710 wrote:

I was thrown by your comment "Note that the external reference is connected to both AREF and ADC3." 

That's so it can measure a bipolar signal in differential mode. Note that the LT1787's output is biased by the 2.5v reference so that the first measurement in bipolar mode determines the sign of the current. You could, if you wanted to, just use that result but it has only 9 bits of resolution. So in this application the sign is used to flip the ADCSRB.IPR bit so the following unipolar measurements get the full 10 bits of resolution. In conjunction with the 20x gain, this gave a decent dynamic range and in practice was able to resolve down to a few mA after filtering.

 

The device was monitoring the 12v leisure battery in a camper van, so current polarity would vary depending on whether the battery was being charged or discharged.

 

Steve

 

Maverick Embedded Technologies Ltd. Home of the wAVR: WiFi AVR ISP/PDI/uPDI Programmer. http://www.maverick-embedded.co.uk/

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I think some reading about how the adc works will clarify your thinking.
https://en.m.wikipedia.org/wiki/Successive_approximation_ADC
Adcs need a reference and the result is a ratio to that reference.
Then for differential mode, read up on op-amps.