aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSelene ToyKeeper2019-11-14 19:44:57 -0700
committerSelene ToyKeeper2019-11-14 19:44:57 -0700
commit1a85469b00856020e061170c031d47173f298d93 (patch)
treeb30725c34f27797516f8bc82b49bcf59801b17c8
parentfixed some compile issues related to delay_4ms() (diff)
parentturned off muggle mode on Q8 and MF01S, to make builds small enough again (diff)
downloadanduril-1a85469b00856020e061170c031d47173f298d93.tar.gz
anduril-1a85469b00856020e061170c031d47173f298d93.tar.bz2
anduril-1a85469b00856020e061170c031d47173f298d93.zip
merged irq-refactor branch, which fixes some small but long-standing issues:
- occasional missed button events while asleep - occasional short/aborted frames in aux LED sleep animation - rare case of bogus voltage and/or temperature values - fixed issue where nice_delay_ms() didn't work in setup() - fixed theoretical possibility of extra-noisy buttons causing a hang Also does some other things: - cleans up the ADC code significantly - adds a voltage stabilizer/lowpass option (enabled on t1634 builds) - greatly reduces time spent per interrupt, which might make a future PWM-DSM technique possible - moves most interrupt-handling logic to a non-critical code path, deferring that code until timing doesn't matter as much - sped up button state measurements - turned off muggle mode on a couple builds which were too big
-rw-r--r--spaghetti-monster/anduril/anduril.c10
-rw-r--r--spaghetti-monster/anduril/cfg-blf-q8.h5
-rw-r--r--spaghetti-monster/anduril/cfg-emisar-d4s.h1
-rw-r--r--spaghetti-monster/anduril/cfg-emisar-d4sv2.h4
-rw-r--r--spaghetti-monster/anduril/cfg-emisar-d4v2.h3
-rw-r--r--spaghetti-monster/anduril/cfg-mateminco-mf01s.h6
-rw-r--r--spaghetti-monster/fsm-adc.c468
-rw-r--r--spaghetti-monster/fsm-adc.h16
-rw-r--r--spaghetti-monster/fsm-events.c15
-rw-r--r--spaghetti-monster/fsm-main.c22
-rw-r--r--spaghetti-monster/fsm-main.h2
-rw-r--r--spaghetti-monster/fsm-pcint.c21
-rw-r--r--spaghetti-monster/fsm-pcint.h1
-rw-r--r--spaghetti-monster/fsm-standby.c19
-rw-r--r--spaghetti-monster/fsm-wdt.c22
-rw-r--r--spaghetti-monster/fsm-wdt.h4
16 files changed, 353 insertions, 266 deletions
diff --git a/spaghetti-monster/anduril/anduril.c b/spaghetti-monster/anduril/anduril.c
index 48f4ac1..19d61b7 100644
--- a/spaghetti-monster/anduril/anduril.c
+++ b/spaghetti-monster/anduril/anduril.c
@@ -2356,7 +2356,7 @@ void rgb_led_update(uint8_t mode, uint8_t arg) {
void factory_reset() {
// display a warning for a few seconds before doing the actual reset,
// so the user has time to abort if they want
- #define SPLODEY_TIME 3000
+ #define SPLODEY_TIME 2500
#define SPLODEY_STEPS 64
#define SPLODEY_TIME_PER_STEP (SPLODEY_TIME/SPLODEY_STEPS)
uint8_t bright;
@@ -2364,9 +2364,9 @@ void factory_reset() {
// wind up to an explosion
for (bright=0; bright<SPLODEY_STEPS; bright++) {
set_level(bright);
- delay_4ms(SPLODEY_TIME_PER_STEP/2/4);
+ nice_delay_ms(SPLODEY_TIME_PER_STEP/2);
set_level(bright>>1);
- delay_4ms(SPLODEY_TIME_PER_STEP/2/4);
+ nice_delay_ms(SPLODEY_TIME_PER_STEP/2);
if (! button_is_pressed()) {
reset = 0;
break;
@@ -2387,14 +2387,14 @@ void factory_reset() {
bright = MAX_LEVEL;
for (; bright > 0; bright--) {
set_level(bright);
- delay_4ms(SPLODEY_TIME_PER_STEP/6/4);
+ nice_delay_ms(SPLODEY_TIME_PER_STEP/8);
}
}
// explosion cancelled, fade away
else {
for (; bright > 0; bright--) {
set_level(bright);
- delay_4ms(SPLODEY_TIME_PER_STEP/3/4);
+ nice_delay_ms(SPLODEY_TIME_PER_STEP/3);
}
}
}
diff --git a/spaghetti-monster/anduril/cfg-blf-q8.h b/spaghetti-monster/anduril/cfg-blf-q8.h
index 970fedb..166b10e 100644
--- a/spaghetti-monster/anduril/cfg-blf-q8.h
+++ b/spaghetti-monster/anduril/cfg-blf-q8.h
@@ -13,6 +13,11 @@
// lockout: blinking (3)
#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 2)
+// doesn't quite fit
+#ifdef USE_MUGGLE_MODE
+#undef USE_MUGGLE_MODE
+#endif
+
// copied from Emisar D4 ramp
// ../../bin/level_calc.py 1 65 7135 1 0.8 150
// ... mixed with this:
diff --git a/spaghetti-monster/anduril/cfg-emisar-d4s.h b/spaghetti-monster/anduril/cfg-emisar-d4s.h
index 230ac7c..6fe95a6 100644
--- a/spaghetti-monster/anduril/cfg-emisar-d4s.h
+++ b/spaghetti-monster/anduril/cfg-emisar-d4s.h
@@ -46,3 +46,4 @@
#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 acc9101..c47e774 100644
--- a/spaghetti-monster/anduril/cfg-emisar-d4sv2.h
+++ b/spaghetti-monster/anduril/cfg-emisar-d4sv2.h
@@ -61,3 +61,7 @@
// 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 b83c65c..0db1062 100644
--- a/spaghetti-monster/anduril/cfg-emisar-d4v2.h
+++ b/spaghetti-monster/anduril/cfg-emisar-d4v2.h
@@ -49,3 +49,6 @@
#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-mateminco-mf01s.h b/spaghetti-monster/anduril/cfg-mateminco-mf01s.h
index 0585b38..2af1305 100644
--- a/spaghetti-monster/anduril/cfg-mateminco-mf01s.h
+++ b/spaghetti-monster/anduril/cfg-mateminco-mf01s.h
@@ -14,6 +14,12 @@
#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 1)
+// doesn't quite fit
+#ifdef USE_MUGGLE_MODE
+#undef USE_MUGGLE_MODE
+#endif
+
+
// don't blink during ramp, it's irrelevant and annoying on this light
#define BLINK_AT_RAMP_CEILING
#undef BLINK_AT_RAMP_MIDDLE
diff --git a/spaghetti-monster/fsm-adc.c b/spaghetti-monster/fsm-adc.c
index 6832e32..c859539 100644
--- a/spaghetti-monster/fsm-adc.c
+++ b/spaghetti-monster/fsm-adc.c
@@ -30,6 +30,7 @@ inline void set_admux_therm() {
#else
#error Unrecognized MCU type
#endif
+ adc_channel = 1;
}
inline void set_admux_voltage() {
@@ -52,6 +53,7 @@ inline void set_admux_voltage() {
#else
#error Unrecognized MCU type
#endif
+ adc_channel = 0;
}
inline void ADC_start_measurement() {
@@ -113,36 +115,158 @@ static inline uint8_t calc_voltage_divider(uint16_t value) {
#else
#define ADC_CYCLES_PER_SECOND 8
#endif
-// TODO: is this better done in main() or WDT()?
+
+#ifdef USE_THERMAL_REGULATION
+#define ADC_STEPS 2
+#else
+#define ADC_STEPS 1
+#endif
+
+// happens every time the ADC sampler finishes a measurement
ISR(ADC_vect) {
- // For some reason, the ADC interrupt is getting called a *lot*
- // more often than it should be, like it's auto-triggering after each
- // measurement, but I don't know why, or how to turn that off...
- // So, skip every call except when explicitly requested.
+ #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
+ }
+ irq_adc_stable = 1;
+
+ // start another measurement
+ // (is explicit because it otherwise doesn't seem to happen during standby mode)
+ ADC_start_measurement();
+}
+
+void ADC_inner() {
+ 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;
+
+ // disable after one iteration
adcint_enable = 0;
+ #ifdef TICK_DURING_STANDBY
+ // 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();
+ #endif
+
+ // what is being measured? 0 = battery voltage, 1 = temperature
static uint8_t adc_step = 0;
- // LVP declarations
#ifdef USE_LVP
- #ifdef USE_LVP_AVG
- #define NUM_VOLTAGE_VALUES 4
- static int16_t voltage_values[NUM_VOLTAGE_VALUES];
+ if (0 == adc_step) { // voltage
+ ADC_voltage_handler();
+ }
+ #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_admux_voltage();
+ #endif
#endif
+
+ irq_adc_stable = 0; // first result is unstable
+}
+
+
+#ifdef USE_LVP
+static inline void ADC_voltage_handler() {
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
+
+ #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
+
+ #ifdef USE_VOLTAGE_DIVIDER
+ voltage = calc_voltage_divider(measurement);
+ #else
+ // 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;
#endif
+ // if low, callback EV_voltage_low / EV_voltage_critical
+ // (but only if it has been more than N ticks 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;
+ }
+ }
+}
+#endif
+
+
+#ifdef USE_THERMAL_REGULATION
+static inline void ADC_temperature_handler() {
// thermal declarations
- #ifdef USE_THERMAL_REGULATION
#ifndef THERMAL_UPDATE_SPEED
#define THERMAL_UPDATE_SPEED 2
#endif
#define NUM_THERMAL_VALUES_HISTORY 8
- #define ADC_STEPS 4
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;
@@ -151,236 +275,132 @@ ISR(ADC_vect) {
#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
- #else
- #define ADC_STEPS 2
- #endif
-
- uint16_t measurement = ADC; // latest 10-bit ADC reading
-
- #ifdef USE_PSEUDO_RAND
- // real-world entropy makes this a true random, not pseudo
- pseudo_rand_seed += measurement;
- #endif
-
- #if defined(TICK_DURING_STANDBY) && defined(USE_SLEEP_LVP)
- // only measure battery voltage while asleep
- if (go_to_standby) adc_step = 1;
- else
- #endif
- adc_step = (adc_step + 1) & (ADC_STEPS-1);
-
- #ifdef USE_LVP
- // voltage
- if (adc_step == 1) {
- #ifdef USE_LVP_AVG
- // prime on first execution
- if (voltage == 0) {
- for(uint8_t i=0; i<NUM_VOLTAGE_VALUES; i++)
- voltage_values[i] = measurement;
- voltage = 42; // the answer to life, the universe, and the voltage of a full li-ion cell
- } else {
- uint16_t total = 0;
- uint8_t i;
- for(i=0; i<NUM_VOLTAGE_VALUES-1; i++) {
- voltage_values[i] = voltage_values[i+1];
- total += voltage_values[i];
- }
- voltage_values[i] = measurement;
- total += measurement;
- total = total >> 2;
-
- #ifdef USE_VOLTAGE_DIVIDER
- voltage = calc_voltage_divider(total);
- #else
- voltage = (uint16_t)(1.1*1024*10)/total + VOLTAGE_FUDGE_FACTOR;
- #endif
- }
- #else // no USE_LVP_AVG
- #ifdef USE_VOLTAGE_DIVIDER
- voltage = calc_voltage_divider(measurement);
- #else
- // 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;
- #endif
- #endif
- // if low, callback EV_voltage_low / EV_voltage_critical
- // (but only if it has been more than N ticks 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;
- }
+ uint16_t measurement = adc_values[1]; // latest 10-bit ADC reading
+
+ // Convert ADC units to Celsius (ish)
+ int16_t temp = measurement - 275 + THERM_CAL_OFFSET + (int16_t)therm_cal_offset;
+
+ // 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 --;
}
}
- #endif // ifdef USE_LVP
+ // guess what the temperature will be in a few seconds
+ int16_t pt;
+ {
+ int16_t diff;
+ int16_t t = temperature;
- #ifdef USE_THERMAL_REGULATION
- // temperature
- else if (adc_step == 3) {
- // Convert ADC units to Celsius (ish)
- int16_t temp = measurement - 275 + THERM_CAL_OFFSET + (int16_t)therm_cal_offset;
-
- // 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 --;
+ // 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 15
+ #elif (THERMAL_UPDATE_SPEED == 2) // new value every 2s
+ #define THERM_HISTORY_STEP_MAX 7
+ #elif (THERMAL_UPDATE_SPEED == 1) // new value every 1s
+ #define THERM_HISTORY_STEP_MAX 3
+ #elif (THERMAL_UPDATE_SPEED == 0) // new value every 0.5s
+ #define THERM_HISTORY_STEP_MAX 1
+ #endif
+ if (0 == (history_step & THERM_HISTORY_STEP_MAX)) {
+ // 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;
}
- // guess what the temperature will be in a few seconds
- int16_t pt;
- {
- int16_t diff;
- int16_t t = temperature;
-
- // 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 15
- #elif (THERMAL_UPDATE_SPEED == 2) // new value every 2s
- #define THERM_HISTORY_STEP_MAX 7
- #elif (THERMAL_UPDATE_SPEED == 1) // new value every 1s
- #define THERM_HISTORY_STEP_MAX 3
- #elif (THERMAL_UPDATE_SPEED == 0) // new value every 0.5s
- #define THERM_HISTORY_STEP_MAX 1
- #endif
- if (0 == (history_step & THERM_HISTORY_STEP_MAX)) {
- // 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;
- }
-
- // 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 --;
- }
- // 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);
+ // 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 --;
}
+ // 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
- }
+ // 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);
- }
+ 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 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?
+ 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);
}
}
}
- #endif // ifdef USE_THERMAL_REGULATION
-
-
- // set the correct type of measurement for next time
- #ifdef USE_THERMAL_REGULATION
- #ifdef USE_LVP
- if (adc_step < 2) set_admux_voltage();
- else set_admux_therm();
- #else
- set_admux_therm();
- #endif
- #else
- #ifdef USE_LVP
- set_admux_voltage();
- #endif
- #endif
-
- #ifdef TICK_DURING_STANDBY
- // if we were asleep, go back to sleep
- if (go_to_standby) ADC_off();
- #endif
}
+#endif
+
#ifdef USE_BATTCHECK
#ifdef BATTCHECK_4bars
diff --git a/spaghetti-monster/fsm-adc.h b/spaghetti-monster/fsm-adc.h
index 274fb4d..6e39750 100644
--- a/spaghetti-monster/fsm-adc.h
+++ b/spaghetti-monster/fsm-adc.h
@@ -38,9 +38,18 @@
#define VOLTAGE_FUDGE_FACTOR 5
#endif
#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_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
+
+static inline void ADC_voltage_handler();
volatile uint8_t voltage = 0;
-volatile uint8_t adcint_enable; // kludge, because adc auto-retrigger won't turn off
void low_voltage();
+
#ifdef USE_BATTCHECK
void battcheck();
#ifdef BATTCHECK_VpT
@@ -50,7 +59,7 @@ void battcheck();
#define USE_BLINK_DIGIT
#endif
#endif
-#endif
+#endif // ifdef USE_LVP
#ifdef USE_THERMAL_REGULATION
@@ -79,7 +88,8 @@ int8_t therm_cal_offset = 0;
//void low_temperature();
//void high_temperature();
volatile uint8_t reset_thermal_history = 1;
-#endif
+static inline void ADC_temperature_handler();
+#endif // ifdef USE_THERMAL_REGULATION
inline void ADC_on();
diff --git a/spaghetti-monster/fsm-events.c b/spaghetti-monster/fsm-events.c
index 3e9a12d..a1b013a 100644
--- a/spaghetti-monster/fsm-events.c
+++ b/spaghetti-monster/fsm-events.c
@@ -110,7 +110,7 @@ uint8_t nice_delay_ms(uint16_t ms) {
/* // delay_zero() implementation
if (ms == 0) {
CLKPR = 1<<CLKPCE; CLKPR = 0; // full speed
- _delay_loop_2(BOGOMIPS*98/100/3);
+ _delay_loop_2(BOGOMIPS*95/100/3);
return 1;
}
*/
@@ -120,15 +120,15 @@ uint8_t nice_delay_ms(uint16_t ms) {
uint8_t level = actual_level; // volatile, avoid repeat access
if (level < QUARTERSPEED_LEVEL) {
clock_prescale_set(clock_div_4);
- _delay_loop_2(BOGOMIPS*98/100/4);
+ _delay_loop_2(BOGOMIPS*95/100/4);
}
//else if (level < HALFSPEED_LEVEL) {
// clock_prescale_set(clock_div_2);
- // _delay_loop_2(BOGOMIPS*98/100/2);
+ // _delay_loop_2(BOGOMIPS*95/100/2);
//}
else {
clock_prescale_set(clock_div_1);
- _delay_loop_2(BOGOMIPS*98/100);
+ _delay_loop_2(BOGOMIPS*95/100);
}
// restore regular clock speed
clock_prescale_set(clock_div_1);
@@ -136,15 +136,18 @@ uint8_t nice_delay_ms(uint16_t ms) {
// underclock MCU to save power
clock_prescale_set(clock_div_4);
// wait
- _delay_loop_2(BOGOMIPS*98/100/4);
+ _delay_loop_2(BOGOMIPS*95/100/4);
// restore regular clock speed
clock_prescale_set(clock_div_1);
#endif // ifdef USE_RAMPING
#else
// wait
- _delay_loop_2(BOGOMIPS*98/100);
+ _delay_loop_2(BOGOMIPS*95/100);
#endif // ifdef USE_DYNAMIC_UNDERCLOCKING
+ // run pending system processes while we wait
+ handle_deferred_interrupts();
+
if ((nice_delay_interrupt) || (old_state != current_state)) {
return 0; // state changed; abort
}
diff --git a/spaghetti-monster/fsm-main.c b/spaghetti-monster/fsm-main.c
index e537a9e..c9ab69b 100644
--- a/spaghetti-monster/fsm-main.c
+++ b/spaghetti-monster/fsm-main.c
@@ -123,7 +123,6 @@ int main() {
#else
delay_4ms(1);
#endif
- empty_event_sequence();
// fallback for handling a few things
#ifndef DONT_USE_DEFAULT_STATE
@@ -160,6 +159,9 @@ int main() {
standby_mode();
}
+ // catch up on interrupts
+ handle_deferred_interrupts();
+
// give the recipe some time slices
loop();
@@ -168,4 +170,22 @@ int main() {
}
}
+
+void handle_deferred_interrupts() {
+ /*
+ if (irq_pcint) { // button pressed or released
+ // nothing to do here
+ // (PCINT only matters during standby)
+ }
+ */
+ if (irq_adc) { // ADC done measuring
+ ADC_inner();
+ // irq_adc = 0; // takes care of itself
+ }
+ if (irq_wdt) { // the clock ticked
+ WDT_inner();
+ // irq_wdt = 0; // takes care of itself
+ }
+}
+
#endif
diff --git a/spaghetti-monster/fsm-main.h b/spaghetti-monster/fsm-main.h
index cc469d7..55ae2ff 100644
--- a/spaghetti-monster/fsm-main.h
+++ b/spaghetti-monster/fsm-main.h
@@ -21,5 +21,7 @@
#define FSM_MAIN_H
int main();
+// needs to run frequently to execute the logic for WDT and ADC and stuff
+void handle_deferred_interrupts();
#endif
diff --git a/spaghetti-monster/fsm-pcint.c b/spaghetti-monster/fsm-pcint.c
index 4928980..a4a496a 100644
--- a/spaghetti-monster/fsm-pcint.c
+++ b/spaghetti-monster/fsm-pcint.c
@@ -24,19 +24,9 @@
#include <util/delay_basic.h>
uint8_t button_is_pressed() {
- // remember the past 32 measurements
- static uint32_t readings = 0;
- // take at least one new measurement,
- // and wait for measurements to settle to all zeroes or all ones
- do {
- // shift past readings and add current value
- readings = (readings << 1) | ((SWITCH_PORT & (1<<SWITCH_PIN)) == 0);
- // wait a moment
- _delay_loop_2(BOGOMIPS/16); // up to 2ms to stabilize
- }
- while ((readings != 0) && (readings != 0xFFFFFFFF));
- button_last_state = readings;
- return readings;
+ uint8_t value = ((SWITCH_PORT & (1<<SWITCH_PIN)) == 0);
+ button_last_state = value;
+ return value;
}
inline void PCINT_on() {
@@ -75,7 +65,10 @@ inline void PCINT_off() {
//void button_change_interrupt() {
#if (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85) || (ATTINY == 1634)
-EMPTY_INTERRUPT(PCINT0_vect);
+//EMPTY_INTERRUPT(PCINT0_vect);
+ISR(PCINT0_vect) {
+ irq_pcint = 1;
+}
#else
#error Unrecognized MCU type
#endif
diff --git a/spaghetti-monster/fsm-pcint.h b/spaghetti-monster/fsm-pcint.h
index ec3ae4b..a7f3733 100644
--- a/spaghetti-monster/fsm-pcint.h
+++ b/spaghetti-monster/fsm-pcint.h
@@ -20,6 +20,7 @@
#ifndef FSM_PCINT_H
#define FSM_PCINT_H
+volatile uint8_t irq_pcint = 0; // pin change interrupt happened?
//static volatile uint8_t button_was_pressed;
#define BP_SAMPLES 32
volatile uint8_t button_last_state;
diff --git a/spaghetti-monster/fsm-standby.c b/spaghetti-monster/fsm-standby.c
index 44b047a..4124d92 100644
--- a/spaghetti-monster/fsm-standby.c
+++ b/spaghetti-monster/fsm-standby.c
@@ -42,13 +42,15 @@ void sleep_until_eswitch_pressed()
// make sure switch isn't currently pressed
while (button_is_pressed()) {}
empty_event_sequence(); // cancel pending input on suspend
- //PCINT_since_WDT = 0; // ensure PCINT won't ignore itself
PCINT_on(); // wake on e-switch event
#ifdef TICK_DURING_STANDBY
+ // detect which type of event caused a wake-up
+ irq_adc = 0;
+ irq_wdt = 0;
+ irq_pcint = 0;
while (go_to_standby) {
- f_wdt = 0; // detect if WDT was what caused a wake-up
#else
go_to_standby = 0;
#endif
@@ -65,10 +67,19 @@ void sleep_until_eswitch_pressed()
sleep_disable();
#ifdef TICK_DURING_STANDBY
- // determine what woke us up... WDT or PCINT
- if (! f_wdt) { // PCINT went off; wake up
+ // determine what woke us up...
+ if (irq_pcint) { // button pressed; wake up
go_to_standby = 0;
}
+ if (irq_adc) { // ADC done measuring
+ adcint_enable = 1;
+ ADC_inner();
+ //ADC_off(); // takes care of itself
+ //irq_adc = 0; // takes care of itself
+ }
+ if (irq_wdt) { // generate a sleep tick
+ WDT_inner();
+ }
}
#endif
diff --git a/spaghetti-monster/fsm-wdt.c b/spaghetti-monster/fsm-wdt.c
index 6e61e87..beab1a2 100644
--- a/spaghetti-monster/fsm-wdt.c
+++ b/spaghetti-monster/fsm-wdt.c
@@ -82,11 +82,23 @@ inline void WDT_off()
// clock tick -- this runs every 16ms (62.5 fps)
ISR(WDT_vect) {
+ irq_wdt = 1; // WDT event happened
+}
+
+void WDT_inner() {
+ irq_wdt = 0; // WDT event handled; reset flag
+
static uint8_t adc_trigger = 0;
- #ifdef TICK_DURING_STANDBY
- f_wdt = 1; // WDT event happened
+ // detect and emit button change events (even during standby)
+ uint8_t was_pressed = button_last_state;
+ uint8_t pressed = button_is_pressed();
+ if (was_pressed != pressed) {
+ go_to_standby = 0;
+ PCINT_inner(pressed);
+ }
+ #ifdef TICK_DURING_STANDBY
static uint16_t sleep_counter = 0;
// handle standby mode specially
if (go_to_standby) {
@@ -107,11 +119,6 @@ ISR(WDT_vect) {
else { sleep_counter = 0; }
#endif
- // detect and emit button change events
- uint8_t was_pressed = button_last_state;
- uint8_t pressed = button_is_pressed();
- if (was_pressed != pressed) PCINT_inner(pressed);
-
// cache this here to reduce ROM size, because it's volatile
uint16_t ticks_since_last = ticks_since_last_event;
@@ -178,6 +185,7 @@ ISR(WDT_vect) {
if (go_to_standby) ADC_on();
#endif
ADC_start_measurement();
+ irq_adc_stable = 0;
adcint_enable = 1;
}
#endif
diff --git a/spaghetti-monster/fsm-wdt.h b/spaghetti-monster/fsm-wdt.h
index 78fe791..d127551 100644
--- a/spaghetti-monster/fsm-wdt.h
+++ b/spaghetti-monster/fsm-wdt.h
@@ -25,9 +25,9 @@
void WDT_on();
inline void WDT_off();
-#ifdef TICK_DURING_STANDBY
-volatile uint8_t f_wdt = 0;
+volatile uint8_t irq_wdt = 0; // WDT interrupt happened?
+#ifdef TICK_DURING_STANDBY
#if defined(USE_INDICATOR_LED) || defined(USE_AUX_RGB_LEDS)
// measure battery charge while asleep
#define USE_SLEEP_LVP
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.