diff options
| author | Selene ToyKeeper | 2017-08-23 19:22:22 -0600 |
|---|---|---|
| committer | Selene ToyKeeper | 2017-08-23 19:22:22 -0600 |
| commit | 5631564b329d0445fb282e5e387217ba4e4ff191 (patch) | |
| tree | 008d8aec159c7ada2b2e650d71c4f092bc6679d5 /spaghetti-monster/fsm-adc.c | |
| parent | Made Baton a little easier to read: (diff) | |
| download | anduril-5631564b329d0445fb282e5e387217ba4e4ff191.tar.gz anduril-5631564b329d0445fb282e5e387217ba4e4ff191.tar.bz2 anduril-5631564b329d0445fb282e5e387217ba4e4ff191.zip | |
Added thermal regulation to SpaghettiMonster / Baton.
Made some LVP values configurable.
Removed high_temperature() / low_temperature() shortcuts for now.
Diffstat (limited to 'spaghetti-monster/fsm-adc.c')
| -rw-r--r-- | spaghetti-monster/fsm-adc.c | 136 |
1 files changed, 132 insertions, 4 deletions
diff --git a/spaghetti-monster/fsm-adc.c b/spaghetti-monster/fsm-adc.c index 11468b9..8af3487 100644 --- a/spaghetti-monster/fsm-adc.c +++ b/spaghetti-monster/fsm-adc.c @@ -35,9 +35,18 @@ inline void ADC_off() { ADCSRA &= ~(1<<ADEN); //ADC off } +// Each full cycle runs 7.8X per second with just voltage enabled, +// or 3.9X per second with voltage and temperature. +#if defined(USE_LVP) && defined(USE_THERMAL_REGULATION) +#define ADC_CYCLES_PER_SECOND 4 +#else +#define ADC_CYCLES_PER_SECOND 8 +#endif // TODO: is this better done in main() or WDT()? ISR(ADC_vect) { static uint8_t adc_step = 0; + + // LVP declarations #ifdef USE_LVP #ifdef USE_LVP_AVG #define NUM_VOLTAGE_VALUES 4 @@ -45,14 +54,24 @@ ISR(ADC_vect) { #endif static uint8_t lvp_timer = 0; static uint8_t lvp_lowpass = 0; - #define LVP_TIMER_START 50 // ticks between LVP warnings - #define LVP_LOWPASS_STRENGTH 4 + #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 #endif + // thermal declarations #ifdef USE_THERMAL_REGULATION #define NUM_THERMAL_VALUES 4 + #define NUM_THERMAL_VALUES_HISTORY 16 #define ADC_STEPS 4 - static int16_t temperature_values[NUM_THERMAL_VALUES]; + static uint8_t first_temp_reading = 1; + static int16_t temperature_values[NUM_THERMAL_VALUES]; // last few readings in C + static int16_t temperature_history[NUM_THERMAL_VALUES_HISTORY]; // 13.2 fixed-point + static uint8_t temperature_timer = 0; + static uint8_t overheat_lowpass = 0; + static uint8_t underheat_lowpass = 0; + #define TEMPERATURE_TIMER_START (THERMAL_WARNING_SECONDS*ADC_CYCLES_PER_SECOND) // N seconds between thermal regulation events + #define OVERHEAT_LOWPASS_STRENGTH ADC_CYCLES_PER_SECOND // lowpass for one second + #define UNDERHEAT_LOWPASS_STRENGTH ADC_CYCLES_PER_SECOND // lowpass for one second #else #define ADC_STEPS 2 #endif @@ -116,7 +135,116 @@ ISR(ADC_vect) { } #endif // ifdef USE_LVP - // TODO: temperature + + #ifdef USE_THERMAL_REGULATION + // temperature + else if (adc_step == 3) { + // Convert ADC units to Celsius (ish) + int16_t temp = measurement - 275 + THERM_CAL_OFFSET; + + // prime on first execution + if (first_temp_reading) { + first_temp_reading = 0; + for(uint8_t i=0; i<NUM_THERMAL_VALUES; i++) + temperature_values[i] = temp; + for(uint8_t i=0; i<NUM_THERMAL_VALUES_HISTORY; i++) + temperature_history[i] = temp; + temperature = temp; + } else { // update our current temperature estimate + uint8_t i; + int16_t total=0; + + // rotate array + for(i=0; i<NUM_THERMAL_VALUES-1; i++) { + temperature_values[i] = temperature_values[i+1]; + total += temperature_values[i]; + } + temperature_values[i] = temp; + total += temp; + + // Divide back to original range: + //temperature = total >> 2; + // More precise method: use noise as extra precision + // (values are now basically fixed-point, signed 13.2) + temperature = total; + } + + // guess what the temperature will be in a few seconds + { + uint8_t i; + int16_t diff; + + // algorithm tweaking; not really intended to be modified + // how far ahead should we predict? + #define THERM_PREDICTION_STRENGTH 4 + // how proportional should the adjustments be? + #define THERM_DIFF_ATTENUATION 4 + // acceptable temperature window size in C + #define THERM_WINDOW_SIZE 8 + // highest temperature allowed + // (convert configured value to 13.2 fixed-point) + #define THERM_CEIL (therm_ceil<<2) + // bottom of target temperature window (13.2 fixed-point) + #define THERM_FLOOR (THERM_CEIL - (THERM_WINDOW_SIZE<<2)) + + // rotate measurements and add a new one + for (i=0; i<NUM_THERMAL_VALUES_HISTORY-1; i++) { + temperature_history[i] = temperature_history[i+1]; + } + temperature_history[NUM_THERMAL_VALUES_HISTORY-1] = temperature; + + // guess what the temp will be several seconds in the future + diff = temperature_history[NUM_THERMAL_VALUES_HISTORY-1] - temperature_history[0]; + projected_temperature = temperature_history[NUM_THERMAL_VALUES_HISTORY-1] + (diff<<THERM_PREDICTION_STRENGTH); + + } + + // cancel counters if necessary + if (projected_temperature > THERM_FLOOR) { + underheat_lowpass = 0; // we're definitely not too cold + } else if (projected_temperature < THERM_CEIL) { + overheat_lowpass = 0; // we're definitely not too hot + } + + if (temperature_timer) { + temperature_timer --; + } else { // it has been long enough since the last warning + + // Too hot? + if (projected_temperature > THERM_CEIL) { + if (overheat_lowpass < OVERHEAT_LOWPASS_STRENGTH) { + overheat_lowpass ++; + } else { + // how far above the ceiling? + int16_t howmuch = (projected_temperature - THERM_CEIL) >> THERM_DIFF_ATTENUATION; + if (howmuch < 1) howmuch = 1; + // try to send out a warning + emit(EV_temperature_high, howmuch); + // reset counters + temperature_timer = TEMPERATURE_TIMER_START; + overheat_lowpass = 0; + } + } + + // Too cold? + else if (projected_temperature < THERM_FLOOR) { + if (underheat_lowpass < UNDERHEAT_LOWPASS_STRENGTH) { + underheat_lowpass ++; + } else { + // how far below the floor? + int16_t howmuch = (THERM_FLOOR - projected_temperature) >> THERM_DIFF_ATTENUATION; + if (howmuch < 1) howmuch = 1; + // try to send out a warning + emit(EV_temperature_low, howmuch); + // reset counters + temperature_timer = TEMPERATURE_TIMER_START; + underheat_lowpass = 0; + } + } + } + } + #endif // ifdef USE_THERMAL_REGULATION + // start another measurement for next time #ifdef USE_THERMAL_REGULATION |
