From 8d2f442317fd3678213a14307f95d2f5b54c5623 Mon Sep 17 00:00:00 2001
From: Selene ToyKeeper
Date: Sat, 19 Aug 2017 12:17:58 -0600
Subject: Renamed from RoundTable to SpaghettiMonster (FSM).
---
RoundTable/round-table.c | 736 -----------------------------------------------
1 file changed, 736 deletions(-)
delete mode 100644 RoundTable/round-table.c
(limited to 'RoundTable/round-table.c')
diff --git a/RoundTable/round-table.c b/RoundTable/round-table.c
deleted file mode 100644
index b18228d..0000000
--- a/RoundTable/round-table.c
+++ /dev/null
@@ -1,736 +0,0 @@
-/*
- * RoundTable: Generic foundation code for e-switch flashlights.
- * Other possible names:
- * - Mostly Harmless
- * - FSM / 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 .
- */
-
-#include "tk-attiny.h"
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-// typedefs
-typedef PROGMEM const uint8_t Event;
-typedef Event * EventPtr;
-typedef uint8_t (*EventCallbackPtr)(EventPtr event, uint16_t arg);
-typedef uint8_t EventCallback(EventPtr event, uint16_t arg);
-typedef uint8_t State(EventPtr event, uint16_t arg);
-typedef State * StatePtr;
-typedef struct Emission {
- EventPtr event;
- uint16_t arg;
-} Emission;
-
-volatile StatePtr current_state;
-#define EV_MAX_LEN 16
-uint8_t current_event[EV_MAX_LEN];
-// at 0.016 ms per tick, 255 ticks = 4.08 s
-// TODO: 16 bits?
-static volatile uint8_t ticks_since_last_event = 0;
-
-#ifdef USE_LVP
-// volts * 10
-#define VOLTAGE_LOW 30
-// MCU sees voltage 0.X volts lower than actual, add X to readings
-#define VOLTAGE_FUDGE_FACTOR 2
-volatile uint8_t voltage;
-void low_voltage();
-#endif
-#ifdef USE_THERMAL_REGULATION
-volatile int16_t temperature;
-void low_temperature();
-void high_temperature();
-#endif
-
-#ifdef USE_DEBUG_BLINK
-#define OWN_DELAY
-#define USE_DELAY_4MS
-#include "tk-delay.h"
-#define DEBUG_FLASH PWM1_LVL = 64; delay_4ms(2); PWM1_LVL = 0;
-void debug_blink(uint8_t num) {
- for(; num>0; num--) {
- PWM1_LVL = 32;
- delay_4ms(100/4);
- PWM1_LVL = 0;
- delay_4ms(100/4);
- }
-}
-#endif
-
-#define A_ENTER_STATE 1
-#define A_LEAVE_STATE 2
-#define A_TICK 3
-#define A_PRESS 4
-#define A_HOLD_START 5
-#define A_HOLD_TICK 6
-#define A_RELEASE 7
-#define A_RELEASE_TIMEOUT 8
-// TODO: add events for over/under-heat conditions (with parameter for severity)
-#define A_OVERHEATING 9
-#define A_UNDERHEATING 10
-// TODO: add events for low voltage conditions
-#define A_VOLTAGE_LOW 11
-//#define A_VOLTAGE_CRITICAL 12
-#define A_DEBUG 255 // test event for debugging
-
-// 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)
-
-// Event types
-Event EV_debug[] = {
- A_DEBUG,
- 0 } ;
-Event EV_enter_state[] = {
- A_ENTER_STATE,
- 0 } ;
-Event EV_leave_state[] = {
- A_LEAVE_STATE,
- 0 } ;
-Event EV_tick[] = {
- A_TICK,
- 0 } ;
-#ifdef USE_LVP
-Event EV_voltage_low[] = {
- A_VOLTAGE_LOW,
- 0 } ;
-#endif
-#ifdef USE_THERMAL_REGULATION
-Event EV_temperature_high[] = {
- A_OVERHEATING,
- 0 } ;
-Event EV_temperature_low[] = {
- A_UNDERHEATING,
- 0 } ;
-#endif
-Event EV_click1_press[] = {
- A_PRESS,
- 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_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_START,
- 0 };
-Event EV_click1_hold_release[] = {
- A_PRESS,
- A_HOLD_START,
- A_RELEASE,
- 0 };
-Event EV_click2_press[] = {
- A_PRESS,
- A_RELEASE,
- A_PRESS,
- 0 };
-Event EV_click2_release[] = {
- A_PRESS,
- A_RELEASE,
- A_PRESS,
- A_RELEASE,
- 0 };
-#define EV_2clicks EV_click2_complete
-Event EV_click2_complete[] = {
- A_PRESS,
- A_RELEASE,
- A_PRESS,
- A_RELEASE,
- A_RELEASE_TIMEOUT,
- 0 };
-Event EV_click3_press[] = {
- A_PRESS,
- A_RELEASE,
- A_PRESS,
- A_RELEASE,
- A_PRESS,
- 0 };
-Event EV_click3_release[] = {
- A_PRESS,
- A_RELEASE,
- A_PRESS,
- A_RELEASE,
- A_PRESS,
- A_RELEASE,
- 0 };
-#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 };
-// ... and so on
-
-// A list of event types for easy iteration
-EventPtr event_sequences[] = {
- EV_click1_press,
- EV_release,
- EV_click1_release,
- EV_click1_complete,
- EV_click1_hold,
- EV_click1_hold_release,
- EV_click2_press,
- EV_click2_release,
- EV_click2_complete,
- EV_click3_press,
- EV_click3_release,
- EV_click3_complete,
- // ...
-};
-
-#define events_match(a,b) compare_event_sequences(a,b)
-// 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=0; i--) {
- uint8_t err = state_stack[i](event, arg);
- if (! err) return 0;
- }
- return 1; // event not handled
-}
-
-void emit(EventPtr 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;
-}
-
-void _set_state(StatePtr new_state) {
- // call old state-exit hook (don't use stack)
- if (current_state != NULL) current_state(EV_leave_state, 0);
- // set new state
- current_state = new_state;
- // call new state-enter hook (don't use stack)
- if (new_state != NULL) current_state(EV_enter_state, 0);
-}
-
-int8_t push_state(StatePtr new_state) {
- if (state_stack_len < STATE_STACK_SIZE) {
- // TODO: call old state's exit hook?
- // new hook for non-exit recursion into child?
- state_stack[state_stack_len] = new_state;
- state_stack_len ++;
- _set_state(new_state);
- return state_stack_len;
- } else {
- // TODO: um... how is a flashlight supposed to handle a recursion depth error?
- return -1;
- }
-}
-
-StatePtr pop_state() {
- // TODO: how to handle pop from empty stack?
- StatePtr old_state = NULL;
- StatePtr new_state = NULL;
- if (state_stack_len > 0) {
- state_stack_len --;
- old_state = state_stack[state_stack_len];
- }
- if (state_stack_len > 0) {
- new_state = state_stack[state_stack_len-1];
- }
- _set_state(new_state);
- return old_state;
-}
-
-uint8_t set_state(StatePtr new_state) {
- // FIXME: this calls exit/enter hooks it shouldn't
- pop_state();
- return push_state(new_state);
-}
-
-// TODO? add events to a queue when inside an interrupt
-// instead of calling the event functions directly?
-// (then empty the queue in main loop?)
-
-// TODO? new delay() functions which handle queue consumption?
-// TODO? new interruptible delay() functions?
-
-
-//static volatile uint8_t button_was_pressed;
-#define BP_SAMPLES 16
-uint8_t button_is_pressed() {
- // debounce a little
- uint8_t highcount = 0;
- // measure for 16/64ths of a ms
- for(uint8_t i=0; i (BP_SAMPLES/2));
- //button_was_pressed = result;
- return result;
-}
-
-//void button_change_interrupt() {
-ISR(PCINT0_vect) {
-
- //DEBUG_FLASH;
-
- // something happened
- //ticks_since_last_event = 0;
-
- // add event to current sequence
- if (button_is_pressed()) {
- push_event(A_PRESS);
- } else {
- push_event(A_RELEASE);
- }
-
- // check if sequence matches any defined sequences
- // if so, send event to current state callback
- emit_current_event(0);
-}
-
-// TODO: implement
-ISR(WDT_vect) {
- /*
- // TODO? safety net for PCINT, in case it misses a press or release
- uint8_t bp = button_is_pressed();
- if (bp != button_was_pressed) {
- // TODO: handle missed button event
- if (bp) {
- push_event(A_PRESS);
- } else {
- push_event(A_RELEASE);
- }
- emit_current_event(0);
- }
- */
-
- //timer ++; // Is this needed at all?
-
- /*
- if (ticks_since_last_event & 0b00000111 ) {
- DEBUG_FLASH;
- }
- */
-
- //if (ticks_since_last_event < 0xff) ticks_since_last_event ++;
- // increment, but loop from 255 back to 128
- ticks_since_last_event = (ticks_since_last_event + 1) \
- | (ticks_since_last_event & 0x80);
-
- //static uint8_t hold_ticks = 0; // TODO: 16 bits?
-
- // callback on each timer tick
- emit(EV_tick, ticks_since_last_event);
-
- // if time since last event exceeds timeout,
- // append timeout to current event sequence, then
- // send event to current state callback
- // //hold_event(ticks)
- // //emit(EV_press_hold, hold_ticks);
- // emit_current_event(hold_ticks);
- // or
- // //release_timeout()
- // //emit(EV_press_release_timeout, 0);
- // emit_current_event(0);
-
- #if defined(USE_LVP) || defined(USE_THERMAL_REGULATION)
- // start a new ADC measurement every 4 ticks
- static uint8_t adc_trigger = 0;
- adc_trigger ++;
- if (adc_trigger > 3) {
- adc_trigger = 0;
- ADCSRA |= (1 << ADSC) | (1 << ADIE);
- }
- #endif
-}
-
-// TODO: implement? (or is it better done in main()?)
-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> 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
-}
-
-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<= 1
- DDRB |= (1 << PWM1_PIN);
- TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...)
- TCCR0A = PHASE;
- #elif PWM_CHANNELS >= 2
- DDRB |= (1 << PWM2_PIN);
- #elif PWM_CHANNELS >= 3
- DDRB |= (1 << PWM3_PIN);
- // Second PWM counter is ... weird
- TCCR1 = _BV (CS10);
- GTCCR = _BV (COM1B1) | _BV (PWM1B);
- OCR1C = 255; // Set ceiling value to maximum
- #elif PWM_CHANNELS == 4
- // FIXME: How exactly do we do PWM on channel 4?
- DDRB |= (1 << PWM4_PIN);
- #endif
-
- // TODO: turn on ADC?
- // configure e-switch
- PORTB = (1 << SWITCH_PIN); // e-switch is the only input
- PCMSK = (1 << SWITCH_PIN); // pin change interrupt uses this pin
-
- // TODO: configure sleep mode
- set_sleep_mode(SLEEP_MODE_PWR_DOWN);
-
- // Read config values and saved state
- // restore_state(); // TODO
-
- // TODO: handle long press vs short press (or even medium press)?
-
- #ifdef USE_DEBUG_BLINK
- //debug_blink(1);
- #endif
-
- // all booted -- turn interrupts back on
- PCINT_on();
- WDT_on();
- ADC_on();
- sei();
-
- // fallback for handling a few things
- push_state(default_state);
-
- // call recipe's setup
- setup();
-
- // main loop
- while (1) {
- // TODO: update e-switch press state?
- // TODO: check voltage?
- // TODO: check temperature?
- // if event queue not empty, process and pop first item in queue?
- if (emissions[0].event != NULL) {
- emit_now(emissions[0].event, emissions[0].arg);
- delete_first_emission();
- }
- }
-}
--
cgit v1.2.3