Arduino Forum

Using Arduino => Microcontrollers => Topic started by: LastSamurai on Mar 02, 2014, 09:39 pm

Title: Measuring VCC with an Attiny 85
Post by: LastSamurai on 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!
Title: Re: Measuring VCC with an Attiny 85
Post by: Jiggy-Ninja on 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?
Title: Re: Measuring VCC with an Attiny 85
Post by: Coding Badly on 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;`

Title: Re: Measuring VCC with an Attiny 85
Post by: smithy on Mar 03, 2014, 05:31 pm
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.
Title: Re: Measuring VCC with an Attiny 85
Post by: fungus on 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;`
Title: Re: Measuring VCC with an Attiny 85
Post by: smithy on 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.
Title: Re: Measuring VCC with an Attiny 85
Post by: Coding Badly on 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.
Title: Re: Measuring VCC with an Attiny 85
Post by: fungus on 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...)

Title: Re: Measuring VCC with an Attiny 85
Post by: Coding Badly on 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.
Title: Re: Measuring VCC with an Attiny 85
Post by: fungus on 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 :)
Title: Re: Measuring VCC with an Attiny 85
Post by: Coding Badly on Mar 08, 2014, 12:38 am

That's  8)!
Title: Re: Measuring VCC with an Attiny 85
Post by: fungus on 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).
Title: Re: Measuring VCC with an Attiny 85
Post by: Jiggy-Ninja on 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;}`
Title: Re: Measuring VCC with an Attiny 85
Post by: cjdelphi on Mar 09, 2014, 04:37 am
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
Title: Re: Measuring VCC with an Attiny 85
Post by: Coding Badly on Mar 09, 2014, 07:17 am

When faced with conflicting information it is always best to check the datasheet.  According to the folks at Atmel the correct value is 1024.
Title: Re: Measuring VCC with an Attiny 85
Post by: Tom Carpenter on Mar 09, 2014, 01:56 pm
Its 1024 as there are 1024 levels.

Essentially it comes down to the way these ADCs work, they measure fence posts not fences.
There are 1024 levels, so for a 3V reference, each level DeltaV=3/1024=2.9296875 [mV]
So this means:

 Level Voltage [mV] 0 0 1 2.93 2 5.86 ... ... 1022 2994.14 1023 2997.07 (1024) (3000)

Notice how regardless of starting at 0 that it is actually the number '1024' which represents the full reference voltage. Now clearly you can never get that reading, but that doesn't change the fact the calculation has to use 1024.
Title: Re: Measuring VCC with an Attiny 85
Post by: smithy on Mar 09, 2014, 09:31 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.

You are right, the compiler is allowed to reorder but as fas as i know not the logical meaning behind it.
If you set one register and then another this should always result into the same order just the structure surrounding theese 2  commands may change. An immediate execution is not guaranteed, but it will never change register 2 first because you manifested to set register 1 first in your code. If no order is guaranteed, coding would be nonsense.

Title: Re: Measuring VCC with an Attiny 85
Post by: Coding Badly on Mar 09, 2014, 10:31 pm
You are right, the compiler is allowed to reorder but as fas as i know not the logical meaning behind it.

Code: [Select]
`a = 1 + 2;`
Code: [Select]
`a = 2 + 1;`

The compiler sees those two lines of code as equivalent.  Why?  Because addition is commutative making the semantics identical.  As long as the semantics are not changed, the compiler can do what ever it wants.

Code: [Select]
`  uint8_t low  = ADCL;  uint8_t high = ADCH;  long result = (high<<8) | low;`
Code: [Select]
`  uint8_t high = ADCH;  uint8_t low  = ADCL;  long result = (high<<8) | low;`
Code: [Select]
`  long result = (ADCH<<8) | ADCL;`
Code: [Select]
`  long result = ADCL | (ADCH<<8);`

The semantics are all the same.  ADCH is read, as requested.  ADCL is read, as requested.  The value from ADCH is shifted, as requested.  The two values are combined using a logical-or, as requested.  Why are the semantics all the same?  Because logical-or is commutative and the compiler has no sense that ADCH and ADCL have a relationship.
Title: Re: Measuring VCC with an Attiny 85
Post by: smithy on Mar 10, 2014, 12:05 am
Well after some research, i found out arduino uses bitwise & for setting a variable which is commutative.

But then i don´t understand why its explained that way in the atmel datasheets and what is ADC then ?

If it´s a macro what code does it replace ?

For the latter part :
Quote

the order doesn´t matter since you are using OR and shift one value 8 to the left.
Example :
Quote

01 1 1 001 1
11111100 00000000
|
11111100 01 1 1 001 1

There will always be zeros in the first 8 bit so it has no effect with OR.

Title: Re: Measuring VCC with an Attiny 85
Post by: fungus on Mar 10, 2014, 02:23 am

But then i don´t understand why its explained that way in the atmel datasheets and what is ADC then ?

Code: [Select]
`#define _MMIO_WORD(mem_addr) (*(volatile uint16_t *)(mem_addr))#define _SFR_MEM16(mem_addr) _MMIO_WORD(mem_addr)...#define ADC     _SFR_MEM16(0x04)`
Title: Re: Measuring VCC with an Attiny 85
Post by: Tom Carpenter on Mar 10, 2014, 10:20 am
http://www.atmel.com/Images/doc8006.pdf

Notice how in the C code examples, they never directly use the ***H and ***L registers.

There is also another problem which the current analogRead() code fails to consider which is interrupts. If an interrupt occurs after you have read ADCL but before you have read ADCH, then you can loose the conversion result. The same is true for using the ADC register name:
Code: [Select]
`uint16_t read;uint8_t oldSREG = SREG;cli();read = ADC;SREG = oldSREG;`
Title: Re: Measuring VCC with an Attiny 85
Post by: smithy on Mar 10, 2014, 10:07 pm
As far as i understand ADC defines a 16bit memory address which is read out directly in the processor in 1 step ?
But then this will only work on chips who have ADCL directly after ADCH registers ?

Quote
uint8_t oldSREG = SREG;
cli();
SREG = oldSREG;

Why are you saving the status register ?
I guess theres an sei(); at the end of this and ADC is setting ADSC to 1.

Thanks for your explanations so far !
Title: Re: Measuring VCC with an Attiny 85
Post by: LastSamurai on Mar 10, 2014, 10:24 pm
Waho, so much feedback ;)

So this should work?!

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 `
So this sets the reference Voltage. I dont really understand this but if it works...

Code: [Select]
`  uint8_t result = ADC; //In one read?  result = 1126400L / result; // Calculate Vcc (in mV); 1125300 = 1.1*1024*1000 // 1024 not 1023?  return result; // Vcc in millivolts}`

What do I need the SREG for? And do I have to use cli() / sei() here (Thats enables/disables intterupts, right?!)?

I am wondering if noone has done this successfully before?! Seems like a "standard" thing to do with mobile battery powered projects.
Title: Re: Measuring VCC with an Attiny 85
Post by: smithy on Mar 10, 2014, 11:05 pm
Have a look at your Datasheet : http://www.atmel.com/Images/Atmel-2586-AVR-8-bit-Microcontroller-ATtiny25-ATtiny45-ATtiny85_Datasheet.pdf

When i understood this right, it should look like :
Quote
ADMUX = _BV(MUX3) | _BV(MUX2);  // selecting bandgap reference of 1.1V  (on page 135)
delay(2); // Wait for bandgap reference to stabilize
uint8_t oldSREG = SREG;   // saving status register
cli();   //deactivate Interrupts
uint16_t result = ADC; //Needs to be 16 bit long ! This only reads the two registers in one command
SREG = oldSREG;  // setting status register to "old" values
sei(); // Activate Interrupts
result = 1126400L / result; // Calculate Vcc (in mV); 1126400L = 1.1*1024*1000 // 1024 should be right
return result; // Vcc in millivolts
}
Title: Re: Measuring VCC with an Attiny 85
Post by: Tom Carpenter on Mar 11, 2014, 12:23 am
no, you don't need the sei() instruction, that is the whole purpose of backing up the SREG (the register which the cli() and sei() instructions act upon).

Imagine interrupts were disabled before calling that function, it will come in, disable them again, and at the end if you call sei() it will enable the interrupts - but they shouldn't be because they were disabled before the function.
By restoring the SREG to what it was before the cli() instruction, you ensure that the state of the interrupt bit is the same as when the function was called.

The 'ADC' register is 16bit and is known to the compiler to be such and the way in which it should be accessed is known to the compiler. It will actually execute two consecutive 'lds' or 'in' instructions, placing the result of each into two consecutive working registers (equivalent to performing the bit shift (<<8) ). Essentially it converts it into the same ADCL then ADCH read instructions be ensures that they are never rearranged.
If you read the datasheet you will see that it is actually in certain circumstances legal to read just the ADCH register and not the ADCL one - for example if you want an 8bit conversion result, rather than doing what everyone seems to do which is:
or
Title: Re: Measuring VCC with an Attiny 85
Post by: Coding Badly on Mar 11, 2014, 01:07 am
There is also another problem which the current analogRead() code fails to consider which is interrupts. If an interrupt occurs after you have read ADCL but before you have read ADCH, then you can loose the conversion result.

The read is not timed.  Why would it matter if an interrupt occurs between reading L and H?
Title: Re: Measuring VCC with an Attiny 85
Post by: Tom Carpenter on Mar 11, 2014, 08:30 pm
As far as I recall, all the 16bit registers share the same 8bit temp register, in which case if you access another 16bit register in an interrupt you are screwed. I could be wrong about that, maybe it was just for the timer modules, if so, ignore the interrupt thing.
Title: Re: Measuring VCC with an Attiny 85
Post by: fungus on Mar 11, 2014, 09:25 pm

As far as I recall, all the 16bit registers share the same 8bit temp register, in which case if you access another 16bit register in an interrupt you are screwed. I could be wrong about that, maybe it was just for the timer modules, if so, ignore the interrupt thing.

Never heard that before...

(and it's the sort of thing the datasheet would mention!)
Title: Re: Measuring VCC with an Attiny 85
Post by: Tom Carpenter on Mar 11, 2014, 09:39 pm
It was just for timer modules, never mind.

"Note that the 16-bit Timer (Timer1) has only one temporary register that is
shared between all it's 16-bit register pairs" [http://www.atmel.com/Images/doc1493.pdf]

But also, if the interrupt that occurs happens to access the same registers, then you need to ensure atomic operation on the reads.
Title: Re: Measuring VCC with an Attiny 85
Post by: Coding Badly on Mar 11, 2014, 10:06 pm
So this should work?!

Looks reasonable to me but I have not tried running it.

Quote
What do I need the SREG for?

You don't.

Quote
And do I have to use cli() / sei() here (Thats enables/disables intterupts, right?!)?

Not necessary.

Quote
I am wondering if noone has done this successfully before?!

http://forum.arduino.cc/index.php/topic,38119.0.html
http://forum.arduino.cc/index.php?topic=133907.msg1007492#msg1007492
(Google will get you the rest of the list)
Title: Re: Measuring VCC with an Attiny 85
Post by: fungus on Mar 12, 2014, 08:54 am

It was just for timer modules, never mind.

No, it's the ADC as well.

If you have an interrupt that accesses ADCH/ADCL then you can't use ADCH/ADCL in the main code without disabling the interrupts. That's obvious...but not usually a problem.