Why do the units matter at all? The ADC just measures a ratio to VREF. All the math inside can be dimensionless and you don't have to worry about scale until the very end when printing to the user.

Starting with a 10 or 12 bit ADC, you may have enough fractional capacity in a 16 bit variable to do useful work. I did this on my reverb effect box (XMEGA based). 16 bits was a bit tight, making some overflow tricky to handle (implementing saturating arithmetic rather than wrapping overflow; I was also using signed arithmetic to simplify summing/filtering operations). Some operations were done with a 24 or 32 bit accumulator; you won't gain anything in C using 24 bits (there is a (u)int24_t primitive available in avr-gcc, but it's basically cast to 32-bit for all arithmetic operations) so just stick to 32 when you need more.

And yes indeed, you can do fixed point by implicitly multiplying everything by some [bit] offset; be very careful to remove the excess bits when multiplying, and make sure arithmetic is done with enough bits to fit the result before shifting: to C, it is absolutely natural to multiply two 16-bit variables into a 16-bit result, as absurd as that is mathematically. In particular, byte shifts are basically free (GCC is awful at optimizing them, but again, unless you're doing ASM, it's just what you have to live with).

So, for example the product of two 8.8 fixed point variables, as 8.8 result, must be written:

`int16_t fracmul(int16_t a, int16_t b) {`

return ((int32_t)a * b) >> 8;

}

but you should also check for overflow to implement saturating arithmetic.

Like uh, let me see,

`int16_t fracmul(int16_t a, int16_t b) {`

int32_t p = (int32_t)a * b;

if (p > 0x00ffffff) return 0x7fff;

if (p < -0x00ffffff) return 0x8000;

return p >> 8;

}

There are better ways to do it (the compiler may realize the low bytes don't need to be checked, but low optimization will do the full four-byte comparison verbatim) but this should at least be correct. (I'm quite proud of the few-CPU-cycle solution I found, in my assembler code, but it's special-casey of course and not something you can do in GCC alone.)

Tim