ATTiny85 reference voltage changing with Vcc?

Hi all. Just getting into this world of microcontollers, and so far the ATTiny85 datasheet and some forum browsing have steered me through the problems I've hit, but I'm stumped at the moment.

I want to run some ATTiny85 projects off of a pair of 18650 batteries in series (so 7.4-8.4V max) that are brought down to 3V via a buck converter. What I'm trying to test now is monitoring the battery voltage to have the device power down if it gets too low.

I'm set up to use the internal 1.1 Vref and am using a 20kohm and 2kohm divider to feed in a usable voltage to the ADC. However, the result I'm getting is about a third of what I expect. I'm also using the 5V supply on an Arduino Uno to power an LCD screen so I can see the results, and if I run the ATTiny off of that 5V supply instead of the 3V at the buck converter output, then I get an output that is fits with the 1.0-1.2 range for the reference voltage.

Multimeter measured battery voltage 7.71V
Vcc = 3V, ADC ranges from 233-241, average 2.78V
Vcc = 5V, ADC ranges from 678-690, average 8.11V

Any ideas as to why this is happening? The datasheet indicates some variance due to the value of Vcc, but nothing like this.

float battVaverage = 0;


void checkVoltageSetup(){
  ADMUX = 0;
  ADMUX |= (1 << REFS1) |    // Sets ref. voltage to 1.1 bandgap voltage
           (1 << MUX1);     // use ADC2 for input (PB4)
  ADCSRA |= (1 << ADPS2);   // set prescaler to 16

}

void checkVoltage(){
  PORTB &= ~(_BV(PORTB4));   // turn off pull up resistor for PB4
  ADCSRA |= (1 << ADEN);     // Enable ADC 
  delayMicroseconds(70); // allow bandgap voltage to settle. P.165 ATTiny85 datasheet
  float battVmeasure [10];
  float battVmax = 0;
  float battVmin = 100;
  float battVsum = 0;
  long adcvalue = 0;

  for(int i=0; i<10; i++){
    
    ADCSRA |= (1 << ADSC);         // start ADC measurement
    while (ADCSRA & (1 << ADSC) ); // wait till conversion complete 
    
    adcvalue = ADC;
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("ADC ");
    lcd.print(adcvalue);
    delay(500);

    battVmeasure[i] = adcvalue * 1.1 / 1024 * 11; // Convert ADC value to voltage. 
    if (battVmeasure[i]>battVmax){
      battVmax = battVmeasure[i];
    }
    else if (battVmeasure[i]<battVmin){
      battVmin = battVmeasure[i];
    }
    battVsum += battVmeasure[i];
  }
  battVaverage = (battVsum - battVmax - battVmin)/8;
  PORTB |= (1<<PORTB4);     // turn on pull up resistor for PB4
  ADCSRA &= ~(_BV(ADEN));   // turn off ADC
 
}

Are there any capacitor in your actual circuit? (other than U1??) If not you will need then across U4 Vcc- gnd buck in and output. Physical wire placement matters.

Suggestion:

Consider running your batteries in parallel. You will not need your buck converter.

  • Batteries must be the same rating and general age
  • Verify your LCD will operate on the reduced voltage.

1. Are you using internal 8 MHz clock or 16 MHz clock (boosted by PLL from 8 MHz) for the ATtiny85?

2. Are you using AVR Porgrammer or Digispark Dev Board for the programming of ATtiny85?

3. What is the cut-off voltage for your Battery?

The above is about to be good!

Vbatt Vcc  Vadc                         ADC value
7.71  5V   (7.71/22000)x2000 = 0.70V    (1023/1.1) x 0.7 = 651

The above is no good!

Vbatt Vcc    Vadc                         ADC value
7.71  3V     (7.71/22000)x2000 = 0.70V    (1023/1.1) x 0.7 = 651

I am investigaing the Vcc = 3V case.

Thanks for the pointers, but I don't think any of those are behind my ADC issue. I'll check on the capacitors, though.

Thanks for looking into it.

I'm running it at 1MHz to reduce power consumption.

I'm using an Arduino Uno as the ISP to program a bare ATtiny85. I'm using the ATTinyCore at http://drazzy.com/package_drazzy.com_index.json

