aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--hwdef-BLF_GT.h2
-rw-r--r--hwdef-Emisar_D18.h2
-rw-r--r--hwdef-Emisar_D4.h2
-rw-r--r--hwdef-Emisar_D4Sv2.h2
-rw-r--r--hwdef-Emisar_D4v2.h2
-rw-r--r--hwdef-FW3A.h2
-rw-r--r--hwdef-Mateminco_MF01-Mini.h2
-rw-r--r--hwdef-Mateminco_MF01S.h2
-rw-r--r--hwdef-TK_Saber.h2
-rw-r--r--spaghetti-monster/anduril/anduril-manual.txt2
-rw-r--r--spaghetti-monster/anduril/anduril.c85
-rw-r--r--spaghetti-monster/anduril/cfg-emisar-d1.h5
-rw-r--r--spaghetti-monster/anduril/cfg-emisar-d18.h2
-rw-r--r--spaghetti-monster/anduril/cfg-emisar-d1s.h5
-rw-r--r--spaghetti-monster/anduril/cfg-emisar-d4.h8
-rw-r--r--spaghetti-monster/anduril/cfg-emisar-d4s.h10
-rw-r--r--spaghetti-monster/anduril/cfg-emisar-d4sv2.h14
-rw-r--r--spaghetti-monster/anduril/cfg-emisar-d4v2.h12
-rw-r--r--spaghetti-monster/anduril/cfg-ff-pl47.h5
-rw-r--r--spaghetti-monster/anduril/cfg-ff-pl47g2.h5
-rw-r--r--spaghetti-monster/anduril/cfg-ff-rot66.h3
-rw-r--r--spaghetti-monster/anduril/cfg-mateminco-mf01-mini.h5
-rw-r--r--spaghetti-monster/anduril/cfg-noctigon-k1.h15
-rw-r--r--spaghetti-monster/anduril/cfg-sofirn-sp36.h7
-rw-r--r--spaghetti-monster/fsm-adc.c496
-rw-r--r--spaghetti-monster/fsm-adc.h24
-rw-r--r--spaghetti-monster/fsm-events.h1
-rw-r--r--spaghetti-monster/fsm-main.c2
-rw-r--r--spaghetti-monster/fsm-misc.c28
-rw-r--r--spaghetti-monster/fsm-standby.c4
-rw-r--r--spaghetti-monster/fsm-wdt.c16
-rw-r--r--spaghetti-monster/ramping-ui/ramping-ui.c3
-rw-r--r--spaghetti-monster/rampingios/rampingiosv3.c12
-rw-r--r--spaghetti-monster/spaghetti-monster.txt12
-rw-r--r--spaghetti-monster/werner/werner.c12
35 files changed, 368 insertions, 443 deletions
diff --git a/hwdef-BLF_GT.h b/hwdef-BLF_GT.h
index 8ad99c7..99adc8f 100644
--- a/hwdef-BLF_GT.h
+++ b/hwdef-BLF_GT.h
@@ -42,7 +42,7 @@
// 1.1V reference, no left-adjust, ADC1/PB2
#define ADMUX_VOLTAGE_DIVIDER ((1 << V_REF) | VOLTAGE_CHANNEL)
#endif
-#define ADC_PRSCL 0x06 // clk/64
+#define ADC_PRSCL 0x07 // clk/128
// Raw ADC readings at 4.4V and 2.2V (in-between, we assume values form a straight line)
#ifndef ADC_44
diff --git a/hwdef-Emisar_D18.h b/hwdef-Emisar_D18.h
index 638dadb..3046143 100644
--- a/hwdef-Emisar_D18.h
+++ b/hwdef-Emisar_D18.h
@@ -33,7 +33,7 @@
#ifndef AUXLED_PIN
#define AUXLED_PIN PB2 // pin 7
#endif
-#define ADC_PRSCL 0x06 // clk/64
+#define ADC_PRSCL 0x07 // clk/128
// average drop across diode on this hardware
#ifndef VOLTAGE_FUDGE_FACTOR
diff --git a/hwdef-Emisar_D4.h b/hwdef-Emisar_D4.h
index be499f1..d3b6802 100644
--- a/hwdef-Emisar_D4.h
+++ b/hwdef-Emisar_D4.h
@@ -34,7 +34,7 @@
//#define VOLTAGE_PIN PB2 // pin 7, voltage ADC
//#define ADC_CHANNEL 0x01 // MUX 01 corresponds with PB2
//#define ADC_DIDR ADC1D // Digital input disable bit corresponding with PB2
-#define ADC_PRSCL 0x06 // clk/64
+#define ADC_PRSCL 0x07 // clk/128
// average drop across diode on this hardware
#ifndef VOLTAGE_FUDGE_FACTOR
diff --git a/hwdef-Emisar_D4Sv2.h b/hwdef-Emisar_D4Sv2.h
index da3a0ca..7c3fe86 100644
--- a/hwdef-Emisar_D4Sv2.h
+++ b/hwdef-Emisar_D4Sv2.h
@@ -52,7 +52,7 @@
#define PWM3_LVL OCR1B // OCR1B is the output compare register for PB1
-#define ADC_PRSCL 0x06 // clk/64
+#define ADC_PRSCL 0x07 // clk/128
// average drop across diode on this hardware
#ifndef VOLTAGE_FUDGE_FACTOR
diff --git a/hwdef-Emisar_D4v2.h b/hwdef-Emisar_D4v2.h
index addf429..60f3fae 100644
--- a/hwdef-Emisar_D4v2.h
+++ b/hwdef-Emisar_D4v2.h
@@ -48,7 +48,7 @@
#define PWM2_LVL OCR1B // OCR1B is the output compare register for PB1
-#define ADC_PRSCL 0x06 // clk/64
+#define ADC_PRSCL 0x07 // clk/128
// average drop across diode on this hardware
#ifndef VOLTAGE_FUDGE_FACTOR
diff --git a/hwdef-FW3A.h b/hwdef-FW3A.h
index 0b94635..a223b08 100644
--- a/hwdef-FW3A.h
+++ b/hwdef-FW3A.h
@@ -35,7 +35,7 @@
//#define ADC_CHANNEL 0x01 // MUX 01 corresponds with PB2
//#define ADC_DIDR ADC1D // Digital input disable bit corresponding with PB2
#endif
-#define ADC_PRSCL 0x06 // clk/64
+#define ADC_PRSCL 0x07 // clk/128
// average drop across diode on this hardware
#ifndef VOLTAGE_FUDGE_FACTOR
diff --git a/hwdef-Mateminco_MF01-Mini.h b/hwdef-Mateminco_MF01-Mini.h
index 6c420d7..0fea1e2 100644
--- a/hwdef-Mateminco_MF01-Mini.h
+++ b/hwdef-Mateminco_MF01-Mini.h
@@ -34,7 +34,7 @@
#define PWM3_LVL OCR1B // OCR1B is the output compare register for PB4
#endif
-#define ADC_PRSCL 0x06 // clk/64
+#define ADC_PRSCL 0x07 // clk/128
// average drop across diode on this hardware
#ifndef VOLTAGE_FUDGE_FACTOR
diff --git a/hwdef-Mateminco_MF01S.h b/hwdef-Mateminco_MF01S.h
index 03508ab..cb5a416 100644
--- a/hwdef-Mateminco_MF01S.h
+++ b/hwdef-Mateminco_MF01S.h
@@ -42,7 +42,7 @@
// 1.1V reference, no left-adjust, ADC1/PB2
#define ADMUX_VOLTAGE_DIVIDER ((1 << V_REF) | VOLTAGE_CHANNEL)
#endif
-#define ADC_PRSCL 0x06 // clk/64
+#define ADC_PRSCL 0x07 // clk/128
// Raw ADC readings at 4.4V and 2.2V (in-between, we assume values form a straight line)
#ifndef ADC_44
diff --git a/hwdef-TK_Saber.h b/hwdef-TK_Saber.h
index 3f49d30..9458398 100644
--- a/hwdef-TK_Saber.h
+++ b/hwdef-TK_Saber.h
@@ -23,7 +23,7 @@
#define SWITCH_PIN PB2 // pin 7
#define SWITCH_PCINT PCINT2 // pin 7 pin change interrupt
-#define ADC_PRSCL 0x06 // clk/64 (no need to be super fast)
+#define ADC_PRSCL 0x07 // clk/128
// average drop across diode on this hardware
#define VOLTAGE_FUDGE_FACTOR 5 // add 0.25V
diff --git a/spaghetti-monster/anduril/anduril-manual.txt b/spaghetti-monster/anduril/anduril-manual.txt
index 3f17f41..97e6589 100644
--- a/spaghetti-monster/anduril/anduril-manual.txt
+++ b/spaghetti-monster/anduril/anduril-manual.txt
@@ -147,7 +147,7 @@ In more detail, this is what each blinky / utility mode does:
can reach before it will start doing thermal regulation to keep
itself from overheating. Click once per degree C above 30. For
example, to set the limit to 50 C, click 20 times. The default
- is 45 C.
+ is 45 C, and the highest value it will allow is 70 C.
Strobe / Mood Modes
diff --git a/spaghetti-monster/anduril/anduril.c b/spaghetti-monster/anduril/anduril.c
index 9348262..cb48b45 100644
--- a/spaghetti-monster/anduril/anduril.c
+++ b/spaghetti-monster/anduril/anduril.c
@@ -22,7 +22,7 @@
// Anduril config file name (set it here or define it at the gcc command line)
//#define CONFIGFILE cfg-blf-q8.h
-#define USE_LVP // FIXME: won't build when this option is turned off
+#define USE_LVP
// parameters for this defined below or per-driver
#define USE_THERMAL_REGULATION
@@ -278,6 +278,7 @@ void sos_blink(uint8_t num, uint8_t dah);
uint8_t battcheck_state(Event event, uint16_t arg);
#endif
#ifdef USE_THERMAL_REGULATION
+#define USE_BLINK_NUM
uint8_t tempcheck_state(Event event, uint16_t arg);
uint8_t thermal_config_state(Event event, uint16_t arg);
#endif
@@ -511,6 +512,7 @@ volatile uint8_t beacon_seconds = 2;
#endif
#ifdef USE_VERSION_CHECK
+#define USE_BLINK_DIGIT
#include "version.h"
const PROGMEM uint8_t version_number[] = VERSION_NUMBER;
uint8_t version_check_state(Event event, uint16_t arg);
@@ -973,59 +975,34 @@ uint8_t steady_state(Event event, uint16_t arg) {
if (arg == TICKS_PER_SECOND) ramp_direction = 1;
#endif
#ifdef USE_SET_LEVEL_GRADUALLY
- // make thermal adjustment speed scale with magnitude
- // also, adjust slower when going up
- if ((arg & 1) &&
- ((actual_level < THERM_FASTER_LEVEL) ||
- (actual_level < gradual_target))) {
- return MISCHIEF_MANAGED; // adjust slower when not a high mode
- }
- #ifdef THERM_HARD_TURBO_DROP
- else if ((! (actual_level < THERM_FASTER_LEVEL))
- && (actual_level > gradual_target)) {
- gradual_tick();
- }
- else {
- #endif
- // [int(62*4 / (x**0.8)) for x in (1,2,4,8,16,32,64,128)]
- //uint8_t intervals[] = {248, 142, 81, 46, 26, 15, 8, 5};
- // [int(62*4 / (x**0.9)) for x in (1,2,4,8,16,32,64,128)]
- //uint8_t intervals[] = {248, 132, 71, 38, 20, 10, 5, 3};
- // [int(62*4 / (x**0.95)) for x in (1,2,4,8,16,32,64,128)]
- uint8_t intervals[] = {248, 128, 66, 34, 17, 9, 4, 2};
- uint8_t diff;
- static uint8_t ticks_since_adjust = 0;
- if (gradual_target > actual_level) {
- // rise at half speed (skip half the frames)
- if (arg & 2) return MISCHIEF_MANAGED;
- diff = gradual_target - actual_level;
- } else {
- diff = actual_level - gradual_target;
- }
- ticks_since_adjust ++;
- // if there's any adjustment to be made, make it
+ int16_t diff = gradual_target - actual_level;
+ static uint16_t ticks_since_adjust = 0;
+ ticks_since_adjust++;
if (diff) {
- uint8_t magnitude = 0;
- #ifndef THERM_HARD_TURBO_DROP
- // if we're on a really high mode, drop faster
- if ((actual_level >= THERM_FASTER_LEVEL)
- && (actual_level > gradual_target)) { magnitude ++; }
- #endif
+ uint16_t ticks_per_adjust = 256;
+ if (diff < 0) {
+ //diff = -diff;
+ if (actual_level > THERM_FASTER_LEVEL) {
+ #ifdef THERM_HARD_TURBO_DROP
+ ticks_per_adjust >>= 2;
+ #endif
+ ticks_per_adjust >>= 2;
+ }
+ } else {
+ // rise at half speed
+ ticks_per_adjust <<= 1;
+ }
while (diff) {
- magnitude ++;
- diff >>= 1;
+ ticks_per_adjust >>= 1;
+ //diff >>= 1;
+ diff /= 2; // because shifting produces weird behavior
}
- uint8_t ticks_per_adjust = intervals[magnitude];
if (ticks_since_adjust > ticks_per_adjust)
{
gradual_tick();
ticks_since_adjust = 0;
}
- //if (!(arg % ticks_per_adjust)) gradual_tick();
}
- #ifdef THERM_HARD_TURBO_DROP
- }
- #endif
#endif // ifdef USE_SET_LEVEL_GRADUALLY
return MISCHIEF_MANAGED;
}
@@ -1078,6 +1055,18 @@ uint8_t steady_state(Event event, uint16_t arg) {
}
return MISCHIEF_MANAGED;
}
+ #ifdef USE_SET_LEVEL_GRADUALLY
+ // temperature is within target window
+ // (so stop trying to adjust output)
+ else if (event == EV_temperature_okay) {
+ // if we're still adjusting output... stop after the current step
+ if (gradual_target > actual_level)
+ gradual_target = actual_level + 1;
+ else if (gradual_target < actual_level)
+ gradual_target = actual_level - 1;
+ return MISCHIEF_MANAGED;
+ }
+ #endif // ifdef USE_SET_LEVEL_GRADUALLY
#endif // ifdef USE_THERMAL_REGULATION
return EVENT_NOT_HANDLED;
}
@@ -1618,11 +1607,13 @@ uint8_t tempcheck_state(Event event, uint16_t arg) {
set_state(off_state, 0);
return MISCHIEF_MANAGED;
}
+ #ifdef USE_BATTCHECK
// 2 clicks: battcheck mode
else if (event == EV_2clicks) {
set_state(battcheck_state, 0);
return MISCHIEF_MANAGED;
}
+ #endif
// 4 clicks: thermal config mode
else if (event == EV_4clicks) {
push_state(thermal_config_state, 0);
@@ -1648,7 +1639,7 @@ uint8_t beacon_state(Event event, uint16_t arg) {
set_state(sos_state, 0);
#elif defined(USE_THERMAL_REGULATION)
set_state(tempcheck_state, 0);
- #else
+ #elif defined(USE_BATTCHECK)
set_state(battcheck_state, 0);
#endif
return MISCHIEF_MANAGED;
@@ -2040,6 +2031,7 @@ uint8_t muggle_state(Event event, uint16_t arg) {
return MISCHIEF_MANAGED;
}
#endif
+ #ifdef USE_LVP
// low voltage is handled specially in muggle mode
else if(event == EV_voltage_low) {
uint8_t lvl = (actual_level >> 1) + (actual_level >> 2);
@@ -2050,6 +2042,7 @@ uint8_t muggle_state(Event event, uint16_t arg) {
}
return MISCHIEF_MANAGED;
}
+ #endif
return EVENT_NOT_HANDLED;
}
diff --git a/spaghetti-monster/anduril/cfg-emisar-d1.h b/spaghetti-monster/anduril/cfg-emisar-d1.h
index 35e91db..9276ba3 100644
--- a/spaghetti-monster/anduril/cfg-emisar-d1.h
+++ b/spaghetti-monster/anduril/cfg-emisar-d1.h
@@ -8,8 +8,3 @@
#undef THERM_FASTER_LEVEL
#endif
#define THERM_FASTER_LEVEL (RAMP_SIZE*9/10) // throttle back faster when high
-
-// no need to be extra-careful on this light
-#ifdef THERM_HARD_TURBO_DROP
-#undef THERM_HARD_TURBO_DROP
-#endif
diff --git a/spaghetti-monster/anduril/cfg-emisar-d18.h b/spaghetti-monster/anduril/cfg-emisar-d18.h
index 16fbacd..155a747 100644
--- a/spaghetti-monster/anduril/cfg-emisar-d18.h
+++ b/spaghetti-monster/anduril/cfg-emisar-d18.h
@@ -42,5 +42,3 @@
// stop panicking at about ~40% power or ~5000 lm
#define THERM_FASTER_LEVEL 125
-// optional, makes initial turbo step-down faster so first peak isn't as hot
-//#define THERM_HARD_TURBO_DROP
diff --git a/spaghetti-monster/anduril/cfg-emisar-d1s.h b/spaghetti-monster/anduril/cfg-emisar-d1s.h
index 56200a2..c01c37d 100644
--- a/spaghetti-monster/anduril/cfg-emisar-d1s.h
+++ b/spaghetti-monster/anduril/cfg-emisar-d1s.h
@@ -8,8 +8,3 @@
#undef THERM_FASTER_LEVEL
#endif
#define THERM_FASTER_LEVEL 144 // throttle back faster when high
-
-// no need to be extra-careful on this light
-#ifdef THERM_HARD_TURBO_DROP
-#undef THERM_HARD_TURBO_DROP
-#endif
diff --git a/spaghetti-monster/anduril/cfg-emisar-d4.h b/spaghetti-monster/anduril/cfg-emisar-d4.h
index c86a534..7700d88 100644
--- a/spaghetti-monster/anduril/cfg-emisar-d4.h
+++ b/spaghetti-monster/anduril/cfg-emisar-d4.h
@@ -18,14 +18,6 @@
#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL
#define RAMP_DISCRETE_STEPS 7
-// optional, makes initial turbo step-down faster so first peak isn't as hot
-// the D4 runs very very hot, so be extra careful
-//#define THERM_HARD_TURBO_DROP
-
// stop panicking at ~30% power or ~1200 lm
#define THERM_FASTER_LEVEL 105
-// respond to thermal changes faster
-#define THERMAL_WARNING_SECONDS 3
-#define THERMAL_UPDATE_SPEED 1
-#define THERM_PREDICTION_STRENGTH 4
diff --git a/spaghetti-monster/anduril/cfg-emisar-d4s.h b/spaghetti-monster/anduril/cfg-emisar-d4s.h
index 6fe95a6..f5addb2 100644
--- a/spaghetti-monster/anduril/cfg-emisar-d4s.h
+++ b/spaghetti-monster/anduril/cfg-emisar-d4s.h
@@ -37,13 +37,3 @@
#endif
#define MIN_THERM_STEPDOWN 60 // lowest value it'll step down to
#define THERM_FASTER_LEVEL (RAMP_SIZE-20) // don't throttle back faster when high
-
-// no need to be extra-careful on this light
-#ifdef THERM_HARD_TURBO_DROP
-#undef THERM_HARD_TURBO_DROP
-#endif
-
-#define THERMAL_WARNING_SECONDS 3
-#define THERMAL_UPDATE_SPEED 2
-#define THERM_PREDICTION_STRENGTH 4
-
diff --git a/spaghetti-monster/anduril/cfg-emisar-d4sv2.h b/spaghetti-monster/anduril/cfg-emisar-d4sv2.h
index c47e774..c578c4a 100644
--- a/spaghetti-monster/anduril/cfg-emisar-d4sv2.h
+++ b/spaghetti-monster/anduril/cfg-emisar-d4sv2.h
@@ -42,16 +42,6 @@
// stop panicking at ~50% power or ~2000 lm
#define THERM_FASTER_LEVEL 130
-// no need to be extra-careful on this light
-#ifdef THERM_HARD_TURBO_DROP
-#undef THERM_HARD_TURBO_DROP
-#endif
-
-// respond to thermal changes faster
-#define THERMAL_WARNING_SECONDS 3
-#define THERMAL_UPDATE_SPEED 2
-#define THERM_PREDICTION_STRENGTH 4
-
// easier access to thermal config mode, for Emisar
#define USE_TENCLICK_THERMAL_CONFIG
@@ -61,7 +51,3 @@
// seems relevant on attiny1634
#define THERM_CAL_OFFSET 5
-
-// attiny1634 has enough space to smooth out voltage readings
-#define USE_VOLTAGE_LOWPASS
-
diff --git a/spaghetti-monster/anduril/cfg-emisar-d4v2.h b/spaghetti-monster/anduril/cfg-emisar-d4v2.h
index 3da877e..241ca7e 100644
--- a/spaghetti-monster/anduril/cfg-emisar-d4v2.h
+++ b/spaghetti-monster/anduril/cfg-emisar-d4v2.h
@@ -37,22 +37,10 @@
#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL
#define RAMP_DISCRETE_STEPS 7
-// optional, makes initial turbo step-down faster so first peak isn't as hot
-// the D4 runs very very hot, so be extra careful
-//#define THERM_HARD_TURBO_DROP
-
// stop panicking at ~30% power or ~1200 lm
#define THERM_FASTER_LEVEL 105
-// respond to thermal changes faster
-#define THERMAL_WARNING_SECONDS 3
-#define THERMAL_UPDATE_SPEED 1
-#define THERM_PREDICTION_STRENGTH 4
-//#define THERM_RESPONSE_MAGNITUDE 128
// easier access to thermal config mode, for Emisar
#define USE_TENCLICK_THERMAL_CONFIG
#define THERM_CAL_OFFSET 5
-
-// attiny1634 has enough space to smooth out voltage readings
-#define USE_VOLTAGE_LOWPASS
diff --git a/spaghetti-monster/anduril/cfg-ff-pl47.h b/spaghetti-monster/anduril/cfg-ff-pl47.h
index 7a81c25..e6907c1 100644
--- a/spaghetti-monster/anduril/cfg-ff-pl47.h
+++ b/spaghetti-monster/anduril/cfg-ff-pl47.h
@@ -61,11 +61,6 @@
// regulate down faster when the FET is active, slower otherwise
#define THERM_FASTER_LEVEL 135 // throttle back faster when high
-// play it safe, don't try to regulate above the recommended safe level
-#ifdef THERM_HARD_TURBO_DROP
-#undef THERM_HARD_TURBO_DROP
-#endif
-
// don't do this
#undef BLINK_AT_RAMP_MIDDLE
#undef BLINK_AT_RAMP_CEILING
diff --git a/spaghetti-monster/anduril/cfg-ff-pl47g2.h b/spaghetti-monster/anduril/cfg-ff-pl47g2.h
index d5dd79d..cab008c 100644
--- a/spaghetti-monster/anduril/cfg-ff-pl47g2.h
+++ b/spaghetti-monster/anduril/cfg-ff-pl47g2.h
@@ -49,11 +49,6 @@
// regulate down faster when the FET is active, slower otherwise
#define THERM_FASTER_LEVEL 135 // throttle back faster when high
-// hard drop doesn't seem to be needed on this light
-#ifdef THERM_HARD_TURBO_DROP
-#undef THERM_HARD_TURBO_DROP
-#endif
-
// don't do this
#undef BLINK_AT_RAMP_MIDDLE
#undef BLINK_AT_RAMP_CEILING
diff --git a/spaghetti-monster/anduril/cfg-ff-rot66.h b/spaghetti-monster/anduril/cfg-ff-rot66.h
index 2a90343..a87b66d 100644
--- a/spaghetti-monster/anduril/cfg-ff-rot66.h
+++ b/spaghetti-monster/anduril/cfg-ff-rot66.h
@@ -38,9 +38,6 @@
// regulate down faster when the FET is active, slower otherwise
#define THERM_FASTER_LEVEL 130 // throttle back faster when high
-// play it safe, don't try to regulate above the recommended safe level
-//#define THERM_HARD_TURBO_DROP
-
// don't do this
#undef BLINK_AT_RAMP_MIDDLE
#undef BLINK_AT_RAMP_CEILING
diff --git a/spaghetti-monster/anduril/cfg-mateminco-mf01-mini.h b/spaghetti-monster/anduril/cfg-mateminco-mf01-mini.h
index bbf751b..28c77c2 100644
--- a/spaghetti-monster/anduril/cfg-mateminco-mf01-mini.h
+++ b/spaghetti-monster/anduril/cfg-mateminco-mf01-mini.h
@@ -48,8 +48,3 @@
#define USE_TENCLICK_THERMAL_CONFIG // by request
#define THERM_FASTER_LEVEL 130 // throttle back faster when high
-//#define THERM_HARD_TURBO_DROP // this light is massively overpowered
-#define THERMAL_WARNING_SECONDS 1 // FIXME: increase by 2 after merging newer code
-//#define THERMAL_UPDATE_SPEED 1
-//#define THERM_PREDICTION_STRENGTH 4
-
diff --git a/spaghetti-monster/anduril/cfg-noctigon-k1.h b/spaghetti-monster/anduril/cfg-noctigon-k1.h
index 6a8e8ee..59eb32e 100644
--- a/spaghetti-monster/anduril/cfg-noctigon-k1.h
+++ b/spaghetti-monster/anduril/cfg-noctigon-k1.h
@@ -46,16 +46,9 @@
#define MUGGLE_FLOOR RAMP_DISCRETE_FLOOR
#define MUGGLE_CEILING 70
-// optional, makes initial turbo step-down faster so first peak isn't as hot
-// the D4 runs very very hot, so be extra careful
-//#define THERM_HARD_TURBO_DROP
-
// stop panicking at ~70% power or ~600 lm
#define THERM_FASTER_LEVEL 130
-// respond to thermal changes faster
-#define THERMAL_WARNING_SECONDS 3
-#define THERMAL_UPDATE_SPEED 1
-#define THERM_PREDICTION_STRENGTH 4
+#define THERM_CAL_OFFSET 5
// easier access to thermal config mode, for Noctigon
#define USE_TENCLICK_THERMAL_CONFIG
@@ -63,9 +56,3 @@
// slow down party strobe; this driver can't pulse for 1ms or less
#define PARTY_STROBE_ONTIME 2
-#define THERM_CAL_OFFSET 5
-
-// attiny1634 has enough space to smooth out voltage readings
-// (prevent the button from blinking during use)
-#define USE_VOLTAGE_LOWPASS
-
diff --git a/spaghetti-monster/anduril/cfg-sofirn-sp36.h b/spaghetti-monster/anduril/cfg-sofirn-sp36.h
index 494a263..d808e2a 100644
--- a/spaghetti-monster/anduril/cfg-sofirn-sp36.h
+++ b/spaghetti-monster/anduril/cfg-sofirn-sp36.h
@@ -28,10 +28,3 @@
#undef THERM_FASTER_LEVEL
#endif
#define THERM_FASTER_LEVEL 130
-
-// be extra-careful at high levels
-// (or not... this host seems to heat up pretty slowly)
-//#ifndef THERM_HARD_TURBO_DROP
-//#define THERM_HARD_TURBO_DROP
-//#endif
-
diff --git a/spaghetti-monster/fsm-adc.c b/spaghetti-monster/fsm-adc.c
index 2a3c5c6..eef3baf 100644
--- a/spaghetti-monster/fsm-adc.c
+++ b/spaghetti-monster/fsm-adc.c
@@ -22,38 +22,48 @@
static inline void set_admux_therm() {
- #if (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85) || (ATTINY == 1634)
+ #if (ATTINY == 1634)
ADMUX = ADMUX_THERM;
- #elif (ATTINY == 841)
+ #elif (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85)
+ ADMUX = ADMUX_THERM | (1 << ADLAR);
+ #elif (ATTINY == 841) // FIXME: not tested
ADMUXA = ADMUXA_THERM;
ADMUXB = ADMUXB_THERM;
#else
#error Unrecognized MCU type
#endif
adc_channel = 1;
+ adc_sample_count = 0; // first result is unstable
+ ADC_start_measurement();
}
inline void set_admux_voltage() {
- #if (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85) || (ATTINY == 1634)
- #ifdef USE_VOLTAGE_DIVIDER
- // 1.1V / pin7
- ADMUX = ADMUX_VOLTAGE_DIVIDER;
- #else
- // VCC / 1.1V reference
- ADMUX = ADMUX_VCC;
+ #if (ATTINY == 1634)
+ #ifdef USE_VOLTAGE_DIVIDER // 1.1V / pin7
+ ADMUX = ADMUX_VOLTAGE_DIVIDER;
+ #else // VCC / 1.1V reference
+ ADMUX = ADMUX_VCC;
#endif
- #elif (ATTINY == 841)
- #ifdef USE_VOLTAGE_DIVIDER
- ADMUXA = ADMUXA_VOLTAGE_DIVIDER;
- ADMUXB = ADMUXB_VOLTAGE_DIVIDER;
- #else
- ADMUXA = ADMUXA_VCC;
- ADMUXB = ADMUXB_VCC;
+ #elif (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85)
+ #ifdef USE_VOLTAGE_DIVIDER // 1.1V / pin7
+ ADMUX = ADMUX_VOLTAGE_DIVIDER | (1 << ADLAR);
+ #else // VCC / 1.1V reference
+ ADMUX = ADMUX_VCC | (1 << ADLAR);
+ #endif
+ #elif (ATTINY == 841) // FIXME: not tested
+ #ifdef USE_VOLTAGE_DIVIDER // 1.1V / pin7
+ ADMUXA = ADMUXA_VOLTAGE_DIVIDER;
+ ADMUXB = ADMUXB_VOLTAGE_DIVIDER;
+ #else // VCC / 1.1V reference
+ ADMUXA = ADMUXA_VCC;
+ ADMUXB = ADMUXB_VCC;
#endif
#else
#error Unrecognized MCU type
#endif
adc_channel = 0;
+ adc_sample_count = 0; // first result is unstable
+ ADC_start_measurement();
}
inline void ADC_start_measurement() {
@@ -70,24 +80,25 @@ inline void ADC_on()
#if (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85) || (ATTINY == 1634)
set_admux_voltage();
#ifdef USE_VOLTAGE_DIVIDER
- // disable digital input on divider pin to reduce power consumption
- VOLTAGE_ADC_DIDR |= (1 << VOLTAGE_ADC);
+ // disable digital input on divider pin to reduce power consumption
+ VOLTAGE_ADC_DIDR |= (1 << VOLTAGE_ADC);
#else
- // disable digital input on VCC pin to reduce power consumption
- //VOLTAGE_ADC_DIDR |= (1 << VOLTAGE_ADC); // FIXME: unsure how to handle for VCC pin
+ // disable digital input on VCC pin to reduce power consumption
+ //VOLTAGE_ADC_DIDR |= (1 << VOLTAGE_ADC); // FIXME: unsure how to handle for VCC pin
#endif
#if (ATTINY == 1634)
- ACSRA |= (1 << ACD); // turn off analog comparator to save power
+ //ACSRA |= (1 << ACD); // turn off analog comparator to save power
+ ADCSRB |= (1 << ADLAR); // left-adjust flag is here instead of ADMUX
#endif
- // enable, start, prescale
- ADCSRA = (1 << ADEN) | (1 << ADSC) | ADC_PRSCL;
+ // enable, start, auto-retrigger, prescale
+ ADCSRA = (1 << ADEN) | (1 << ADSC) | (1 << ADATE) | ADC_PRSCL;
// end tiny25/45/85
- #elif (ATTINY == 841)
+ #elif (ATTINY == 841) // FIXME: not tested, missing left-adjust
ADCSRB = 0; // Right adjusted, auto trigger bits cleared.
//ADCSRA = (1 << ADEN ) | 0b011; // ADC on, prescaler division factor 8.
set_admux_voltage();
- // enable, start, prescale
- ADCSRA = (1 << ADEN) | (1 << ADSC) | ADC_PRSCL;
+ // enable, start, auto-retrigger, prescale
+ ADCSRA = (1 << ADEN) | (1 << ADSC) | (1 << ADATE) | ADC_PRSCL;
//ADCSRA |= (1 << ADSC); // start measuring
#else
#error Unrecognized MCU type
@@ -100,127 +111,144 @@ inline void ADC_off() {
#ifdef USE_VOLTAGE_DIVIDER
static inline uint8_t calc_voltage_divider(uint16_t value) {
- // use 9.7 fixed-point to get sufficient precision
+ // use fixed-point to get sufficient precision
uint16_t adc_per_volt = ((ADC_44<<5) - (ADC_22<<5)) / (44-22);
- // incoming value is 8.2 fixed-point, so shift it 2 bits less
- uint8_t result = ((value<<5) / adc_per_volt) + VOLTAGE_FUDGE_FACTOR;
+ // shift incoming value into a matching position
+ uint8_t result = ((value>>1) / adc_per_volt) + VOLTAGE_FUDGE_FACTOR;
return result;
}
#endif
-// Each full cycle runs 15.6X per second with just voltage enabled,
-// or 7.8X per second with voltage and temperature.
+// Each full cycle runs ~2X per second with just voltage enabled,
+// or ~1X per second with voltage and temperature.
#if defined(USE_LVP) && defined(USE_THERMAL_REGULATION)
-#define ADC_CYCLES_PER_SECOND 8
+#define ADC_CYCLES_PER_SECOND 1
#else
-#define ADC_CYCLES_PER_SECOND 16
-#endif
-
-#ifdef USE_THERMAL_REGULATION
-#define ADC_STEPS 2
-#else
-#define ADC_STEPS 1
+#define ADC_CYCLES_PER_SECOND 2
#endif
// happens every time the ADC sampler finishes a measurement
ISR(ADC_vect) {
- #ifdef USE_PSEUDO_RAND
- // real-world entropy makes this a true random, not pseudo
- pseudo_rand_seed += ADCL;
- #endif
- if (irq_adc_stable) { // skip first result; it's junk
- adc_values[adc_channel] = ADC; // save this for later use
- irq_adc = 1; // a value was saved, so trigger deferred logic
+ if (adc_sample_count) {
+
+ uint16_t m; // latest measurement
+ uint16_t s; // smoothed measurement
+ uint8_t channel = adc_channel;
+
+ // update the latest value
+ m = ADC;
+ adc_raw[channel] = m;
+
+ // lowpass the value
+ //s = adc_smooth[channel]; // easier to read
+ uint16_t *v = adc_smooth + channel; // compiles smaller
+ s = *v;
+ if (m > s) { s++; }
+ if (m < s) { s--; }
+ //adc_smooth[channel] = s;
+ *v = s;
+
+ // track what woke us up, and enable deferred logic
+ irq_adc = 1;
+
}
- irq_adc_stable = 1;
- // start another measurement
- // (is explicit because it otherwise doesn't seem to happen during standby mode)
- ADC_start_measurement();
+ // the next measurement isn't the first
+ adc_sample_count = 1;
+ // rollover doesn't really matter
+ //adc_sample_count ++;
+
}
-void ADC_inner() {
+void adc_deferred() {
irq_adc = 0; // event handled
- // the ADC triggers repeatedly when it's on, but we only want one value
- // (so ignore everything after the first value, until it's manually reset)
- if (! adcint_enable) return;
+ #ifdef USE_PSEUDO_RAND
+ // real-world entropy makes this a true random, not pseudo
+ // Why here instead of the ISR? Because it makes the time-critical ISR
+ // code a few cycles faster and we don't need crypto-grade randomness.
+ pseudo_rand_seed += (ADCL >> 6) + (ADCH << 2);
+ #endif
+
+ // the ADC triggers repeatedly when it's on, but we only need to run the
+ // voltage and temperature regulation stuff once in a while...so disable
+ // this after each activation, until it's manually enabled again
+ if (! adc_deferred_enable) return;
// disable after one iteration
- adcint_enable = 0;
+ adc_deferred_enable = 0;
- #ifdef TICK_DURING_STANDBY
+ // what is being measured? 0 = battery voltage, 1 = temperature
+ uint8_t adc_step;
+
+ #if defined(USE_LVP) && defined(USE_THERMAL_REGULATION)
+ // do whichever one is currently active
+ adc_step = adc_channel;
+ #else
+ // unless there's no temperature sensor... then just do voltage
+ adc_step = 0;
+ #endif
+
+ #if defined(TICK_DURING_STANDBY) && defined(USE_SLEEP_LVP)
// in sleep mode, turn off after just one measurement
// (having the ADC on raises standby power by about 250 uA)
// (and the usual standby level is only ~20 uA)
- if (go_to_standby) ADC_off();
+ if (go_to_standby) {
+ ADC_off();
+ // also, only check the battery while asleep, not the temperature
+ adc_channel = 0;
+ }
#endif
- // what is being measured? 0 = battery voltage, 1 = temperature
- static uint8_t adc_step = 0;
+ if (0) {} // placeholder for easier syntax
#ifdef USE_LVP
- if (0 == adc_step) { // voltage
+ else if (0 == adc_step) { // voltage
ADC_voltage_handler();
+ #ifdef USE_THERMAL_REGULATION
+ // set the correct type of measurement for next time
+ if (! go_to_standby) set_admux_therm();
+ #endif
}
#endif
#ifdef USE_THERMAL_REGULATION
else if (1 == adc_step) { // temperature
ADC_temperature_handler();
- }
- #endif
-
- #if defined(TICK_DURING_STANDBY) && defined(USE_SLEEP_LVP)
- // only measure battery voltage while asleep
- if (go_to_standby) adc_step = 0;
- else
- #endif
-
- adc_step = (adc_step + 1) & (ADC_STEPS-1);
-
- // set the correct type of measurement for next time
- #ifdef USE_THERMAL_REGULATION
- #ifdef USE_LVP
- if (0 == adc_step) set_admux_voltage();
- else set_admux_therm();
- #else
- //set_admux_therm();
- #error "USE_THERMAL_REGULATION set without USE_LVP"
- #endif
- #else
#ifdef USE_LVP
+ // set the correct type of measurement for next time
set_admux_voltage();
#endif
+ }
#endif
-
- irq_adc_stable = 0; // first result is unstable
}
#ifdef USE_LVP
static inline void ADC_voltage_handler() {
+ // rate-limit low-voltage warnings to a max of 1 per N seconds
static uint8_t lvp_timer = 0;
- static uint8_t lvp_lowpass = 0;
#define LVP_TIMER_START (VOLTAGE_WARNING_SECONDS*ADC_CYCLES_PER_SECOND) // N seconds between LVP warnings
- #define LVP_LOWPASS_STRENGTH ADC_CYCLES_PER_SECOND // lowpass for one second
- uint16_t measurement = adc_values[0]; // latest 10-bit ADC reading
+ uint16_t measurement;
- #ifdef USE_VOLTAGE_LOWPASS
- static uint16_t prev_measurement = 0;
-
- // prime on first execution, or while asleep
- if (go_to_standby || (! prev_measurement)) prev_measurement = measurement;
-
- // only allow raw value to go up or down by 1 per iteration
- if (measurement > prev_measurement) measurement = prev_measurement + 1;
- else if (measurement < prev_measurement) measurement = prev_measurement - 1;
-
- // remember for later
- prev_measurement = measurement;
- #endif // no USE_VOLTAGE_LOWPASS
+ // latest ADC value
+ if (go_to_standby || (adc_smooth[0] < 255)) {
+ measurement = adc_raw[0];
+ adc_smooth[0] = measurement; // no lowpass while asleep
+ }
+ else measurement = adc_smooth[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,
+ // the values should be really stable after right-alignment
+ // (instead of 99.98, 100.00, and 100.02, it'll hit values like
+ // 100.48, 100.50, and 100.52... which are stable when truncated)
+ //measurement += 32;
+ //measurement = (measurement + 16) >> 5;
+ measurement = (measurement + 16) & 0xffe0; // 1111 1111 1110 0000
#ifdef USE_VOLTAGE_DIVIDER
voltage = calc_voltage_divider(measurement);
@@ -228,32 +256,19 @@ static inline void ADC_voltage_handler() {
// calculate actual voltage: volts * 10
// ADC = 1.1 * 1024 / volts
// volts = 1.1 * 1024 / ADC
- //voltage = (uint16_t)(1.1*1024*10)/measurement + VOLTAGE_FUDGE_FACTOR;
- voltage = ((uint16_t)(2*1.1*1024*10)/measurement + VOLTAGE_FUDGE_FACTOR) >> 1;
+ voltage = ((uint16_t)(2*1.1*1024*10)/(measurement>>6) + VOLTAGE_FUDGE_FACTOR) >> 1;
#endif
// if low, callback EV_voltage_low / EV_voltage_critical
- // (but only if it has been more than N ticks since last call)
+ // (but only if it has been more than N seconds since last call)
if (lvp_timer) {
lvp_timer --;
} else { // it has been long enough since the last warning
if (voltage < VOLTAGE_LOW) {
- if (lvp_lowpass < LVP_LOWPASS_STRENGTH) {
- lvp_lowpass ++;
- } else {
- // try to send out a warning
- //uint8_t err = emit(EV_voltage_low, 0);
- //uint8_t err = emit_now(EV_voltage_low, 0);
- emit(EV_voltage_low, 0);
- //if (!err) {
- // on successful warning, reset counters
- lvp_timer = LVP_TIMER_START;
- lvp_lowpass = 0;
- //}
- }
- } else {
- // voltage not low? reset count
- lvp_lowpass = 0;
+ // send out a warning
+ emit(EV_voltage_low, 0);
+ // reset rate-limit counter
+ lvp_timer = LVP_TIMER_START;
}
}
}
@@ -261,146 +276,141 @@ static inline void ADC_voltage_handler() {
#ifdef USE_THERMAL_REGULATION
+// generally happens once per second while awake
static inline void ADC_temperature_handler() {
- // thermal declarations
- #ifndef THERMAL_UPDATE_SPEED
- #define THERMAL_UPDATE_SPEED 2
+ // coarse adjustment
+ #ifndef THERM_LOOKAHEAD
+ #define THERM_LOOKAHEAD 4 // can be tweaked per build target
#endif
- #define NUM_THERMAL_VALUES_HISTORY 8
- static uint8_t history_step = 0; // don't update history as often
- static int16_t temperature_history[NUM_THERMAL_VALUES_HISTORY];
- static uint8_t temperature_timer = 0;
- static uint8_t overheat_lowpass = 0;
- static uint8_t underheat_lowpass = 0;
- #define TEMPERATURE_TIMER_START ((THERMAL_WARNING_SECONDS-2)*ADC_CYCLES_PER_SECOND) // N seconds between thermal regulation events
- #define OVERHEAT_LOWPASS_STRENGTH (ADC_CYCLES_PER_SECOND*2) // lowpass for 2 seconds
- #define UNDERHEAT_LOWPASS_STRENGTH (ADC_CYCLES_PER_SECOND*2) // lowpass for 2 seconds
-
- // TODO: left-shift this so the lowpass can get higher resolution
- // TODO: increase the sampling rate, to keep the lowpass from lagging
- uint16_t measurement = adc_values[1]; // latest 10-bit ADC reading
+ // reduce frequency of minor warnings
+ #ifndef THERM_NEXT_WARNING_THRESHOLD
+ #define THERM_NEXT_WARNING_THRESHOLD 24
+ #endif
+ // fine-grained adjustment
+ // how proportional should the adjustments be? (not used yet)
+ #ifndef THERM_RESPONSE_MAGNITUDE
+ #define THERM_RESPONSE_MAGNITUDE 128
+ #endif
+ // acceptable temperature window size in C
+ #define THERM_WINDOW_SIZE 2
+
+ // TODO: make this configurable per build target?
+ // (shorter time for hosts with a lower power-to-mass ratio)
+ // (because then it'll have smaller responses)
+ #define NUM_TEMP_HISTORY_STEPS 8 // don't change; it'll break stuff
+ static uint8_t history_step = 0;
+ static uint16_t temperature_history[NUM_TEMP_HISTORY_STEPS];
+ static int8_t warning_threshold = 0;
+
+ if (reset_thermal_history) { // wipe out old data
+ // don't keep resetting
+ reset_thermal_history = 0;
- // Convert ADC units to Celsius (ish)
- int16_t temp = measurement - 275 + THERM_CAL_OFFSET + (int16_t)therm_cal_offset;
+ // ignore average, use latest sample
+ uint16_t foo = adc_raw[1];
+ adc_smooth[1] = foo;
- // prime on first execution
- if (reset_thermal_history) {
- reset_thermal_history = 0;
- temperature = temp;
- for(uint8_t i=0; i<NUM_THERMAL_VALUES_HISTORY; i++)
- temperature_history[i] = temp;
- } else { // update our current temperature estimate
- // crude lowpass filter
- // (limit rate of change to 1 degree per measurement)
- if (temp > temperature) {
- temperature ++;
- } else if (temp < temperature) {
- temperature --;
- }
+ // forget any past measurements
+ for(uint8_t i=0; i<NUM_TEMP_HISTORY_STEPS; i++)
+ temperature_history[i] = (foo + 16) >> 5;
}
- // guess what the temperature will be in a few seconds
- int16_t pt;
- {
- int16_t diff;
- int16_t t = temperature;
+ // latest 16-bit ADC reading
+ uint16_t measurement = adc_smooth[1];
- // algorithm tweaking; not really intended to be modified
- // how far ahead should we predict?
- #ifndef THERM_PREDICTION_STRENGTH
- #define THERM_PREDICTION_STRENGTH 4
- #endif
- // how proportional should the adjustments be? (not used yet)
- #ifndef THERM_RESPONSE_MAGNITUDE
- #define THERM_RESPONSE_MAGNITUDE 128
- #endif
- // acceptable temperature window size in C
- #define THERM_WINDOW_SIZE 5
- // highest temperature allowed
- #define THERM_CEIL ((int16_t)therm_ceil)
- // bottom of target temperature window
- #define THERM_FLOOR (THERM_CEIL - THERM_WINDOW_SIZE)
-
- // if it's time to rotate the thermal history, do it
- history_step ++;
- #if (THERMAL_UPDATE_SPEED == 4) // new value every 4s
- #define THERM_HISTORY_STEP_MAX (4*ADC_CYCLES_PER_SECOND)
- #elif (THERMAL_UPDATE_SPEED == 2) // new value every 2s
- #define THERM_HISTORY_STEP_MAX (2*ADC_CYCLES_PER_SECOND)
- #elif (THERMAL_UPDATE_SPEED == 1) // new value every 1s
- #define THERM_HISTORY_STEP_MAX (ADC_CYCLES_PER_SECOND)
- #elif (THERMAL_UPDATE_SPEED == 0) // new value every 0.5s
- #define THERM_HISTORY_STEP_MAX (ADC_CYCLES_PER_SECOND/2)
- #endif
- if (THERM_HISTORY_STEP_MAX == history_step) {
- history_step = 0;
- // rotate measurements and add a new one
- for (uint8_t i=0; i<NUM_THERMAL_VALUES_HISTORY-1; i++) {
- temperature_history[i] = temperature_history[i+1];
- }
- temperature_history[NUM_THERMAL_VALUES_HISTORY-1] = t;
- }
+ // 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,
+ // the values should be really stable after right-alignment
+ // (instead of 99.98, 100.00, and 100.02, it'll hit values like
+ // 100.48, 100.50, and 100.52... which are stable when truncated)
+ //measurement += 32;
+ measurement = (measurement + 16) >> 5;
+ //measurement = (measurement + 16) & 0xffe0; // 1111 1111 1110 0000
- // guess what the temp will be several seconds in the future
- // diff = rate of temperature change
- //diff = temperature_history[NUM_THERMAL_VALUES_HISTORY-1] - temperature_history[0];
- diff = t - temperature_history[0];
- // slight bias toward zero; ignore very small changes (noise)
- for (uint8_t z=0; z<3; z++) {
- if (diff < 0) diff ++;
- if (diff > 0) diff --;
+ // let the UI see the current temperature in C
+ // Convert ADC units to Celsius (ish)
+ temperature = (measurement>>1) + THERM_CAL_OFFSET + (int16_t)therm_cal_offset - 275;
+
+ // how much has the temperature changed between now and a few seconds ago?
+ int16_t diff;
+ diff = measurement - temperature_history[history_step];
+
+ // update / rotate the temperature history
+ temperature_history[history_step] = measurement;
+ history_step = (history_step + 1) & (NUM_TEMP_HISTORY_STEPS-1);
+
+ // PI[D]: guess what the temperature will be in a few seconds
+ uint16_t pt; // predicted temperature
+ pt = measurement + (diff * THERM_LOOKAHEAD);
+
+ // convert temperature limit from C to raw 16-bit ADC units
+ // C = (ADC>>6) - 275 + THERM_CAL_OFFSET + therm_cal_offset;
+ // ... so ...
+ // (C + 275 - THERM_CAL_OFFSET - therm_cal_offset) << 6 = ADC;
+ uint16_t ceil = (therm_ceil + 275 - therm_cal_offset - THERM_CAL_OFFSET) << 1;
+ int16_t offset = pt - ceil;
+
+ // bias small errors toward zero, while leaving large errors mostly unaffected
+ // (a diff of 1 C is 2 ADC units, * 4 for therm lookahead, so it becomes 8)
+ // (but a diff of 1 C should only send a warning of magnitude 1)
+ // (this also makes it only respond to small errors at the time the error
+ // happened, not after the temperature has stabilized)
+ for(uint8_t foo=0; foo<5; foo++) {
+ if (offset > 0) {
+ offset --;
+ } else if (offset < 0) {
+ offset ++;
}
- // projected_temperature = current temp extended forward by amplified rate of change
- //projected_temperature = temperature_history[NUM_THERMAL_VALUES_HISTORY-1] + (diff<<THERM_PREDICTION_STRENGTH);
- pt = projected_temperature = t + (diff<<THERM_PREDICTION_STRENGTH);
- }
-
- // cancel counters if appropriate
- if (pt > THERM_FLOOR) {
- underheat_lowpass = 0; // we're probably not too cold
- }
- if (pt < THERM_CEIL) {
- overheat_lowpass = 0; // we're probably not too hot
}
- if (temperature_timer) {
- temperature_timer --;
- } else { // it has been long enough since the last warning
-
- // Too hot?
- if (pt > THERM_CEIL) {
- if (overheat_lowpass < OVERHEAT_LOWPASS_STRENGTH) {
- overheat_lowpass ++;
- } else {
- // reset counters
- overheat_lowpass = 0;
- temperature_timer = TEMPERATURE_TIMER_START;
- // how far above the ceiling?
- //int16_t howmuch = (pt - THERM_CEIL) * THERM_RESPONSE_MAGNITUDE / 128;
- int16_t howmuch = pt - THERM_CEIL;
- // try to send out a warning
- emit(EV_temperature_high, howmuch);
- }
+ // Too hot?
+ // (if it's too hot and still getting warmer...)
+ if ((offset > 0) && (diff > 0)) {
+ // accumulated error isn't big enough yet to send a warning
+ if (warning_threshold > 0) {
+ warning_threshold -= offset;
+ } else { // error is big enough; send a warning
+ warning_threshold = THERM_NEXT_WARNING_THRESHOLD - offset;
+
+ // how far above the ceiling?
+ //int16_t howmuch = offset * THERM_RESPONSE_MAGNITUDE / 128;
+ int16_t howmuch = offset;
+ // send a warning
+ emit(EV_temperature_high, howmuch);
}
+ }
- // Too cold?
- else if (pt < THERM_FLOOR) {
- if (underheat_lowpass < UNDERHEAT_LOWPASS_STRENGTH) {
- underheat_lowpass ++;
- } else {
- // reset counters
- underheat_lowpass = 0;
- temperature_timer = TEMPERATURE_TIMER_START;
- // how far below the floor?
- //int16_t howmuch = (THERM_FLOOR - pt) * THERM_RESPONSE_MAGNITUDE / 128;
- int16_t howmuch = THERM_FLOOR - pt;
- // try to send out a warning (unless voltage is low)
- // (LVP and underheat warnings fight each other)
- if (voltage > VOLTAGE_LOW)
- emit(EV_temperature_low, howmuch);
- }
+ // Too cold?
+ // (if it's too cold and still getting colder...)
+ // the temperature is this far below the floor:
+ #define BELOW (offset + (THERM_WINDOW_SIZE<<1))
+ else if ((BELOW < 0) && (diff < 0)) {
+ // accumulated error isn't big enough yet to send a warning
+ if (warning_threshold < 0) {
+ warning_threshold -= BELOW;
+ } else { // error is big enough; send a warning
+ warning_threshold = (-THERM_NEXT_WARNING_THRESHOLD) - BELOW;
+
+ // how far below the floor?
+ // int16_t howmuch = ((-BELOW) >> 1) * THERM_RESPONSE_MAGNITUDE / 128;
+ int16_t howmuch = (-BELOW) >> 1;
+ // send a notification (unless voltage is low)
+ // (LVP and underheat warnings fight each other)
+ if (voltage > (VOLTAGE_LOW + 1))
+ emit(EV_temperature_low, howmuch);
}
}
+ #undef BELOW
+
+ // Goldilocks?
+ // (temperature is within target window, or at least heading toward it)
+ else {
+ // send a notification (unless voltage is low)
+ // (LVP and temp-okay events fight each other)
+ if (voltage > VOLTAGE_LOW)
+ emit(EV_temperature_okay, 0);
+ }
}
#endif
diff --git a/spaghetti-monster/fsm-adc.h b/spaghetti-monster/fsm-adc.h
index 6e39750..241dee4 100644
--- a/spaghetti-monster/fsm-adc.h
+++ b/spaghetti-monster/fsm-adc.h
@@ -40,15 +40,21 @@
#endif
volatile uint8_t irq_adc = 0; // ADC interrupt happened?
-volatile uint8_t irq_adc_stable = 0; // have we passed the 1st junk value yet?
+uint8_t adc_sample_count = 0; // skip the first sample; it's junk
uint8_t adc_channel = 0; // 0=voltage, 1=temperature
-uint16_t adc_values[2]; // last ADC measurements (0=voltage, 1=temperature)
-uint8_t adcint_enable = 0; // is the current ADC result needed?
-void ADC_inner(); // do the actual ADC-related calculations
+uint16_t adc_raw[2]; // last ADC measurements (0=voltage, 1=temperature)
+uint16_t adc_smooth[2]; // lowpassed ADC measurements (0=voltage, 1=temperature)
+// ADC code is split into two parts:
+// - ISR: runs immediately at each interrupt, does the bare minimum because time is critical here
+// - deferred: the bulk of the logic runs later when time isn't so critical
+uint8_t adc_deferred_enable = 0; // stop waiting and run the deferred code
+void adc_deferred(); // do the actual ADC-related calculations
static inline void ADC_voltage_handler();
volatile uint8_t voltage = 0;
+#ifdef USE_LVP
void low_voltage();
+#endif
#ifdef USE_BATTCHECK
void battcheck();
@@ -63,10 +69,6 @@ void battcheck();
#ifdef USE_THERMAL_REGULATION
-// default 5 seconds between thermal regulation events
-#ifndef THERMAL_WARNING_SECONDS
-#define THERMAL_WARNING_SECONDS 5
-#endif
// try to keep temperature below 45 C
#ifndef DEFAULT_THERM_CEIL
#define DEFAULT_THERM_CEIL 45
@@ -79,14 +81,10 @@ void battcheck();
#ifndef THERM_CAL_OFFSET
#define THERM_CAL_OFFSET 0
#endif
-// temperature now, in C (ish) * 2 (14.1 fixed-point)
+// temperature now, in C (ish)
volatile int16_t temperature;
-// temperature in a few seconds, in C (ish) * 2 (14.1 fixed-point)
-volatile int16_t projected_temperature; // Fight the future!
uint8_t therm_ceil = DEFAULT_THERM_CEIL;
int8_t therm_cal_offset = 0;
-//void low_temperature();
-//void high_temperature();
volatile uint8_t reset_thermal_history = 1;
static inline void ADC_temperature_handler();
#endif // ifdef USE_THERMAL_REGULATION
diff --git a/spaghetti-monster/fsm-events.h b/spaghetti-monster/fsm-events.h
index 39ad3aa..6760fdd 100644
--- a/spaghetti-monster/fsm-events.h
+++ b/spaghetti-monster/fsm-events.h
@@ -85,6 +85,7 @@ static volatile uint16_t ticks_since_last_event = 0;
#ifdef USE_THERMAL_REGULATION
#define EV_temperature_high (B_SYSTEM|0b00000101)
#define EV_temperature_low (B_SYSTEM|0b00000110)
+#define EV_temperature_okay (B_SYSTEM|0b00000111)
#endif
// Button press events
diff --git a/spaghetti-monster/fsm-main.c b/spaghetti-monster/fsm-main.c
index 4ad1e16..790cc68 100644
--- a/spaghetti-monster/fsm-main.c
+++ b/spaghetti-monster/fsm-main.c
@@ -181,7 +181,7 @@ void handle_deferred_interrupts() {
}
*/
if (irq_adc) { // ADC done measuring
- ADC_inner();
+ adc_deferred();
// irq_adc = 0; // takes care of itself
}
if (irq_wdt) { // the clock ticked
diff --git a/spaghetti-monster/fsm-misc.c b/spaghetti-monster/fsm-misc.c
index 8da7b5b..152f047 100644
--- a/spaghetti-monster/fsm-misc.c
+++ b/spaghetti-monster/fsm-misc.c
@@ -46,19 +46,41 @@ uint8_t blink_digit(uint8_t num) {
//StatePtr old_state = current_state;
// "zero" digit gets a single short blink
- uint8_t ontime = BLINK_SPEED * 2 / 10;
+ uint8_t ontime = BLINK_SPEED * 2 / 12;
if (!num) { ontime = 8; num ++; }
for (; num>0; num--) {
set_level(BLINK_BRIGHTNESS);
nice_delay_ms(ontime);
set_level(0);
- nice_delay_ms(BLINK_SPEED * 3 / 10);
+ nice_delay_ms(BLINK_SPEED * 3 / 12);
}
- return nice_delay_ms(BLINK_SPEED * 5 / 10);
+ return nice_delay_ms(BLINK_SPEED * 8 / 12);
}
#endif
+#ifdef USE_BLINK_BIG_NUM
+uint8_t blink_big_num(uint16_t num) {
+ uint16_t digits[] = { 10000, 1000, 100, 10, 1 };
+ uint8_t started = 0;
+ for (uint8_t digit=0; digit<sizeof(digits)/sizeof(uint16_t); digit++) {
+ uint16_t scale = digits[digit];
+ if (num >= scale) {
+ started = 1;
+ }
+ if (started) {
+ uint8_t digit = 0;
+ while (num >= scale) {
+ num -= scale;
+ digit ++;
+ }
+ if (! blink_digit(digit)) return 0;
+ }
+ }
+
+ return nice_delay_ms(1000);
+}
+#endif
#ifdef USE_BLINK_NUM
uint8_t blink_num(uint8_t num) {
//StatePtr old_state = current_state;
diff --git a/spaghetti-monster/fsm-standby.c b/spaghetti-monster/fsm-standby.c
index 9398f52..b002b91 100644
--- a/spaghetti-monster/fsm-standby.c
+++ b/spaghetti-monster/fsm-standby.c
@@ -73,8 +73,8 @@ void sleep_until_eswitch_pressed()
go_to_standby = 0;
}
if (irq_adc) { // ADC done measuring
- adcint_enable = 1;
- ADC_inner();
+ adc_deferred_enable = 1;
+ adc_deferred();
//ADC_off(); // takes care of itself
//irq_adc = 0; // takes care of itself
}
diff --git a/spaghetti-monster/fsm-wdt.c b/spaghetti-monster/fsm-wdt.c
index 0c49a75..94266c1 100644
--- a/spaghetti-monster/fsm-wdt.c
+++ b/spaghetti-monster/fsm-wdt.c
@@ -111,7 +111,7 @@ void WDT_inner() {
#ifdef TICK_DURING_STANDBY
// handle standby mode specially
if (go_to_standby) {
- // emit a halfsleep tick, and process it
+ // emit a sleep tick, and process it
emit(EV_sleep_tick, ticks_since_last);
process_emissions();
@@ -119,9 +119,9 @@ void WDT_inner() {
return; // no sleep LVP needed if nothing drains power while off
#else
// stop here, usually... but proceed often enough for sleep LVP to work
- if (0 != (ticks_since_last & 0x7f)) return;
+ if (0 != (ticks_since_last & 0x3f)) return;
- adc_trigger = 255; // make sure a measurement will happen
+ adc_trigger = 0; // make sure a measurement will happen
ADC_on(); // enable ADC voltage measurement functions temporarily
#endif
}
@@ -178,13 +178,13 @@ void WDT_inner() {
#endif
#if defined(USE_LVP) || defined(USE_THERMAL_REGULATION)
- // start a new ADC measurement every 4 ticks
- adc_trigger ++;
- if (0 == (adc_trigger & 3)) {
+ // enable the deferred ADC handler once in a while
+ if (! adc_trigger) {
ADC_start_measurement();
- irq_adc_stable = 0;
- adcint_enable = 1;
+ adc_deferred_enable = 1;
}
+ // timing for the ADC handler is every 32 ticks (~2Hz)
+ adc_trigger = (adc_trigger + 1) & 31;
#endif
}
diff --git a/spaghetti-monster/ramping-ui/ramping-ui.c b/spaghetti-monster/ramping-ui/ramping-ui.c
index 18f488d..5eb7d8f 100644
--- a/spaghetti-monster/ramping-ui/ramping-ui.c
+++ b/spaghetti-monster/ramping-ui/ramping-ui.c
@@ -22,7 +22,6 @@
#define USE_THERMAL_REGULATION
#define DEFAULT_THERM_CEIL 32
#define USE_DELAY_MS
-#define USE_DELAY_4MS
#define USE_DELAY_ZERO
#define USE_RAMPING
#define USE_BATTCHECK
@@ -353,7 +352,7 @@ void loop() {
battcheck();
}
else if (current_state == tempcheck_state) {
- blink_num(projected_temperature>>2);
+ blink_num(temperature);
nice_delay_ms(1000);
}
#endif
diff --git a/spaghetti-monster/rampingios/rampingiosv3.c b/spaghetti-monster/rampingios/rampingiosv3.c
index d72e971..7f03e77 100644
--- a/spaghetti-monster/rampingios/rampingiosv3.c
+++ b/spaghetti-monster/rampingios/rampingiosv3.c
@@ -123,6 +123,7 @@ uint8_t ramp_config_state(Event event, uint16_t arg);
uint8_t battcheck_state(Event event, uint16_t arg);
#endif
#ifdef USE_THERMAL_REGULATION
+#define USE_BLINK_NUM
uint8_t tempcheck_state(Event event, uint16_t arg);
uint8_t thermal_config_state(Event event, uint16_t arg);
#endif
@@ -930,14 +931,15 @@ void thermal_config_save() {
// calibrate room temperature
val = config_state_values[0];
if (val) {
- int8_t rawtemp = (temperature >> 1) - therm_cal_offset;
+ int8_t rawtemp = temperature - therm_cal_offset;
therm_cal_offset = val - rawtemp;
+ reset_thermal_history = 1; // invalidate all recent temperature data
}
val = config_state_values[1];
if (val) {
// set maximum heat limit
- therm_ceil = 30 + val;
+ therm_ceil = 30 + val - 1;
}
if (therm_ceil > MAX_THERM_CEIL) therm_ceil = MAX_THERM_CEIL;
}
@@ -966,9 +968,9 @@ uint8_t beacon_config_state(Event event, uint16_t arg) {
inline void beacon_mode_iter() {
// one iteration of main loop()
set_level(memorized_level);
- nice_delay_ms(500);
+ nice_delay_ms(100);
set_level(0);
- nice_delay_ms(((beacon_seconds) * 1000) - 500);
+ nice_delay_ms(((beacon_seconds) * 1000) - 100);
}
#endif // #ifdef USE_BEACON_MODE
@@ -1235,7 +1237,7 @@ void loop() {
#ifdef USE_THERMAL_REGULATION
// TODO: blink out therm_ceil during thermal_config_state?
else if (state == tempcheck_state) {
- blink_num(temperature>>1);
+ blink_num(temperature);
nice_delay_ms(1000);
}
#endif
diff --git a/spaghetti-monster/spaghetti-monster.txt b/spaghetti-monster/spaghetti-monster.txt
index 9e051f1..434e1bc 100644
--- a/spaghetti-monster/spaghetti-monster.txt
+++ b/spaghetti-monster/spaghetti-monster.txt
@@ -124,14 +124,13 @@ Event types:
between events.
- EV_temperature_high: Sent whenever the MCU's projected temperature
- is higher than therm_ceil. Minimum of THERMAL_WARNING_SECONDS
- between events. The 'arg' indicates how far the temperature
- exceeds the limit.
+ is higher than therm_ceil. Minimum of one second between events.
+ The 'arg' indicates how far the temperature exceeds the limit.
- EV_temperature_low: Sent whenever the MCU's projected temperature
is lower than (therm_ceil - THERMAL_WINDOW_SIZE). Minimum of
- THERMAL_WARNING_SECONDS between events. The 'arg' indicates how
- far the temperature exceeds the limit.
+ one second between events. The 'arg' indicates how far the
+ temperature exceeds the limit.
Button presses:
@@ -297,9 +296,6 @@ Useful #defines:
- DEFAULT_THERM_CEIL: Set the temperature limit to use by default
when the user hasn't configured anything.
- - THERMAL_WARNING_SECONDS: How long to wait between temperature
- events.
-
- USE_RAMPING: Enable smooth ramping helpers.
- RAMP_LENGTH: Pick a pre-defined ramp by length. Defined sizes
diff --git a/spaghetti-monster/werner/werner.c b/spaghetti-monster/werner/werner.c
index 7c47cd7..4159fc6 100644
--- a/spaghetti-monster/werner/werner.c
+++ b/spaghetti-monster/werner/werner.c
@@ -467,14 +467,15 @@ void thermal_config_save() {
// calibrate room temperature
val = config_state_values[0];
if (val) {
- int8_t rawtemp = (temperature >> 1) - therm_cal_offset;
+ int8_t rawtemp = temperature - therm_cal_offset;
therm_cal_offset = val - rawtemp;
+ reset_thermal_history = 1; // invalidate all recent temperature data
}
val = config_state_values[1];
if (val) {
// set maximum heat limit
- therm_ceil = 30 + val;
+ therm_ceil = 30 + val - 1;
}
if (therm_ceil > MAX_THERM_CEIL) therm_ceil = MAX_THERM_CEIL;
}
@@ -589,7 +590,7 @@ uint8_t nearest_level(int16_t target) {
for(uint8_t i=0; i<ramp_discrete_steps; i++) {
this_level = ramp_discrete_floor + (i * (uint16_t)ramp_range / (ramp_discrete_steps-1));
- int8_t diff = target - this_level;
+ int16_t diff = target - this_level;
if (diff < 0) diff = -diff;
if (diff <= (ramp_discrete_step_size>>1))
return this_level;
@@ -684,9 +685,6 @@ void loop() {
StatePtr state = current_state;
- #ifdef USE_DYNAMIC_UNDERCLOCKING
- auto_clock_speed();
- #endif
if (0) {}
#ifdef USE_BATTCHECK
@@ -697,7 +695,7 @@ void loop() {
#ifdef USE_THERMAL_REGULATION
// TODO: blink out therm_ceil during thermal_config_state
else if (state == tempcheck_state) {
- blink_num(temperature>>1);
+ blink_num(temperature);
nice_delay_ms(1000);
}
#endif