From 9c0d1832464e4ee7ee8c4c63092ac4337347483b Mon Sep 17 00:00:00 2001 From: Selene ToyKeeper Date: Wed, 29 Jan 2020 05:08:14 -0700 Subject: rewrote ADC code to use a continuous lowpass system on all measurements, to eliminate noise and maybe increase precision (thermal code still needs to be rewritten though) --- spaghetti-monster/fsm-adc.c | 296 ++++++++++++++++++++++++---------------- spaghetti-monster/fsm-adc.h | 12 +- spaghetti-monster/fsm-main.c | 2 +- spaghetti-monster/fsm-standby.c | 8 +- spaghetti-monster/fsm-wdt.c | 6 +- 5 files changed, 194 insertions(+), 130 deletions(-) (limited to 'spaghetti-monster') diff --git a/spaghetti-monster/fsm-adc.c b/spaghetti-monster/fsm-adc.c index 3763a3e..0a80461 100644 --- a/spaghetti-monster/fsm-adc.c +++ b/spaghetti-monster/fsm-adc.c @@ -22,38 +22,48 @@ static inline void set_admux_therm() { - #if (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85) || (ATTINY == 1634) + #if (ATTINY == 1634) ADMUX = ADMUX_THERM; - #elif (ATTINY == 841) + #elif (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85) + ADMUX = ADMUX_THERM | (1 << ADLAR); + #elif (ATTINY == 841) // FIXME: not tested ADMUXA = ADMUXA_THERM; ADMUXB = ADMUXB_THERM; #else #error Unrecognized MCU type #endif adc_channel = 1; + adc_sample_count = 0; // first result is unstable + ADC_start_measurement(); } inline void set_admux_voltage() { - #if (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85) || (ATTINY == 1634) - #ifdef USE_VOLTAGE_DIVIDER - // 1.1V / pin7 - ADMUX = ADMUX_VOLTAGE_DIVIDER; - #else - // VCC / 1.1V reference - ADMUX = ADMUX_VCC; + #if (ATTINY == 1634) + #ifdef USE_VOLTAGE_DIVIDER // 1.1V / pin7 + ADMUX = ADMUX_VOLTAGE_DIVIDER; + #else // VCC / 1.1V reference + ADMUX = ADMUX_VCC; #endif - #elif (ATTINY == 841) - #ifdef USE_VOLTAGE_DIVIDER - ADMUXA = ADMUXA_VOLTAGE_DIVIDER; - ADMUXB = ADMUXB_VOLTAGE_DIVIDER; - #else - ADMUXA = ADMUXA_VCC; - ADMUXB = ADMUXB_VCC; + #elif (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85) + #ifdef USE_VOLTAGE_DIVIDER // 1.1V / pin7 + ADMUX = ADMUX_VOLTAGE_DIVIDER | (1 << ADLAR); + #else // VCC / 1.1V reference + ADMUX = ADMUX_VCC | (1 << ADLAR); + #endif + #elif (ATTINY == 841) // FIXME: not tested + #ifdef USE_VOLTAGE_DIVIDER // 1.1V / pin7 + ADMUXA = ADMUXA_VOLTAGE_DIVIDER; + ADMUXB = ADMUXB_VOLTAGE_DIVIDER; + #else // VCC / 1.1V reference + ADMUXA = ADMUXA_VCC; + ADMUXB = ADMUXB_VCC; #endif #else #error Unrecognized MCU type #endif adc_channel = 0; + adc_sample_count = 0; // first result is unstable + ADC_start_measurement(); } inline void ADC_start_measurement() { @@ -70,24 +80,25 @@ inline void ADC_on() #if (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85) || (ATTINY == 1634) set_admux_voltage(); #ifdef USE_VOLTAGE_DIVIDER - // disable digital input on divider pin to reduce power consumption - DIDR0 |= (1 << VOLTAGE_ADC_DIDR); + // 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 + // 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 + ADCSRB |= (1 << ADLAR); // left-adjust flag is here instead of ADMUX #endif - // enable, start, prescale - ADCSRA = (1 << ADEN) | (1 << ADSC) | ADC_PRSCL; + // enable, start, auto-retrigger, prescale + ADCSRA = (1 << ADEN) | (1 << ADSC) | (1 << ADATE) | ADC_PRSCL; // end tiny25/45/85 - #elif (ATTINY == 841) + #elif (ATTINY == 841) // FIXME: not tested, missing left-adjust ADCSRB = 0; // Right adjusted, auto trigger bits cleared. //ADCSRA = (1 << ADEN ) | 0b011; // ADC on, prescaler division factor 8. set_admux_voltage(); - // enable, start, prescale - ADCSRA = (1 << ADEN) | (1 << ADSC) | ADC_PRSCL; + // enable, start, auto-retrigger, prescale + ADCSRA = (1 << ADEN) | (1 << ADSC) | (1 << ADATE) | ADC_PRSCL; //ADCSRA |= (1 << ADSC); // start measuring #else #error Unrecognized MCU type @@ -102,8 +113,8 @@ inline void ADC_off() { 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); - // incoming value is 8.2 fixed-point, so shift it 2 bits less - uint8_t result = ((value<<5) / adc_per_volt) + VOLTAGE_FUDGE_FACTOR; + // incoming value is left-adjusted, so shift it into a matching position + uint8_t result = ((value>>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/anduril/anduril.c | 10 ++++ spaghetti-monster/fsm-adc.c | 107 +++++++++++++----------------------- spaghetti-monster/fsm-adc.h | 4 +- spaghetti-monster/fsm-events.h | 1 + spaghetti-monster/fsm-wdt.c | 13 +++-- 5 files changed, 59 insertions(+), 76 deletions(-) (limited to 'spaghetti-monster') diff --git a/spaghetti-monster/anduril/anduril.c b/spaghetti-monster/anduril/anduril.c index 380e2f9..2b11486 100644 --- a/spaghetti-monster/anduril/anduril.c +++ b/spaghetti-monster/anduril/anduril.c @@ -1062,6 +1062,16 @@ uint8_t steady_state(Event event, uint16_t arg) { } return MISCHIEF_MANAGED; } + #ifdef USE_SET_LEVEL_GRADUALLY + // temperature is within target window + // (so stop trying to adjust output) + else if (event == EV_temperature_okay) { + // if we're still adjusting output... stop + gradual_target = actual_level; + //set_level_gradually(actual_level); + return MISCHIEF_MANAGED; + } + #endif // ifdef USE_SET_LEVEL_GRADUALLY #endif // ifdef USE_THERMAL_REGULATION return EVENT_NOT_HANDLED; } 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 diff --git a/spaghetti-monster/fsm-adc.h b/spaghetti-monster/fsm-adc.h index f4c1332..6283b2c 100644 --- a/spaghetti-monster/fsm-adc.h +++ b/spaghetti-monster/fsm-adc.h @@ -67,9 +67,9 @@ void battcheck(); #ifdef USE_THERMAL_REGULATION -// default 5 seconds between thermal regulation events +// default 1 seconds between thermal regulation events #ifndef THERMAL_WARNING_SECONDS -#define THERMAL_WARNING_SECONDS 5 +#define THERMAL_WARNING_SECONDS 1 #endif // try to keep temperature below 45 C #ifndef DEFAULT_THERM_CEIL diff --git a/spaghetti-monster/fsm-events.h b/spaghetti-monster/fsm-events.h index 39ad3aa..6760fdd 100644 --- a/spaghetti-monster/fsm-events.h +++ b/spaghetti-monster/fsm-events.h @@ -85,6 +85,7 @@ static volatile uint16_t ticks_since_last_event = 0; #ifdef USE_THERMAL_REGULATION #define EV_temperature_high (B_SYSTEM|0b00000101) #define EV_temperature_low (B_SYSTEM|0b00000110) +#define EV_temperature_okay (B_SYSTEM|0b00000111) #endif // Button press events diff --git a/spaghetti-monster/fsm-wdt.c b/spaghetti-monster/fsm-wdt.c index e9dca3a..459010f 100644 --- a/spaghetti-monster/fsm-wdt.c +++ b/spaghetti-monster/fsm-wdt.c @@ -119,7 +119,7 @@ void WDT_inner() { return; // no sleep LVP needed if nothing drains power while off #else // stop here, usually... but proceed often enough for sleep LVP to work - if (0 != (ticks_since_last & 0x7f)) return; + if (0 != (ticks_since_last & 0x3f)) return; adc_trigger = 255; // make sure a measurement will happen ADC_on(); // enable ADC voltage measurement functions temporarily @@ -178,12 +178,15 @@ void WDT_inner() { #endif #if defined(USE_LVP) || defined(USE_THERMAL_REGULATION) - // start a new ADC measurement every 4 ticks + // start a new ADC measurement every 16 ticks adc_trigger ++; - if (0 == (adc_trigger & 3)) { - // in case we're in standby mode and auto-retrigger is turned off + if (0 == (adc_trigger & 15)) { + // in case we're in standby mode and the ADC is turned off + if (go_to_standby) { + //set_admux_voltage(); + ADC_on(); + } ADC_start_measurement(); - adc_sample_count = 0; // allow regulation logic to run adc_deferred_enable = 1; } -- cgit v1.2.3 From f6f553bc5dbef8ef4a00b5e19838d775eff9ab41 Mon Sep 17 00:00:00 2001 From: Selene ToyKeeper Date: Mon, 3 Feb 2020 02:52:53 -0700 Subject: mention the maximum allowed temperature in the manual --- spaghetti-monster/anduril/anduril-manual.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'spaghetti-monster') diff --git a/spaghetti-monster/anduril/anduril-manual.txt b/spaghetti-monster/anduril/anduril-manual.txt index 3f17f41..97e6589 100644 --- a/spaghetti-monster/anduril/anduril-manual.txt +++ b/spaghetti-monster/anduril/anduril-manual.txt @@ -147,7 +147,7 @@ In more detail, this is what each blinky / utility mode does: can reach before it will start doing thermal regulation to keep itself from overheating. Click once per degree C above 30. For example, to set the limit to 50 C, click 20 times. The default - is 45 C. + is 45 C, and the highest value it will allow is 70 C. Strobe / Mood Modes -- cgit v1.2.3 From 32cc8a10f500456a0288eff38d41e8b820ee2a7d Mon Sep 17 00:00:00 2001 From: Selene ToyKeeper Date: Wed, 5 Feb 2020 02:35:10 -0700 Subject: replaced Anduril's gradual adjustment speed code with a smaller simpler version --- spaghetti-monster/anduril/anduril.c | 60 +++++++++++-------------------------- 1 file changed, 17 insertions(+), 43 deletions(-) (limited to 'spaghetti-monster') diff --git a/spaghetti-monster/anduril/anduril.c b/spaghetti-monster/anduril/anduril.c index 2b11486..69dd118 100644 --- a/spaghetti-monster/anduril/anduril.c +++ b/spaghetti-monster/anduril/anduril.c @@ -957,59 +957,33 @@ uint8_t steady_state(Event event, uint16_t arg) { if (arg == TICKS_PER_SECOND) ramp_direction = 1; #endif #ifdef USE_SET_LEVEL_GRADUALLY - // make thermal adjustment speed scale with magnitude - // also, adjust slower when going up - if ((arg & 1) && - ((actual_level < THERM_FASTER_LEVEL) || - (actual_level < gradual_target))) { - return MISCHIEF_MANAGED; // adjust slower when not a high mode - } - #ifdef THERM_HARD_TURBO_DROP - else if ((! (actual_level < THERM_FASTER_LEVEL)) - && (actual_level > gradual_target)) { - gradual_tick(); - } - else { - #endif - // [int(62*4 / (x**0.8)) for x in (1,2,4,8,16,32,64,128)] - //uint8_t intervals[] = {248, 142, 81, 46, 26, 15, 8, 5}; - // [int(62*4 / (x**0.9)) for x in (1,2,4,8,16,32,64,128)] - //uint8_t intervals[] = {248, 132, 71, 38, 20, 10, 5, 3}; - // [int(62*4 / (x**0.95)) for x in (1,2,4,8,16,32,64,128)] - uint8_t intervals[] = {248, 128, 66, 34, 17, 9, 4, 2}; - uint8_t diff; - static uint8_t ticks_since_adjust = 0; - if (gradual_target > actual_level) { - // rise at half speed (skip half the frames) - if (arg & 2) return MISCHIEF_MANAGED; - diff = gradual_target - actual_level; - } else { - diff = actual_level - gradual_target; - } - ticks_since_adjust ++; - // if there's any adjustment to be made, make it + int16_t diff = gradual_target - actual_level; + static uint16_t ticks_since_adjust = 0; + ticks_since_adjust++; if (diff) { - uint8_t magnitude = 0; - #ifndef THERM_HARD_TURBO_DROP - // if we're on a really high mode, drop faster - if ((actual_level >= THERM_FASTER_LEVEL) - && (actual_level > gradual_target)) { magnitude ++; } - #endif + uint16_t ticks_per_adjust = 256; + if (diff < 0) { + diff = -diff; + if (actual_level > THERM_FASTER_LEVEL) { + #ifdef THERM_HARD_TURBO_DROP + ticks_per_adjust >>= 2; + #endif + ticks_per_adjust >>= 2; + } + } else { + // rise at half speed + ticks_per_adjust <<= 1; + } while (diff) { - magnitude ++; + ticks_per_adjust >>= 1; diff >>= 1; } - uint8_t ticks_per_adjust = intervals[magnitude]; if (ticks_since_adjust > ticks_per_adjust) { gradual_tick(); ticks_since_adjust = 0; } - //if (!(arg % ticks_per_adjust)) gradual_tick(); } - #ifdef THERM_HARD_TURBO_DROP - } - #endif #endif // ifdef USE_SET_LEVEL_GRADUALLY return MISCHIEF_MANAGED; } -- 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') 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') 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 +++++++++++++------------------- spaghetti-monster/fsm-adc.h | 2 +- 2 files changed, 14 insertions(+), 20 deletions(-) (limited to 'spaghetti-monster') 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) diff --git a/spaghetti-monster/fsm-adc.h b/spaghetti-monster/fsm-adc.h index 6283b2c..7acb505 100644 --- a/spaghetti-monster/fsm-adc.h +++ b/spaghetti-monster/fsm-adc.h @@ -40,7 +40,7 @@ #endif volatile uint8_t irq_adc = 0; // ADC interrupt happened? -uint8_t adc_sample_count = 0; // skip the first sample; it's junk +uint16_t adc_sample_count = 0; // skip the first sample; it's junk uint8_t adc_channel = 0; // 0=voltage, 1=temperature uint16_t adc_raw[2]; // last ADC measurements (0=voltage, 1=temperature) uint16_t adc_smooth[2]; // lowpassed ADC measurements (0=voltage, 1=temperature) -- 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 ++++++++++++++++++++++++++------------------ spaghetti-monster/fsm-wdt.c | 2 +- 2 files changed, 74 insertions(+), 52 deletions(-) (limited to 'spaghetti-monster') 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) diff --git a/spaghetti-monster/fsm-wdt.c b/spaghetti-monster/fsm-wdt.c index 459010f..12aab7b 100644 --- a/spaghetti-monster/fsm-wdt.c +++ b/spaghetti-monster/fsm-wdt.c @@ -180,7 +180,7 @@ void WDT_inner() { #if defined(USE_LVP) || defined(USE_THERMAL_REGULATION) // start a new ADC measurement every 16 ticks adc_trigger ++; - if (0 == (adc_trigger & 15)) { + if (0 == (adc_trigger & 31)) { // in case we're in standby mode and the ADC is turned off if (go_to_standby) { //set_admux_voltage(); -- 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/anduril/anduril.c | 13 ++++++++----- spaghetti-monster/fsm-adc.c | 17 ++++++++++------- spaghetti-monster/fsm-wdt.c | 2 +- 3 files changed, 19 insertions(+), 13 deletions(-) (limited to 'spaghetti-monster') diff --git a/spaghetti-monster/anduril/anduril.c b/spaghetti-monster/anduril/anduril.c index 69dd118..469d0d8 100644 --- a/spaghetti-monster/anduril/anduril.c +++ b/spaghetti-monster/anduril/anduril.c @@ -963,7 +963,7 @@ uint8_t steady_state(Event event, uint16_t arg) { if (diff) { uint16_t ticks_per_adjust = 256; if (diff < 0) { - diff = -diff; + //diff = -diff; if (actual_level > THERM_FASTER_LEVEL) { #ifdef THERM_HARD_TURBO_DROP ticks_per_adjust >>= 2; @@ -976,7 +976,8 @@ uint8_t steady_state(Event event, uint16_t arg) { } while (diff) { ticks_per_adjust >>= 1; - diff >>= 1; + //diff >>= 1; + diff /= 2; // because shifting produces weird behavior } if (ticks_since_adjust > ticks_per_adjust) { @@ -1040,9 +1041,11 @@ uint8_t steady_state(Event event, uint16_t arg) { // temperature is within target window // (so stop trying to adjust output) else if (event == EV_temperature_okay) { - // if we're still adjusting output... stop - gradual_target = actual_level; - //set_level_gradually(actual_level); + // if we're still adjusting output... stop after the current step + if (gradual_target > actual_level) + gradual_target = actual_level + 1; + else if (gradual_target < actual_level) + gradual_target = actual_level - 1; return MISCHIEF_MANAGED; } #endif // ifdef USE_SET_LEVEL_GRADUALLY 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); } diff --git a/spaghetti-monster/fsm-wdt.c b/spaghetti-monster/fsm-wdt.c index 12aab7b..1d630a4 100644 --- a/spaghetti-monster/fsm-wdt.c +++ b/spaghetti-monster/fsm-wdt.c @@ -178,7 +178,7 @@ void WDT_inner() { #endif #if defined(USE_LVP) || defined(USE_THERMAL_REGULATION) - // start a new ADC measurement every 16 ticks + // enable the deferred ADC handler every 32 ticks adc_trigger ++; if (0 == (adc_trigger & 31)) { // in case we're in standby mode and the ADC is turned off -- 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') 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') 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 +++-- spaghetti-monster/fsm-adc.h | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'spaghetti-monster') 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 ++; } diff --git a/spaghetti-monster/fsm-adc.h b/spaghetti-monster/fsm-adc.h index 7acb505..6283b2c 100644 --- a/spaghetti-monster/fsm-adc.h +++ b/spaghetti-monster/fsm-adc.h @@ -40,7 +40,7 @@ #endif volatile uint8_t irq_adc = 0; // ADC interrupt happened? -uint16_t adc_sample_count = 0; // skip the first sample; it's junk +uint8_t adc_sample_count = 0; // skip the first sample; it's junk uint8_t adc_channel = 0; // 0=voltage, 1=temperature uint16_t adc_raw[2]; // last ADC measurements (0=voltage, 1=temperature) uint16_t adc_smooth[2]; // lowpassed ADC measurements (0=voltage, 1=temperature) -- 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') 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 6e7884cf5b4756ffba9f6dc9dd1fb94184779bf1 Mon Sep 17 00:00:00 2001 From: Selene ToyKeeper Date: Mon, 16 Mar 2020 00:13:44 -0600 Subject: cleaned up WDT ADC timer code slightly, and removed a bit of redundant code --- spaghetti-monster/fsm-wdt.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'spaghetti-monster') diff --git a/spaghetti-monster/fsm-wdt.c b/spaghetti-monster/fsm-wdt.c index 1d630a4..9858e09 100644 --- a/spaghetti-monster/fsm-wdt.c +++ b/spaghetti-monster/fsm-wdt.c @@ -111,7 +111,7 @@ void WDT_inner() { #ifdef TICK_DURING_STANDBY // handle standby mode specially if (go_to_standby) { - // emit a halfsleep tick, and process it + // emit a sleep tick, and process it emit(EV_sleep_tick, ticks_since_last); process_emissions(); @@ -121,7 +121,7 @@ void WDT_inner() { // stop here, usually... but proceed often enough for sleep LVP to work if (0 != (ticks_since_last & 0x3f)) return; - adc_trigger = 255; // make sure a measurement will happen + adc_trigger = 0; // make sure a measurement will happen ADC_on(); // enable ADC voltage measurement functions temporarily #endif } @@ -178,18 +178,20 @@ void WDT_inner() { #endif #if defined(USE_LVP) || defined(USE_THERMAL_REGULATION) - // enable the deferred ADC handler every 32 ticks - adc_trigger ++; - if (0 == (adc_trigger & 31)) { + // enable the deferred ADC handler once in a while + if (! adc_trigger) { + /* redundant; it was already turned on earlier in this function // in case we're in standby mode and the ADC is turned off if (go_to_standby) { //set_admux_voltage(); ADC_on(); } + */ ADC_start_measurement(); - // allow regulation logic to run adc_deferred_enable = 1; } + // timing for the ADC handler is every 32 ticks (~2Hz) + adc_trigger = (adc_trigger + 1) & 31; #endif } -- cgit v1.2.3 From 46e0f50a224c300b7e66ef00719edc0cef1f4c8d Mon Sep 17 00:00:00 2001 From: Selene ToyKeeper Date: Mon, 16 Mar 2020 00:15:19 -0600 Subject: deleted commented-out WDT code from previous revision (had only saved it so there would be a note in history about why it's gone) --- spaghetti-monster/fsm-wdt.c | 7 ------- 1 file changed, 7 deletions(-) (limited to 'spaghetti-monster') diff --git a/spaghetti-monster/fsm-wdt.c b/spaghetti-monster/fsm-wdt.c index 9858e09..94266c1 100644 --- a/spaghetti-monster/fsm-wdt.c +++ b/spaghetti-monster/fsm-wdt.c @@ -180,13 +180,6 @@ void WDT_inner() { #if defined(USE_LVP) || defined(USE_THERMAL_REGULATION) // enable the deferred ADC handler once in a while if (! adc_trigger) { - /* redundant; it was already turned on earlier in this function - // in case we're in standby mode and the ADC is turned off - if (go_to_standby) { - //set_admux_voltage(); - ADC_on(); - } - */ ADC_start_measurement(); adc_deferred_enable = 1; } -- cgit v1.2.3 From 84f9a0c9daf991b23d665a5dffdb3b76e6753491 Mon Sep 17 00:00:00 2001 From: Selene ToyKeeper Date: Mon, 16 Mar 2020 03:17:14 -0600 Subject: increased blink speed slightly, and added a library function to blink out 16-bit numbers --- spaghetti-monster/fsm-misc.c | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) (limited to 'spaghetti-monster') diff --git a/spaghetti-monster/fsm-misc.c b/spaghetti-monster/fsm-misc.c index 8da7b5b..152f047 100644 --- a/spaghetti-monster/fsm-misc.c +++ b/spaghetti-monster/fsm-misc.c @@ -46,19 +46,41 @@ uint8_t blink_digit(uint8_t num) { //StatePtr old_state = current_state; // "zero" digit gets a single short blink - uint8_t ontime = BLINK_SPEED * 2 / 10; + uint8_t ontime = BLINK_SPEED * 2 / 12; if (!num) { ontime = 8; num ++; } for (; num>0; num--) { set_level(BLINK_BRIGHTNESS); nice_delay_ms(ontime); set_level(0); - nice_delay_ms(BLINK_SPEED * 3 / 10); + nice_delay_ms(BLINK_SPEED * 3 / 12); } - return nice_delay_ms(BLINK_SPEED * 5 / 10); + return nice_delay_ms(BLINK_SPEED * 8 / 12); } #endif +#ifdef USE_BLINK_BIG_NUM +uint8_t blink_big_num(uint16_t num) { + uint16_t digits[] = { 10000, 1000, 100, 10, 1 }; + uint8_t started = 0; + for (uint8_t digit=0; digit= scale) { + started = 1; + } + if (started) { + uint8_t digit = 0; + while (num >= scale) { + num -= scale; + digit ++; + } + if (! blink_digit(digit)) return 0; + } + } + + return nice_delay_ms(1000); +} +#endif #ifdef USE_BLINK_NUM uint8_t blink_num(uint8_t num) { //StatePtr old_state = current_state; -- 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/anduril/anduril.c | 10 ++++++++-- spaghetti-monster/anduril/cfg-emisar-d18.h | 2 -- spaghetti-monster/anduril/cfg-emisar-d4.h | 4 ---- spaghetti-monster/anduril/cfg-emisar-d4s.h | 5 ----- spaghetti-monster/anduril/cfg-emisar-d4sv2.h | 14 -------------- spaghetti-monster/anduril/cfg-emisar-d4v2.h | 12 ------------ spaghetti-monster/anduril/cfg-ff-pl47.h | 5 ----- spaghetti-monster/anduril/cfg-ff-pl47g2.h | 5 ----- spaghetti-monster/anduril/cfg-ff-rot66.h | 3 --- spaghetti-monster/anduril/cfg-mateminco-mf01-mini.h | 5 ----- spaghetti-monster/anduril/cfg-sofirn-sp36.h | 7 ------- spaghetti-monster/fsm-adc.c | 5 +++-- spaghetti-monster/fsm-adc.h | 12 +++--------- spaghetti-monster/fsm-standby.c | 4 ---- spaghetti-monster/spaghetti-monster.txt | 12 ++++-------- 15 files changed, 18 insertions(+), 87 deletions(-) (limited to 'spaghetti-monster') diff --git a/spaghetti-monster/anduril/anduril.c b/spaghetti-monster/anduril/anduril.c index 469d0d8..bc2f9c6 100644 --- a/spaghetti-monster/anduril/anduril.c +++ b/spaghetti-monster/anduril/anduril.c @@ -22,7 +22,7 @@ // Anduril config file name (set it here or define it at the gcc command line) //#define CONFIGFILE cfg-blf-q8.h -#define USE_LVP // FIXME: won't build when this option is turned off +#define USE_LVP // parameters for this defined below or per-driver #define USE_THERMAL_REGULATION @@ -275,6 +275,7 @@ void sos_blink(uint8_t num, uint8_t dah); uint8_t battcheck_state(Event event, uint16_t arg); #endif #ifdef USE_THERMAL_REGULATION +#define USE_BLINK_NUM uint8_t tempcheck_state(Event event, uint16_t arg); uint8_t thermal_config_state(Event event, uint16_t arg); #endif @@ -497,6 +498,7 @@ volatile uint8_t beacon_seconds = 2; #endif #ifdef USE_VERSION_CHECK +#define USE_BLINK_DIGIT #include "version.h" const PROGMEM uint8_t version_number[] = VERSION_NUMBER; uint8_t version_check_state(Event event, uint16_t arg); @@ -1585,11 +1587,13 @@ uint8_t tempcheck_state(Event event, uint16_t arg) { set_state(off_state, 0); return MISCHIEF_MANAGED; } + #ifdef USE_BATTCHECK // 2 clicks: battcheck mode else if (event == EV_2clicks) { set_state(battcheck_state, 0); return MISCHIEF_MANAGED; } + #endif // 4 clicks: thermal config mode else if (event == EV_4clicks) { push_state(thermal_config_state, 0); @@ -1615,7 +1619,7 @@ uint8_t beacon_state(Event event, uint16_t arg) { set_state(sos_state, 0); #elif defined(USE_THERMAL_REGULATION) set_state(tempcheck_state, 0); - #else + #elif defined(USE_BATTCHECK) set_state(battcheck_state, 0); #endif return MISCHIEF_MANAGED; @@ -1997,6 +2001,7 @@ uint8_t muggle_state(Event event, uint16_t arg) { return MISCHIEF_MANAGED; } #endif + #ifdef USE_LVP // low voltage is handled specially in muggle mode else if(event == EV_voltage_low) { uint8_t lvl = (actual_level >> 1) + (actual_level >> 2); @@ -2007,6 +2012,7 @@ uint8_t muggle_state(Event event, uint16_t arg) { } return MISCHIEF_MANAGED; } + #endif return EVENT_NOT_HANDLED; } diff --git a/spaghetti-monster/anduril/cfg-emisar-d18.h b/spaghetti-monster/anduril/cfg-emisar-d18.h index 16fbacd..155a747 100644 --- a/spaghetti-monster/anduril/cfg-emisar-d18.h +++ b/spaghetti-monster/anduril/cfg-emisar-d18.h @@ -42,5 +42,3 @@ // stop panicking at about ~40% power or ~5000 lm #define THERM_FASTER_LEVEL 125 -// optional, makes initial turbo step-down faster so first peak isn't as hot -//#define THERM_HARD_TURBO_DROP diff --git a/spaghetti-monster/anduril/cfg-emisar-d4.h b/spaghetti-monster/anduril/cfg-emisar-d4.h index c86a534..501b9c7 100644 --- a/spaghetti-monster/anduril/cfg-emisar-d4.h +++ b/spaghetti-monster/anduril/cfg-emisar-d4.h @@ -24,8 +24,4 @@ // stop panicking at ~30% power or ~1200 lm #define THERM_FASTER_LEVEL 105 -// respond to thermal changes faster -#define THERMAL_WARNING_SECONDS 3 -#define THERMAL_UPDATE_SPEED 1 -#define THERM_PREDICTION_STRENGTH 4 diff --git a/spaghetti-monster/anduril/cfg-emisar-d4s.h b/spaghetti-monster/anduril/cfg-emisar-d4s.h index 6fe95a6..88465da 100644 --- a/spaghetti-monster/anduril/cfg-emisar-d4s.h +++ b/spaghetti-monster/anduril/cfg-emisar-d4s.h @@ -42,8 +42,3 @@ #ifdef THERM_HARD_TURBO_DROP #undef THERM_HARD_TURBO_DROP #endif - -#define THERMAL_WARNING_SECONDS 3 -#define THERMAL_UPDATE_SPEED 2 -#define THERM_PREDICTION_STRENGTH 4 - diff --git a/spaghetti-monster/anduril/cfg-emisar-d4sv2.h b/spaghetti-monster/anduril/cfg-emisar-d4sv2.h index c47e774..c578c4a 100644 --- a/spaghetti-monster/anduril/cfg-emisar-d4sv2.h +++ b/spaghetti-monster/anduril/cfg-emisar-d4sv2.h @@ -42,16 +42,6 @@ // stop panicking at ~50% power or ~2000 lm #define THERM_FASTER_LEVEL 130 -// no need to be extra-careful on this light -#ifdef THERM_HARD_TURBO_DROP -#undef THERM_HARD_TURBO_DROP -#endif - -// respond to thermal changes faster -#define THERMAL_WARNING_SECONDS 3 -#define THERMAL_UPDATE_SPEED 2 -#define THERM_PREDICTION_STRENGTH 4 - // easier access to thermal config mode, for Emisar #define USE_TENCLICK_THERMAL_CONFIG @@ -61,7 +51,3 @@ // seems relevant on attiny1634 #define THERM_CAL_OFFSET 5 - -// attiny1634 has enough space to smooth out voltage readings -#define USE_VOLTAGE_LOWPASS - diff --git a/spaghetti-monster/anduril/cfg-emisar-d4v2.h b/spaghetti-monster/anduril/cfg-emisar-d4v2.h index 3da877e..241ca7e 100644 --- a/spaghetti-monster/anduril/cfg-emisar-d4v2.h +++ b/spaghetti-monster/anduril/cfg-emisar-d4v2.h @@ -37,22 +37,10 @@ #define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL #define RAMP_DISCRETE_STEPS 7 -// optional, makes initial turbo step-down faster so first peak isn't as hot -// the D4 runs very very hot, so be extra careful -//#define THERM_HARD_TURBO_DROP - // stop panicking at ~30% power or ~1200 lm #define THERM_FASTER_LEVEL 105 -// respond to thermal changes faster -#define THERMAL_WARNING_SECONDS 3 -#define THERMAL_UPDATE_SPEED 1 -#define THERM_PREDICTION_STRENGTH 4 -//#define THERM_RESPONSE_MAGNITUDE 128 // easier access to thermal config mode, for Emisar #define USE_TENCLICK_THERMAL_CONFIG #define THERM_CAL_OFFSET 5 - -// attiny1634 has enough space to smooth out voltage readings -#define USE_VOLTAGE_LOWPASS diff --git a/spaghetti-monster/anduril/cfg-ff-pl47.h b/spaghetti-monster/anduril/cfg-ff-pl47.h index 7a81c25..e6907c1 100644 --- a/spaghetti-monster/anduril/cfg-ff-pl47.h +++ b/spaghetti-monster/anduril/cfg-ff-pl47.h @@ -61,11 +61,6 @@ // regulate down faster when the FET is active, slower otherwise #define THERM_FASTER_LEVEL 135 // throttle back faster when high -// play it safe, don't try to regulate above the recommended safe level -#ifdef THERM_HARD_TURBO_DROP -#undef THERM_HARD_TURBO_DROP -#endif - // don't do this #undef BLINK_AT_RAMP_MIDDLE #undef BLINK_AT_RAMP_CEILING diff --git a/spaghetti-monster/anduril/cfg-ff-pl47g2.h b/spaghetti-monster/anduril/cfg-ff-pl47g2.h index d5dd79d..cab008c 100644 --- a/spaghetti-monster/anduril/cfg-ff-pl47g2.h +++ b/spaghetti-monster/anduril/cfg-ff-pl47g2.h @@ -49,11 +49,6 @@ // regulate down faster when the FET is active, slower otherwise #define THERM_FASTER_LEVEL 135 // throttle back faster when high -// hard drop doesn't seem to be needed on this light -#ifdef THERM_HARD_TURBO_DROP -#undef THERM_HARD_TURBO_DROP -#endif - // don't do this #undef BLINK_AT_RAMP_MIDDLE #undef BLINK_AT_RAMP_CEILING diff --git a/spaghetti-monster/anduril/cfg-ff-rot66.h b/spaghetti-monster/anduril/cfg-ff-rot66.h index 2a90343..a87b66d 100644 --- a/spaghetti-monster/anduril/cfg-ff-rot66.h +++ b/spaghetti-monster/anduril/cfg-ff-rot66.h @@ -38,9 +38,6 @@ // regulate down faster when the FET is active, slower otherwise #define THERM_FASTER_LEVEL 130 // throttle back faster when high -// play it safe, don't try to regulate above the recommended safe level -//#define THERM_HARD_TURBO_DROP - // don't do this #undef BLINK_AT_RAMP_MIDDLE #undef BLINK_AT_RAMP_CEILING diff --git a/spaghetti-monster/anduril/cfg-mateminco-mf01-mini.h b/spaghetti-monster/anduril/cfg-mateminco-mf01-mini.h index bbf751b..28c77c2 100644 --- a/spaghetti-monster/anduril/cfg-mateminco-mf01-mini.h +++ b/spaghetti-monster/anduril/cfg-mateminco-mf01-mini.h @@ -48,8 +48,3 @@ #define USE_TENCLICK_THERMAL_CONFIG // by request #define THERM_FASTER_LEVEL 130 // throttle back faster when high -//#define THERM_HARD_TURBO_DROP // this light is massively overpowered -#define THERMAL_WARNING_SECONDS 1 // FIXME: increase by 2 after merging newer code -//#define THERMAL_UPDATE_SPEED 1 -//#define THERM_PREDICTION_STRENGTH 4 - diff --git a/spaghetti-monster/anduril/cfg-sofirn-sp36.h b/spaghetti-monster/anduril/cfg-sofirn-sp36.h index 494a263..d808e2a 100644 --- a/spaghetti-monster/anduril/cfg-sofirn-sp36.h +++ b/spaghetti-monster/anduril/cfg-sofirn-sp36.h @@ -28,10 +28,3 @@ #undef THERM_FASTER_LEVEL #endif #define THERM_FASTER_LEVEL 130 - -// be extra-careful at high levels -// (or not... this host seems to heat up pretty slowly) -//#ifndef THERM_HARD_TURBO_DROP -//#define THERM_HARD_TURBO_DROP -//#endif - diff --git a/spaghetti-monster/fsm-adc.c b/spaghetti-monster/fsm-adc.c index 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 diff --git a/spaghetti-monster/fsm-adc.h b/spaghetti-monster/fsm-adc.h index 6283b2c..241dee4 100644 --- a/spaghetti-monster/fsm-adc.h +++ b/spaghetti-monster/fsm-adc.h @@ -52,7 +52,9 @@ void adc_deferred(); // do the actual ADC-related calculations static inline void ADC_voltage_handler(); volatile uint8_t voltage = 0; +#ifdef USE_LVP void low_voltage(); +#endif #ifdef USE_BATTCHECK void battcheck(); @@ -67,10 +69,6 @@ void battcheck(); #ifdef USE_THERMAL_REGULATION -// default 1 seconds between thermal regulation events -#ifndef THERMAL_WARNING_SECONDS -#define THERMAL_WARNING_SECONDS 1 -#endif // try to keep temperature below 45 C #ifndef DEFAULT_THERM_CEIL #define DEFAULT_THERM_CEIL 45 @@ -83,14 +81,10 @@ void battcheck(); #ifndef THERM_CAL_OFFSET #define THERM_CAL_OFFSET 0 #endif -// temperature now, in C (ish) * 2 (14.1 fixed-point) +// temperature now, in C (ish) volatile int16_t temperature; -// temperature in a few seconds, in C (ish) * 2 (14.1 fixed-point) -volatile int16_t projected_temperature; // Fight the future! uint8_t therm_ceil = DEFAULT_THERM_CEIL; int8_t therm_cal_offset = 0; -//void low_temperature(); -//void high_temperature(); volatile uint8_t reset_thermal_history = 1; static inline void ADC_temperature_handler(); #endif // ifdef USE_THERMAL_REGULATION diff --git a/spaghetti-monster/fsm-standby.c b/spaghetti-monster/fsm-standby.c index 14b6df1..b002b91 100644 --- a/spaghetti-monster/fsm-standby.c +++ b/spaghetti-monster/fsm-standby.c @@ -87,10 +87,6 @@ void sleep_until_eswitch_pressed() #ifdef USE_THERMAL_REGULATION // forget what the temperature was last time we were on reset_thermal_history = 1; - // FIXME: not sure if this should be here - // (the intent is to make sure temperature gets measured before - // thermal logic gets executed) - //set_admux_therm(); #endif // go back to normal running mode diff --git a/spaghetti-monster/spaghetti-monster.txt b/spaghetti-monster/spaghetti-monster.txt index 9e051f1..434e1bc 100644 --- a/spaghetti-monster/spaghetti-monster.txt +++ b/spaghetti-monster/spaghetti-monster.txt @@ -124,14 +124,13 @@ Event types: between events. - EV_temperature_high: Sent whenever the MCU's projected temperature - is higher than therm_ceil. Minimum of THERMAL_WARNING_SECONDS - between events. The 'arg' indicates how far the temperature - exceeds the limit. + is higher than therm_ceil. Minimum of one second between events. + The 'arg' indicates how far the temperature exceeds the limit. - EV_temperature_low: Sent whenever the MCU's projected temperature is lower than (therm_ceil - THERMAL_WINDOW_SIZE). Minimum of - THERMAL_WARNING_SECONDS between events. The 'arg' indicates how - far the temperature exceeds the limit. + one second between events. The 'arg' indicates how far the + temperature exceeds the limit. Button presses: @@ -297,9 +296,6 @@ Useful #defines: - DEFAULT_THERM_CEIL: Set the temperature limit to use by default when the user hasn't configured anything. - - THERMAL_WARNING_SECONDS: How long to wait between temperature - events. - - USE_RAMPING: Enable smooth ramping helpers. - RAMP_LENGTH: Pick a pre-defined ramp by length. Defined sizes -- cgit v1.2.3 From b7f6dd21c396d1431e8864158d1868620ae334d6 Mon Sep 17 00:00:00 2001 From: Selene ToyKeeper Date: Mon, 16 Mar 2020 03:33:51 -0600 Subject: fixed a few things in ramping-ui, rampingiosv3, and werner's UI (just copied a few updates from anduril, particularly regarding FSM's "temperature" variable) --- spaghetti-monster/ramping-ui/ramping-ui.c | 3 +-- spaghetti-monster/rampingios/rampingiosv3.c | 12 +++++++----- spaghetti-monster/werner/werner.c | 12 +++++------- 3 files changed, 13 insertions(+), 14 deletions(-) (limited to 'spaghetti-monster') diff --git a/spaghetti-monster/ramping-ui/ramping-ui.c b/spaghetti-monster/ramping-ui/ramping-ui.c index 18f488d..5eb7d8f 100644 --- a/spaghetti-monster/ramping-ui/ramping-ui.c +++ b/spaghetti-monster/ramping-ui/ramping-ui.c @@ -22,7 +22,6 @@ #define USE_THERMAL_REGULATION #define DEFAULT_THERM_CEIL 32 #define USE_DELAY_MS -#define USE_DELAY_4MS #define USE_DELAY_ZERO #define USE_RAMPING #define USE_BATTCHECK @@ -353,7 +352,7 @@ void loop() { battcheck(); } else if (current_state == tempcheck_state) { - blink_num(projected_temperature>>2); + blink_num(temperature); nice_delay_ms(1000); } #endif diff --git a/spaghetti-monster/rampingios/rampingiosv3.c b/spaghetti-monster/rampingios/rampingiosv3.c index d72e971..7f03e77 100644 --- a/spaghetti-monster/rampingios/rampingiosv3.c +++ b/spaghetti-monster/rampingios/rampingiosv3.c @@ -123,6 +123,7 @@ uint8_t ramp_config_state(Event event, uint16_t arg); uint8_t battcheck_state(Event event, uint16_t arg); #endif #ifdef USE_THERMAL_REGULATION +#define USE_BLINK_NUM uint8_t tempcheck_state(Event event, uint16_t arg); uint8_t thermal_config_state(Event event, uint16_t arg); #endif @@ -930,14 +931,15 @@ void thermal_config_save() { // calibrate room temperature val = config_state_values[0]; if (val) { - int8_t rawtemp = (temperature >> 1) - therm_cal_offset; + int8_t rawtemp = temperature - therm_cal_offset; therm_cal_offset = val - rawtemp; + reset_thermal_history = 1; // invalidate all recent temperature data } val = config_state_values[1]; if (val) { // set maximum heat limit - therm_ceil = 30 + val; + therm_ceil = 30 + val - 1; } if (therm_ceil > MAX_THERM_CEIL) therm_ceil = MAX_THERM_CEIL; } @@ -966,9 +968,9 @@ uint8_t beacon_config_state(Event event, uint16_t arg) { inline void beacon_mode_iter() { // one iteration of main loop() set_level(memorized_level); - nice_delay_ms(500); + nice_delay_ms(100); set_level(0); - nice_delay_ms(((beacon_seconds) * 1000) - 500); + nice_delay_ms(((beacon_seconds) * 1000) - 100); } #endif // #ifdef USE_BEACON_MODE @@ -1235,7 +1237,7 @@ void loop() { #ifdef USE_THERMAL_REGULATION // TODO: blink out therm_ceil during thermal_config_state? else if (state == tempcheck_state) { - blink_num(temperature>>1); + blink_num(temperature); nice_delay_ms(1000); } #endif diff --git a/spaghetti-monster/werner/werner.c b/spaghetti-monster/werner/werner.c index 7c47cd7..4159fc6 100644 --- a/spaghetti-monster/werner/werner.c +++ b/spaghetti-monster/werner/werner.c @@ -467,14 +467,15 @@ void thermal_config_save() { // calibrate room temperature val = config_state_values[0]; if (val) { - int8_t rawtemp = (temperature >> 1) - therm_cal_offset; + int8_t rawtemp = temperature - therm_cal_offset; therm_cal_offset = val - rawtemp; + reset_thermal_history = 1; // invalidate all recent temperature data } val = config_state_values[1]; if (val) { // set maximum heat limit - therm_ceil = 30 + val; + therm_ceil = 30 + val - 1; } if (therm_ceil > MAX_THERM_CEIL) therm_ceil = MAX_THERM_CEIL; } @@ -589,7 +590,7 @@ uint8_t nearest_level(int16_t target) { for(uint8_t i=0; i>1)) return this_level; @@ -684,9 +685,6 @@ void loop() { StatePtr state = current_state; - #ifdef USE_DYNAMIC_UNDERCLOCKING - auto_clock_speed(); - #endif if (0) {} #ifdef USE_BATTCHECK @@ -697,7 +695,7 @@ void loop() { #ifdef USE_THERMAL_REGULATION // TODO: blink out therm_ceil during thermal_config_state else if (state == tempcheck_state) { - blink_num(temperature>>1); + blink_num(temperature); nice_delay_ms(1000); } #endif -- cgit v1.2.3