aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--arch/avr32dd20.c60
-rw-r--r--arch/avr32dd20.h15
-rw-r--r--fsm/adc.c31
-rw-r--r--hw/thefreeman/avr32dd20-devkit/hwdef.c13
-rw-r--r--hw/thefreeman/avr32dd20-devkit/hwdef.h12
5 files changed, 97 insertions, 34 deletions
diff --git a/arch/avr32dd20.c b/arch/avr32dd20.c
index 45923a8..998e9f6 100644
--- a/arch/avr32dd20.c
+++ b/arch/avr32dd20.c
@@ -52,19 +52,19 @@ inline void mcu_set_admux_therm() {
ADC0.SAMPCTRL = 32;
// set single-ended or differential
// set resolution to 12 bits
- // set left- or right-adjust (right)
+ // set left- or right-adjust
// set free-running mode or not (yes)
ADC0.CTRLA = ADC_CONVMODE_SINGLEENDED_gc
| ADC_RESSEL_12BIT_gc
- //| ADC_LEFTADJ_bm // not in temperature mode
+ | ADC_LEFTADJ_bm
| ADC_FREERUN_bm;
// set number of samples (requires adjustment in formula too)
ADC0.CTRLB = ADC_SAMPNUM_NONE_gc;
- // TODO: accumulate more samples for more resolution
- // (and probably set the prescale faster too)
- //ADC0.CTRLB = ADC_SAMPNUM_ACC16_gc; // 16 samples per result
+ // accumulate more samples for more resolution
+ ADC0.CTRLB = ADC_SAMPNUM_ACC16_gc; // 16 samples per result
// set a clock prescaler
- ADC0.CTRLC = ADC_PRESC_DIV64_gc;
+ //ADC0.CTRLC = ADC_PRESC_DIV64_gc; // use this when no accumulation
+ ADC0.CTRLC = ADC_PRESC_DIV4_gc; // measure faster when oversampling
// enable the ADC
ADC0.CTRLA |= ADC_ENABLE_bm;
// actually start measuring (happens in another function)
@@ -90,7 +90,7 @@ inline void mcu_set_admux_voltage() {
// set number of samples
ADC0.CTRLB = ADC_SAMPNUM_ACC16_gc; // 16 samples per result
// set a clock prescaler
- ADC0.CTRLC = ADC_PRESC_DIV16_gc; // not too fast, not too slow
+ ADC0.CTRLC = ADC_PRESC_DIV4_gc; // measure faster when oversampling
// select the positive ADC input with MUXPOS
#ifdef USE_VOLTAGE_DIVIDER // external voltage divider
// ADC input pin / Vref
@@ -113,11 +113,8 @@ inline void mcu_adc_sleep_mode() {
}
inline void mcu_adc_start_measurement() {
- // FIXME: enable this after getting ADC stuff fixed
- #if 0
ADC0.INTCTRL |= ADC_RESRDY_bm; // enable interrupt
ADC0.COMMAND |= ADC_STCONV_bm; // actually start measuring
- #endif
}
/*
@@ -135,24 +132,35 @@ inline void mcu_adc_vect_clear() {
ADC0.INTFLAGS = ADC_RESRDY_bm; // clear the interrupt
}
-inline uint16_t mcu_adc_result_temp() {
- // FIXME: better math, higher precision
- // Use the factory calibrated values in SIGROW.TEMPSENSE0 and
- // SIGROW.TEMPSENSE1 to calculate a temperature reading in Kelvin, then
- // left-align it.
- int8_t sigrow_offset = SIGROW.TEMPSENSE1; // Read signed value from signature row
- uint8_t sigrow_gain = SIGROW.TEMPSENSE0; // Read unsigned value from signature row
- uint32_t temp = ADC0.RES - sigrow_offset;
- temp *= sigrow_gain; // Result might overflow 16 bit variable (10bit+8bit)
- temp += 0x80; // Add 1/2 to get correct rounding on division below
- //temp >>= 8; // Divide result to get Kelvin
- //return temp << 6; // left align it
- return temp >> 2; // left-aligned uint16_t
+inline uint16_t mcu_adc_result() {
+ // value is 12-bit left-aligned + 16x oversampling = 16 bits total
+ return ADC0.RES;
}
-inline uint16_t mcu_adc_result_volts() {
- // voltage is 12-bit right-aligned + 16x oversampling = 16 bits total
- return ADC0.RES;
+inline uint16_t mcu_vdd_raw2cooked(uint16_t measurement) {
+ // In : 65535 * (Vbat / 10) / 1.024V
+ // Out: 65535 * (Vbat / 10) / 1.024V
+ // This MCU's native format is already correct
+ return measurement;
+}
+
+inline uint16_t mcu_temp_raw2cooked(uint16_t measurement) {
+ // convert raw ADC values to calibrated temperature
+ // In: ADC raw temperature (16-bit, or 12-bit left-aligned)
+ // Out: Kelvin << 6
+ // Precision: 1/64th Kelvin (but noisy)
+ // AVR DD datasheet section 33.3.3.8
+ uint16_t sigrow_slope = SIGROW.TEMPSENSE0; // factory calibration data
+ uint16_t sigrow_offset = SIGROW.TEMPSENSE1; // 12-bit value
+ //const uint32_t scaling_factor = 4096; // use top 12 bits of ADC data
+ //uint32_t temp = sigrow_offset - (measurement >> 4);
+ const uint32_t scaling_factor = 65536; // use all 16 bits of ADC data
+ uint32_t temp = (sigrow_offset << 4) - measurement;
+ temp *= sigrow_slope; // 24-bit result
+ temp += scaling_factor / 8; // Add 1/8th K to get correct rounding on later divisions
+ //temp = temp >> 6; // change (K << 12) to (K << 6)
+ temp = temp >> 10; // change (K << 16) to (K << 6)
+ return temp; // left-aligned uint16_t, 0 to 1023.98 Kelvin
}
inline uint8_t mcu_adc_lsb() {
diff --git a/arch/avr32dd20.h b/arch/avr32dd20.h
index 7a6b8f1..3ebb05a 100644
--- a/arch/avr32dd20.h
+++ b/arch/avr32dd20.h
@@ -50,12 +50,21 @@ inline void mcu_adc_off();
#define ADC_vect ADC0_RESRDY_vect
inline void mcu_adc_vect_clear();
+// both readings are left-aligned
+inline uint16_t mcu_adc_result();
+
// read ADC differently for temperature and voltage
-#define MCU_ADC_RESULT_PER_TYPE
+//#define MCU_ADC_RESULT_PER_TYPE
+//inline uint16_t mcu_adc_result_temp();
+//inline uint16_t mcu_adc_result_volts();
-inline uint16_t mcu_adc_result_temp();
+// return (centiVolts << 6), range 0 to 10.24V
+#define voltage_raw2cooked mcu_vdd_raw2cooked
+inline uint16_t mcu_vdd_raw2cooked(uint16_t measurement);
-inline uint16_t mcu_adc_result_volts();
+// return (temp in Kelvin << 6)
+#define temp_raw2cooked mcu_temp_raw2cooked
+inline uint16_t mcu_temp_raw2cooked(uint16_t measurement);
inline uint8_t mcu_adc_lsb();
diff --git a/fsm/adc.c b/fsm/adc.c
index 13a76b6..73e282d 100644
--- a/fsm/adc.c
+++ b/fsm/adc.c
@@ -30,6 +30,7 @@ void adc_voltage_mode() {
}
+#if 0
#ifdef USE_VOLTAGE_DIVIDER
static inline uint8_t calc_voltage_divider(uint16_t value) {
// use 9.7 fixed-point to get sufficient precision
@@ -44,6 +45,7 @@ static inline uint8_t calc_voltage_divider(uint16_t value) {
return result;
}
#endif
+#endif
// Each full cycle runs ~2X per second with just voltage enabled,
// or ~1X per second with voltage and temperature.
@@ -67,7 +69,6 @@ ISR(ADC_vect) {
// update the latest value
#ifdef MCU_ADC_RESULT_PER_TYPE
- // thermal, convert ADC reading to left-aligned Kelvin
if (channel) m = mcu_adc_result_temp();
else m = mcu_adc_result_volts();
#else
@@ -208,6 +209,19 @@ static inline void ADC_voltage_handler() {
#endif
else measurement = adc_smooth[0];
+ // convert raw ADC value to FSM voltage units: (V * 100) << 6
+ // 0 .. 65535 = 0.0V .. 10.24V
+ measurement = voltage_raw2cooked(measurement) / (10 << 5);
+
+ // calculate actual voltage: volts * 10
+ // TODO: should be (volts * 40) for extra precision
+ voltage = (measurement + VOLTAGE_FUDGE_FACTOR
+ #ifdef USE_VOLTAGE_CORRECTION
+ + VOLT_CORR - 7
+ #endif
+ ) >> 1;
+
+ #if 0
// values stair-step between intervals of 64, with random variations
// of 1 or 2 in either direction, so if we chop off the last 6 bits
// it'll flap between N and N-1... but if we add half an interval,
@@ -231,6 +245,7 @@ static inline void ADC_voltage_handler() {
#endif
) >> 1;
#endif
+ #endif
// if low, callback EV_voltage_low / EV_voltage_critical
// (but only if it has been more than N seconds since last call)
@@ -290,8 +305,14 @@ static inline void ADC_temperature_handler() {
}
// latest 16-bit ADC reading
- uint16_t measurement = adc_smooth[1];
+ // convert raw ADC value to Kelvin << 6
+ // 0 .. 65535 = 0 K .. 1024 K
+ uint16_t measurement = temp_raw2cooked(adc_smooth[1]);
+ // (Kelvin << 6) to Celsius
+ temperature = (measurement>>6) + THERM_CAL_OFFSET + (int16_t)TH_CAL - 275;
+
+ #if 0
// values stair-step between intervals of 64, with random variations
// of 1 or 2 in either direction, so if we chop off the last 6 bits
// it'll flap between N and N-1... but if we add half an interval,
@@ -313,6 +334,12 @@ static inline void ADC_temperature_handler() {
// external sensor
temperature = EXTERN_TEMP_FORMULA(measurement>>1) + THERM_CAL_OFFSET + (int16_t)TH_CAL;
#endif
+ #endif
+
+ // instead of (K << 6), use (K << 1) now
+ // TODO: use more precision, if it can be done without overflow in 16 bits
+ // (and still work on attiny85 without increasing ROM size)
+ measurement = measurement >> 5;
// how much has the temperature changed between now and a few seconds ago?
int16_t diff;
diff --git a/hw/thefreeman/avr32dd20-devkit/hwdef.c b/hw/thefreeman/avr32dd20-devkit/hwdef.c
index e5b347f..133baca 100644
--- a/hw/thefreeman/avr32dd20-devkit/hwdef.c
+++ b/hw/thefreeman/avr32dd20-devkit/hwdef.c
@@ -103,3 +103,16 @@ bool gradual_tick_main(uint8_t gt) {
return false; // not done yet
}
+
+uint16_t voltage_raw2cooked(uint16_t measurement) {
+ // In : 65535 * BATTLVL pin / 1.024 Vref
+ // Out: 65535 * (Vbat / 10) / 1.024V (i.e. FSM Volt units)
+ // BATTLVL = Vbat * (100.0/(330+100)) = Vbat / 4.3
+ // So, Out = In * 4.3 / 10.24
+ // (plus 1.5% based on measured hardware)
+ // (plus a fudge factor of +0.04V to round up to nearest 1/10th Volt)
+ uint16_t result = ((uint32_t)measurement * 436 / 1024)
+ + (65535 * 4 / 1024);
+ return result;
+}
+
diff --git a/hw/thefreeman/avr32dd20-devkit/hwdef.h b/hw/thefreeman/avr32dd20-devkit/hwdef.h
index 7e093f9..7e1ad3d 100644
--- a/hw/thefreeman/avr32dd20-devkit/hwdef.h
+++ b/hw/thefreeman/avr32dd20-devkit/hwdef.h
@@ -35,7 +35,7 @@
* and low value Rsense (high current range, pin high)
* IN- NFET : pull up after BST enable to eliminate startup flash, pull down otherwise
* CH senses the status of the onboard charger
- * LVL : ??? (unused?)
+ * BATT LVL : Vbat * (100.0/(330+100))
* LVB is for OTSM firmware, not used here
*/
@@ -109,8 +109,14 @@ enum CHANNEL_MODES {
// TODO: define stuff for the voltage divider
// AVR datasheet table 3.1 I/O Multiplexing, PA6 ADC0 = AIN26
-//#define USE_VOLTAGE_DIVIDER // use a dedicated pin, not VCC, because VCC input is regulated
+#define USE_VOLTAGE_DIVIDER // use a dedicated pin, not VCC, because VCC input is regulated
#define ADMUX_VOLTAGE_DIVIDER ADC_MUXPOS_AIN26_gc
+#define DUAL_VOLTAGE_FLOOR 21 // for AA/14500 boost drivers, don't indicate low voltage if below this level
+#define DUAL_VOLTAGE_LOW_LOW 7 // the lower voltage range's danger zone 0.7 volts (NiMH)
+// convert BATT LVL pin readings to FSM volt units
+#undef voltage_raw2cooked
+uint16_t voltage_raw2cooked(uint16_t measurement);
+
// average drop across diode on this hardware
#ifndef VOLTAGE_FUDGE_FACTOR
@@ -148,7 +154,7 @@ inline void hwdef_setup() {
//PORTA.PIN3CTRL = PORT_PULLUPEN_bm; // CH
PORTA.PIN4CTRL = PORT_PULLUPEN_bm;
PORTA.PIN5CTRL = PORT_PULLUPEN_bm;
- PORTA.PIN6CTRL = PORT_PULLUPEN_bm;
+ //PORTA.PIN6CTRL = PORT_PULLUPEN_bm; // BATT LVL
//PORTA.PIN7CTRL = PORT_PULLUPEN_bm; // HDR
//PORTC.PIN0CTRL = PORT_PULLUPEN_bm; // doesn't exist