diff options
| author | Selene ToyKeeper | 2019-08-11 15:16:23 -0600 |
|---|---|---|
| committer | Selene ToyKeeper | 2019-08-11 15:16:23 -0600 |
| commit | 89a86871aa6330218043586f0347c0c1e7c79dab (patch) | |
| tree | e3a4cc2a4517e5563e91cdaaf86718170d4f0a37 /spaghetti-monster/fsm-adc.c | |
| parent | Added setup info for Fedora. (diff) | |
| parent | added scripts to flash attiny1634, which were missing before (diff) | |
| download | anduril-89a86871aa6330218043586f0347c0c1e7c79dab.tar.gz anduril-89a86871aa6330218043586f0347c0c1e7c79dab.tar.bz2 anduril-89a86871aa6330218043586f0347c0c1e7c79dab.zip | |
merged fsm to trunk... lots of updates:
+ attiny1634 support
+ Emisar D4v2 support
+ Mateminco MF01S / MT18 support
+ Fireflies E01 and E07v2 support
+ RGB aux LED support
+ added factory reset function
+ added manual / automatic memory toggle
+ added 2-level brightness during lockout
+ added Fireflies UI
+ made momentary mode also support strobe-group modes
* thermal regulation rewritten, behaves mostly better now
* strobe modes auto-reverse their ramp now
* muggle mode fixes
* UI diagram and manual updated
* button timing adjusted, and compile-time options added for it
* general refactoring
Diffstat (limited to 'spaghetti-monster/fsm-adc.c')
| -rw-r--r-- | spaghetti-monster/fsm-adc.c | 244 |
1 files changed, 146 insertions, 98 deletions
diff --git a/spaghetti-monster/fsm-adc.c b/spaghetti-monster/fsm-adc.c index 8decd9d..6832e32 100644 --- a/spaghetti-monster/fsm-adc.c +++ b/spaghetti-monster/fsm-adc.c @@ -20,27 +20,76 @@ #ifndef FSM_ADC_C #define FSM_ADC_C -#ifdef USE_VOLTAGE_DIVIDER -// 1.1V / pin7 -#define ADMUX_VOLTAGE ADMUX_VOLTAGE_DIVIDER -#else -// VCC / 1.1V reference -#define ADMUX_VOLTAGE ADMUX_VCC -#endif +inline void set_admux_therm() { + #if (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85) || (ATTINY == 1634) + ADMUX = ADMUX_THERM; + #elif (ATTINY == 841) + ADMUXA = ADMUXA_THERM; + ADMUXB = ADMUXB_THERM; + #else + #error Unrecognized MCU type + #endif +} + +inline void set_admux_voltage() { + #if (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85) || (ATTINY == 1634) + #ifdef USE_VOLTAGE_DIVIDER + // 1.1V / pin7 + ADMUX = ADMUX_VOLTAGE_DIVIDER; + #else + // VCC / 1.1V reference + ADMUX = ADMUX_VCC; + #endif + #elif (ATTINY == 841) + #ifdef USE_VOLTAGE_DIVIDER + ADMUXA = ADMUXA_VOLTAGE_DIVIDER; + ADMUXB = ADMUXB_VOLTAGE_DIVIDER; + #else + ADMUXA = ADMUXA_VCC; + ADMUXB = ADMUXB_VCC; + #endif + #else + #error Unrecognized MCU type + #endif +} + +inline void ADC_start_measurement() { + #if (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85) || (ATTINY == 841) || (ATTINY == 1634) + ADCSRA |= (1 << ADSC) | (1 << ADIE); + #else + #error unrecognized MCU type + #endif +} + +// set up ADC for reading battery voltage inline void ADC_on() { - // read voltage on VCC by default - ADMUX = ADMUX_VOLTAGE; - #ifdef USE_VOLTAGE_DIVIDER - // disable digital input on divider pin to reduce power consumption - DIDR0 |= (1 << VOLTAGE_ADC_DIDR); + #if (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85) || (ATTINY == 1634) + set_admux_voltage(); + #ifdef USE_VOLTAGE_DIVIDER + // disable digital input on divider pin to reduce power consumption + DIDR0 |= (1 << VOLTAGE_ADC_DIDR); + #else + // disable digital input on VCC pin to reduce power consumption + //DIDR0 |= (1 << ADC_DIDR); // FIXME: unsure how to handle for VCC pin + #endif + #if (ATTINY == 1634) + ACSRA |= (1 << ACD); // turn off analog comparator to save power + #endif + // enable, start, prescale + ADCSRA = (1 << ADEN) | (1 << ADSC) | ADC_PRSCL; + // end tiny25/45/85 + #elif (ATTINY == 841) + ADCSRB = 0; // Right adjusted, auto trigger bits cleared. + //ADCSRA = (1 << ADEN ) | 0b011; // ADC on, prescaler division factor 8. + set_admux_voltage(); + // enable, start, prescale + ADCSRA = (1 << ADEN) | (1 << ADSC) | ADC_PRSCL; + //ADCSRA |= (1 << ADSC); // start measuring #else - // disable digital input on VCC pin to reduce power consumption - //DIDR0 |= (1 << ADC_DIDR); // FIXME: unsure how to handle for VCC pin + #error Unrecognized MCU type #endif - // enable, start, prescale - ADCSRA = (1 << ADEN) | (1 << ADSC) | ADC_PRSCL; } inline void ADC_off() { @@ -89,20 +138,19 @@ ISR(ADC_vect) { // thermal declarations #ifdef USE_THERMAL_REGULATION - #define NUM_THERMAL_VALUES 8 - #define NUM_THERMAL_VALUES_HISTORY 16 - #define NUM_THERMAL_PROJECTED_HISTORY 8 + #ifndef THERMAL_UPDATE_SPEED + #define THERMAL_UPDATE_SPEED 2 + #endif + #define NUM_THERMAL_VALUES_HISTORY 8 #define ADC_STEPS 4 - static int16_t temperature_values[NUM_THERMAL_VALUES]; // last few readings in C - static int16_t temperature_history[NUM_THERMAL_VALUES_HISTORY]; // 14.1 fixed-point - static int16_t projected_temperature_history[NUM_THERMAL_PROJECTED_HISTORY]; // 14.1 fixed-point - static uint8_t projected_temperature_history_counter = 0; + static uint8_t history_step = 0; // don't update history as often + static int16_t temperature_history[NUM_THERMAL_VALUES_HISTORY]; static uint8_t temperature_timer = 0; static uint8_t overheat_lowpass = 0; static uint8_t underheat_lowpass = 0; - #define TEMPERATURE_TIMER_START (THERMAL_WARNING_SECONDS*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 + #define TEMPERATURE_TIMER_START ((THERMAL_WARNING_SECONDS-2)*ADC_CYCLES_PER_SECOND) // N seconds between thermal regulation events + #define OVERHEAT_LOWPASS_STRENGTH (ADC_CYCLES_PER_SECOND*2) // lowpass for 2 seconds + #define UNDERHEAT_LOWPASS_STRENGTH (ADC_CYCLES_PER_SECOND*2) // lowpass for 2 seconds #else #define ADC_STEPS 2 #endif @@ -114,6 +162,12 @@ ISR(ADC_vect) { pseudo_rand_seed += measurement; #endif + #if defined(TICK_DURING_STANDBY) && defined(USE_SLEEP_LVP) + // only measure battery voltage while asleep + if (go_to_standby) adc_step = 1; + else + #endif + adc_step = (adc_step + 1) & (ADC_STEPS-1); #ifdef USE_LVP @@ -124,7 +178,7 @@ ISR(ADC_vect) { if (voltage == 0) { for(uint8_t i=0; i<NUM_VOLTAGE_VALUES; i++) voltage_values[i] = measurement; - voltage = 42; // Life, the Universe, and Everything (*) + voltage = 42; // the answer to life, the universe, and the voltage of a full li-ion cell } else { uint16_t total = 0; uint8_t i; @@ -185,93 +239,84 @@ ISR(ADC_vect) { // temperature else if (adc_step == 3) { // Convert ADC units to Celsius (ish) - int16_t temp = measurement - 275 + THERM_CAL_OFFSET + therm_cal_offset; + int16_t temp = measurement - 275 + THERM_CAL_OFFSET + (int16_t)therm_cal_offset; // prime on first execution if (reset_thermal_history) { reset_thermal_history = 0; - for(uint8_t i=0; i<NUM_THERMAL_VALUES; i++) - temperature_values[i] = temp; + temperature = temp; for(uint8_t i=0; i<NUM_THERMAL_VALUES_HISTORY; i++) - temperature_history[i] = temp<<1; - for(uint8_t i=0; i<NUM_THERMAL_PROJECTED_HISTORY; i++) - projected_temperature_history[i] = temp<<1; - temperature = temp<<1; + temperature_history[i] = temp; } else { // update our current temperature estimate - uint8_t i; - int16_t total=0; - - // rotate array - // FIXME: just move the index, don't move the values? - for(i=0; i<NUM_THERMAL_VALUES-1; i++) { - temperature_values[i] = temperature_values[i+1]; - total += temperature_values[i]; + // crude lowpass filter + // (limit rate of change to 1 degree per measurement) + if (temp > temperature) { + temperature ++; + } else if (temp < temperature) { + temperature --; } - 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; - // 14.1 is less prone to overflows - temperature = total >> 2; } // guess what the temperature will be in a few seconds int16_t pt; { - uint8_t i; int16_t diff; int16_t t = temperature; // algorithm tweaking; not really intended to be modified // how far ahead should we predict? + #ifndef THERM_PREDICTION_STRENGTH #define THERM_PREDICTION_STRENGTH 4 - // how proportional should the adjustments be? - #define THERM_DIFF_ATTENUATION 3 + #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 10 + #define THERM_WINDOW_SIZE 5 // highest temperature allowed - // (convert configured value to 14.1 fixed-point) - #define THERM_CEIL (((int16_t)therm_ceil)<<1) - // bottom of target temperature window (14.1 fixed-point) - #define THERM_FLOOR (THERM_CEIL - (THERM_WINDOW_SIZE<<1)) - - // rotate measurements and add a new one - for (i=0; i<NUM_THERMAL_VALUES_HISTORY-1; i++) { - temperature_history[i] = temperature_history[i+1]; + #define THERM_CEIL ((int16_t)therm_ceil) + // bottom of target temperature window + #define THERM_FLOOR (THERM_CEIL - THERM_WINDOW_SIZE) + + // if it's time to rotate the thermal history, do it + history_step ++; + #if (THERMAL_UPDATE_SPEED == 4) // new value every 4s + #define THERM_HISTORY_STEP_MAX 15 + #elif (THERMAL_UPDATE_SPEED == 2) // new value every 2s + #define THERM_HISTORY_STEP_MAX 7 + #elif (THERMAL_UPDATE_SPEED == 1) // new value every 1s + #define THERM_HISTORY_STEP_MAX 3 + #elif (THERMAL_UPDATE_SPEED == 0) // new value every 0.5s + #define THERM_HISTORY_STEP_MAX 1 + #endif + if (0 == (history_step & THERM_HISTORY_STEP_MAX)) { + // rotate measurements and add a new one + for (uint8_t i=0; i<NUM_THERMAL_VALUES_HISTORY-1; i++) { + temperature_history[i] = temperature_history[i+1]; + } + temperature_history[NUM_THERMAL_VALUES_HISTORY-1] = t; } - temperature_history[NUM_THERMAL_VALUES_HISTORY-1] = t; // guess what the temp will be several seconds in the future // diff = rate of temperature change //diff = temperature_history[NUM_THERMAL_VALUES_HISTORY-1] - temperature_history[0]; diff = t - temperature_history[0]; + // slight bias toward zero; ignore very small changes (noise) + for (uint8_t z=0; z<3; z++) { + if (diff < 0) diff ++; + if (diff > 0) diff --; + } // projected_temperature = current temp extended forward by amplified rate of change //projected_temperature = temperature_history[NUM_THERMAL_VALUES_HISTORY-1] + (diff<<THERM_PREDICTION_STRENGTH); pt = projected_temperature = t + (diff<<THERM_PREDICTION_STRENGTH); - - // store prediction for later averaging - projected_temperature_history[projected_temperature_history_counter] = pt; - projected_temperature_history_counter = (projected_temperature_history_counter + 1) & (NUM_THERMAL_PROJECTED_HISTORY-1); } - // average prediction to reduce noise - int16_t avg_projected_temperature = 0; - uint8_t i; - for (i = 0; - (i < NUM_THERMAL_PROJECTED_HISTORY) && (avg_projected_temperature < 16000); - i++) - avg_projected_temperature += projected_temperature_history[i]; - avg_projected_temperature /= NUM_THERMAL_PROJECTED_HISTORY; - //avg_projected_temperature /= i; - // cancel counters if appropriate if (pt > THERM_FLOOR) { underheat_lowpass = 0; // we're probably not too cold - } else if (pt < THERM_CEIL) { + } + if (pt < THERM_CEIL) { overheat_lowpass = 0; // we're probably not too hot } @@ -280,7 +325,7 @@ ISR(ADC_vect) { } else { // it has been long enough since the last warning // Too hot? - if (avg_projected_temperature > THERM_CEIL) { + if (pt > THERM_CEIL) { if (overheat_lowpass < OVERHEAT_LOWPASS_STRENGTH) { overheat_lowpass ++; } else { @@ -288,16 +333,15 @@ ISR(ADC_vect) { overheat_lowpass = 0; temperature_timer = TEMPERATURE_TIMER_START; // how far above the ceiling? - int16_t howmuch = (avg_projected_temperature - THERM_CEIL) >> THERM_DIFF_ATTENUATION; - if (howmuch > 0) { - // try to send out a warning - emit(EV_temperature_high, howmuch); - } + //int16_t howmuch = (pt - THERM_CEIL) * THERM_RESPONSE_MAGNITUDE / 128; + int16_t howmuch = pt - THERM_CEIL; + // try to send out a warning + emit(EV_temperature_high, howmuch); } } // Too cold? - else if (avg_projected_temperature < THERM_FLOOR) { + else if (pt < THERM_FLOOR) { if (underheat_lowpass < UNDERHEAT_LOWPASS_STRENGTH) { underheat_lowpass ++; } else { @@ -305,13 +349,12 @@ ISR(ADC_vect) { underheat_lowpass = 0; temperature_timer = TEMPERATURE_TIMER_START; // how far below the floor? - int16_t howmuch = (THERM_FLOOR - avg_projected_temperature) >> THERM_DIFF_ATTENUATION; - if (howmuch > 0) { - // 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); - } + //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); } } } @@ -322,16 +365,21 @@ ISR(ADC_vect) { // set the correct type of measurement for next time #ifdef USE_THERMAL_REGULATION #ifdef USE_LVP - if (adc_step < 2) ADMUX = ADMUX_VOLTAGE; - else ADMUX = ADMUX_THERM; + if (adc_step < 2) set_admux_voltage(); + else set_admux_therm(); #else - ADMUX = ADMUX_THERM; + set_admux_therm(); #endif #else #ifdef USE_LVP - ADMUX = ADMUX_VOLTAGE; + set_admux_voltage(); #endif #endif + + #ifdef TICK_DURING_STANDBY + // if we were asleep, go back to sleep + if (go_to_standby) ADC_off(); + #endif } #ifdef USE_BATTCHECK |
