diff options
| author | Selene ToyKeeper | 2018-11-11 05:14:47 -0700 |
|---|---|---|
| committer | Selene ToyKeeper | 2018-11-11 05:14:47 -0700 |
| commit | e942fa7c47f446891e3bb0a07316d22cc32c00c2 (patch) | |
| tree | a74c2cbe1a48554f66d8ca18a0dbc1bf0d69ebdc /spaghetti-monster | |
| parent | calibrated Sofirn SP36 config (diff) | |
| download | anduril-e942fa7c47f446891e3bb0a07316d22cc32c00c2.tar.gz anduril-e942fa7c47f446891e3bb0a07316d22cc32c00c2.tar.bz2 anduril-e942fa7c47f446891e3bb0a07316d22cc32c00c2.zip | |
Rewrote the event system to use a single byte for each event instead of an array of actions.
Not thoroughly tested yet, not done yet, have only updated Anduril to fit, and only partially.
Diffstat (limited to 'spaghetti-monster')
| -rw-r--r-- | spaghetti-monster/anduril/anduril.c | 116 | ||||
| -rw-r--r-- | spaghetti-monster/fsm-events.c | 96 | ||||
| -rw-r--r-- | spaghetti-monster/fsm-events.h | 607 | ||||
| -rw-r--r-- | spaghetti-monster/fsm-pcint.c | 4 | ||||
| -rw-r--r-- | spaghetti-monster/fsm-states.c | 4 | ||||
| -rw-r--r-- | spaghetti-monster/fsm-states.h | 6 | ||||
| -rw-r--r-- | spaghetti-monster/fsm-wdt.c | 38 |
7 files changed, 253 insertions, 618 deletions
diff --git a/spaghetti-monster/anduril/anduril.c b/spaghetti-monster/anduril/anduril.c index df478ee..de4f996 100644 --- a/spaghetti-monster/anduril/anduril.c +++ b/spaghetti-monster/anduril/anduril.c @@ -226,45 +226,45 @@ typedef enum { // FSM states -uint8_t off_state(EventPtr event, uint16_t arg); +uint8_t off_state(Event event, uint16_t arg); // simple numeric entry config menu -uint8_t config_state_base(EventPtr event, uint16_t arg, +uint8_t config_state_base(Event event, uint16_t arg, uint8_t num_config_steps, void (*savefunc)()); #define MAX_CONFIG_VALUES 3 uint8_t config_state_values[MAX_CONFIG_VALUES]; // ramping mode and its related config mode -uint8_t steady_state(EventPtr event, uint16_t arg); -uint8_t ramp_config_state(EventPtr event, uint16_t arg); +uint8_t steady_state(Event event, uint16_t arg); +uint8_t ramp_config_state(Event event, uint16_t arg); // party and tactical strobes #ifdef USE_STROBE_STATE -uint8_t strobe_state(EventPtr event, uint16_t arg); +uint8_t strobe_state(Event event, uint16_t arg); #endif #ifdef USE_BATTCHECK -uint8_t battcheck_state(EventPtr event, uint16_t arg); +uint8_t battcheck_state(Event event, uint16_t arg); #endif #ifdef USE_THERMAL_REGULATION -uint8_t tempcheck_state(EventPtr event, uint16_t arg); -uint8_t thermal_config_state(EventPtr event, uint16_t arg); +uint8_t tempcheck_state(Event event, uint16_t arg); +uint8_t thermal_config_state(Event event, uint16_t arg); #endif // 1-hour ramp down from low, then automatic off -uint8_t goodnight_state(EventPtr event, uint16_t arg); +uint8_t goodnight_state(Event event, uint16_t arg); // beacon mode and its related config mode -uint8_t beacon_state(EventPtr event, uint16_t arg); -uint8_t beacon_config_state(EventPtr event, uint16_t arg); +uint8_t beacon_state(Event event, uint16_t arg); +uint8_t beacon_config_state(Event event, uint16_t arg); // soft lockout #define MOON_DURING_LOCKOUT_MODE -uint8_t lockout_state(EventPtr event, uint16_t arg); +uint8_t lockout_state(Event event, uint16_t arg); // momentary / signalling mode -uint8_t momentary_state(EventPtr event, uint16_t arg); +uint8_t momentary_state(Event event, uint16_t arg); #ifdef USE_MUGGLE_MODE // muggle mode, super-simple, hard to exit -uint8_t muggle_state(EventPtr event, uint16_t arg); +uint8_t muggle_state(Event event, uint16_t arg); uint8_t muggle_mode_active = 0; #endif // general helper function for config modes -uint8_t number_entry_state(EventPtr event, uint16_t arg); +uint8_t number_entry_state(Event event, uint16_t arg); // return value from number_entry_state() volatile uint8_t number_entry_value; @@ -387,7 +387,7 @@ uint8_t triangle_wave(uint8_t phase); volatile uint8_t beacon_seconds = 2; -uint8_t off_state(EventPtr event, uint16_t arg) { +uint8_t off_state(Event event, uint16_t arg) { // turn emitter off when entering state if (event == EV_enter_state) { set_level(0); @@ -527,7 +527,7 @@ uint8_t off_state(EventPtr event, uint16_t arg) { } -uint8_t steady_state(EventPtr event, uint16_t arg) { +uint8_t steady_state(Event event, uint16_t arg) { uint8_t mode_min = ramp_smooth_floor; uint8_t mode_max = ramp_smooth_ceil; uint8_t ramp_step_size = 1; @@ -846,7 +846,7 @@ uint8_t steady_state(EventPtr event, uint16_t arg) { #ifdef USE_STROBE_STATE -uint8_t strobe_state(EventPtr event, uint16_t arg) { +uint8_t strobe_state(Event event, uint16_t arg) { // 'st' reduces ROM size by avoiding access to a volatile var // (maybe I should just make it nonvolatile?) strobe_mode_te st = strobe_type; @@ -1059,7 +1059,7 @@ uint8_t strobe_state(EventPtr event, uint16_t arg) { #ifdef USE_BATTCHECK -uint8_t battcheck_state(EventPtr event, uint16_t arg) { +uint8_t battcheck_state(Event event, uint16_t arg) { // 1 click: off if (event == EV_1click) { set_state(off_state, 0); @@ -1076,7 +1076,7 @@ uint8_t battcheck_state(EventPtr event, uint16_t arg) { #ifdef USE_THERMAL_REGULATION -uint8_t tempcheck_state(EventPtr event, uint16_t arg) { +uint8_t tempcheck_state(Event event, uint16_t arg) { // 1 click: off if (event == EV_1click) { set_state(off_state, 0); @@ -1097,7 +1097,7 @@ uint8_t tempcheck_state(EventPtr event, uint16_t arg) { #endif -uint8_t beacon_state(EventPtr event, uint16_t arg) { +uint8_t beacon_state(Event event, uint16_t arg) { // 1 click: off if (event == EV_1click) { set_state(off_state, 0); @@ -1124,7 +1124,7 @@ uint8_t beacon_state(EventPtr event, uint16_t arg) { #define GOODNIGHT_TICKS_PER_STEPDOWN (GOODNIGHT_TIME*TICKS_PER_SECOND*60L/GOODNIGHT_LEVEL) -uint8_t goodnight_state(EventPtr event, uint16_t arg) { +uint8_t goodnight_state(Event event, uint16_t arg) { static uint16_t ticks_since_stepdown = 0; // blink on start if (event == EV_enter_state) { @@ -1163,28 +1163,27 @@ uint8_t goodnight_state(EventPtr event, uint16_t arg) { } -uint8_t lockout_state(EventPtr event, uint16_t arg) { +uint8_t lockout_state(Event event, uint16_t arg) { #ifdef MOON_DURING_LOCKOUT_MODE // momentary(ish) moon mode during lockout // not all presses will be counted; // it depends on what is in the master event_sequences table - uint8_t last = 0; - for(uint8_t i=0; pgm_read_byte(event + i) && (i<EV_MAX_LEN); i++) - last = pgm_read_byte(event + i); if (arg == 0) { // Only turn on/off when button state changes - if ((last == A_PRESS) || (last == A_HOLD)) { - #ifdef LOCKOUT_MOON_LOWEST - // Use lowest moon configured - uint8_t lvl = ramp_smooth_floor; - if (ramp_discrete_floor < lvl) lvl = ramp_discrete_floor; - set_level(lvl); - #else - // Use moon from current ramp - set_level(nearest_level(1)); - #endif - } - else if ((last == A_RELEASE) || (last == A_RELEASE_TIMEOUT)) { - set_level(0); + if (! (event & B_SYSTEM)) { // event is a button click type + if (event & B_PRESS) { // button is being held + #ifdef LOCKOUT_MOON_LOWEST + // Use lowest moon configured + uint8_t lvl = ramp_smooth_floor; + if (ramp_discrete_floor < lvl) lvl = ramp_discrete_floor; + set_level(lvl); + #else + // Use moon from current ramp + set_level(nearest_level(1)); + #endif + } + else { // button not being held + set_level(0); + } } } #endif @@ -1278,21 +1277,33 @@ uint8_t lockout_state(EventPtr event, uint16_t arg) { } -uint8_t momentary_state(EventPtr event, uint16_t arg) { +uint8_t momentary_state(Event event, uint16_t arg) { // TODO: momentary strobe here? (for light painting) - if (event == EV_click1_press) { + + // light up when the button is pressed; go dark otherwise + #if 0 + if ((event ^ B_SYSTEM) & B_PRESS) { set_level(memorized_level); - empty_event_sequence(); // don't attempt to parse multiple clicks return MISCHIEF_MANAGED; } - - else if (event == EV_release) { + else if (((event ^ B_SYSTEM) & B_PRESS) == 0) { set_level(0); - empty_event_sequence(); // don't attempt to parse multiple clicks + empty_event_sequence(); //go_to_standby = 1; // sleep while light is off - // TODO: lighted button should use lockout config? return MISCHIEF_MANAGED; } + #else + if (! (event & B_SYSTEM)) { // is a button-related event + if (event & B_PRESS) { // button is pressed + set_level(memorized_level); + } else { // button was released + set_level(0); + empty_event_sequence(); + //go_to_standby = 1; // sleep while light is off + } + return MISCHIEF_MANAGED; + } + #endif // Sleep, dammit! (but wait a few seconds first) // (because standby mode uses such little power that it can interfere @@ -1302,6 +1313,7 @@ uint8_t momentary_state(EventPtr event, uint16_t arg) { else if ((event == EV_tick) && (actual_level == 0)) { if (arg > TICKS_PER_SECOND*15) { // sleep after 15 seconds go_to_standby = 1; // sleep while light is off + // TODO: lighted button should use lockout config? } return MISCHIEF_MANAGED; } @@ -1311,7 +1323,7 @@ uint8_t momentary_state(EventPtr event, uint16_t arg) { #ifdef USE_MUGGLE_MODE -uint8_t muggle_state(EventPtr event, uint16_t arg) { +uint8_t muggle_state(Event event, uint16_t arg) { static int8_t ramp_direction; static int8_t muggle_off_mode; @@ -1461,7 +1473,7 @@ uint8_t muggle_state(EventPtr event, uint16_t arg) { // ask the user for a sequence of numbers, then save them and return to caller -uint8_t config_state_base(EventPtr event, uint16_t arg, +uint8_t config_state_base(Event event, uint16_t arg, uint8_t num_config_steps, void (*savefunc)()) { static uint8_t config_step; @@ -1520,7 +1532,7 @@ void ramp_config_save() { } } -uint8_t ramp_config_state(EventPtr event, uint16_t arg) { +uint8_t ramp_config_state(Event event, uint16_t arg) { uint8_t num_config_steps; num_config_steps = 2 + ramp_style; return config_state_base(event, arg, @@ -1548,7 +1560,7 @@ void thermal_config_save() { if (therm_ceil > MAX_THERM_CEIL) therm_ceil = MAX_THERM_CEIL; } -uint8_t thermal_config_state(EventPtr event, uint16_t arg) { +uint8_t thermal_config_state(Event event, uint16_t arg) { return config_state_base(event, arg, 2, thermal_config_save); } @@ -1563,13 +1575,13 @@ void beacon_config_save() { } } -uint8_t beacon_config_state(EventPtr event, uint16_t arg) { +uint8_t beacon_config_state(Event event, uint16_t arg) { return config_state_base(event, arg, 1, beacon_config_save); } -uint8_t number_entry_state(EventPtr event, uint16_t arg) { +uint8_t number_entry_state(Event event, uint16_t arg) { static uint8_t value; static uint8_t blinks_left; static uint8_t entry_step; diff --git a/spaghetti-monster/fsm-events.c b/spaghetti-monster/fsm-events.c index ee7bc97..091d4cf 100644 --- a/spaghetti-monster/fsm-events.c +++ b/spaghetti-monster/fsm-events.c @@ -20,25 +20,8 @@ #ifndef FSM_EVENTS_C #define FSM_EVENTS_C -// TODO: maybe compare events by number instead of pointer? -// (number = index in event types array) -// (comparison would use full event content, but send off index to callbacks) -// (saves space by using uint8_t instead of a pointer) -// (also eliminates the need to duplicate single-entry events like for voltage or timer tick) - -// return 1 if (a == b), 0 otherwise -uint8_t compare_event_sequences(uint8_t *a, const uint8_t *b) { - for(uint8_t i=0; (i<EV_MAX_LEN) && (a[i] == pgm_read_byte(b+i)); i++) { - // end of zero-terminated sequence - if (a[i] == 0) return 1; - } - // if we ever fall out, that means something was different - // (or the sequence is too long) - return 0; -} - void empty_event_sequence() { - for(uint8_t i=0; i<EV_MAX_LEN; i++) current_event[i] = 0; + current_event = 0; // when the user completes an input sequence, interrupt any running timers // to cancel any delays currently in progress // This eliminates a whole bunch of extra code: @@ -49,46 +32,41 @@ void empty_event_sequence() { uint8_t push_event(uint8_t ev_type) { ticks_since_last_event = 0; // something happened - uint8_t i; - //uint8_t prev_event = 0; // never push the same event twice in a row - for(i=0; current_event[i] && (i<EV_MAX_LEN); i++) { - // this doesn't actually seem to be necessary any more... - //prev_event = current_event[i]; + + // only click events are sent to this function + //current_event |= B_CLICK; + + // handle button presses + if (ev_type == B_PRESS) { + // set press flag + current_event |= B_PRESS; + // increase click counter + if ((current_event & B_COUNT) < (B_COUNT-1)) { + current_event ++; + return 1; // event pushed + } + return 0; // maximum number of clicks reached } - //if ((i < EV_MAX_LEN) && (prev_event != ev_type)) { - //if (prev_event != ev_type) { - if (i < EV_MAX_LEN) { - current_event[i] = ev_type; + // handle button releases + else if (ev_type == B_RELEASE) { + // clear the press flag + current_event ^= B_PRESS; + // if a "hold" event just ended, set the timeout flag + // to indicate that the event is done and can be cleared + if (current_event & B_HOLD) { current_event |= B_TIMEOUT; } return 1; // event pushed - } else { - // TODO: ... something? } - return 0; // no event pushed -} -// find and return last action in the current event sequence -/* -uint8_t last_event(uint8_t offset) { - uint8_t i; - for(i=0; current_event[i] && (i<EV_MAX_LEN); i++); - if (i == EV_MAX_LEN) return current_event[EV_MAX_LEN-offset]; - else if (i >= offset) return current_event[i-offset]; - return 0; -} -*/ + return 0; // unexpected event type -inline uint8_t last_event_num() { - uint8_t i; - for(i=0; current_event[i] && (i<EV_MAX_LEN); i++); - return i; } -void append_emission(EventPtr event, uint16_t arg) { +void append_emission(Event event, uint16_t arg) { uint8_t i; // find last entry for(i=0; - (i<EMISSION_QUEUE_LEN) && (emissions[i].event != NULL); + (i<EMISSION_QUEUE_LEN) && (emissions[i].event != EV_none); i++) { } // add new entry if (i < EMISSION_QUEUE_LEN) { @@ -105,12 +83,12 @@ void delete_first_emission() { emissions[i].event = emissions[i+1].event; emissions[i].arg = emissions[i+1].arg; } - emissions[i].event = NULL; + emissions[i].event = EV_none; emissions[i].arg = 0; } void process_emissions() { - while (emissions[0].event != NULL) { + while (emissions[0].event != EV_none) { emit_now(emissions[0].event, emissions[0].arg); delete_first_emission(); } @@ -202,7 +180,7 @@ uint8_t nice_delay_s() { */ // Call stacked callbacks for the given event until one handles it. -uint8_t emit_now(EventPtr event, uint16_t arg) { +uint8_t emit_now(Event event, uint16_t arg) { for(int8_t i=state_stack_len-1; i>=0; i--) { uint8_t err = state_stack[i](event, arg); if (! err) return 0; @@ -210,26 +188,10 @@ uint8_t emit_now(EventPtr event, uint16_t arg) { return 1; // event not handled } -void emit(EventPtr event, uint16_t arg) { +void emit(Event event, uint16_t arg) { // add this event to the queue for later, // so we won't use too much time during an interrupt append_emission(event, arg); } -// Search the pre-defined event list for one matching what the user just did, -// and emit it if one was found. -void emit_current_event(uint16_t arg) { - //uint8_t err = 1; - for (uint8_t i=0; i<(sizeof(event_sequences)/sizeof(EventPtr)); i++) { - if (events_match(current_event, event_sequences[i])) { - //DEBUG_FLASH; - //err = emit(event_sequences[i], arg); - //return err; - emit(event_sequences[i], arg); - return; - } - } - //return err; -} - #endif diff --git a/spaghetti-monster/fsm-events.h b/spaghetti-monster/fsm-events.h index 0212c2c..d838800 100644 --- a/spaghetti-monster/fsm-events.h +++ b/spaghetti-monster/fsm-events.h @@ -23,10 +23,9 @@ #include <avr/pgmspace.h> // typedefs -typedef PROGMEM const uint8_t Event; -typedef Event * EventPtr; +typedef uint8_t Event; typedef struct Emission { - EventPtr event; + Event event; uint16_t arg; } Emission; @@ -36,11 +35,11 @@ typedef struct Emission { #define MISCHIEF_NOT_MANAGED EVENT_NOT_HANDLED #ifndef MAX_CLICKS -#define MAX_CLICKS 4 +#define MAX_CLICKS 15 #endif #define EV_MAX_LEN ((MAX_CLICKS*2)+3) -uint8_t current_event[EV_MAX_LEN]; +Event current_event; // at 0.016 ms per tick, 255 ticks = 4.08 s static volatile uint16_t ticks_since_last_event = 0; @@ -52,509 +51,175 @@ static volatile uint16_t ticks_since_last_event = 0; #define RELEASE_TIMEOUT 24 #endif -#define A_ENTER_STATE 1 -#define A_LEAVE_STATE 2 -#define A_REENTER_STATE 3 -#define A_TICK 4 -#define A_SLEEP_TICK 5 -#define A_PRESS 6 -#define A_HOLD 7 -#define A_RELEASE 8 -#define A_RELEASE_TIMEOUT 9 -#define A_OVERHEATING 10 -#define A_UNDERHEATING 11 -#define A_VOLTAGE_LOW 12 -//#define A_VOLTAGE_CRITICAL 13 -#define A_DEBUG 255 // test event for debugging +/* Event structure + * Bit 7: 1 for a button input event, 0 for all others. + * If bit 7 is 1: + * Bits 0,1,2,3: Click counter. Up to 15 clicks. + * Bit 4: 1 for a "press" event, 0 for a "release" event. + * Bit 5: 1 for a "hold" event, 0 otherwise. + * Bit 6: 1 for a "timeout" event, 0 otherwise. + * If bit 7 is 0: + * Not yet defined. + */ + +// event masks / bits +#define B_SYSTEM 0b10000000 +#define B_CLICK 0b00000000 +#define B_TIMEOUT 0b01000000 +#define B_HOLD 0b00100000 +#define B_PRESS 0b00010000 +#define B_RELEASE 0b00000000 +#define B_COUNT 0b00001111 +#define B_FLAGS 0b11110000 // Event types -// TODO: make these progmem-only? -Event EV_debug[] = { - A_DEBUG, - 0 } ; -Event EV_enter_state[] = { - A_ENTER_STATE, - 0 } ; -Event EV_leave_state[] = { - A_LEAVE_STATE, - 0 } ; -Event EV_reenter_state[] = { - A_REENTER_STATE, - 0 } ; -Event EV_tick[] = { - A_TICK, - 0 } ; +#define EV_debug (B_SYSTEM|0b01111111) +#define EV_enter_state (B_SYSTEM|0b00001000) +#define EV_leave_state (B_SYSTEM|0b00001001) +#define EV_reenter_state (B_SYSTEM|0b00001010) +#define EV_tick (B_SYSTEM|0b00000001) #ifdef TICK_DURING_STANDBY -Event EV_sleep_tick[] = { - A_SLEEP_TICK, - 0 } ; +#define EV_sleep_tick (B_SYSTEM|0b00000011) #endif #ifdef USE_LVP -Event EV_voltage_low[] = { - A_VOLTAGE_LOW, - 0 } ; +#define EV_voltage_low (B_SYSTEM|0b00000100) #endif #ifdef USE_THERMAL_REGULATION -Event EV_temperature_high[] = { - A_OVERHEATING, - 0 } ; -Event EV_temperature_low[] = { - A_UNDERHEATING, - 0 } ; +#define EV_temperature_high (B_SYSTEM|0b00000101) +#define EV_temperature_low (B_SYSTEM|0b00000110) #endif -Event EV_click1_press[] = { - A_PRESS, - 0 }; + +#define EV_none 0 + // shouldn't normally happen, but UI might reset event while button is down // so a release with no recorded prior hold could be possible -Event EV_release[] = { - A_RELEASE, - 0 }; -Event EV_click1_release[] = { - A_PRESS, - A_RELEASE, - 0 }; +#define EV_release (B_CLICK|B_RELEASE|0) + +#define EV_click1_press (B_CLICK|B_PRESS|1) +#define EV_click1_hold (B_CLICK|B_HOLD|B_PRESS|1) +#define EV_click1_hold_release (B_CLICK|B_TIMEOUT|B_HOLD|B_RELEASE|1) +#define EV_click1_release (B_CLICK|B_RELEASE|1) +#define EV_click1_complete (B_CLICK|B_TIMEOUT|1) #define EV_1click EV_click1_complete -Event EV_click1_complete[] = { - A_PRESS, - A_RELEASE, - A_RELEASE_TIMEOUT, - 0 }; #define EV_hold EV_click1_hold -// FIXME: Should holds use "start+tick" or just "tick" with a tick number? -// Or "start+tick" with a tick number? -Event EV_click1_hold[] = { - A_PRESS, - A_HOLD, - 0 }; -Event EV_click1_hold_release[] = { - A_PRESS, - A_HOLD, - A_RELEASE, - 0 }; -#if MAX_CLICKS >= 2 -Event EV_click2_press[] = { - A_PRESS, - A_RELEASE, - A_PRESS, - 0 }; -Event EV_click2_hold[] = { - A_PRESS, - A_RELEASE, - A_PRESS, - A_HOLD, - 0 }; -Event EV_click2_hold_release[] = { - A_PRESS, - A_RELEASE, - A_PRESS, - A_HOLD, - A_RELEASE, - 0 }; -Event EV_click2_release[] = { - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - 0 }; + +#define EV_click2_press (B_CLICK|B_PRESS|2) +#define EV_click2_hold (B_CLICK|B_HOLD|B_PRESS|2) +#define EV_click2_hold_release (B_CLICK|B_TIMEOUT|B_HOLD|B_RELEASE|2) +#define EV_click2_release (B_CLICK|B_RELEASE|2) +#define EV_click2_complete (B_CLICK|B_TIMEOUT|2) #define EV_2clicks EV_click2_complete -Event EV_click2_complete[] = { - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_RELEASE_TIMEOUT, - 0 }; -#endif // MAX_CLICKS >= 2 -#if MAX_CLICKS >= 3 -Event EV_click3_press[] = { - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - 0 }; -Event EV_click3_hold[] = { - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_HOLD, - 0 }; -Event EV_click3_hold_release[] = { - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_HOLD, - A_RELEASE, - 0 }; -Event EV_click3_release[] = { - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - 0 }; + +#define EV_click3_press (B_CLICK|B_PRESS|3) +#define EV_click3_hold (B_CLICK|B_HOLD|B_PRESS|3) +#define EV_click3_hold_release (B_CLICK|B_TIMEOUT|B_HOLD|B_RELEASE|3) +#define EV_click3_release (B_CLICK|B_RELEASE|3) +#define EV_click3_complete (B_CLICK|B_TIMEOUT|3) #define EV_3clicks EV_click3_complete -Event EV_click3_complete[] = { - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_RELEASE_TIMEOUT, - 0 }; -#endif // MAX_CLICKS >= 3 -#if MAX_CLICKS >= 4 + +#define EV_click4_press (B_CLICK|B_PRESS|4) +#define EV_click4_hold (B_CLICK|B_HOLD|B_PRESS|4) +#define EV_click4_hold_release (B_CLICK|B_TIMEOUT|B_HOLD|B_RELEASE|4) +#define EV_click4_release (B_CLICK|B_RELEASE|4) +#define EV_click4_complete (B_CLICK|B_TIMEOUT|4) #define EV_4clicks EV_click4_complete -Event EV_click4_complete[] = { - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_RELEASE_TIMEOUT, - 0 }; -#endif -#if MAX_CLICKS >= 5 + +#define EV_click5_press (B_CLICK|B_PRESS|5) +#define EV_click5_hold (B_CLICK|B_HOLD|B_PRESS|5) +#define EV_click5_hold_release (B_CLICK|B_TIMEOUT|B_HOLD|B_RELEASE|5) +#define EV_click5_release (B_CLICK|B_RELEASE|5) +#define EV_click5_complete (B_CLICK|B_TIMEOUT|5) #define EV_5clicks EV_click5_complete -Event EV_click5_complete[] = { - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_RELEASE_TIMEOUT, - 0 }; -#endif -#if MAX_CLICKS >= 6 + +#define EV_click6_press (B_CLICK|B_PRESS|6) +#define EV_click6_hold (B_CLICK|B_HOLD|B_PRESS|6) +#define EV_click6_hold_release (B_CLICK|B_TIMEOUT|B_HOLD|B_RELEASE|6) +#define EV_click6_release (B_CLICK|B_RELEASE|6) +#define EV_click6_complete (B_CLICK|B_TIMEOUT|6) #define EV_6clicks EV_click6_complete -Event EV_click6_complete[] = { - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_RELEASE_TIMEOUT, - 0 }; -#endif -#if MAX_CLICKS >= 7 + +#define EV_click7_press (B_CLICK|B_PRESS|7) +#define EV_click7_hold (B_CLICK|B_HOLD|B_PRESS|7) +#define EV_click7_hold_release (B_CLICK|B_TIMEOUT|B_HOLD|B_RELEASE|7) +#define EV_click7_release (B_CLICK|B_RELEASE|7) +#define EV_click7_complete (B_CLICK|B_TIMEOUT|7) #define EV_7clicks EV_click7_complete -Event EV_click7_complete[] = { - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_RELEASE_TIMEOUT, - 0 }; -#endif -#if MAX_CLICKS >= 8 + +#define EV_click8_press (B_CLICK|B_PRESS|8) +#define EV_click8_hold (B_CLICK|B_HOLD|B_PRESS|8) +#define EV_click8_hold_release (B_CLICK|B_TIMEOUT|B_HOLD|B_RELEASE|8) +#define EV_click8_release (B_CLICK|B_RELEASE|8) +#define EV_click8_complete (B_CLICK|B_TIMEOUT|8) #define EV_8clicks EV_click8_complete -Event EV_click8_complete[] = { - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_RELEASE_TIMEOUT, - 0 }; -#endif -#if MAX_CLICKS >= 9 + +#define EV_click9_press (B_CLICK|B_PRESS|9) +#define EV_click9_hold (B_CLICK|B_HOLD|B_PRESS|9) +#define EV_click9_hold_release (B_CLICK|B_TIMEOUT|B_HOLD|B_RELEASE|9) +#define EV_click9_release (B_CLICK|B_RELEASE|9) +#define EV_click9_complete (B_CLICK|B_TIMEOUT|9) #define EV_9clicks EV_click9_complete -Event EV_click9_complete[] = { - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_RELEASE_TIMEOUT, - 0 }; -#endif -#if MAX_CLICKS >= 10 + +#define EV_click10_press (B_CLICK|B_PRESS|10) +#define EV_click10_hold (B_CLICK|B_HOLD|B_PRESS|10) +#define EV_click10_hold_release (B_CLICK|B_TIMEOUT|B_HOLD|B_RELEASE|10) +#define EV_click10_release (B_CLICK|B_RELEASE|10) +#define EV_click10_complete (B_CLICK|B_TIMEOUT|10) #define EV_10clicks EV_click10_complete -Event EV_click10_complete[] = { - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_RELEASE_TIMEOUT, - 0 }; -#endif -#if MAX_CLICKS >= 11 + +#define EV_click11_press (B_CLICK|B_PRESS|11) +#define EV_click11_hold (B_CLICK|B_HOLD|B_PRESS|11) +#define EV_click11_hold_release (B_CLICK|B_TIMEOUT|B_HOLD|B_RELEASE|11) +#define EV_click11_release (B_CLICK|B_RELEASE|11) +#define EV_click11_complete (B_CLICK|B_TIMEOUT|11) #define EV_11clicks EV_click11_complete -Event EV_click11_complete[] = { - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_RELEASE_TIMEOUT, - 0 }; -#endif -#if MAX_CLICKS >= 12 + +#define EV_click12_press (B_CLICK|B_PRESS|12) +#define EV_click12_hold (B_CLICK|B_HOLD|B_PRESS|12) +#define EV_click12_hold_release (B_CLICK|B_TIMEOUT|B_HOLD|B_RELEASE|12) +#define EV_click12_release (B_CLICK|B_RELEASE|12) +#define EV_click12_complete (B_CLICK|B_TIMEOUT|12) #define EV_12clicks EV_click12_complete -Event EV_click12_complete[] = { - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_RELEASE_TIMEOUT, - 0 }; -#endif -#if MAX_CLICKS >= 13 + +#define EV_click13_press (B_CLICK|B_PRESS|13) +#define EV_click13_hold (B_CLICK|B_HOLD|B_PRESS|13) +#define EV_click13_hold_release (B_CLICK|B_TIMEOUT|B_HOLD|B_RELEASE|13) +#define EV_click13_release (B_CLICK|B_RELEASE|13) +#define EV_click13_complete (B_CLICK|B_TIMEOUT|13) #define EV_13clicks EV_click13_complete -Event EV_click13_complete[] = { - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_RELEASE_TIMEOUT, - 0 }; -#endif -#if MAX_CLICKS >= 14 + +#define EV_click14_press (B_CLICK|B_PRESS|14) +#define EV_click14_hold (B_CLICK|B_HOLD|B_PRESS|14) +#define EV_click14_hold_release (B_CLICK|B_TIMEOUT|B_HOLD|B_RELEASE|14) +#define EV_click14_release (B_CLICK|B_RELEASE|14) +#define EV_click14_complete (B_CLICK|B_TIMEOUT|14) #define EV_14clicks EV_click14_complete -Event EV_click14_complete[] = { - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_RELEASE_TIMEOUT, - 0 }; -#endif -// ... and so on - -// A list of button event types for easy iteration -// TODO: make this progmem-only? -EventPtr event_sequences[] = { - EV_click1_press, - EV_release, - EV_click1_release, - EV_click1_complete, - EV_click1_hold, - EV_click1_hold_release, - #if MAX_CLICKS >= 2 - EV_click2_press, - EV_click2_hold, - EV_click2_hold_release, - EV_click2_release, - EV_click2_complete, - #endif - #if MAX_CLICKS >= 3 - EV_click3_press, - EV_click3_hold, - EV_click3_hold_release, - EV_click3_release, - EV_click3_complete, - #endif - #if MAX_CLICKS >= 4 - EV_click4_complete, - #endif - #if MAX_CLICKS >= 5 - EV_click5_complete, - #endif - #if MAX_CLICKS >= 6 - EV_click6_complete, - #endif - #if MAX_CLICKS >= 7 - EV_click7_complete, - #endif - #if MAX_CLICKS >= 8 - EV_click8_complete, - #endif - #if MAX_CLICKS >= 9 - EV_click9_complete, - #endif - #if MAX_CLICKS >= 10 - EV_click10_complete, - #endif - #if MAX_CLICKS >= 11 - EV_click11_complete, - #endif - #if MAX_CLICKS >= 12 - EV_click12_complete, - #endif - #if MAX_CLICKS >= 13 - EV_click13_complete, - #endif - #if MAX_CLICKS >= 14 - EV_click14_complete, - #endif - // ... -}; - -#define events_match(a,b) compare_event_sequences(a,b) + +#define EV_click15_press (B_CLICK|B_PRESS|15) +#define EV_click15_hold (B_CLICK|B_HOLD|B_PRESS|15) +#define EV_click15_hold_release (B_CLICK|B_TIMEOUT|B_HOLD|B_RELEASE|15) +#define EV_click15_release (B_CLICK|B_RELEASE|15) +#define EV_click15_complete (B_CLICK|B_TIMEOUT|15) +#define EV_15clicks EV_click15_complete + + +#define events_match(a,b) (a == b) // return 1 if (a == b), 0 otherwise -uint8_t compare_event_sequences(uint8_t *a, const uint8_t *b); +#define compare_event_sequences(a,b) (a == b) void empty_event_sequence(); uint8_t push_event(uint8_t ev_type); -// uint8_t last_event(uint8_t offset); -inline uint8_t last_event_num(); #define EMISSION_QUEUE_LEN 16 // no comment about "volatile emissions" volatile Emission emissions[EMISSION_QUEUE_LEN]; -void append_emission(EventPtr event, uint16_t arg); +void append_emission(Event event, uint16_t arg); void delete_first_emission(); void process_emissions(); //#define emit_now emit -uint8_t emit_now(EventPtr event, uint16_t arg); -void emit(EventPtr event, uint16_t arg); -void emit_current_event(uint16_t arg); +uint8_t emit_now(Event event, uint16_t arg); +void emit(Event event, uint16_t arg); +#define emit_current_event(arg) emit(current_event, arg) uint8_t nice_delay_ms(uint16_t ms); //uint8_t nice_delay_4ms(uint8_t ms); diff --git a/spaghetti-monster/fsm-pcint.c b/spaghetti-monster/fsm-pcint.c index a79572d..acb627d 100644 --- a/spaghetti-monster/fsm-pcint.c +++ b/spaghetti-monster/fsm-pcint.c @@ -75,9 +75,9 @@ void PCINT_inner(uint8_t pressed) { uint8_t pushed; if (pressed) { - pushed = push_event(A_PRESS); + pushed = push_event(B_PRESS); } else { - pushed = push_event(A_RELEASE); + pushed = push_event(B_RELEASE); } // check if sequence matches any defined sequences diff --git a/spaghetti-monster/fsm-states.c b/spaghetti-monster/fsm-states.c index 09ae804..f91dc4b 100644 --- a/spaghetti-monster/fsm-states.c +++ b/spaghetti-monster/fsm-states.c @@ -32,7 +32,7 @@ // TODO: function to call stacked callbacks until one returns "handled" void _set_state(StatePtr new_state, uint16_t arg, - EventPtr exit_event, EventPtr enter_event) { + Event exit_event, Event enter_event) { // call old state-exit hook (don't use stack) if (current_state != NULL) current_state(exit_event, arg); // set new state @@ -82,7 +82,7 @@ uint8_t set_state(StatePtr new_state, uint16_t arg) { #ifndef DONT_USE_DEFAULT_STATE // bottom state on stack // handles default actions for LVP, thermal regulation, etc -uint8_t default_state(EventPtr event, uint16_t arg) { +uint8_t default_state(Event event, uint16_t arg) { if (0) {} // this should get compiled out #ifdef USE_LVP diff --git a/spaghetti-monster/fsm-states.h b/spaghetti-monster/fsm-states.h index 6e4a2a0..7d9361b 100644 --- a/spaghetti-monster/fsm-states.h +++ b/spaghetti-monster/fsm-states.h @@ -23,7 +23,7 @@ #include "fsm-adc.h" // typedefs -typedef uint8_t State(EventPtr event, uint16_t arg); +typedef uint8_t State(Event event, uint16_t arg); typedef State * StatePtr; // top of the stack @@ -36,12 +36,12 @@ StatePtr state_stack[STATE_STACK_SIZE]; uint8_t state_stack_len = 0; void _set_state(StatePtr new_state, uint16_t arg, - EventPtr exit_event, EventPtr enter_event); + Event exit_event, Event enter_event); int8_t push_state(StatePtr new_state, uint16_t arg); StatePtr pop_state(); uint8_t set_state(StatePtr new_state, uint16_t arg); #ifndef DONT_USE_DEFAULT_STATE -uint8_t default_state(EventPtr event, uint16_t arg); +uint8_t default_state(Event event, uint16_t arg); #endif #endif diff --git a/spaghetti-monster/fsm-wdt.c b/spaghetti-monster/fsm-wdt.c index e8419bc..cfff1fa 100644 --- a/spaghetti-monster/fsm-wdt.c +++ b/spaghetti-monster/fsm-wdt.c @@ -85,15 +85,8 @@ ISR(WDT_vect) { // append timeout to current event sequence, then // send event to current state callback - // preload recent events - uint8_t le_num = last_event_num(); - uint8_t last_event = 0; - uint8_t prev_event = 0; - if (le_num >= 1) last_event = current_event[le_num-1]; - if (le_num >= 2) prev_event = current_event[le_num-2]; - // callback on each timer tick - if (last_event == A_HOLD) { + if ((current_event & B_FLAGS) == (B_HOLD | B_PRESS)) { emit(EV_tick, 0); // override tick counter while holding button } else { @@ -101,29 +94,32 @@ ISR(WDT_vect) { } // user held button long enough to count as a long click? - if (last_event == A_PRESS) { - if (ticks_since_last_event >= HOLD_TIMEOUT) { - push_event(A_HOLD); - emit_current_event(0); + if (current_event & B_PRESS) { + // during a "hold", send a hold event each tick, with a timer + if (current_event & B_HOLD) { + emit_current_event(ticks_since_last_event); + } + // has button been down long enough to become a "hold"? + else { + if (ticks_since_last_event >= HOLD_TIMEOUT) { + current_event |= B_HOLD; + emit_current_event(0); + } } } - // user is still holding button, so tick - else if (last_event == A_HOLD) { - emit_current_event(ticks_since_last_event); - } - - // detect completed button presses with expired timeout - else if (last_event == A_RELEASE) { + // event in progress, but button not currently down + else if (current_event) { + // "hold" event just ended // no timeout required when releasing a long-press // TODO? move this logic to PCINT() and simplify things here? - if (prev_event == A_HOLD) { + if (current_event & B_HOLD) { //emit_current_event(0); // should have been emitted by PCINT empty_event_sequence(); } // end and clear event after release timeout else if (ticks_since_last_event >= RELEASE_TIMEOUT) { - push_event(A_RELEASE_TIMEOUT); + current_event |= B_TIMEOUT; emit_current_event(0); empty_event_sequence(); } |
