From 849fcfd3e1b828c6f35d62792a308917b2ef0053 Mon Sep 17 00:00:00 2001 From: Selene ToyKeeper Date: Fri, 26 Jul 2019 20:51:38 -0600 Subject: merged a sanitized copy of the Emisar D4v2 branch; history summarized below: ------------------------------------------------------------ revno: 457 committer: Selene ToyKeeper branch nick: emisar-d4v2 timestamp: Thu 2019-07-18 22:42:50 -0600 message: make sure no thermal events are handled while off (even though it shouldn't be possible, since it only happens after 15 minutes, and the light is only awake in muggle-off mode for 1 second) modified: ToyKeeper/spaghetti-monster/anduril/anduril.c ------------------------------------------------------------ revno: 456 committer: Selene ToyKeeper branch nick: emisar-d4v2 timestamp: Thu 2019-07-18 20:09:58 -0600 message: don't measure temperature while asleep (fixes bug on D4 V2, where it would start flashing after 15 minutes turned off in muggle mode) (also should speed up response to battery voltage changes while asleep) modified: ToyKeeper/spaghetti-monster/fsm-adc.c ------------------------------------------------------------ revno: 455 committer: Selene ToyKeeper branch nick: emisar-d4v2 timestamp: Thu 2019-07-18 20:05:56 -0600 message: made strobe brightness configurable per build target modified: ToyKeeper/spaghetti-monster/anduril/anduril.c ------------------------------------------------------------ revno: 454 committer: Selene ToyKeeper branch nick: emisar-d4v2 timestamp: Wed 2019-07-10 10:22:44 -0600 message: made the "therm faster" code only affect ramping down, and fixed a couple minor logic issues in the thermal regulation code modified: ToyKeeper/spaghetti-monster/anduril/anduril.c ToyKeeper/spaghetti-monster/fsm-adc.c ------------------------------------------------------------ revno: 452 committer: Selene ToyKeeper branch nick: emisar-d4v2 timestamp: Sat 2019-07-06 08:18:25 -0600 message: fixed spelling error in D4 / D4V2 files modified: ToyKeeper/spaghetti-monster/anduril/cfg-emisar-d4.h ToyKeeper/spaghetti-monster/anduril/cfg-emisar-d4v2.h ------------------------------------------------------------ revno: 451 committer: Selene ToyKeeper branch nick: emisar-d4v2 timestamp: Sat 2019-07-06 08:16:57 -0600 message: fixed placement of #ifdefs on reboot loop prevention modified: ToyKeeper/spaghetti-monster/fsm-main.c ------------------------------------------------------------ revno: 450 committer: Selene ToyKeeper branch nick: emisar-d4v2 timestamp: Sat 2019-07-06 08:15:59 -0600 message: made level_calc.py also find and show the channel transition points, so I won't have to do this manually all the time modified: bin/level_calc.py ------------------------------------------------------------ revno: 449 [merge] committer: Selene ToyKeeper branch nick: emisar-d4v2 timestamp: Sat 2019-07-06 05:31:37 -0600 message: merged from fsm branch to get factory reset function modified: ToyKeeper/spaghetti-monster/anduril/anduril-manual.txt ToyKeeper/spaghetti-monster/anduril/anduril.c ToyKeeper/spaghetti-monster/anduril/anduril.txt ToyKeeper/spaghetti-monster/anduril/cfg-fw3a.h ToyKeeper/spaghetti-monster/fsm-main.c ToyKeeper/spaghetti-monster/fsm-misc.c ToyKeeper/spaghetti-monster/fsm-misc.h ------------------------------------------------------------ revno: 448 committer: Selene ToyKeeper branch nick: emisar-d4v2 timestamp: Sat 2019-07-06 05:13:40 -0600 message: started adding some ideas for later (maybe), but not currently used... ... may remove again later modified: ToyKeeper/spaghetti-monster/fsm-adc.c ------------------------------------------------------------ revno: 447 committer: Selene ToyKeeper branch nick: emisar-d4v2 timestamp: Sat 2019-07-06 05:13:11 -0600 message: adjusted D4 / D4v2 thermal response again; it really doesn't want to behave modified: ToyKeeper/spaghetti-monster/anduril/cfg-emisar-d4.h ToyKeeper/spaghetti-monster/anduril/cfg-emisar-d4v2.h ------------------------------------------------------------ revno: 446 committer: Selene ToyKeeper branch nick: emisar-d4v2 timestamp: Mon 2019-06-17 03:18:06 -0600 message: made d4 ramp slightly smoother at clock speed change modified: ToyKeeper/spaghetti-monster/anduril/cfg-emisar-d4.h ToyKeeper/spaghetti-monster/anduril/cfg-emisar-d4v2.h ------------------------------------------------------------ revno: 445 committer: Selene ToyKeeper branch nick: emisar-d4v2 timestamp: Mon 2019-06-17 03:17:40 -0600 message: improved D4S thermal response; less prone to overshooting and oscillation modified: ToyKeeper/spaghetti-monster/anduril/cfg-emisar-d4s.h ------------------------------------------------------------ revno: 444 committer: Selene ToyKeeper branch nick: emisar-d4v2 timestamp: Thu 2019-06-06 23:12:26 -0600 message: adjusted Emisar D4 thermal response values... (this light really doesn't want to behave thermally, so the values here don't quite get an ideal response, but they're a lot better than they were) modified: ToyKeeper/spaghetti-monster/anduril/cfg-emisar-d4.h ToyKeeper/spaghetti-monster/anduril/cfg-emisar-d4v2.h ------------------------------------------------------------ revno: 443 committer: Selene ToyKeeper branch nick: emisar-d4v2 timestamp: Thu 2019-06-06 23:11:08 -0600 message: made anduril raise brightness slower when doing thermal regulation, made hard turbo drop only activate when it's actually at turbo modified: ToyKeeper/spaghetti-monster/anduril/anduril.c ------------------------------------------------------------ revno: 442 committer: Selene ToyKeeper branch nick: emisar-d4v2 timestamp: Thu 2019-06-06 23:09:54 -0600 message: made more thermal parameters configurable... - the speed of thermal history rotation (every 1s, 2s, or 4s) - prediction strength also made minimum time between temperature warnings factor in the lowpass, so the default is 5s again instead of 7s modified: ToyKeeper/spaghetti-monster/fsm-adc.c ------------------------------------------------------------ revno: 441 committer: Selene ToyKeeper branch nick: emisar-d4v2 timestamp: Wed 2019-06-05 03:39:34 -0600 message: adjusted d4v2 ramp to hit 1x7135 exactly at level 4/7, turned off hard turbo drop because it doesn't seem to be needed any more modified: ToyKeeper/spaghetti-monster/anduril/cfg-emisar-d4v2.h ------------------------------------------------------------ revno: 440 [merge] committer: Selene ToyKeeper branch nick: emisar-d4v2 timestamp: Wed 2019-06-05 03:33:21 -0600 message: merged updates from fsm branch added: ToyKeeper/spaghetti-monster/anduril/cfg-ff-e01.h ToyKeeper/spaghetti-monster/fireflies-ui/cfg-ff-e07-2.h renamed: ToyKeeper/spaghetti-monster/fireflies-ui/cfg-ff-edc-thrower.h => ToyKeeper/spaghetti-monster/fireflies-ui/cfg-ff-e01.h modified: ToyKeeper/spaghetti-monster/anduril/anduril.c ToyKeeper/spaghetti-monster/fireflies-ui/Makefile ToyKeeper/spaghetti-monster/fireflies-ui/build-all.sh ToyKeeper/spaghetti-monster/fireflies-ui/fireflies-ui.c ToyKeeper/spaghetti-monster/fireflies-ui/meta ToyKeeper/spaghetti-monster/fsm-events.h ToyKeeper/spaghetti-monster/fireflies-ui/cfg-ff-e01.h ------------------------------------------------------------ revno: 439 committer: Selene ToyKeeper branch nick: emisar-d4v2 timestamp: Fri 2019-05-31 02:10:02 -0600 message: added D4v2-219 build for reduced power (only 75% FET) added: ToyKeeper/spaghetti-monster/anduril/cfg-emisar-d4v2-219.h ------------------------------------------------------------ revno: 438 committer: Selene ToyKeeper branch nick: emisar-d4v2 timestamp: Fri 2019-05-31 01:57:14 -0600 message: made MCU stay awake at boot long enough to get accurate voltage reading, sped up return-to-sleep delays, made RGB "voltage" preview mode last twice as long modified: ToyKeeper/spaghetti-monster/anduril/anduril.c ------------------------------------------------------------ revno: 437 committer: Selene ToyKeeper branch nick: emisar-d4v2 timestamp: Fri 2019-05-31 01:56:18 -0600 message: sped up button release timeout slightly to reduce lag when turning off light modified: ToyKeeper/spaghetti-monster/fsm-events.h ------------------------------------------------------------ revno: 436 [merge] committer: Selene ToyKeeper branch nick: emisar-d4v2 timestamp: Thu 2019-05-23 00:43:14 -0600 message: merged from fsm branch to get a bunch of recent updates modified: ... a lot of files ------------------------------------------------------------ revno: 435 committer: Selene ToyKeeper branch nick: emisar-d4v2 timestamp: Tue 2019-05-21 14:25:28 -0600 message: no muggle mode on D4v2, by request modified: ToyKeeper/spaghetti-monster/anduril/cfg-emisar-d4v2.h ------------------------------------------------------------ revno: 434 committer: Selene ToyKeeper branch nick: emisar-d4v2 timestamp: Mon 2019-05-20 00:16:26 -0600 message: improved RGB aux LED previews for voltage mode and blinking mode modified: ToyKeeper/spaghetti-monster/anduril/anduril.c ------------------------------------------------------------ revno: 433 committer: Selene ToyKeeper branch nick: emisar-d4v2 timestamp: Fri 2019-05-10 23:53:47 -0600 message: fixed D4v2 ramp shape and thermal limit modified: ToyKeeper/spaghetti-monster/anduril/cfg-emisar-d4v2.h ------------------------------------------------------------ revno: 432 [merge] committer: Selene ToyKeeper branch nick: emisar-d4v2 timestamp: Fri 2019-05-10 23:39:48 -0600 message: merged from lantern branch, to get newer candle mode added: ToyKeeper/spaghetti-monster/anduril/cfg-blf-lantern.h modified: ToyKeeper/spaghetti-monster/anduril/anduril.c ToyKeeper/spaghetti-monster/fsm-ramping.c ToyKeeper/spaghetti-monster/fsm-ramping.h ------------------------------------------------------------ revno: 431 [merge] committer: Selene ToyKeeper branch nick: emisar-d4v2 timestamp: Fri 2019-05-10 23:29:28 -0600 message: merged from upstream fsm branch added: ToyKeeper/spaghetti-monster/anduril/cfg-fw3a-219.h modified: ToyKeeper/spaghetti-monster/anduril/anduril-manual.txt ------------------------------------------------------------ revno: 430 committer: Selene ToyKeeper branch nick: emisar-d4v2 timestamp: Fri 2019-05-10 23:27:55 -0600 message: use only red, green, and blue for voltage display... don't do color mixes modified: ToyKeeper/spaghetti-monster/anduril/anduril.c ------------------------------------------------------------ revno: 429 committer: Selene ToyKeeper branch nick: emisar-d4v2 timestamp: Fri 2019-05-10 23:27:30 -0600 message: flash-tiny1634-fuses had a typo modified: bin/flash-tiny1634-fuses.sh ------------------------------------------------------------ revno: 428 committer: Selene ToyKeeper branch nick: emisar-d4v2 timestamp: Sat 2019-05-04 18:03:28 -0600 message: fixed builds without sleep ticks enabled modified: ToyKeeper/spaghetti-monster/fsm-wdt.c ------------------------------------------------------------ revno: 427 committer: Selene ToyKeeper branch nick: emisar-d4v2 timestamp: Sat 2019-05-04 18:01:17 -0600 message: made aux LEDs turn on at boot time, instead of waiting until sleep LVP measures the battery modified: ToyKeeper/spaghetti-monster/anduril/anduril.c ------------------------------------------------------------ revno: 426 committer: Selene ToyKeeper branch nick: emisar-d4v2 timestamp: Sat 2019-05-04 18:00:34 -0600 message: implemented LVP in standby mode (which allows LVP to turn off high-drain aux LEDs while asleep, and allows RGB readout to change with battery charge) modified: ToyKeeper/spaghetti-monster/fsm-adc.c ToyKeeper/spaghetti-monster/fsm-adc.h ToyKeeper/spaghetti-monster/fsm-wdt.c ------------------------------------------------------------ revno: 425 committer: Selene ToyKeeper branch nick: emisar-d4v2 timestamp: Sat 2019-05-04 03:14:33 -0600 message: enable tenclick thermal config in D4v2 target modified: ToyKeeper/spaghetti-monster/anduril/cfg-emisar-d4v2.h ------------------------------------------------------------ revno: 424 committer: Selene ToyKeeper branch nick: emisar-d4v2 timestamp: Sat 2019-05-04 03:14:16 -0600 message: added RGB support to lockout mode, improved RGB behavior, set defaults to low because the high mode is way too bright modified: ToyKeeper/spaghetti-monster/anduril/anduril.c ------------------------------------------------------------ revno: 423 committer: Selene ToyKeeper branch nick: emisar-d4v2 timestamp: Sat 2019-05-04 02:36:55 -0600 message: got RGB aux LED modes working -- (off, low, high, blinking) and (7 colors + rainbow + voltage) (off mode only, for now, no lockout) modified: ToyKeeper/spaghetti-monster/anduril/anduril.c ------------------------------------------------------------ revno: 422 committer: Selene ToyKeeper branch nick: emisar-d4v2 timestamp: Fri 2019-05-03 18:25:23 -0600 message: Turn off aux LEDs when voltage is low. They use too much power on high mode. modified: ToyKeeper/spaghetti-monster/anduril/anduril.c ------------------------------------------------------------ revno: 421 committer: Selene ToyKeeper branch nick: emisar-d4v2 timestamp: Fri 2019-05-03 18:24:15 -0600 message: Calibrated D4v2 voltage. modified: ToyKeeper/hwdef-Emisar_D4v2.h ------------------------------------------------------------ revno: 420 committer: Selene ToyKeeper branch nick: emisar-d4v2 timestamp: Fri 2019-05-03 17:48:51 -0600 message: fixed RGB aux LED low mode modified: ToyKeeper/hwdef-Emisar_D4v2.h ToyKeeper/spaghetti-monster/fsm-misc.c ------------------------------------------------------------ revno: 419 committer: Selene ToyKeeper branch nick: emisar-d4v2 timestamp: Fri 2019-05-03 17:35:47 -0600 message: started on RGB aux LED support in Anduril, but it's just an early test right now modified: ToyKeeper/spaghetti-monster/anduril/anduril.c ToyKeeper/spaghetti-monster/anduril/cfg-emisar-d4v2.h ------------------------------------------------------------ revno: 418 committer: Selene ToyKeeper branch nick: emisar-d4v2 timestamp: Fri 2019-05-03 17:35:05 -0600 message: adjust ramping auto-aux-LED code to include RGB version (shut off when main LEDs are on) modified: ToyKeeper/spaghetti-monster/fsm-ramping.c ------------------------------------------------------------ revno: 417 committer: Selene ToyKeeper branch nick: emisar-d4v2 timestamp: Fri 2019-05-03 17:33:08 -0600 message: started on tiny1634 RGB aux LED support (seems to work except for the "low" output mode) modified: ToyKeeper/hwdef-Emisar_D4v2.h ToyKeeper/spaghetti-monster/fsm-misc.c ToyKeeper/spaghetti-monster/fsm-misc.h ------------------------------------------------------------ revno: 416 committer: Selene ToyKeeper branch nick: emisar-d4v2 timestamp: Fri 2019-05-03 16:22:15 -0600 message: fixed D4v2 PWM speed and delay speed, fixed hardware setup reference comments modified: ToyKeeper/hwdef-Emisar_D4v2.h ------------------------------------------------------------ revno: 415 committer: Selene ToyKeeper branch nick: emisar-d4v2 timestamp: Fri 2019-05-03 16:20:50 -0600 message: fixed attiny1634 clock speed adjustments modified: ToyKeeper/tk-attiny.h ------------------------------------------------------------ revno: 414 committer: Selene ToyKeeper branch nick: emisar-d4v2 timestamp: Fri 2019-05-03 15:38:52 -0600 message: renamed SWITCH_PIN_D (pin data) to SWITCH_PORT, to match AVR manual terminology (even though the value should be PINA or PINB or PINC) modified: ToyKeeper/hwdef-Emisar_D4v2.h ToyKeeper/spaghetti-monster/fsm-pcint.c ToyKeeper/tk-attiny.h ------------------------------------------------------------ revno: 413 committer: Selene ToyKeeper branch nick: emisar-d4v2 timestamp: Fri 2019-05-03 01:48:10 -0600 message: added tiny1634 flashing scripts added: bin/flash-tiny1634-fuses.sh bin/flash-tiny1634.sh ------------------------------------------------------------ revno: 411 committer: Selene ToyKeeper branch nick: emisar-d4v2 timestamp: Fri 2019-05-03 01:42:35 -0600 message: started adding tiny1634 support to FSM ... so much kludge, such need clean modified: ToyKeeper/spaghetti-monster/fsm-adc.c ToyKeeper/spaghetti-monster/fsm-main.c ToyKeeper/spaghetti-monster/fsm-pcint.c ToyKeeper/spaghetti-monster/fsm-wdt.c ToyKeeper/tk-attiny.h ------------------------------------------------------------ revno: 410 committer: Selene ToyKeeper branch nick: emisar-d4v2 timestamp: Fri 2019-05-03 01:40:15 -0600 message: added D4v2-1634 config and hwdef files modified: ToyKeeper/hwdef-Emisar_D4v2.h ToyKeeper/spaghetti-monster/anduril/cfg-emisar-d4v2.h ------------------------------------------------------------ revno: 409 committer: Selene ToyKeeper branch nick: emisar-d4v2 timestamp: Fri 2019-05-03 01:39:24 -0600 message: made build-all.sh detect attiny type from cfg file, and use it modified: ToyKeeper/spaghetti-monster/anduril/build-all.sh ------------------------------------------------------------ revno: 403 [merge] committer: Selene ToyKeeper branch nick: emisar-d4v2 timestamp: Sun 2019-04-14 18:14:51 -0600 message: merged updates from upstream added/modified: ... lots of files ------------------------------------------------------------ revno: 402 committer: Selene ToyKeeper branch nick: emisar-d4v2 timestamp: Sun 2019-04-14 18:08:08 -0600 message: merged in some aux LED code updates from anduril modified: ToyKeeper/spaghetti-monster/rampingios/rampingiosv3.c ------------------------------------------------------------ revno: 401 committer: Selene ToyKeeper branch nick: emisar-d4v2 timestamp: Thu 2018-12-27 16:23:09 -0700 message: tk-attiny.h: started adding other MCU support also removed redundant eeprom size hints modified: ToyKeeper/tk-attiny.h ------------------------------------------------------------ revno: 400 committer: Selene ToyKeeper branch nick: emisar-d4v2 timestamp: Thu 2018-12-27 16:21:59 -0700 message: made dynamic clocking safer and more portable (uses library function instead of direct register access) (lib disables interrupts properly, which I wasn't doing) modified: ToyKeeper/spaghetti-monster/anduril/anduril.c ToyKeeper/spaghetti-monster/fsm-events.c ToyKeeper/spaghetti-monster/fsm-misc.c ToyKeeper/spaghetti-monster/spaghetti-monster.h --- spaghetti-monster/fsm-adc.c | 137 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 108 insertions(+), 29 deletions(-) (limited to 'spaghetti-monster/fsm-adc.c') diff --git a/spaghetti-monster/fsm-adc.c b/spaghetti-monster/fsm-adc.c index bcf49ed..6832e32 100644 --- a/spaghetti-monster/fsm-adc.c +++ b/spaghetti-monster/fsm-adc.c @@ -20,27 +20,76 @@ #ifndef FSM_ADC_C #define FSM_ADC_C -#ifdef USE_VOLTAGE_DIVIDER -// 1.1V / pin7 -#define ADMUX_VOLTAGE ADMUX_VOLTAGE_DIVIDER -#else -// VCC / 1.1V reference -#define ADMUX_VOLTAGE ADMUX_VCC -#endif +inline void set_admux_therm() { + #if (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85) || (ATTINY == 1634) + ADMUX = ADMUX_THERM; + #elif (ATTINY == 841) + ADMUXA = ADMUXA_THERM; + ADMUXB = ADMUXB_THERM; + #else + #error Unrecognized MCU type + #endif +} + +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; + #endif + #elif (ATTINY == 841) + #ifdef USE_VOLTAGE_DIVIDER + ADMUXA = ADMUXA_VOLTAGE_DIVIDER; + ADMUXB = ADMUXB_VOLTAGE_DIVIDER; + #else + ADMUXA = ADMUXA_VCC; + ADMUXB = ADMUXB_VCC; + #endif + #else + #error Unrecognized MCU type + #endif +} + +inline void ADC_start_measurement() { + #if (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85) || (ATTINY == 841) || (ATTINY == 1634) + ADCSRA |= (1 << ADSC) | (1 << ADIE); + #else + #error unrecognized MCU type + #endif +} + +// set up ADC for reading battery voltage inline void ADC_on() { - // read voltage on VCC by default - ADMUX = ADMUX_VOLTAGE; - #ifdef USE_VOLTAGE_DIVIDER - // disable digital input on divider pin to reduce power consumption - DIDR0 |= (1 << VOLTAGE_ADC_DIDR); + #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 + DIDR0 |= (1 << VOLTAGE_ADC_DIDR); + #else + // disable digital input on VCC pin to reduce power consumption + //DIDR0 |= (1 << ADC_DIDR); // FIXME: unsure how to handle for VCC pin + #endif + #if (ATTINY == 1634) + ACSRA |= (1 << ACD); // turn off analog comparator to save power + #endif + // enable, start, prescale + ADCSRA = (1 << ADEN) | (1 << ADSC) | ADC_PRSCL; + // end tiny25/45/85 + #elif (ATTINY == 841) + 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; + //ADCSRA |= (1 << ADSC); // start measuring #else - // disable digital input on VCC pin to reduce power consumption - //DIDR0 |= (1 << ADC_DIDR); // FIXME: unsure how to handle for VCC pin + #error Unrecognized MCU type #endif - // enable, start, prescale - ADCSRA = (1 << ADEN) | (1 << ADSC) | ADC_PRSCL; } inline void ADC_off() { @@ -89,6 +138,9 @@ ISR(ADC_vect) { // 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 @@ -96,7 +148,7 @@ ISR(ADC_vect) { 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*ADC_CYCLES_PER_SECOND) // N seconds between thermal regulation events + #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 @@ -110,6 +162,12 @@ ISR(ADC_vect) { 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 @@ -120,7 +178,7 @@ ISR(ADC_vect) { if (voltage == 0) { for(uint8_t i=0; i THERM_FLOOR) { underheat_lowpass = 0; // we're probably not too cold - } else if (pt < THERM_CEIL) { + } + if (pt < THERM_CEIL) { overheat_lowpass = 0; // we're probably not too hot } @@ -261,6 +333,7 @@ ISR(ADC_vect) { 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); @@ -276,6 +349,7 @@ ISR(ADC_vect) { 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) @@ -291,16 +365,21 @@ ISR(ADC_vect) { // set the correct type of measurement for next time #ifdef USE_THERMAL_REGULATION #ifdef USE_LVP - if (adc_step < 2) ADMUX = ADMUX_VOLTAGE; - else ADMUX = ADMUX_THERM; + if (adc_step < 2) set_admux_voltage(); + else set_admux_therm(); #else - ADMUX = ADMUX_THERM; + set_admux_therm(); #endif #else #ifdef USE_LVP - ADMUX = ADMUX_VOLTAGE; + set_admux_voltage(); #endif #endif + + #ifdef TICK_DURING_STANDBY + // if we were asleep, go back to sleep + if (go_to_standby) ADC_off(); + #endif } #ifdef USE_BATTCHECK -- cgit v1.2.3 From 0df827391ced9bb0b7114248c78b696de4676b25 Mon Sep 17 00:00:00 2001 From: Selene ToyKeeper Date: Sat, 28 Sep 2019 23:14:16 -0600 Subject: remapped D1S V2 pins to match new driver (and changed a bit about how ADC / DIDR definitions work, since this now uses DIDR1 instead of DIDR0) --- spaghetti-monster/fsm-adc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'spaghetti-monster/fsm-adc.c') diff --git a/spaghetti-monster/fsm-adc.c b/spaghetti-monster/fsm-adc.c index 6832e32..a5507a9 100644 --- a/spaghetti-monster/fsm-adc.c +++ b/spaghetti-monster/fsm-adc.c @@ -69,10 +69,10 @@ inline void ADC_on() set_admux_voltage(); #ifdef USE_VOLTAGE_DIVIDER // disable digital input on divider pin to reduce power consumption - DIDR0 |= (1 << VOLTAGE_ADC_DIDR); + VOLTAGE_ADC_DIDR |= (1 << VOLTAGE_ADC); #else // disable digital input on VCC pin to reduce power consumption - //DIDR0 |= (1 << ADC_DIDR); // FIXME: unsure how to handle for VCC pin + //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 -- cgit v1.2.3 From 8b59e880614455bd7bc7d8595de847dd6fe9b5b2 Mon Sep 17 00:00:00 2001 From: Selene ToyKeeper Date: Thu, 14 Nov 2019 01:52:50 -0700 Subject: refactored how interrupts work... set a flag and return immediately, then handle the actual logic later during a less-critical code path Enables smarter responses to standby wakeups. Seems to fix missed button presses during standby, and most of the too-fast sleep ticks. Also eliminated waits from button state measurement, so it can happen easier during standby. (also eliminates the chance of an infinite loop on extra-noisy hardware) Also might improve timing-sensitive interrupts like attiny85 PWM channel 4, or a PWM-DSM hybrid technique I'd like to try. BUT this change also appears to break the thermal sensor, so that needs to be fixed. --- spaghetti-monster/fsm-adc.c | 45 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 35 insertions(+), 10 deletions(-) (limited to 'spaghetti-monster/fsm-adc.c') diff --git a/spaghetti-monster/fsm-adc.c b/spaghetti-monster/fsm-adc.c index 6832e32..1fc5472 100644 --- a/spaghetti-monster/fsm-adc.c +++ b/spaghetti-monster/fsm-adc.c @@ -113,15 +113,44 @@ 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()? + +// save the measurement result, set a flag to show something happened, +// and count how many times we've triggered since last counter reset 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. + #if 0 // the fancy method is probably not even needed + // count up but wrap around from 255 to 128; not 255 to 0 + // TODO: find a way to do this faster if possible + uint8_t val = irq_adc; // cache volatile value + irq_adc = (val + 1) | (val & 0b10000000); + #else + irq_adc ++; + #endif + adc_value = ADC; // save this for later use +} + +void ADC_inner() { + // ignore the first measurement; the docs say it's junk + if (irq_adc < 2) { + ADC_start_measurement(); // start a second measurement + return; + } + + // 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; + + // if we're actually runnning, reset the status flags / counters + irq_adc = 0; 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/1 = battery voltage, 2/3 = temperature static uint8_t adc_step = 0; // LVP declarations @@ -155,7 +184,7 @@ ISR(ADC_vect) { #define ADC_STEPS 2 #endif - uint16_t measurement = ADC; // latest 10-bit ADC reading + uint16_t measurement = adc_value; // latest 10-bit ADC reading #ifdef USE_PSEUDO_RAND // real-world entropy makes this a true random, not pseudo @@ -376,10 +405,6 @@ ISR(ADC_vect) { #endif #endif - #ifdef TICK_DURING_STANDBY - // if we were asleep, go back to sleep - if (go_to_standby) ADC_off(); - #endif } #ifdef USE_BATTCHECK -- cgit v1.2.3 From d4a899d99a366c09f5e38eb9ef71e538e4def2c1 Mon Sep 17 00:00:00 2001 From: Selene ToyKeeper Date: Thu, 14 Nov 2019 01:56:29 -0700 Subject: started refactoring ADC code to split voltage and temperature into their own functions --- spaghetti-monster/fsm-adc.c | 432 +++++++++++++++++++++++--------------------- 1 file changed, 222 insertions(+), 210 deletions(-) (limited to 'spaghetti-monster/fsm-adc.c') diff --git a/spaghetti-monster/fsm-adc.c b/spaghetti-monster/fsm-adc.c index 1fc5472..ddf4c65 100644 --- a/spaghetti-monster/fsm-adc.c +++ b/spaghetti-monster/fsm-adc.c @@ -114,6 +114,12 @@ static inline uint8_t calc_voltage_divider(uint16_t value) { #define ADC_CYCLES_PER_SECOND 8 #endif +#ifdef USE_THERMAL_REGULATION +#define ADC_STEPS 4 +#else +#define ADC_STEPS 2 +#endif + // save the measurement result, set a flag to show something happened, // and count how many times we've triggered since last counter reset ISR(ADC_vect) { @@ -153,8 +159,51 @@ void ADC_inner() { // what is being measured? 0/1 = battery voltage, 2/3 = temperature static uint8_t adc_step = 0; - // LVP declarations + #ifdef USE_PSEUDO_RAND + // real-world entropy makes this a true random, not pseudo + pseudo_rand_seed += adc_value; + #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 + if (adc_step == 1) { // voltage + ADC_voltage_handler(); + } + #endif + + #ifdef USE_THERMAL_REGULATION + else if (adc_step == 3) { // temperature + ADC_temperature_handler(); + } + #endif + + // 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 USE_LVP +static inline void ADC_voltage_handler() { + // LVP declarations #ifdef USE_LVP_AVG #define NUM_VOLTAGE_VALUES 4 static int16_t voltage_values[NUM_VOLTAGE_VALUES]; @@ -163,15 +212,78 @@ void ADC_inner() { 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_value; // latest 10-bit ADC reading + + #ifdef USE_LVP_AVG + // prime on first execution + if (voltage == 0) { + for(uint8_t i=0; i> 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; + } + } +} +#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; @@ -180,232 +292,132 @@ void ADC_inner() { #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_value; // 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> 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; - } + // 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 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 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 0) diff --; - } - // projected_temperature = current temp extended forward by amplified rate of change - //projected_temperature = temperature_history[NUM_THERMAL_VALUES_HISTORY-1] + (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_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 - } +#endif + #ifdef USE_BATTCHECK #ifdef BATTCHECK_4bars -- cgit v1.2.3 From ae082e6331d75c2cbe339290fbce4e79c2aa2ede Mon Sep 17 00:00:00 2001 From: Selene ToyKeeper Date: Thu, 14 Nov 2019 18:00:54 -0700 Subject: fixed ADC code; measures and behaves correctly now, and is easier to read... ... but factory reset's auto-calibrate still doesn't get the right values for some reason (manual calibration works, but not auto) --- spaghetti-monster/fsm-adc.c | 76 ++++++++++++++++++++++----------------------- 1 file changed, 37 insertions(+), 39 deletions(-) (limited to 'spaghetti-monster/fsm-adc.c') diff --git a/spaghetti-monster/fsm-adc.c b/spaghetti-monster/fsm-adc.c index ddf4c65..d447189 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() { @@ -115,38 +117,37 @@ static inline uint8_t calc_voltage_divider(uint16_t value) { #endif #ifdef USE_THERMAL_REGULATION -#define ADC_STEPS 4 -#else #define ADC_STEPS 2 +#else +#define ADC_STEPS 1 #endif -// save the measurement result, set a flag to show something happened, -// and count how many times we've triggered since last counter reset +// happens every time the ADC sampler finishes a measurement ISR(ADC_vect) { - #if 0 // the fancy method is probably not even needed - // count up but wrap around from 255 to 128; not 255 to 0 - // TODO: find a way to do this faster if possible - uint8_t val = irq_adc; // cache volatile value - irq_adc = (val + 1) | (val & 0b10000000); - #else - irq_adc ++; + #ifdef USE_PSEUDO_RAND + // real-world entropy makes this a true random, not pseudo + pseudo_rand_seed += ADCL; #endif - adc_value = ADC; // save this for later use + + 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() { - // ignore the first measurement; the docs say it's junk - if (irq_adc < 2) { - ADC_start_measurement(); // start a second measurement - return; - } + 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; - // if we're actually runnning, reset the status flags / counters - irq_adc = 0; + // disable after one iteration adcint_enable = 0; #ifdef TICK_DURING_STANDBY @@ -156,41 +157,37 @@ void ADC_inner() { if (go_to_standby) ADC_off(); #endif - // what is being measured? 0/1 = battery voltage, 2/3 = temperature + // what is being measured? 0 = battery voltage, 1 = temperature static uint8_t adc_step = 0; - #ifdef USE_PSEUDO_RAND - // real-world entropy makes this a true random, not pseudo - pseudo_rand_seed += adc_value; - #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 - if (adc_step == 1) { // voltage + if (0 == adc_step) { // voltage ADC_voltage_handler(); } #endif #ifdef USE_THERMAL_REGULATION - else if (adc_step == 3) { // temperature + 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 (adc_step < 2) set_admux_voltage(); + if (0 == adc_step) set_admux_voltage(); else set_admux_therm(); #else - set_admux_therm(); + //set_admux_therm(); + #error "USE_THERMAL_REGULATION set without USE_LVP" #endif #else #ifdef USE_LVP @@ -198,6 +195,7 @@ void ADC_inner() { #endif #endif + irq_adc_stable = 0; // first result is unstable } @@ -213,7 +211,7 @@ static inline void ADC_voltage_handler() { #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_value; // latest 10-bit ADC reading + uint16_t measurement = adc_values[0]; // latest 10-bit ADC reading #ifdef USE_LVP_AVG // prime on first execution @@ -293,7 +291,7 @@ static inline void ADC_temperature_handler() { #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 - uint16_t measurement = adc_value; // latest 10-bit ADC reading + 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; -- cgit v1.2.3 From 221371a13918c745d397fdd9b5c1cb8cb76c62a2 Mon Sep 17 00:00:00 2001 From: Selene ToyKeeper Date: Thu, 14 Nov 2019 19:09:22 -0700 Subject: added a lowpass filter for battery voltage measurements (but only on attiny1634 devices, since it costs a bit of space and isn't strictly necessary) --- spaghetti-monster/fsm-adc.c | 59 +++++++++++++++++---------------------------- 1 file changed, 22 insertions(+), 37 deletions(-) (limited to 'spaghetti-monster/fsm-adc.c') diff --git a/spaghetti-monster/fsm-adc.c b/spaghetti-monster/fsm-adc.c index d447189..c859539 100644 --- a/spaghetti-monster/fsm-adc.c +++ b/spaghetti-monster/fsm-adc.c @@ -201,11 +201,6 @@ void ADC_inner() { #ifdef USE_LVP static inline void ADC_voltage_handler() { - // LVP declarations - #ifdef USE_LVP_AVG - #define NUM_VOLTAGE_VALUES 4 - static int16_t voltage_values[NUM_VOLTAGE_VALUES]; - #endif 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 @@ -213,40 +208,30 @@ static inline void ADC_voltage_handler() { uint16_t measurement = adc_values[0]; // latest 10-bit ADC reading - #ifdef USE_LVP_AVG - // prime on first execution - if (voltage == 0) { - for(uint8_t i=0; i> 2; + #ifdef USE_VOLTAGE_LOWPASS + static uint16_t prev_measurement = 0; - #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 + // 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) { -- cgit v1.2.3 From 492fdf3281aa7bbcb5aa37b10420766b433e8d34 Mon Sep 17 00:00:00 2001 From: Selene ToyKeeper Date: Tue, 19 Nov 2019 00:21:15 -0700 Subject: fixed ADC cycles running 2X as fast as intended --- spaghetti-monster/fsm-adc.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'spaghetti-monster/fsm-adc.c') diff --git a/spaghetti-monster/fsm-adc.c b/spaghetti-monster/fsm-adc.c index c859539..ccf1ca2 100644 --- a/spaghetti-monster/fsm-adc.c +++ b/spaghetti-monster/fsm-adc.c @@ -108,12 +108,12 @@ static inline uint8_t calc_voltage_divider(uint16_t value) { } #endif -// Each full cycle runs 7.8X per second with just voltage enabled, -// or 3.9X per second with voltage and temperature. +// Each full cycle runs 15.6X per second with just voltage enabled, +// or 7.8X per second with voltage and temperature. #if defined(USE_LVP) && defined(USE_THERMAL_REGULATION) -#define ADC_CYCLES_PER_SECOND 4 -#else #define ADC_CYCLES_PER_SECOND 8 +#else +#define ADC_CYCLES_PER_SECOND 16 #endif #ifdef USE_THERMAL_REGULATION @@ -322,13 +322,13 @@ static inline void ADC_temperature_handler() { // 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 + #define THERM_HISTORY_STEP_MAX ((2*ADC_CYCLES_PER_SECOND)-1) #elif (THERMAL_UPDATE_SPEED == 2) // new value every 2s - #define THERM_HISTORY_STEP_MAX 7 + #define THERM_HISTORY_STEP_MAX (ADC_CYCLES_PER_SECOND-1) #elif (THERMAL_UPDATE_SPEED == 1) // new value every 1s - #define THERM_HISTORY_STEP_MAX 3 + #define THERM_HISTORY_STEP_MAX ((ADC_CYCLES_PER_SECOND/2)-1) #elif (THERMAL_UPDATE_SPEED == 0) // new value every 0.5s - #define THERM_HISTORY_STEP_MAX 1 + #define THERM_HISTORY_STEP_MAX ((ADC_CYCLES_PER_SECOND/4)-1) #endif if (0 == (history_step & THERM_HISTORY_STEP_MAX)) { // rotate measurements and add a new one -- cgit v1.2.3 From 134c6de1eea4aee9a609a3057091d21109a833d5 Mon Sep 17 00:00:00 2001 From: Selene ToyKeeper Date: Tue, 19 Nov 2019 00:36:50 -0700 Subject: reduced build size by a few bytes --- spaghetti-monster/fsm-adc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'spaghetti-monster/fsm-adc.c') diff --git a/spaghetti-monster/fsm-adc.c b/spaghetti-monster/fsm-adc.c index ccf1ca2..4ee2018 100644 --- a/spaghetti-monster/fsm-adc.c +++ b/spaghetti-monster/fsm-adc.c @@ -21,7 +21,7 @@ #define FSM_ADC_C -inline void set_admux_therm() { +static inline void set_admux_therm() { #if (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85) || (ATTINY == 1634) ADMUX = ADMUX_THERM; #elif (ATTINY == 841) -- cgit v1.2.3 From 1ab6cd428c3b43a80ffac01f29ca0c27a438e3b1 Mon Sep 17 00:00:00 2001 From: Selene ToyKeeper Date: Mon, 25 Nov 2019 18:41:24 -0700 Subject: calibrated Noctigon K1, changed voltage divider calibration values to 10-bit (was 8-bit before) --- spaghetti-monster/fsm-adc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'spaghetti-monster/fsm-adc.c') diff --git a/spaghetti-monster/fsm-adc.c b/spaghetti-monster/fsm-adc.c index 9e52ab6..b8b00de 100644 --- a/spaghetti-monster/fsm-adc.c +++ b/spaghetti-monster/fsm-adc.c @@ -101,7 +101,7 @@ 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 - uint16_t adc_per_volt = ((ADC_44<<7) - (ADC_22<<7)) / (44-22); + 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; return result; -- cgit v1.2.3 From e7f75ac1177d49d9081755c1d9b54885c996ec80 Mon Sep 17 00:00:00 2001 From: Selene ToyKeeper Date: Tue, 17 Dec 2019 18:17:58 -0700 Subject: fixed too-slow thermal response (was introduced in the irq-refactor branch) --- spaghetti-monster/fsm-adc.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'spaghetti-monster/fsm-adc.c') diff --git a/spaghetti-monster/fsm-adc.c b/spaghetti-monster/fsm-adc.c index 4ee2018..3763a3e 100644 --- a/spaghetti-monster/fsm-adc.c +++ b/spaghetti-monster/fsm-adc.c @@ -276,6 +276,8 @@ static inline void ADC_temperature_handler() { #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 // Convert ADC units to Celsius (ish) @@ -322,15 +324,16 @@ static inline void ADC_temperature_handler() { // 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 ((2*ADC_CYCLES_PER_SECOND)-1) + #define THERM_HISTORY_STEP_MAX (4*ADC_CYCLES_PER_SECOND) #elif (THERMAL_UPDATE_SPEED == 2) // new value every 2s - #define THERM_HISTORY_STEP_MAX (ADC_CYCLES_PER_SECOND-1) + #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/2)-1) + #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/4)-1) + #define THERM_HISTORY_STEP_MAX (ADC_CYCLES_PER_SECOND/2) #endif - if (0 == (history_step & THERM_HISTORY_STEP_MAX)) { + if (THERM_HISTORY_STEP_MAX == history_step) { + history_step = 0; // rotate measurements and add a new one for (uint8_t i=0; i>1) / adc_per_volt) + VOLTAGE_FUDGE_FACTOR; return result; } #endif @@ -124,78 +135,115 @@ static inline uint8_t calc_voltage_divider(uint16_t value) { // 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 + // skip the first measurement; it's junk + //if (adc_sample_count) { + // slow down even more than ADC_PRSCL + // (result is about 600 Hz or a maximum of ~9 ADC units per second) + // (8 MHz / 128 prescale / 13.5 ticks per measurement / 8 = ~578 Hz) + // (~578 Hz / 64X resolution = ~9 original-resolution units per second) + if (1 == (adc_sample_count & 7)) { + + 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; + + } + + // the next measurement isn't the first + //adc_sample_count = 1; + adc_sample_count ++; + + /* + if (adc_sample_count) { // 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; + adc_sample_count = 1; // start another measurement // (is explicit because it otherwise doesn't seem to happen during standby mode) ADC_start_measurement(); + */ } -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; + + // what is being measured? 0 = battery voltage, 1 = temperature + uint8_t adc_step; - #ifdef TICK_DURING_STANDBY + #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 } @@ -206,21 +254,14 @@ static inline void ADC_voltage_handler() { #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; + uint16_t measurement = adc_smooth[0]; // latest 16-bit ADC value - // 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 + // jump-start the lowpass seed at boot + // (otherwise it takes a while to rise from zero) + if (measurement < 255) { + measurement = adc_raw[0]; + adc_smooth[0] = measurement; + } #ifdef USE_VOLTAGE_DIVIDER voltage = calc_voltage_divider(measurement); @@ -229,7 +270,7 @@ static inline void ADC_voltage_handler() { // 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 @@ -268,7 +309,7 @@ static inline void ADC_temperature_handler() { #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 uint16_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; @@ -276,34 +317,61 @@ static inline void ADC_temperature_handler() { #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 + // latest 16-bit ADC reading (left-adjusted, lowpassed) + uint16_t measurement; - // 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) { + // don't keep resetting reset_thermal_history = 0; - temperature = temp; + + // ignore lowpass, use latest sample + measurement = adc_raw[1]; + + // reset lowpass to latest sample + adc_smooth[1] = measurement; + + // forget any past measurements for(uint8_t i=0; i temperature) { - temperature ++; - } else if (temp < temperature) { - temperature --; + temperature_history[i] = measurement; + } + else { + measurement = adc_smooth[1]; // average of recent samples + } + + { // rotate the temperature history + // if it's time to rotate the thermal history, do it + // FIXME? allow more than 255 frames per step + // (that's only about 8 seconds maximum) + 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 + // FIXME: rotate the index instead of moving the values + if (THERM_HISTORY_STEP_MAX == history_step) { + history_step = 0; + // rotate measurements and add a new one + for (uint8_t i=0; i>6) - 275 + THERM_CAL_OFFSET + (int16_t)therm_cal_offset; + // guess what the temperature will be in a few seconds int16_t pt; { int16_t diff; - int16_t t = temperature; + uint16_t t = measurement; // algorithm tweaking; not really intended to be modified // how far ahead should we predict? @@ -315,44 +383,28 @@ static inline void ADC_temperature_handler() { #define THERM_RESPONSE_MAGNITUDE 128 #endif // acceptable temperature window size in C - #define THERM_WINDOW_SIZE 5 + #define THERM_WINDOW_SIZE (3<<6) // highest temperature allowed - #define THERM_CEIL ((int16_t)therm_ceil) + #define THERM_CEIL (((int16_t)therm_ceil)<<6) // 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 0) diff --; } + */ // projected_temperature = current temp extended forward by amplified rate of change //projected_temperature = temperature_history[NUM_THERMAL_VALUES_HISTORY-1] + (diff<>1) / adc_per_volt) + VOLTAGE_FUDGE_FACTOR; return result; } @@ -134,40 +134,41 @@ static inline uint8_t calc_voltage_divider(uint16_t value) { #endif // happens every time the ADC sampler finishes a measurement +// collects an average of 64 samples, which increases effective number of +// bits from 10 to about 16 (ish, probably more like 14 really) +// (64 was chosen because it's the largest sample size which allows the +// sum to still fit into a 16-bit integer, and for speed and size reasons, +// we want to avoid doing 32-bit math) ISR(ADC_vect) { - // slow down even more than ADC_PRSCL - // (result is about 600 Hz or a maximum of ~9 ADC units per second) - // (8 MHz / 128 prescale / 13.5 ticks per measurement / 8 = ~578 Hz) - // (~578 Hz / 64X resolution = ~9 original-resolution units per second) - if (1 == (adc_sample_count & 7)) { + static uint16_t adc_sum; - uint16_t m; // latest measurement - uint16_t s; // smoothed measurement - uint8_t channel = adc_channel; + // keep this moving along + adc_sample_count ++; + // reset on first sample + // also, ignore first value since it's probably junk + if (1 == adc_sample_count) { + adc_sum = 0; + return; + } + // 64 samples collected, save the result + else if (66 == adc_sample_count) { + adc_smooth[adc_channel] = adc_sum; + } + // add the latest measurement to the pile + else { + uint16_t m = ADC; + // add to the running total + adc_sum += m; // 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; - + adc_raw[adc_channel] = m; } + // don't worry about the running total overflowing after sample 64... + // it doesn't matter - // the next measurement isn't the first - //adc_sample_count = 1; - adc_sample_count ++; - + // track what woke us up, and enable deferred logic + irq_adc = 1; } void adc_deferred() { @@ -177,7 +178,7 @@ void adc_deferred() { // 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); + pseudo_rand_seed += ADCL; #endif // the ADC triggers repeatedly when it's on, but we only need to run the @@ -241,22 +242,11 @@ static inline void ADC_voltage_handler() { #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_smooth[0]; // latest 16-bit ADC value - - // jump-start the lowpass seed at boot - // (otherwise it takes a while to rise from zero) - if (measurement < 255) { - measurement = adc_raw[0]; - adc_smooth[0] = measurement; - } + uint16_t measurement; - // 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; + // latest ADC value + if (go_to_standby) measurement = adc_raw[0] << 6; + else measurement = adc_smooth[0]; #ifdef USE_VOLTAGE_DIVIDER voltage = calc_voltage_divider(measurement); @@ -264,7 +254,6 @@ 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>>6) + VOLTAGE_FUDGE_FACTOR) >> 1; #endif @@ -308,34 +297,24 @@ static inline void ADC_temperature_handler() { #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 - // latest 16-bit ADC reading (left-adjusted, lowpassed) + // latest 16-bit ADC reading uint16_t measurement; if (! reset_thermal_history) { - measurement = adc_smooth[1]; // average of recent samples + // average of recent samples + measurement = adc_smooth[1]; } else { // wipe out old data // don't keep resetting reset_thermal_history = 0; - // ignore lowpass, use latest sample - measurement = adc_raw[1]; - - // reset lowpass to latest sample - adc_smooth[1] = measurement; + // ignore average, use latest sample + measurement = adc_raw[1] << 6; // forget any past measurements for(uint8_t i=0; i>= 1; + adc_sample_count = 33; } // add the latest measurement to the pile else { @@ -164,8 +171,6 @@ ISR(ADC_vect) { // update the latest value adc_raw[adc_channel] = m; } - // don't worry about the running total overflowing after sample 64... - // it doesn't matter // track what woke us up, and enable deferred logic irq_adc = 1; -- cgit v1.2.3 From d275f50525ed9a0950c743faa317c7aa4fe9420b Mon Sep 17 00:00:00 2001 From: Selene ToyKeeper Date: Thu, 30 Jan 2020 23:10:25 -0700 Subject: saving state of ADC / WDT refactoring before doing more changes... what changed so far: - removed LVP lowpass and thermal regulation lowpass logic; it's probably redundant now - slowed ADC deferred logic timing to 4X per second instead of 16X, because there doesn't seem to be much reason to do it any faster - reduced thermal event rate-limit to just 1 second, for more responsive regulation - added "EV_temperature_okay" signal, to help stop adjustments at an appropriate time instead of going to far - sped up sleep LVP to one measurement every 8 seconds instead of 16, to help the aux LEDs respond to voltage changes faster (effect on standby time is negligible) - make sure the WDT doesn't set the ADC channel or counter... except in standby mode --- spaghetti-monster/fsm-adc.c | 107 ++++++++++++++++---------------------------- 1 file changed, 38 insertions(+), 69 deletions(-) (limited to 'spaghetti-monster/fsm-adc.c') diff --git a/spaghetti-monster/fsm-adc.c b/spaghetti-monster/fsm-adc.c index f10114f..6fae262 100644 --- a/spaghetti-monster/fsm-adc.c +++ b/spaghetti-monster/fsm-adc.c @@ -119,18 +119,12 @@ static inline uint8_t calc_voltage_divider(uint16_t value) { } #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 ~4X per second with just voltage enabled, +// or ~2X 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 2 #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 4 #endif // happens every time the ADC sampler finishes a measurement @@ -242,10 +236,9 @@ void adc_deferred() { #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; @@ -263,23 +256,15 @@ static inline void ADC_voltage_handler() { #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 - emit(EV_voltage_low, 0); - // 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; } } } @@ -296,11 +281,7 @@ static inline void ADC_temperature_handler() { static uint8_t history_step = 0; // don't update history as often static uint16_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 + #define TEMPERATURE_TIMER_START (THERMAL_WARNING_SECONDS*ADC_CYCLES_PER_SECOND) // N seconds between thermal regulation events // latest 16-bit ADC reading uint16_t measurement; @@ -373,28 +354,17 @@ static inline void ADC_temperature_handler() { // 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) - // FIXME: this is way too small for left-adjusted values /* + // FIXME: this is way too small for 16-bit values 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_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) { @@ -403,39 +373,38 @@ static inline void ADC_temperature_handler() { // 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); - } + // reset counters + 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) >> 6; + // 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); - } + // reset counters + 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) >> 6; + // send a notification (unless voltage is low) + // (LVP and underheat warnings fight each other) + if (voltage > VOLTAGE_LOW) + emit(EV_temperature_low, howmuch); + } + + // Goldilocks? (temperature is within target window) + else { + // reset counters + temperature_timer = TEMPERATURE_TIMER_START; + // send a notification (unless voltage is low) + // (LVP and temp-okay events fight each other) + if (voltage > VOLTAGE_LOW) + emit(EV_temperature_okay, 0); } - // TODO: add EV_temperature_okay signal } } #endif -- cgit v1.2.3 From 7e994a1a76d7a61e3cdd921b3275169d9a725b6a Mon Sep 17 00:00:00 2001 From: Selene ToyKeeper Date: Wed, 5 Feb 2020 02:36:24 -0700 Subject: first pass at a smaller simpler thermal regulation algorithm... ... doesn't work well, but I'm saving it so I can experiment with other methods and maybe revert back later. --- spaghetti-monster/fsm-adc.c | 127 ++++++++++++++++++-------------------------- 1 file changed, 52 insertions(+), 75 deletions(-) (limited to 'spaghetti-monster/fsm-adc.c') diff --git a/spaghetti-monster/fsm-adc.c b/spaghetti-monster/fsm-adc.c index 6fae262..d1efff3 100644 --- a/spaghetti-monster/fsm-adc.c +++ b/spaghetti-monster/fsm-adc.c @@ -273,15 +273,24 @@ static inline void ADC_voltage_handler() { #ifdef USE_THERMAL_REGULATION 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 5 // 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 uint16_t temperature_history[NUM_THERMAL_VALUES_HISTORY]; + // 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 3 + + #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 uint8_t temperature_timer = 0; - #define TEMPERATURE_TIMER_START (THERMAL_WARNING_SECONDS*ADC_CYCLES_PER_SECOND) // N seconds between thermal regulation events + // N seconds between thermal regulation events + #define TEMPERATURE_TIMER_START (THERMAL_WARNING_SECONDS*ADC_CYCLES_PER_SECOND) // latest 16-bit ADC reading uint16_t measurement; @@ -297,105 +306,73 @@ static inline void ADC_temperature_handler() { measurement = adc_raw[1] << 6; // forget any past measurements - for(uint8_t i=0; i>6) - 275 + THERM_CAL_OFFSET + (int16_t)therm_cal_offset; - // guess what the temperature will be in a few seconds - int16_t pt; - { - int16_t diff; - uint16_t t = measurement; + // 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 + int16_t pt; // predicted temperature + pt = measurement + (diff * THERM_LOOKAHEAD); + + // P[I]D: average of recent measurements + uint16_t avg = 0; + for(uint8_t i=0; i>3); + + uint16_t ceil = therm_ceil << 6; + //uint16_t floor = ceil - (THERM_WINDOW_SIZE << 6); + int16_t offset_pt, offset_avg; + offset_pt = pt - ceil; + offset_avg = avg - ceil; + int16_t offset = offset_pt + offset_avg; + //int16_t offset = (pt - ceil) + (avg - ceil); - // 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 (3<<6) - // highest temperature allowed - #define THERM_CEIL (((int16_t)therm_ceil)<<6) - // bottom of target temperature window - #define THERM_FLOOR (THERM_CEIL - THERM_WINDOW_SIZE) - - // guess what the temp will be several seconds in the future - // diff = rate of temperature change - diff = t - temperature_history[0]; - // slight bias toward zero; ignore very small changes (noise) - /* - // FIXME: this is way too small for 16-bit values - 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 - pt = projected_temperature = t + (diff< THERM_CEIL) { + // (if it's too hot and not getting colder...) + if ((offset > 0) && (diff > (-1 << 5))) { // reset counters 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) >> 6; + //int16_t howmuch = (offset >> 6) * THERM_RESPONSE_MAGNITUDE / 128; + int16_t howmuch = (offset >> 6); // send a warning emit(EV_temperature_high, howmuch); } // Too cold? - else if (pt < THERM_FLOOR) { + // (if it's too cold and not getting warmer...) + else if ((offset < -(THERM_WINDOW_SIZE << 6)) + && (diff < (1 << 5))) { // reset counters 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) >> 6; + //int16_t howmuch = (((-offset) - (THERM_WINDOW_SIZE<<6)) >> 7) * THERM_WINDOW_SIZE / 128; + int16_t howmuch = ((-offset) - (THERM_WINDOW_SIZE<<6)) >> 7; // send a notification (unless voltage is low) // (LVP and underheat warnings fight each other) if (voltage > VOLTAGE_LOW) emit(EV_temperature_low, howmuch); } - // Goldilocks? (temperature is within target window) + // Goldilocks? + // (temperature is within target window, or at least heading toward it) else { // reset counters temperature_timer = TEMPERATURE_TIMER_START; -- cgit v1.2.3 From 46af6026a2dc2bae92d41609a8ecd6144082825e Mon Sep 17 00:00:00 2001 From: Selene ToyKeeper Date: Wed, 5 Feb 2020 22:12:45 -0700 Subject: still doesn't work, but at least it's a bit less broken than before... (ceiling value was all wrong, and the response magnitude was way too big) (also, temperatures here are unsigned, since freezing is about 270 in ADC units) --- spaghetti-monster/fsm-adc.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) (limited to 'spaghetti-monster/fsm-adc.c') diff --git a/spaghetti-monster/fsm-adc.c b/spaghetti-monster/fsm-adc.c index d1efff3..2df884e 100644 --- a/spaghetti-monster/fsm-adc.c +++ b/spaghetti-monster/fsm-adc.c @@ -275,7 +275,7 @@ static inline void ADC_voltage_handler() { static inline void ADC_temperature_handler() { // coarse adjustment #ifndef THERM_LOOKAHEAD - #define THERM_LOOKAHEAD 5 // can be tweaked per build target + #define THERM_LOOKAHEAD 3 // can be tweaked per build target #endif // fine-grained adjustment // how proportional should the adjustments be? (not used yet) @@ -323,7 +323,7 @@ static inline void ADC_temperature_handler() { history_step = (history_step + 1) & (NUM_TEMP_HISTORY_STEPS-1); // PI[D]: guess what the temperature will be in a few seconds - int16_t pt; // predicted temperature + uint16_t pt; // predicted temperature pt = measurement + (diff * THERM_LOOKAHEAD); // P[I]D: average of recent measurements @@ -331,11 +331,15 @@ static inline void ADC_temperature_handler() { for(uint8_t i=0; i>3); - uint16_t ceil = therm_ceil << 6; + // 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) << 6; //uint16_t floor = ceil - (THERM_WINDOW_SIZE << 6); int16_t offset_pt, offset_avg; - offset_pt = pt - ceil; - offset_avg = avg - ceil; + offset_pt = (pt - ceil) >> 1; + offset_avg = (avg - ceil) >> 1; int16_t offset = offset_pt + offset_avg; //int16_t offset = (pt - ceil) + (avg - ceil); @@ -351,7 +355,7 @@ static inline void ADC_temperature_handler() { temperature_timer = TEMPERATURE_TIMER_START; // how far above the ceiling? //int16_t howmuch = (offset >> 6) * THERM_RESPONSE_MAGNITUDE / 128; - int16_t howmuch = (offset >> 6); + int16_t howmuch = (offset >> 8); // send a warning emit(EV_temperature_high, howmuch); } @@ -359,12 +363,12 @@ static inline void ADC_temperature_handler() { // Too cold? // (if it's too cold and not getting warmer...) else if ((offset < -(THERM_WINDOW_SIZE << 6)) - && (diff < (1 << 5))) { + && (diff < (1 << 4))) { // reset counters temperature_timer = TEMPERATURE_TIMER_START; // how far below the floor? //int16_t howmuch = (((-offset) - (THERM_WINDOW_SIZE<<6)) >> 7) * THERM_WINDOW_SIZE / 128; - int16_t howmuch = ((-offset) - (THERM_WINDOW_SIZE<<6)) >> 7; + int16_t howmuch = ((-offset) - (THERM_WINDOW_SIZE<<6)) >> 9; // send a notification (unless voltage is low) // (LVP and underheat warnings fight each other) if (voltage > VOLTAGE_LOW) -- cgit v1.2.3 From 14ad6787546b3a2c55c129c8bd95eb6b98f14531 Mon Sep 17 00:00:00 2001 From: Selene ToyKeeper Date: Wed, 5 Feb 2020 23:34:44 -0700 Subject: brute force method for reducing ADC noise -- average a ridiculous number of samples (because, for some reason, even though 64 samples is plenty in a test program, it ends up being extremely erratic when used inside Anduril... and I'm not sure why) also, use 15-bit ADC values instead of 16 bits, in the temperature logic (to help protect against integer overflows) ... but this code still doesn't work well. It regulates down *very* fast, and then gradually rises until the next extra-fast drop-down. :( ... also, tempcheck mode sometimes changes by 4-5 C between readouts, which is worrisome. ... and factory reset is still broken. --- spaghetti-monster/fsm-adc.c | 32 +++++++++++++------------------- 1 file changed, 13 insertions(+), 19 deletions(-) (limited to 'spaghetti-monster/fsm-adc.c') diff --git a/spaghetti-monster/fsm-adc.c b/spaghetti-monster/fsm-adc.c index 2df884e..65669b3 100644 --- a/spaghetti-monster/fsm-adc.c +++ b/spaghetti-monster/fsm-adc.c @@ -135,7 +135,7 @@ static inline uint8_t calc_voltage_divider(uint16_t value) { // doing 32-bit math) ISR(ADC_vect) { - static uint16_t adc_sum; + static uint32_t adc_sum; // keep this moving along adc_sample_count ++; @@ -146,16 +146,10 @@ ISR(ADC_vect) { adc_sum = 0; return; } - // 64 samples collected, save the result - // (actually triggers at 64 and every 32 afterward) - else if (66 == adc_sample_count) { + // 2048 samples collected, save the result + else if (2050 == adc_sample_count) { // save the latest result - adc_smooth[adc_channel] = adc_sum; - // cut sum in half and set up another half-window of samples - // (for sort of a continuous average) - // (this seems to significantly reduce noise) - adc_sum >>= 1; - adc_sample_count = 33; + adc_smooth[adc_channel] = adc_sum >> 5; } // add the latest measurement to the pile else { @@ -275,7 +269,7 @@ static inline void ADC_voltage_handler() { static inline void ADC_temperature_handler() { // coarse adjustment #ifndef THERM_LOOKAHEAD - #define THERM_LOOKAHEAD 3 // can be tweaked per build target + #define THERM_LOOKAHEAD 4 // can be tweaked per build target #endif // fine-grained adjustment // how proportional should the adjustments be? (not used yet) @@ -297,13 +291,13 @@ static inline void ADC_temperature_handler() { if (! reset_thermal_history) { // average of recent samples - measurement = adc_smooth[1]; + measurement = adc_smooth[1] >> 1; } else { // wipe out old data // don't keep resetting reset_thermal_history = 0; // ignore average, use latest sample - measurement = adc_raw[1] << 6; + measurement = adc_raw[1] << 5; // forget any past measurements for(uint8_t i=0; i>6) - 275 + THERM_CAL_OFFSET + (int16_t)therm_cal_offset; + temperature = (measurement>>5) - 275 + THERM_CAL_OFFSET + (int16_t)therm_cal_offset; // how much has the temperature changed between now and a few seconds ago? int16_t diff; @@ -335,7 +329,7 @@ static inline void ADC_temperature_handler() { // 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) << 6; + uint16_t ceil = (therm_ceil + 275 - therm_cal_offset - THERM_CAL_OFFSET) << 5; //uint16_t floor = ceil - (THERM_WINDOW_SIZE << 6); int16_t offset_pt, offset_avg; offset_pt = (pt - ceil) >> 1; @@ -350,7 +344,7 @@ static inline void ADC_temperature_handler() { // Too hot? // (if it's too hot and not getting colder...) - if ((offset > 0) && (diff > (-1 << 5))) { + if ((offset > 0) && (diff > (-1 << 4))) { // reset counters temperature_timer = TEMPERATURE_TIMER_START; // how far above the ceiling? @@ -362,13 +356,13 @@ static inline void ADC_temperature_handler() { // Too cold? // (if it's too cold and not getting warmer...) - else if ((offset < -(THERM_WINDOW_SIZE << 6)) - && (diff < (1 << 4))) { + else if ((offset < -(THERM_WINDOW_SIZE << 5)) + && (diff < (1 << 3))) { // reset counters temperature_timer = TEMPERATURE_TIMER_START; // how far below the floor? //int16_t howmuch = (((-offset) - (THERM_WINDOW_SIZE<<6)) >> 7) * THERM_WINDOW_SIZE / 128; - int16_t howmuch = ((-offset) - (THERM_WINDOW_SIZE<<6)) >> 9; + int16_t howmuch = ((-offset) - (THERM_WINDOW_SIZE<<5)) >> 8; // send a notification (unless voltage is low) // (LVP and underheat warnings fight each other) if (voltage > VOLTAGE_LOW) -- cgit v1.2.3 From 930752b496ad8a1d9f3db96184839022c16a5c7f Mon Sep 17 00:00:00 2001 From: Selene ToyKeeper Date: Fri, 28 Feb 2020 02:06:53 -0700 Subject: went back to continuous lowpass because it had the best noise reduction (also, now treating smoothed ADC values as 11-bit, with the lowest 5 bits chopped off to eliminate noise) --- spaghetti-monster/fsm-adc.c | 124 ++++++++++++++++++++++++++------------------ 1 file changed, 73 insertions(+), 51 deletions(-) (limited to 'spaghetti-monster/fsm-adc.c') diff --git a/spaghetti-monster/fsm-adc.c b/spaghetti-monster/fsm-adc.c index 65669b3..59d4e5c 100644 --- a/spaghetti-monster/fsm-adc.c +++ b/spaghetti-monster/fsm-adc.c @@ -25,7 +25,7 @@ static inline void set_admux_therm() { #if (ATTINY == 1634) ADMUX = ADMUX_THERM; #elif (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85) - ADMUX = ADMUX_THERM; + ADMUX = ADMUX_THERM | (1 << ADLAR); #elif (ATTINY == 841) // FIXME: not tested ADMUXA = ADMUXA_THERM; ADMUXB = ADMUXB_THERM; @@ -46,9 +46,9 @@ inline void set_admux_voltage() { #endif #elif (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85) #ifdef USE_VOLTAGE_DIVIDER // 1.1V / pin7 - ADMUX = ADMUX_VOLTAGE_DIVIDER; + ADMUX = ADMUX_VOLTAGE_DIVIDER | (1 << ADLAR); #else // VCC / 1.1V reference - ADMUX = ADMUX_VCC; + ADMUX = ADMUX_VCC | (1 << ADLAR); #endif #elif (ATTINY == 841) // FIXME: not tested #ifdef USE_VOLTAGE_DIVIDER // 1.1V / pin7 @@ -88,7 +88,7 @@ inline void ADC_on() #endif #if (ATTINY == 1634) //ACSRA |= (1 << ACD); // turn off analog comparator to save power - //ADCSRB |= (1 << ADLAR); // left-adjust flag is here instead of ADMUX + ADCSRB |= (1 << ADLAR); // left-adjust flag is here instead of ADMUX #endif // enable, start, auto-retrigger, prescale ADCSRA = (1 << ADEN) | (1 << ADSC) | (1 << ADATE) | ADC_PRSCL; @@ -122,46 +122,42 @@ static inline uint8_t calc_voltage_divider(uint16_t value) { // Each full cycle runs ~4X per second with just voltage enabled, // or ~2X per second with voltage and temperature. #if defined(USE_LVP) && defined(USE_THERMAL_REGULATION) -#define ADC_CYCLES_PER_SECOND 2 +#define ADC_CYCLES_PER_SECOND 1 #else -#define ADC_CYCLES_PER_SECOND 4 +#define ADC_CYCLES_PER_SECOND 2 #endif // happens every time the ADC sampler finishes a measurement -// collects a rolling average of 64+ samples, which increases effective number -// of bits from 10 to about 16 (ish, probably more like 14 really) (64 was -// chosen because it's the largest sample size which allows the sum to still -// fit into a 16-bit integer, and for speed and size reasons, we want to avoid -// doing 32-bit math) ISR(ADC_vect) { - static uint32_t adc_sum; + if (adc_sample_count) { - // keep this moving along - adc_sample_count ++; + uint16_t m; // latest measurement + uint16_t s; // smoothed measurement + uint8_t channel = adc_channel; - // reset on first sample - // also, ignore first value since it's probably junk - if (1 == adc_sample_count) { - adc_sum = 0; - return; - } - // 2048 samples collected, save the result - else if (2050 == adc_sample_count) { - // save the latest result - adc_smooth[adc_channel] = adc_sum >> 5; - } - // add the latest measurement to the pile - else { - uint16_t m = ADC; - // add to the running total - adc_sum += m; // update the latest value - adc_raw[adc_channel] = m; + 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; + } - // track what woke us up, and enable deferred logic - irq_adc = 1; + // the next measurement isn't the first + //adc_sample_count = 1; + adc_sample_count ++; + } void adc_deferred() { @@ -171,7 +167,7 @@ void adc_deferred() { // 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; + pseudo_rand_seed += (ADCL >> 6) + (ADCH << 2); #endif // the ADC triggers repeatedly when it's on, but we only need to run the @@ -237,9 +233,22 @@ static inline void ADC_voltage_handler() { uint16_t measurement; // latest ADC value - if (go_to_standby) measurement = adc_raw[0] << 6; + 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); #else @@ -286,27 +295,35 @@ static inline void ADC_temperature_handler() { // N seconds between thermal regulation events #define TEMPERATURE_TIMER_START (THERMAL_WARNING_SECONDS*ADC_CYCLES_PER_SECOND) - // latest 16-bit ADC reading - uint16_t measurement; - - if (! reset_thermal_history) { - // average of recent samples - measurement = adc_smooth[1] >> 1; - } else { // wipe out old data + if (reset_thermal_history) { // wipe out old data // don't keep resetting reset_thermal_history = 0; // ignore average, use latest sample - measurement = adc_raw[1] << 5; + uint16_t foo = adc_raw[1]; + adc_smooth[1] = foo; // forget any past measurements for(uint8_t i=0; i> 5; } + // latest 16-bit ADC reading + uint16_t measurement = adc_smooth[1]; + + // 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 + // let the UI see the current temperature in C // Convert ADC units to Celsius (ish) - temperature = (measurement>>5) - 275 + THERM_CAL_OFFSET + (int16_t)therm_cal_offset; + 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; @@ -320,22 +337,27 @@ static inline void ADC_temperature_handler() { uint16_t pt; // predicted temperature pt = measurement + (diff * THERM_LOOKAHEAD); + /* seems unnecessary; simply sending repeated warnings has a similar effect // P[I]D: average of recent measurements uint16_t avg = 0; for(uint8_t i=0; i>3); + */ // 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) << 5; + uint16_t ceil = (therm_ceil + 275 - therm_cal_offset - THERM_CAL_OFFSET) << 1; //uint16_t floor = ceil - (THERM_WINDOW_SIZE << 6); + /* average of I and D terms int16_t offset_pt, offset_avg; offset_pt = (pt - ceil) >> 1; offset_avg = (avg - ceil) >> 1; int16_t offset = offset_pt + offset_avg; //int16_t offset = (pt - ceil) + (avg - ceil); + */ + int16_t offset = pt - ceil; if (temperature_timer) { @@ -344,25 +366,25 @@ static inline void ADC_temperature_handler() { // Too hot? // (if it's too hot and not getting colder...) - if ((offset > 0) && (diff > (-1 << 4))) { + if ((offset > 0) && (diff > (-1))) { // reset counters temperature_timer = TEMPERATURE_TIMER_START; // how far above the ceiling? //int16_t howmuch = (offset >> 6) * THERM_RESPONSE_MAGNITUDE / 128; - int16_t howmuch = (offset >> 8); + int16_t howmuch = (offset >> 1); // send a warning emit(EV_temperature_high, howmuch); } // Too cold? // (if it's too cold and not getting warmer...) - else if ((offset < -(THERM_WINDOW_SIZE << 5)) - && (diff < (1 << 3))) { + else if ((offset < -(THERM_WINDOW_SIZE << 1)) + && (diff < (1))) { // reset counters temperature_timer = TEMPERATURE_TIMER_START; // how far below the floor? //int16_t howmuch = (((-offset) - (THERM_WINDOW_SIZE<<6)) >> 7) * THERM_WINDOW_SIZE / 128; - int16_t howmuch = ((-offset) - (THERM_WINDOW_SIZE<<5)) >> 8; + int16_t howmuch = ((-offset) - (THERM_WINDOW_SIZE<<1)) >> 1; // send a notification (unless voltage is low) // (LVP and underheat warnings fight each other) if (voltage > VOLTAGE_LOW) -- cgit v1.2.3 From 4d1c7a34fd00a4dd19e591ea0602f0f47f10d9f0 Mon Sep 17 00:00:00 2001 From: Selene ToyKeeper Date: Thu, 5 Mar 2020 17:51:27 -0700 Subject: initial support for Noctigon KR4 (not complete, but far enough that it installs and runs) New hardware support features: - allow using PCINT other than 0 (PCINT1, PCINT2, etc) - option to ignore voltage ADC while the button is pressed (because my prototype shorts the voltage divider to 0 while the button is down) --- spaghetti-monster/fsm-adc.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'spaghetti-monster/fsm-adc.c') diff --git a/spaghetti-monster/fsm-adc.c b/spaghetti-monster/fsm-adc.c index 2a3c5c6..2d8d01f 100644 --- a/spaghetti-monster/fsm-adc.c +++ b/spaghetti-monster/fsm-adc.c @@ -206,6 +206,12 @@ static inline void ADC_voltage_handler() { #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 + #ifdef NO_LVP_WHILE_BUTTON_PRESSED + // don't run if button is currently being held + // (because the button causes a reading of zero volts) + if (button_last_state) return; + #endif + uint16_t measurement = adc_values[0]; // latest 10-bit ADC reading #ifdef USE_VOLTAGE_LOWPASS -- cgit v1.2.3 From 7110fdbae15c6303eb405705bf0b319fc1381a4f Mon Sep 17 00:00:00 2001 From: Selene ToyKeeper Date: Fri, 13 Mar 2020 18:06:27 -0600 Subject: tried to make thermal code a bit less twitchy... it regulates really fast on D4, but once it's stable, the adjustments are too large --- spaghetti-monster/fsm-adc.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) (limited to 'spaghetti-monster/fsm-adc.c') diff --git a/spaghetti-monster/fsm-adc.c b/spaghetti-monster/fsm-adc.c index 59d4e5c..358ff26 100644 --- a/spaghetti-monster/fsm-adc.c +++ b/spaghetti-monster/fsm-adc.c @@ -288,6 +288,9 @@ static inline void ADC_temperature_handler() { // acceptable temperature window size in C #define THERM_WINDOW_SIZE 3 + // 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]; @@ -365,21 +368,21 @@ static inline void ADC_temperature_handler() { } else { // it has been long enough since the last warning // Too hot? - // (if it's too hot and not getting colder...) - if ((offset > 0) && (diff > (-1))) { + // (if it's too hot and still getting warmer...) + if ((offset > 0) && (diff > 0)) { // reset counters temperature_timer = TEMPERATURE_TIMER_START; // how far above the ceiling? //int16_t howmuch = (offset >> 6) * THERM_RESPONSE_MAGNITUDE / 128; - int16_t howmuch = (offset >> 1); + //int16_t howmuch = (offset >> 1); + int16_t howmuch = offset; // send a warning emit(EV_temperature_high, howmuch); } // Too cold? - // (if it's too cold and not getting warmer...) - else if ((offset < -(THERM_WINDOW_SIZE << 1)) - && (diff < (1))) { + // (if it's too cold and still getting colder...) + else if ((offset < -(THERM_WINDOW_SIZE << 1)) && (diff < 0)) { // reset counters temperature_timer = TEMPERATURE_TIMER_START; // how far below the floor? @@ -387,7 +390,7 @@ static inline void ADC_temperature_handler() { int16_t howmuch = ((-offset) - (THERM_WINDOW_SIZE<<1)) >> 1; // send a notification (unless voltage is low) // (LVP and underheat warnings fight each other) - if (voltage > VOLTAGE_LOW) + if (voltage > (VOLTAGE_LOW + 1)) emit(EV_temperature_low, howmuch); } -- cgit v1.2.3 From ccc82a57904097ffd1c1225ef5a8f0082f7046d8 Mon Sep 17 00:00:00 2001 From: Selene ToyKeeper Date: Sun, 15 Mar 2020 19:21:37 -0600 Subject: replaced temperature_timer (which wasn't even being used) with a variable delay between warnings, so large warnings can remain frequent while small warnings are separated by more time, based on a cumulative error counter which must pass a threshold before the next warning is sent (this is producing good test results so far on D4v2 and D4Sv2) --- spaghetti-monster/fsm-adc.c | 70 ++++++++++++++++++++++++++++----------------- 1 file changed, 43 insertions(+), 27 deletions(-) (limited to 'spaghetti-monster/fsm-adc.c') diff --git a/spaghetti-monster/fsm-adc.c b/spaghetti-monster/fsm-adc.c index 358ff26..93c58f1 100644 --- a/spaghetti-monster/fsm-adc.c +++ b/spaghetti-monster/fsm-adc.c @@ -280,6 +280,10 @@ static inline void ADC_temperature_handler() { #ifndef THERM_LOOKAHEAD #define THERM_LOOKAHEAD 4 // can be tweaked per build target #endif + // 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 @@ -294,7 +298,7 @@ static inline void ADC_temperature_handler() { #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 uint8_t temperature_timer = 0; + static int8_t warning_threshold = 0; // N seconds between thermal regulation events #define TEMPERATURE_TIMER_START (THERMAL_WARNING_SECONDS*ADC_CYCLES_PER_SECOND) @@ -363,15 +367,17 @@ static inline void ADC_temperature_handler() { int16_t offset = pt - ceil; - if (temperature_timer) { - temperature_timer --; - } else { // it has been long enough since the last warning + //int16_t below = offset + (THERM_WINDOW_SIZE<<1); + + // 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; - // Too hot? - // (if it's too hot and still getting warmer...) - if ((offset > 0) && (diff > 0)) { - // reset counters - temperature_timer = TEMPERATURE_TIMER_START; // how far above the ceiling? //int16_t howmuch = (offset >> 6) * THERM_RESPONSE_MAGNITUDE / 128; //int16_t howmuch = (offset >> 1); @@ -379,32 +385,42 @@ static inline void ADC_temperature_handler() { // send a warning emit(EV_temperature_high, 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 ((offset < -(THERM_WINDOW_SIZE << 1)) && (diff < 0)) { + else if ((BELOW < 0) && (diff < 0)) { + // accumulated error isn't big enough yet to send a warning + if (warning_threshold < 0) { + //warning_threshold += ((THERM_WINDOW_SIZE<<1) - offset); + //warning_threshold -= (offset + (THERM_WINDOW_SIZE<<1)); + warning_threshold -= BELOW; + } else { // error is big enough; send a warning + //warning_threshold = (-THERM_NEXT_WARNING_THRESHOLD) - (offset + (THERM_WINDOW_SIZE<<1)); + warning_threshold = (-THERM_NEXT_WARNING_THRESHOLD) - BELOW; - // Too cold? - // (if it's too cold and still getting colder...) - else if ((offset < -(THERM_WINDOW_SIZE << 1)) && (diff < 0)) { - // reset counters - temperature_timer = TEMPERATURE_TIMER_START; // how far below the floor? //int16_t howmuch = (((-offset) - (THERM_WINDOW_SIZE<<6)) >> 7) * THERM_WINDOW_SIZE / 128; - int16_t howmuch = ((-offset) - (THERM_WINDOW_SIZE<<1)) >> 1; + //int16_t howmuch = ((-offset) - (THERM_WINDOW_SIZE<<1)) >> 1; + 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); } - - // Goldilocks? - // (temperature is within target window, or at least heading toward it) - else { - // reset counters - temperature_timer = TEMPERATURE_TIMER_START; - // send a notification (unless voltage is low) - // (LVP and temp-okay events fight each other) - if (voltage > VOLTAGE_LOW) - emit(EV_temperature_okay, 0); - } - + } + #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 -- cgit v1.2.3 From 4c1d17f4604bf38140381649a45a3c7c109ee97a Mon Sep 17 00:00:00 2001 From: Selene ToyKeeper Date: Sun, 15 Mar 2020 19:56:50 -0600 Subject: removed dead comments and dead code --- spaghetti-monster/fsm-adc.c | 30 ++---------------------------- 1 file changed, 2 insertions(+), 28 deletions(-) (limited to 'spaghetti-monster/fsm-adc.c') diff --git a/spaghetti-monster/fsm-adc.c b/spaghetti-monster/fsm-adc.c index 93c58f1..725902f 100644 --- a/spaghetti-monster/fsm-adc.c +++ b/spaghetti-monster/fsm-adc.c @@ -299,8 +299,6 @@ static inline void ADC_temperature_handler() { static uint8_t history_step = 0; static uint16_t temperature_history[NUM_TEMP_HISTORY_STEPS]; static int8_t warning_threshold = 0; - // N seconds between thermal regulation events - #define TEMPERATURE_TIMER_START (THERMAL_WARNING_SECONDS*ADC_CYCLES_PER_SECOND) if (reset_thermal_history) { // wipe out old data // don't keep resetting @@ -344,31 +342,13 @@ static inline void ADC_temperature_handler() { uint16_t pt; // predicted temperature pt = measurement + (diff * THERM_LOOKAHEAD); - /* seems unnecessary; simply sending repeated warnings has a similar effect - // P[I]D: average of recent measurements - uint16_t avg = 0; - for(uint8_t i=0; i>3); - */ - // 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; - //uint16_t floor = ceil - (THERM_WINDOW_SIZE << 6); - /* average of I and D terms - int16_t offset_pt, offset_avg; - offset_pt = (pt - ceil) >> 1; - offset_avg = (avg - ceil) >> 1; - int16_t offset = offset_pt + offset_avg; - //int16_t offset = (pt - ceil) + (avg - ceil); - */ int16_t offset = pt - ceil; - - //int16_t below = offset + (THERM_WINDOW_SIZE<<1); - // Too hot? // (if it's too hot and still getting warmer...) if ((offset > 0) && (diff > 0)) { @@ -379,8 +359,7 @@ static inline void ADC_temperature_handler() { warning_threshold = THERM_NEXT_WARNING_THRESHOLD - offset; // how far above the ceiling? - //int16_t howmuch = (offset >> 6) * THERM_RESPONSE_MAGNITUDE / 128; - //int16_t howmuch = (offset >> 1); + //int16_t howmuch = offset * THERM_RESPONSE_MAGNITUDE / 128; int16_t howmuch = offset; // send a warning emit(EV_temperature_high, howmuch); @@ -391,20 +370,15 @@ static inline void ADC_temperature_handler() { // (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 ((offset < -(THERM_WINDOW_SIZE << 1)) && (diff < 0)) { else if ((BELOW < 0) && (diff < 0)) { // accumulated error isn't big enough yet to send a warning if (warning_threshold < 0) { - //warning_threshold += ((THERM_WINDOW_SIZE<<1) - offset); - //warning_threshold -= (offset + (THERM_WINDOW_SIZE<<1)); warning_threshold -= BELOW; } else { // error is big enough; send a warning - //warning_threshold = (-THERM_NEXT_WARNING_THRESHOLD) - (offset + (THERM_WINDOW_SIZE<<1)); warning_threshold = (-THERM_NEXT_WARNING_THRESHOLD) - BELOW; // how far below the floor? - //int16_t howmuch = (((-offset) - (THERM_WINDOW_SIZE<<6)) >> 7) * THERM_WINDOW_SIZE / 128; - //int16_t howmuch = ((-offset) - (THERM_WINDOW_SIZE<<1)) >> 1; + // 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) -- cgit v1.2.3 From eccf9c3d4df44c5a8fd88571ee2aaeca70975926 Mon Sep 17 00:00:00 2001 From: Selene ToyKeeper Date: Sun, 15 Mar 2020 19:58:35 -0600 Subject: the ADC sample count doesn't need to be 16-bit any more, and isn't really a count any more... ... just a boolean flag for whether this is the first sample or a later sample (so I changed it and reduced the ROM size by ~28 bytes) --- spaghetti-monster/fsm-adc.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'spaghetti-monster/fsm-adc.c') diff --git a/spaghetti-monster/fsm-adc.c b/spaghetti-monster/fsm-adc.c index 725902f..c382a8a 100644 --- a/spaghetti-monster/fsm-adc.c +++ b/spaghetti-monster/fsm-adc.c @@ -155,8 +155,9 @@ ISR(ADC_vect) { } // the next measurement isn't the first - //adc_sample_count = 1; - adc_sample_count ++; + adc_sample_count = 1; + // rollover doesn't really matter + //adc_sample_count ++; } -- cgit v1.2.3 From 79c9e662b98bf4219de9419eb2ccb171f80ef12b Mon Sep 17 00:00:00 2001 From: Selene ToyKeeper Date: Mon, 16 Mar 2020 00:11:02 -0600 Subject: reduced regulation jitter by biasing errors toward zero by a constant amount, which mostly impacts small errors (and reduces jitter during the flat phase of regulation) while leaving large errors pretty much unaffected... also, made acceptable thermal window smaller to make up for this new extra tolerance --- spaghetti-monster/fsm-adc.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'spaghetti-monster/fsm-adc.c') diff --git a/spaghetti-monster/fsm-adc.c b/spaghetti-monster/fsm-adc.c index c382a8a..dd43cb9 100644 --- a/spaghetti-monster/fsm-adc.c +++ b/spaghetti-monster/fsm-adc.c @@ -291,7 +291,7 @@ static inline void ADC_temperature_handler() { #define THERM_RESPONSE_MAGNITUDE 128 #endif // acceptable temperature window size in C - #define THERM_WINDOW_SIZE 3 + #define THERM_WINDOW_SIZE 2 // TODO: make this configurable per build target? // (shorter time for hosts with a lower power-to-mass ratio) @@ -350,6 +350,19 @@ static inline void ADC_temperature_handler() { 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 ++; + } + } + // Too hot? // (if it's too hot and still getting warmer...) if ((offset > 0) && (diff > 0)) { -- cgit v1.2.3 From 49f1b5ccd2033109814b99ea4650375e8f33a6be Mon Sep 17 00:00:00 2001 From: Selene ToyKeeper Date: Mon, 16 Mar 2020 03:19:42 -0600 Subject: merged some misc fixes from pakutrai, cleaned up comments, removed unused symbols --- spaghetti-monster/fsm-adc.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'spaghetti-monster/fsm-adc.c') diff --git a/spaghetti-monster/fsm-adc.c b/spaghetti-monster/fsm-adc.c index dd43cb9..59d624b 100644 --- a/spaghetti-monster/fsm-adc.c +++ b/spaghetti-monster/fsm-adc.c @@ -119,8 +119,8 @@ static inline uint8_t calc_voltage_divider(uint16_t value) { } #endif -// Each full cycle runs ~4X per second with just voltage enabled, -// or ~2X 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 1 #else @@ -276,6 +276,7 @@ static inline void ADC_voltage_handler() { #ifdef USE_THERMAL_REGULATION +// generally happens once per second while awake static inline void ADC_temperature_handler() { // coarse adjustment #ifndef THERM_LOOKAHEAD -- cgit v1.2.3 From 227b54b1095e47ae9d0ab4b4c2a49862fd708559 Mon Sep 17 00:00:00 2001 From: Selene ToyKeeper Date: Mon, 16 Mar 2020 04:13:55 -0600 Subject: fixed calc_voltage_divider() (use 10-bit calibration values, not 8-bit) --- spaghetti-monster/fsm-adc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'spaghetti-monster/fsm-adc.c') diff --git a/spaghetti-monster/fsm-adc.c b/spaghetti-monster/fsm-adc.c index 7880238..e7b7feb 100644 --- a/spaghetti-monster/fsm-adc.c +++ b/spaghetti-monster/fsm-adc.c @@ -112,7 +112,7 @@ 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 - uint16_t adc_per_volt = ((ADC_44<<7) - (ADC_22<<7)) / (44-22); + uint16_t adc_per_volt = ((ADC_44<<5) - (ADC_22<<5)) / (44-22); // shift incoming value into a matching position uint8_t result = ((value>>1) / adc_per_volt) + VOLTAGE_FUDGE_FACTOR; return result; -- cgit v1.2.3 From ef1d96b85485000e70ef5423c9494eb91286d3d7 Mon Sep 17 00:00:00 2001 From: Selene ToyKeeper Date: Thu, 26 Mar 2020 03:46:47 -0600 Subject: made thermal regulation use a smaller target window, and prioritize cooling more --- spaghetti-monster/fsm-adc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'spaghetti-monster/fsm-adc.c') diff --git a/spaghetti-monster/fsm-adc.c b/spaghetti-monster/fsm-adc.c index e7b7feb..edf1809 100644 --- a/spaghetti-monster/fsm-adc.c +++ b/spaghetti-monster/fsm-adc.c @@ -362,7 +362,7 @@ static inline void ADC_temperature_handler() { // (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++) { + for(uint8_t foo=0; foo<3; foo++) { if (offset > 0) { offset --; } else if (offset < 0) { @@ -371,8 +371,8 @@ static inline void ADC_temperature_handler() { } // Too hot? - // (if it's too hot and still getting warmer...) - if ((offset > 0) && (diff > 0)) { + // (if it's too hot and not getting cooler...) + if ((offset > 0) && (diff > -1)) { // accumulated error isn't big enough yet to send a warning if (warning_threshold > 0) { warning_threshold -= offset; -- cgit v1.2.3 From 43eec5d7b84f1ac617519723373850733d3adcb1 Mon Sep 17 00:00:00 2001 From: Selene ToyKeeper Date: Sun, 19 Apr 2020 03:17:24 -0600 Subject: made thermal response larger when error is large, smaller when error is small (helps on KR4, but will probably need to add the response magnitude thing to adjust speed per build target) --- spaghetti-monster/fsm-adc.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'spaghetti-monster/fsm-adc.c') diff --git a/spaghetti-monster/fsm-adc.c b/spaghetti-monster/fsm-adc.c index edf1809..760acc4 100644 --- a/spaghetti-monster/fsm-adc.c +++ b/spaghetti-monster/fsm-adc.c @@ -377,11 +377,17 @@ static inline void ADC_temperature_handler() { if (warning_threshold > 0) { warning_threshold -= offset; } else { // error is big enough; send a warning - warning_threshold = THERM_NEXT_WARNING_THRESHOLD - offset; + //warning_threshold = THERM_NEXT_WARNING_THRESHOLD - offset; // how far above the ceiling? //int16_t howmuch = offset * THERM_RESPONSE_MAGNITUDE / 128; - int16_t howmuch = offset; + //int16_t howmuch = offset; + // increase the amount, except for small values + // 1:1, 2:1, 3:3, 4:5, 6:9, 8:13, 10:17, 40:77 + int16_t howmuch = offset + offset - 3; + if (howmuch < 1) howmuch = 1; + warning_threshold = THERM_NEXT_WARNING_THRESHOLD - (uint8_t)howmuch; + // send a warning emit(EV_temperature_high, howmuch); } -- cgit v1.2.3 From 536e9c0d3e89fea0e37a840c0c72136c25f71889 Mon Sep 17 00:00:00 2001 From: Selene ToyKeeper Date: Sun, 19 Apr 2020 03:29:43 -0600 Subject: added tweakable thermal_response_magnitude option, adjusted KR4 thermal vars, made KR4 rainbow mode faster --- spaghetti-monster/fsm-adc.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'spaghetti-monster/fsm-adc.c') diff --git a/spaghetti-monster/fsm-adc.c b/spaghetti-monster/fsm-adc.c index 760acc4..68361ae 100644 --- a/spaghetti-monster/fsm-adc.c +++ b/spaghetti-monster/fsm-adc.c @@ -286,21 +286,21 @@ static inline void ADC_voltage_handler() { static inline void ADC_temperature_handler() { // coarse adjustment #ifndef THERM_LOOKAHEAD - #define THERM_LOOKAHEAD 4 // can be tweaked per build target + #define THERM_LOOKAHEAD 4 #endif // 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) + // how proportional should the adjustments be? #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? + // 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 @@ -377,14 +377,14 @@ static inline void ADC_temperature_handler() { 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; - // increase the amount, except for small values - // 1:1, 2:1, 3:3, 4:5, 6:9, 8:13, 10:17, 40:77 - int16_t howmuch = offset + offset - 3; + // original method works, but is too slow on some small hosts: + // (and typically has a minimum response magnitude of 2 instead of 1) + // int16_t howmuch = offset; + // ... so increase the amount, except for small values + // (for example, 1:1, 2:1, 3:3, 4:5, 6:9, 8:13, 10:17, 40:77) + // ... and let us tune the response per build target if desired + int16_t howmuch = (offset + offset - 3) * THERM_RESPONSE_MAGNITUDE / 128; if (howmuch < 1) howmuch = 1; warning_threshold = THERM_NEXT_WARNING_THRESHOLD - (uint8_t)howmuch; -- cgit v1.2.3 From 0f0085f244d9ec7f49780d3f7286eb824825dfe6 Mon Sep 17 00:00:00 2001 From: Selene ToyKeeper Date: Mon, 27 Apr 2020 17:52:46 -0600 Subject: set default thermal response magnitude back to how it was before the KR4 updates, because it worked well on many hosts before... --- spaghetti-monster/fsm-adc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'spaghetti-monster/fsm-adc.c') diff --git a/spaghetti-monster/fsm-adc.c b/spaghetti-monster/fsm-adc.c index 68361ae..45a4297 100644 --- a/spaghetti-monster/fsm-adc.c +++ b/spaghetti-monster/fsm-adc.c @@ -295,7 +295,7 @@ static inline void ADC_temperature_handler() { // fine-grained adjustment // how proportional should the adjustments be? #ifndef THERM_RESPONSE_MAGNITUDE - #define THERM_RESPONSE_MAGNITUDE 128 + #define THERM_RESPONSE_MAGNITUDE 64 #endif // acceptable temperature window size in C #define THERM_WINDOW_SIZE 2 -- cgit v1.2.3