Go Down

Topic: Measuring VCC with an Attiny 85 (Read 20938 times)previous topic - next topic

LastSamurai

Mar 02, 2014, 09:39 pm
Hey guys, I am using an Attiny 85 with a 3V battery. I do wanna measure the current voltage to know when the battery is empty.
I did some google search and found this:

Code: [Select]
`long readVcc() {   // 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  ADCSRA |= _BV(ADSC); // Start conversion  while (bit_is_set(ADCSRA,ADSC)); // measuring   uint8_t low  = ADCL; // must read ADCL first - it then locks ADCH    uint8_t high = ADCH; // unlocks both   long result = (high<<8) | low;   result = 1125300L / result; // Calculate Vcc (in mV); 1125300 = 1.1*1023*1000  return result; // Vcc in millivolts}`

Sadly that doesn't give me any (right) values. I do get ~55-250 which cant be true (right?!). Does anyone know where the error is? Or is there a better methode?
Thanks!

Jiggy-Ninja

#1
Mar 02, 2014, 11:01 pm
Working backwards, with the values you're getting from the division, result must be between 20,500 and 4,500. For an ADC result, this is absurd.

Instead of printing the result of the division, print the raw count value you're reading from the ADC. If it's not between 0 and 1023, you have a problem. (and if it is, that's even weirder).

However, you have only posted a snippet. It's good practice to post your FULL code in case the problem lies somewhere else.

How are you displaying this value?

#2
Mar 03, 2014, 01:11 am

Code: [Select]
`  result = 1125300L / result; // Calculate Vcc (in mV); 1125300 = 1.1*1023*1000`

1023 is not correct.  The correct value is 1024.

1.1 is rarely the correct value.  The value is between 1.0 and 1.2.  It is specific to each processor.  If you want readVcc to be accurate you will have to determine the correct value for your processor.

Code: [Select]
`  uint8_t low  = ADCL; // must read ADCL first - it then locks ADCH    uint8_t high = ADCH; // unlocks both  long result = (high<<8) | low;`

smithy

#3
Mar 03, 2014, 05:31 pmLast Edit: Mar 03, 2014, 05:54 pm by smithy Reason: 1
Try to go with this :

Code: [Select]
`long Vrail_leo() {    // Read 1.1V reference against AVcc  // set the reference to Vcc and the measurement to the internal 1.1V reference  #if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)    ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);  #elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)    ADMUX = _BV(MUX5) | _BV(MUX0);  #elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)    ADMUX = _BV(MUX3) | _BV(MUX2);  #else    ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);  #endif    delay(2); // Wait for Vref to settle  ADCSRA |= _BV(ADSC); // Start conversion  while (bit_is_set(ADCSRA,ADSC)); // measuring   uint8_t low  = ADCL; // must read ADCL first - it then locks ADCH    uint8_t high = ADCH; // unlocks both   long result = (high<<8) | low;   result = 1125300L / result; // Calculate Vcc (in mV); 1125300 = 1.1*1023*1000                               return result;  }`

Oh sry when comparing to yours, it seems it should do the same on your 85. I used that for 328p, 32u4 and it worked fine. I think the settings should be right, are you testing it in a standalone sketch ?

Quote

No it´s how an analog read with 10 bits is done with all avrs i have messed with.
It´s all in the Datasheets, even a differential read is done the same way.

fungus

#4
Mar 03, 2014, 05:52 pm

Quote

No it´s how an analog read with 10 bits is done with all avrs i have messed with.

The clever part is you don't have to remember anything. Just do
Code: [Select]
`unsigned int n = ADC;`

smithy

#5
Mar 03, 2014, 08:35 pm
Well, if you do ADC or analogRead the analog read is done the same way as triggering the hardware registers themselves, even if you use an automatic trigger function via registers  (haven´t found any different info in the datasheets i read) so it should be exactly the same.

#6
Mar 03, 2014, 09:41 pm
...so it should be exactly the same.

Unless the compiler reorders the two lines of code.  Which it is allowed to do.  Splitting the read is a bad idea.  Just use ADC.

fungus

#7
Mar 07, 2014, 09:49 pm

...so it should be exactly the same.

Unless the compiler reorders the two lines of code.  Which it is allowed to do.

Not if they're declared "volatile" (which I hope they are...)

#8
Mar 07, 2014, 10:17 pm
Not if they're declared "volatile" (which I hope they are...)

A subject which has been discussed, debated, and argued ad nauseam on not just AVR Freaks but wherever C++ developers congregate.  The conclusion is always the same.  volatile does not prevent the compiler from reordering.  I have no idea if it applies elsewhere but in the AVR-GCC world a "memory barrier" is used to guarantee ordering; which is most certainly not present in smithy's code.  And is also not necessary because the solution to the problem is trivial: just use ADC.

fungus

#9
Mar 07, 2014, 11:32 pm
I just stuck this code into my firefly jar BIOS...

It can now read the battery voltage and display it by coded flashes of the flies

#10

That's  !

fungus

#11
Mar 08, 2014, 10:14 pm
It seems to work, more or less.

Accuracy isn't very good.  :-(  I know the 1.1V is only a "nominal" but it reads very low on the chip I'm using. Still, it's a free feature so we mustn't complain.

I made the jar display the voltage to two decimal places. It outputs each digit as a series of pulses with a delay in between digits. A zero is a long pulse.

Here's one I made last May - it's been running 24/7 since then on the same button cell. and it's still going strong(!)

There's a magnet in the butterfly "key" to switch it on/off via. a hidden reed switch. It also has a mercury switch inside so you can shake the jar to make them more active (they slow down over time to save battery).

We're making some more in a workshop next week so I was just revising the software. I replaced the mercury switch with capacitive touch sensing (touch the lid to make them more active) and now it has voltage display as well! (turn it off and hold the key in place for a few seconds extra to see the voltage).

Jiggy-Ninja

#12
Mar 09, 2014, 03:41 am

...so it should be exactly the same.

Unless the compiler reorders the two lines of code.  Which it is allowed to do.  Splitting the read is a bad idea.  Just use ADC.

Arduino core's analogRead() function splits the read, and it doesn't seem to have problems.
Code: [Select]
`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;}`

cjdelphi

#13
Mar 09, 2014, 04:37 amLast Edit: Mar 09, 2014, 07:16 am by Coding Badly Reason: 1
Code: [Select]
`  result = 1125300L / result; // Calculate Vcc (in mV); 1125300 = 1.1*1023*1000`
1023 is not correct.  The correct value is 1024.

0 - 1023
1 - 1024

The tutirial div's by 1023 ...  if a value of 0 is returned then it's div 1023 not 1024?

Moderator edit: quote trimmed