aboutsummaryrefslogtreecommitdiff
path: root/spaghetti-monster/fsm-adc.c
diff options
context:
space:
mode:
authorSelene ToyKeeper2017-08-19 17:20:46 -0600
committerSelene ToyKeeper2017-08-19 17:20:46 -0600
commit73a5a6974a98aa73ab392272b4d69d285c85dee5 (patch)
tree8bbd44cd1875da1f1af81d5bcdc997015361fe19 /spaghetti-monster/fsm-adc.c
parentExtra debouncing in PCINT (don't emit event if push was rejected). (diff)
downloadanduril-73a5a6974a98aa73ab392272b4d69d285c85dee5.tar.gz
anduril-73a5a6974a98aa73ab392272b4d69d285c85dee5.tar.bz2
anduril-73a5a6974a98aa73ab392272b4d69d285c85dee5.zip
Completely reorganized SpaghettiMonster code into smaller logical pieces: fsm-*.c and fsm-*.h.
Diffstat (limited to 'spaghetti-monster/fsm-adc.c')
-rw-r--r--spaghetti-monster/fsm-adc.c136
1 files changed, 136 insertions, 0 deletions
diff --git a/spaghetti-monster/fsm-adc.c b/spaghetti-monster/fsm-adc.c
new file mode 100644
index 0000000..11468b9
--- /dev/null
+++ b/spaghetti-monster/fsm-adc.c
@@ -0,0 +1,136 @@
+/*
+ * fsm-adc.c: ADC (voltage, temperature) functions for SpaghettiMonster.
+ *
+ * Copyright (C) 2017 Selene ToyKeeper
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef FSM_ADC_C
+#define FSM_ADC_C
+
+inline void ADC_on()
+{
+ // read voltage on VCC by default
+ // disable digital input on VCC pin to reduce power consumption
+ //DIDR0 |= (1 << ADC_DIDR); // FIXME: unsure how to handle for VCC pin
+ // VCC / 1.1V reference
+ ADMUX = ADMUX_VCC;
+ // enable, start, prescale
+ ADCSRA = (1 << ADEN) | (1 << ADSC) | ADC_PRSCL;
+}
+
+inline void ADC_off() {
+ ADCSRA &= ~(1<<ADEN); //ADC off
+}
+
+// TODO: is this better done in main() or WDT()?
+ISR(ADC_vect) {
+ static uint8_t adc_step = 0;
+ #ifdef USE_LVP
+ #ifdef USE_LVP_AVG
+ #define NUM_VOLTAGE_VALUES 4
+ static int16_t voltage_values[NUM_VOLTAGE_VALUES];
+ #endif
+ static uint8_t lvp_timer = 0;
+ static uint8_t lvp_lowpass = 0;
+ #define LVP_TIMER_START 50 // ticks between LVP warnings
+ #define LVP_LOWPASS_STRENGTH 4
+ #endif
+
+ #ifdef USE_THERMAL_REGULATION
+ #define NUM_THERMAL_VALUES 4
+ #define ADC_STEPS 4
+ static int16_t temperature_values[NUM_THERMAL_VALUES];
+ #else
+ #define ADC_STEPS 2
+ #endif
+
+ uint16_t measurement = ADC; // latest 10-bit ADC reading
+
+ adc_step = (adc_step + 1) & (ADC_STEPS-1);
+
+ #ifdef USE_LVP
+ // voltage
+ if (adc_step == 1) {
+ #ifdef USE_LVP_AVG
+ // prime on first execution
+ if (voltage == 0) {
+ for(uint8_t i=0; i<NUM_VOLTAGE_VALUES; i++)
+ voltage_values[i] = measurement;
+ voltage = 42; // Life, the Universe, and Everything (*)
+ } else {
+ uint16_t total = 0;
+ uint8_t i;
+ for(i=0; i<NUM_VOLTAGE_VALUES-1; i++) {
+ voltage_values[i] = voltage_values[i+1];
+ total += voltage_values[i];
+ }
+ voltage_values[i] = measurement;
+ total += measurement;
+ total = total >> 2;
+
+ voltage = (uint16_t)(1.1*1024*10)/total + VOLTAGE_FUDGE_FACTOR;
+ }
+ #else // no USE_LVP_AVG
+ // 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;
+ #endif
+ // if low, callback EV_voltage_low / EV_voltage_critical
+ // (but only if it has been more than N ticks since last call)
+ if (lvp_timer) {
+ lvp_timer --;
+ } else { // it has been long enough since the last warning
+ if (voltage < VOLTAGE_LOW) {
+ if (lvp_lowpass < LVP_LOWPASS_STRENGTH) {
+ lvp_lowpass ++;
+ } else {
+ // try to send out a warning
+ //uint8_t err = emit(EV_voltage_low, 0);
+ //uint8_t err = emit_now(EV_voltage_low, 0);
+ emit(EV_voltage_low, 0);
+ //if (!err) {
+ // on successful warning, reset counters
+ lvp_timer = LVP_TIMER_START;
+ lvp_lowpass = 0;
+ //}
+ }
+ } else {
+ // voltage not low? reset count
+ lvp_lowpass = 0;
+ }
+ }
+ }
+ #endif // ifdef USE_LVP
+
+ // TODO: temperature
+
+ // start another measurement for next time
+ #ifdef USE_THERMAL_REGULATION
+ #ifdef USE_LVP
+ if (adc_step < 2) ADMUX = ADMUX_VCC;
+ else ADMUX = ADMUX_THERM;
+ #else
+ ADMUX = ADMUX_THERM;
+ #endif
+ #else
+ #ifdef USE_LVP
+ ADMUX = ADMUX_VCC;
+ #endif
+ #endif
+}
+
+#endif