I don't have a datasheet for the batteries, but I'd like to stop them each at around 3V. So that should be 6V total if I can pair similar ones together. I am buying them at the same time, but there's a bit of variation in these. I anticipate building in a margin of error, but haven't decided on how much yet.

Thanks for answering to the questions.

If you use a voltage divider of 15k over 2k that will drop 8.4V to about 0.99V or an ADC count of around 920 with VREF of 1.1V

True, but I have 20k and 10k handy and don't feel like constructing 15k. Once I've established that I can get the ADC working, I can optimize.

The ATTiny85 requires bypass capacitors to work. I don't know what crazy things it will do without. I forgot to add one to a breadboard and it did weird things.

A buck converter creates a lot of electrical noise and voltage spikes, if one gets coupled into the A/D input it will definitely give you spurious readings.

For troubleshooting I would:

  1. run on one battery, 3.7 v
  2. disconnect the buck converter
  3. Add some caps.
  4. run the ATTiny at normal (ie not low power) mode.

Test the A/D function.

1 Like

@bro_giant
Will you let me know the current values of the fuse bytes of your ATtiny85 (EF, HF, LF)? How do you perform fuse bit read/write operations with your ATtiny85 -- using AVR Programmer or ROM Programmer?

Your problem is Vcc = 3V for which yo do not get the expected counts from the ADC? To me, it is ok for Vcc = 5V as I have mentioned in post#3 though your manual calculation was incorrect.

I'll give that a try when I get a minute. I did test it this morning with a decoupling capacitor between Vcc and GND while still running through the buck converter. The ADC results were about the same as they were before adding the capacitor.

Yes, that is the problem I described in the original post. The reading is within the expected accuracy when powering with 5V from the Arduino, but gives about 1/3 of the expected result when powering from the battery at 3V through the buck converter.

The values listed were not hand calculations, but averaged results of how it is calculating voltage at the battery, not at the ADC pin.

I'll check on the fuses when I'm back at the desk.

Then how it is 8.11V (see below from your post #1?; you should check itmanually by doing back calculation) instead of 0.73V which is the correct value for the given correct ADC value.

I have discovered your Vcc = 3V problem which I will publish after experimental confirmation.

@bro_giant

Here is the experimental results summary, and I think your problem is solved. I leave it on you to explore -- can you operate your system with Vcc = 3.0/3.3V (regulated), Vbatt-cut = 3.0V (unregulated), and Vrref = 1.1V INTERNAL)? You can't!!!

Vcc   Vref   ADCin   LCD Readings   Vbatt-cut    Fault indication
3.3V  3.3V   1.7 V   214, 1.7 V     3.0 V        No
3.3V  3.3V   2.3 V   2BB  2.3V      3.0 V        No
3.3V  3.3V   2.9 V   38B, 2.9V      3.0 V        No
3.3V  3.3V   3.0+ V  ------------   3.0 V        Battery is Down, 
                                                 LED is On

This is my sketch compiled on ATTinyCore (with fuse bytes of ATtiny85 (EF = 0xFF, HF = 0xDF, LF = 0x62) and 1 MHz internal clock source). I would recoommend to use Vbatt = 2x3.7 V, Vbatt-cut = 3.0 V, zener regulated Vcc = 3.3V, and Vref = DEFAULT (but use 3.3 in the Formula). (As you are using ATTinyCore, you can avoid using register level codes.)

#include<Wire.h>
#include<LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);
#define LED 1    //PB1 = MISO pin onboard LED

void setup()
{
  Wire.begin();
  pinMode(LED, OUTPUT);
  analogReference(DEFAULT); //use 3.3V in the equation 
  //analogReference(INTERNAL); //1.1V

  //-------system reset indication-----
  for (int i = 0; i < 3; i++)
  {
    digitalWrite(LED, HIGH);
    delay(200);
    digitalWrite(LED, LOW);
    delay(200);
  }
  lcd.begin();//or lcd.init();
  lcd.backlight();
  lcd.setCursor(0, 0);  //DPos (0-15), LPos (0-1)
  //----------------------
  Wire.beginTransmission(0x27);
  byte busStatus = Wire.endTransmission();
  if (busStatus != 0)
  {
    //lcd.print("I2CLCD is not found!");
    while (true)
    {
      digitalWrite(LED, HIGH);  //error message
      delay(100);
      digitalWrite(LED, LOW);
      delay(100);
    }
    while (1); //wait for ever
  }
  lcd.print("I2CLCD is found!");
  delay(2000);
  lcd.clear();
}

