aboutsummaryrefslogtreecommitdiff
path: root/fsm/standby.c
diff options
context:
space:
mode:
Diffstat (limited to 'fsm/standby.c')
-rw-r--r--fsm/standby.c105
1 files changed, 105 insertions, 0 deletions
diff --git a/fsm/standby.c b/fsm/standby.c
new file mode 100644
index 0000000..5def07c
--- /dev/null
+++ b/fsm/standby.c
@@ -0,0 +1,105 @@
+// fsm-standby.c: standby mode functions for SpaghettiMonster.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include <avr/interrupt.h>
+#include <avr/sleep.h>
+
+#include "fsm-adc.h"
+#include "fsm-wdt.h"
+#include "fsm-pcint.h"
+
+// low-power standby mode used while off but power still connected
+#define standby_mode sleep_until_eswitch_pressed
+void sleep_until_eswitch_pressed()
+{
+ #ifdef TICK_DURING_STANDBY
+ WDT_slow();
+ #else
+ WDT_off();
+ #endif
+
+ ADC_off();
+
+ // make sure switch isn't currently pressed
+ while (button_is_pressed()) {}
+ empty_event_sequence(); // cancel pending input on suspend
+
+ PCINT_on(); // wake on e-switch event
+
+ #ifdef TICK_DURING_STANDBY
+ // detect which type of event caused a wake-up
+ irq_adc = 0;
+ irq_wdt = 0;
+ irq_pcint = 0;
+ while (go_to_standby) {
+ #else
+ go_to_standby = 0;
+ #endif
+
+ // configure sleep mode
+ #ifdef TICK_DURING_STANDBY
+ // needs a special sleep mode during measurements
+ if (adc_active_now) adc_sleep_mode();
+ else
+ #endif
+ set_sleep_mode(SLEEP_MODE_PWR_DOWN);
+
+ sleep_enable();
+ #ifdef BODCR // only do this on MCUs which support it
+ sleep_bod_disable();
+ #endif
+ sleep_cpu(); // wait here
+
+ // something happened; wake up
+ sleep_disable();
+
+ #ifdef TICK_DURING_STANDBY
+ // determine what woke us up...
+ if (irq_pcint) { // button pressed; wake up
+ go_to_standby = 0;
+ }
+ if (irq_adc) { // ADC done measuring
+ #ifndef USE_LOWPASS_WHILE_ASLEEP
+ adc_reset = 1; // don't lowpass while asleep
+ #endif
+ adc_deferred_enable = 1;
+ adc_deferred();
+ //ADC_off(); // takes care of itself
+ //irq_adc = 0; // takes care of itself
+ }
+ if (irq_wdt) { // generate a sleep tick
+ WDT_inner();
+ }
+ }
+ #endif
+
+ // don't lowpass immediately after waking
+ // also, reset thermal history
+ adc_reset = 2;
+
+ // go back to normal running mode
+ // PCINT not needed any more, and can cause problems if on
+ // (occasional reboots on wakeup-by-button-press)
+ PCINT_off();
+ // restore normal awake-mode interrupts
+ ADC_on();
+ WDT_on();
+}
+
+#ifdef USE_IDLE_MODE
+void idle_mode()
+{
+ // configure sleep mode
+ set_sleep_mode(SLEEP_MODE_IDLE);
+
+ sleep_enable();
+ sleep_cpu(); // wait here
+
+ // something happened; wake up
+ sleep_disable();
+}
+#endif
+