The math for this is trivially easy if you can do algebra.
First, the datasheet formula:
ADC is the independent variable since that's the result generated by the hardware (between 0 and 1023 inclusive). So now there's two unknowns left, VIN and VREF.
The normal way is to take a fixed VREF with VIN as the dependent variable that is solved for. This arrangement creates a direct linear relationship between the dependent and independent variables, so has less error when converted to a real unit.
But there is no reason that you can't use the formula the other way. A fixed VIN (the 1.1V internal bandgap) can be used to calculate a variable VREF (AVCC). The internal reference is convenient to use since it requires no extra hardware, but has poor tolerance (only about 10%). If greater accuracy is needed, a separate voltage reference chip can be used on one of the analog inputs to do the same job. The only thing you would need to do is adjust the formula.
A second option for measuring the power voltage is to use a fixed reference for VREF, and use a voltage divider to scale down the voltage to within the ADC's input range. This is necessary if you are measuring a different power rail than what is supplying AVCC, but is inconvenient for measuring the chip's own power supply since it needs extra hardware.
adwsystems:
I have NEVER seen a compiler that is free to swap to assignment statements at its own choosing.
Welcome to 1970. Compilers have included that sort of optimization at least since then.
Microsoft C included that sort of optimization 27 years ago.
That you "have never seen a compiler that is free to swap assignment statements" is hardly proof that such things do not exist. I have never seen a koala bear yet I have no doubt of their existence.
Then I expect the low byte assignment to be executed first followed by the high byte.
Your expectations are irrelevant.
Then there is an issue with the compiler.
Take it up with the C++ standards committee.
Will the compiler still rearrange all three lines as it sees fit?
Of course.
We still haven't addresses rounded vs truncated.
Addressed what? Round. Truncate. It's your application. Do whichever is appropriate. You have working code for each.
ADCL and ADCH are declared volatile. The compiler is not allowed to change the order of access to volatile variables (since that's the definition of a side effect), but it is free to move other, non-volatile statements around as it sees fit, even putting them between the two volatile accesses.
Jiggy-Ninja:
The compiler is not allowed to change the order of access to volatile variables...
This is, quite simply, BULLSHIT. That exact problem has been discussed ad nauseum on AVR Freaks and other places. That is precisely the reason the avr-gcc compiler has a very specific read/write byte order for pointers to volatile 16 bit data.
I skimmed through both of those pages, and I don't see how they are relevant to what I posted (which was a response to adwsystems's post). Those pages deal with how the compiler accesses data through a 16-bit-referencing pointer. They don't seem to have anything to do with the situation of manually accessing the H and L bytes like this:
low = ADCL;
high = ADCH;
Because ADCL and ADCH are accessed as volatile references__*__, the compiler is not permitted to optimize away access to them or reorder them relative to each other. Since L is written before H, the compiler must order than that way. Accessing the ADC register as an entire word is a completely separate issue.
The compiler is not supposed to re-order accesses to volatiles. CB's first reference says specifically:
For every read from a volatile variable by the abstract machine, the actual machine must load from the memory address corresponding to that variable. Also, each read may return a different value. For every write to a volatile variable by the abstract machine, the actual machine must store to the corresponding address. Otherwise, the address should not be accessed (with some exceptions) and also accesses to volatiles should not be reordered (with some exceptions).
and -
The C standard is unambiguous that volatile side effects must not move past sequence points
There are some issues that DO cause problems:
Expecting an expression like "Val = (ADCH<<8) + ADCL;" to imply a "read" order of the registers.
Expecting a 16bit read to read 8bit address space in a particular order (the subject CB's second link.)
volatile access CAN be re-ordered WRT no-volatile accesses (explained in first link.)
Volatile wouldn't be USEFUL if volatile accesses could be re-ordered, and we wouldn't be able to access IO registers with C at all (we could with functions written in assembler: external, inline, or intrinsic. That was the solution prior to volatile.)
As for re-ordering code in general, that happens all the time, and we expect it. The easiest example is probably
for (i=0; i < 10; i++) {
int x = constant;
foo[i] = x;
}
We hope and expect that the "x=constant" part will be moved outside of the loop, since its results don't depend on i and it doesn't need to be recalculated all i times. In the old days we might have had to move it ourselves, but since the compiler does it for us, we can write less cryptic code...
adwsystems:
But it should be noted that this line is most suspect because of the ADC, where the bytes must be read in a specific order.
The avr-gcc folks have made that guarantee. The compiler always produces the correct code for pointers to 16 bit volatile data. Follow the second link for details.