void loop()
{
  lcd.setCursor(0, 0);  //Display position, Display Line Top
  //------------------
  unsigned y = analogRead(A2);
  lcd.print(y, HEX);
  //-----------------------
  lcd.setCursor(0, 1);  //Display position, Display Line Bottom
  float testVolt = (3.3 / 1023) * y;
  lcd.print(testVolt, 1);
  lcd.print(" V");
  //---------------
  if (testVolt >= 3.0)
  {
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Battery is Down.");
    digitalWrite(LED, HIGH);  //fault indication
    while (true); //wait for ever
  }
}

If you look in my posted code, you'll see that it is calculating the combined battery voltage based on the reference voltage and the voltage divider. At the ADC results I was getting, yes the voltage at the ADC should be between .728 and .741, but if you multiply both of those by 11 to account for the 20k/2k voltage divider, then you get a range of 8.01-8.15. My code takes 10 samples, tosses out the high and low and divides the rest by 8 to get an average, which at that time with those batteries was pretty consistently 8.11V.

I did test running it without the converter on a single 3.7V battery, and it's giving the same values as running off of the 5V supply from the Arduino, so the fault is definitely with the buck converter and not the microcontroller. I also tested it with decoupling capacitors at Vcc and the input and output of the buck converter, but that didn't help things.

So, it looks like I'll be going forward without using the converters in these projects. Thanks for all the assistance.

I may be a bit late in replying but the gem from Amtel is worth a read.

Measure VCC/Battery Voltage Without Using I/O Pin on tinyAVR and megaAVR (microchip.com)

Thanks. This ended up being the method I used after I decided to ditch the buck converter.

I read the document but I wasn´t able to understand how to obtain Vbg and RESadc on the equation below:

image

I guess Vbg is the 1.1V of the Attiny85, but how can I get the value of RESadc? Is there a way to "read" Attiny´s VCC pin?

Would any of you mind to explain it?

Yes, the Vbg is the 1.1 bandgap voltage. RESadc is the result of the analog to digital converter. When you measure Vbg with the ADC, it will give you a result less than 1024.

If the ADC is used to measure the voltage on a pin, the measured voltage is equal to the reference voltage multiplied by the ADC result and divided by the max 10 bit ADC value of 1024.

Vm = Vr * RESadc / 1024

Here, the measured voltage is the bandgap and the reference is Vcc

Vbg = Vcc * RESadc / 1024

A little rearranging gets you to the formula you posted.

It may be worth noting that the bandgap voltage can range from 1.0 - 1.2 for different attiny85s. I tested against my multimeter and changed Vbg to calibrate.

If it's helpful, here is the code I ended up using:

void checkVoltageSetup(){
  ADMUX = 0;                              //clear ADMUX which sets Vcc as reference voltage
  ADMUX |= (1 << MUX3) | (1 << MUX2);     // use 1.1 bandgbap voltage for input
  ADCSRA |= (1 << ADPS2);                 // set prescaler to 16 since using 1MHz clock speed
}

void checkVoltage(){
  ADCSRA |= (1 << ADEN);      // Enable ADC 
  delayMicroseconds(70);      // allow bandgap voltage to settle. P.165 ATTiny85 datasheet
  int samples = 4;
  float battVmeasure [samples];
  float battVsum = 0;
  long adcvalue = 0;

  for(int i=0; i<samples; i++){
    
    ADCSRA |= (1 << ADSC);         // start ADC measurement
    while (ADCSRA & (1 << ADSC) ); // wait till conversion complete 
    adcvalue = ADC;

    // Convert ADC value to voltage. Bandgap voltage should be between 
    // 1.0 and 1.2 and needs experimentation for each processor to get the right value
    battVmeasure[i] =  1.09 * 1024 / adcvalue; 
    battVsum += battVmeasure[i];      // Add each voltage as loop progresses
  }
  battVaverage = battVsum / samples;  // Average all the voltage measurements
  PORTB |= (1<<PORTB4);               // turn on pull up resistor for PB4
  ADCSRA &= ~(1<<ADEN);               // turn off ADC
}
1 Like

Thank you! :slightly_smiling_face: