ATTINY85 Vcc measurement skews at higher Vcc voltages?

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

I apologize for what might be a very basic question, but I'm burned out on searching without finding an answer, so I'm hoping someone here knows and is willing to help me--

 

I have a simple battery powered project using an ATTINY85 10PU, it's running on 3 x AAA's which can provide anywhere between 2.9V and 5.1V (the upper end being fresh Energizer "Lithium" AAA's).

 

What I'm finding is that using the bandgap method to read Vcc I see accurate measurements while Vcc is in low 3's, but the measurements seem to skew high when my Vcc gets into the 4.6-5.1V range.

 

For example--Using a 3.3V bench supply my ATTINY and DMM both agree that they see 3300mV.  Using a 5V bench supply my DMM will show ~5000mV but the ATTINY will measure something like 6800mV.  The ATTINY measured value gets closer to a DMM reading as Vcc drops, but even at typical battery voltage levels like 4.0-4.4V the ATTINY is giving results which are too high.

 

My specific use case is as a low battery indicator, so realistically as long as my measurement of Vcc at 2.9-3.1V is accurate I am fine, but it would be nice to have accurate values as the battery pack drops.

 

Below I've pasted the code I'm using to getVcc, even without my flaky multiple reading / averaging code the values are still skewed.  This value gets recorded and later the ATTINY becomes an I2C Slave and has various data, including this Vcc measurement, read from it by another device.

 

Thank you for any help or advice

 

uint16_t getVcc() {
  // Read 1.1V reference against AVcc
  // set the reference to Vcc and the measurement to the internal 1.1V reference
  ADMUX = _BV(MUX3) | _BV(MUX2);
  delay(2); // Wait for Vref to settle

  uint16_t result = 0;
 
  // Do 32 conversions, skip the first 16 and average the last 16

  for (int x = 0; x < 32; x++){
    ADCSRA |= _BV(ADSC); // Start conversion
    while (bit_is_set(ADCSRA,ADSC)); // measuring
    if (x > 15){
      result += (int16_t)((int16_t)(ADC - result) / 2);
    }
    else{
      result = ADC;
    }
 
  uint16_t voltage = 1125300 / result; // Calculate Vcc (in mV); 1125300 = 1.1*1023*1000

  return voltage;

}

 

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

Something seems quite strange...A higher ADC reading gives a higher result (good), but you divide a constant by the result...so a higher ADC reading gives a lower voltage (bad).

 

If using the bandgap, all of your actual pin voltages to the adc must be less than the lowest possible bandgap tolerance (I believe 1V min ....1.0 to 1.2V is the tolerance for many, but not all, of the AVR's ).

 

Why not just add up the final 16 ADC readings,  then afterwards divide by a constant to scale as you wish?...if you only want to set a trip point (not readout voltage), you don't actually have to divide at all, just scale the trip point.

For example, if your divider provides 1V to ADC at 5Vsupply & you want to trip at 2.0Vsupply, for 2V, you'd meas about 372 counts per reading (if Vref=1.1 nominal)...372*16=5952 count trip point

If instead you divide the total by 298 you'd read X.X volts (45 means 4.5 volts)  

5952/298=19.9 (round up)=20==>2.0 volts

When in the dark remember-the future looks brighter than ever.

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

As mentioned, do you know if the problem is with the conversion, or the transfer function?  In the end, I always need to get back to the basics:  List the value of the reference and the ADC counts, and go from there.  Pencil and paper; spreadsheet; ...

 

I'll dig out some of my apps that do this in a bit. Generally, I pre-calculate the nominal and trip points in ADC counts, and don't worry about the transformation to volts.  Remember to do your battery measurement under max load -- when idle batteries tend to recover to near nominal.

 

Remember that the BG can be about +/- 10% from nominal.  In apps where it is important I do a one-time cal during ISP/test.

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.

Last Edited: Mon. Feb 12, 2018 - 03:03 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Below is the code fragment used to periodically process the ADC results [averaged and in "worknum"], and light red/green appropriately.  I allowed the comments to remain, as they illustrate the situation(s) when changing cal V and similar.  ee_bg_cal is the measured ADC counts for the BG at the calibration Vcc level.

 

			// See chart of ADC counts vs. bandgap level in EEPROM declarations.
			//
			//	Short answer for a trip at 1.0V, it is 2.5x the cal counts
			worknum = ee_bg_cal;	// nominal 225 at 5V Vcc and 1.2VBG

#if 0
//			worknum *= 5;
//			worknum /= 2;			// x2.5 => 562; nominal 2.0V trip
			worknum *= 9;
			worknum /= 4;			// x2.25 => 512; nominal 2.2V trip
			// ([green] LEDs don't fire at 2.0V or below)
#endif
			// Rev. g  Adjust the above calculation for a 3.3V calibration voltage.
			//	Nominal is 341 counts.
			//		341 * 1.5 => 511.5; close enough. ;)
//			worknum *= 3;
//			worknum /= 2;			// x1.5 => 511.5; nominal 2.2V trip
// REvise again for 2.3V trip -- motor doesn't start...
//	...can that for now -- increase PWM duty proportional to adc_batt to get a
//	nominal ~2V PWM
//	Do both -- 2.3V is ~490 counts.  x1.436.  Find a ratio...
//	341 * 13 / 9 => 492; close enough
			worknum *= 13;
			worknum /= 9;			// x1.44 => 492.5; nominal 2.3V trip

			if (worknum > adc_batt)
				{
				OUTPUT_GREEN = OUTPUT_ON;
				}
			else
				{
				OUTPUT_RED = OUTPUT_ON;
				}

Now let's go find my table...


BG V	1.2	1.1	1.0

VCC		ADC COUNTS
5		246	225	205

VCC		ADC COUNTS		Rev. G adjust to 3.3V Vcc during cal
3.3		372	341	310

3.2		384	352	320
3.1		396	363	330
3		409	375	341
2.9		423	388	353
2.8		438	402	365
2.7		455	417	379
2.6		472	433	393
2.5		491	450	409
2.4		512	469	426
2.3		534	489	445
2.2		558	512	465
2.1		585	536	487
2		614	563	512

2.0V is 2x alkaline in series, used for Vcc, at 1.0V/cell.

The batteries are well on the highway to hell, but should be good
for another 100 hours at 5mA.

==========

I'll have to try to remember an app where I actually reported the voltage.

 

Oh, yeah -- Let me find the "do it badly" thread...

https://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

avrcandies wrote:
If using the bandgap, all of your actual pin voltages to the adc must be less than the lowest possible bandgap tolerance

Look again at the OP...

hmbemis wrote:
// Read 1.1V reference against AVcc // set the reference to Vcc and the measurement to the internal 1.1V reference ADMUX = _BV(MUX3) | _BV(MUX2);

That has nothing to do with your "limit" of BG voltage.

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
uint16_t voltage = 1125300 / result;

will the compiler do a 32 bit calc here ? (I'm not sure so I would help it) 

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

I think it would be forced to do 32 bits as the dividend is long.  And OP is getting usable results at nominal.

 

[so, how are these results being read?]

 

Can we see the ADC setup, and know the AVR clock speed?  Too fast an ADC clock?

 

 

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

Look again at the OP...

 

hmbemis wrote:

// Read 1.1V reference against AVcc // set the reference to Vcc and the measurement to the internal 1.1V reference ADMUX = _BV(MUX3) | _BV(MUX2);

 

That has nothing to do with your "limit" of BG voltage.

Ah, I see what is being done, not bad.   Won't allow you to measure low input voltages (down to zero)  & set's up a non-linear (1/x) relationship.  But it does avoid the need for a divider--good for battery.

 

When in the dark remember-the future looks brighter than ever.

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

avrcandies wrote:
Won't allow you to measure low input voltages (down to zero)

???  What are you going on about?

 

What AVR model, or any microcontroller make/model, has an onboard charge pump that lets it run down to 0V and also generate a bandgap of ~1V?

 

I'd suggest a trip to the "do it badly" thread.

 

Anyway, it will be interesting to find out OP's problem cause.  Looking forward to seeing the numbers.

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

???  What are you going on about?

I'm just saying normally one measures with respect to a fixed, known reference...which gives the number a generally preferred linear relation to the measurement from zero on up, to the max allowed ADC number. 

Measuring the known ref against an unknown voltage gives a nonlinear relation, that can only go as low as the level of the ref (giving the max adc number), rather than the often desired zero.  Of course no processor today runs close to zero volts, so your observation is well taken.     

When in the dark remember-the future looks brighter than ever.