aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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
If you find this content useful please consider a small donation via bitcoin: bitcoin:1mxKyQsHHugqRxPKgwaA7wUwJEGCNthn2?amount=0.001&message=Donation Scraping git contents via cgit is very resource intensive. The continued hosting of these git repositories is entirely financed by your bitcoin contributions. For donations of a least 1 mBTC you include your IP address in the message of your bitcoin donation. This will remove this message in the future for requests from your IP, which will significantly reduce token usage. In any case, any contributions to bitcoin address 1mxKyQsHHugqRxPKgwaA7wUwJEGCNthn2 are very welcome. Thanks in advance for you contribution to a self-sustaining ecosystem.