altos: Use 5V reference data to correct accelerometer measurements.
authorKeith Packard <keithp@keithp.com>
Sat, 15 Jan 2011 20:26:53 +0000 (12:26 -0800)
committerKeith Packard <keithp@keithp.com>
Mon, 17 Jan 2011 05:52:50 +0000 (21:52 -0800)
When the 3.3V and 5V values shift relative to each other (usually due
to changes in power consumption), the measured acceleration will
appear to shift. This patch converts the 3.3V referenced acceleration
value into a 5V referenced acceleration, eliminating this error.

Signed-off-by: Keith Packard <keithp@keithp.com>
src/ao_flight.c

index 637acd5..81aecad 100644 (file)
@@ -158,6 +158,90 @@ ao_flight(void)
                        ao_adc = &ao_adc_ring[ao_flight_adc];
                        ao_flight_tick = ao_adc->tick;
                        ao_raw_accel = ao_adc->accel;
+#if HAS_ACCEL_REF
+                       /*
+                        * Ok, the math here is a bit tricky.
+                        *
+                        * ao_raw_accel:  ADC output for acceleration
+                        * ao_accel_ref:  ADC output for the 5V reference.
+                        * ao_cook_accel: Corrected acceleration value
+                        * Vcc:           3.3V supply to the CC1111
+                        * Vac:           5V supply to the accelerometer
+                        * accel:         input voltage to accelerometer ADC pin
+                        * ref:           input voltage to 5V reference ADC pin
+                        *
+                        *
+                        * Measured acceleration is ratiometric to Vcc:
+                        *
+                        *     ao_raw_accel   accel
+                        *     ------------ = -----
+                        *        32767        Vcc
+                        *
+                        * Measured 5v reference is also ratiometric to Vcc:
+                        *
+                        *     ao_accel_ref    ref
+                        *     ------------ = -----
+                        *        32767        Vcc
+                        *
+                        *
+                        *      ao_accel_ref = 32767 * (ref / Vcc)
+                        *
+                        * Acceleration is measured ratiometric to the 5V supply,
+                        * so what we want is:
+                        *
+                        *      ao_cook_accel    accel
+                        *      ------------- =  -----
+                        *          32767         ref
+                        *
+                        *
+                        *                      accel    Vcc
+                        *                    = ----- *  ---
+                        *                       Vcc     ref
+                        *
+                        *                      ao_raw_accel       32767
+                        *                    = ------------ *  ------------
+                        *                         32737        ao_accel_ref
+                        *
+                        * Multiply through by 32767:
+                        *
+                        *                      ao_raw_accel * 32767
+                        *      ao_cook_accel = --------------------
+                        *                          ao_accel_ref
+                        *
+                        * Now, the tricky part. Getting this to compile efficiently
+                        * and keeping all of the values in-range.
+                        *
+                        * First off, we need to use a shift of 16 instead of * 32767 as SDCC
+                        * does the obvious optimizations for byte-granularity shifts:
+                        *
+                        *      ao_cook_accel = (ao_raw_accel << 16) / ao_accel_ref
+                        *
+                        * Next, lets check our input ranges:
+                        *
+                        *      0 <= ao_raw_accel <= 0x7fff             (singled ended ADC conversion)
+                        *      0x7000 <= ao_accel_ref <= 0x7fff        (the 5V ref value is close to 0x7fff)
+                        *
+                        * Plugging in our input ranges, we get an output range of 0 - 0x12490,
+                        * which is 17 bits. That won't work. If we take the accel ref and shift
+                        * by a bit, we'll change its range:
+                        *
+                        *      0xe000 <= ao_accel_ref<<1 <= 0xfffe
+                        *
+                        *      ao_cook_accel = (ao_raw_accel << 16) / (ao_accel_ref << 1)
+                        *
+                        * Now the output range is 0 - 0x9248, which nicely fits in 16 bits. It
+                        * is, however, one bit too large for our signed computations. So, we
+                        * take the result and shift that by a bit:
+                        *
+                        *      ao_cook_accel = ((ao_raw_accel << 16) / (ao_accel_ref << 1)) >> 1
+                        *
+                        * This finally creates an output range of 0 - 0x4924. As the ADC only
+                        * provides 11 bits of data, we haven't actually lost any precision,
+                        * just dropped a bit of noise off the low end.
+                        */
+                       ao_raw_accel = (uint16_t) ((((uint32_t) ao_raw_accel << 16) / (ao_accel_ref[ao_flight_adc] << 1))) >> 1;
+                       ao_adc->accel = ao_raw_accel;
+#endif
                        ao_raw_pres = ao_adc->pres;
 
                        ao_flight_accel -= ao_flight_accel >> 4;