From cee85486b04e970e2eb0bf912f319cbdf1b3d5b1 Mon Sep 17 00:00:00 2001 From: Selene ToyKeeper Date: Sat, 19 Aug 2017 02:14:57 -0600 Subject: Made LVP work. Converted emit handling to an async queue, to avoid long-running interrupts. (moved potentially long parts to main()) Improved button press debouncing (was getting stuck sometimes). Added an EV_debug event for testing. Added and turned off some voltage averaging/lowpass logic. --- RoundTable/round-table.c | 199 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 163 insertions(+), 36 deletions(-) (limited to 'RoundTable/round-table.c') diff --git a/RoundTable/round-table.c b/RoundTable/round-table.c index c6ffc08..4d2d14e 100644 --- a/RoundTable/round-table.c +++ b/RoundTable/round-table.c @@ -15,6 +15,10 @@ 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 @@ -24,7 +28,11 @@ uint8_t current_event[EV_MAX_LEN]; static volatile uint8_t ticks_since_last_event = 0; #ifdef USE_LVP -volatile int16_t voltage; +// 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 @@ -62,6 +70,7 @@ void debug_blink(uint8_t num) { // 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) @@ -70,6 +79,9 @@ void debug_blink(uint8_t num) { // (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 } ; @@ -212,6 +224,35 @@ void push_event(uint8_t ev_type) { } } +#define EMISSION_QUEUE_LEN 8 +// no comment about "volatile emissions" +volatile Emission emissions[EMISSION_QUEUE_LEN]; + +void append_emission(EventPtr event, uint16_t arg) { + uint8_t i; + // find last entry + for(i=0; + (i=0; i--) { uint8_t err = state_stack[i](event, arg); if (! err) return 0; @@ -234,18 +275,26 @@ uint8_t emit(EventPtr event, uint16_t arg) { 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. -uint8_t emit_current_event(uint16_t arg) { - uint8_t err = 1; +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; + //err = emit(event_sequences[i], arg); + //return err; + emit(event_sequences[i], arg); + return; } } - return err; + //return err; } void _set_state(StatePtr new_state) { @@ -300,19 +349,23 @@ uint8_t set_state(StatePtr new_state) { // 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 measurement = 0; - // measure for 8/64ths of a ms - for(uint8_t i=0; i<8; i++) { + 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() { @@ -337,6 +390,20 @@ ISR(PCINT0_vect) { // 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? /* @@ -345,7 +412,10 @@ ISR(WDT_vect) { } */ - if (ticks_since_last_event < 0xff) ticks_since_last_event ++; + //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? @@ -363,17 +433,29 @@ ISR(WDT_vect) { // //emit(EV_press_release_timeout, 0); // emit_current_event(0); - // add 4-step voltage / temperature thing? - // with averaged values, - // once every N ticks? + #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; - #define LVP_TIMER_START 255 // TODO: calibrate this value + 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 @@ -384,23 +466,64 @@ ISR(ADC_vect) { #define ADC_STEPS 2 #endif - int16_t measurement = ADC; // latest 10-bit ADC reading + uint16_t measurement = ADC; // latest 10-bit ADC reading adc_step = (adc_step + 1) & (ADC_STEPS-1); #ifdef USE_LVP - // TODO: voltage - // TODO: 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 { - // if (voltage is low) { - // uint8_t err = emit(EV_voltage_low, 0); - // if (!err) lvp_timer = LVP_TIMER_START; - // } - // } - #endif + // 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 @@ -475,7 +598,7 @@ void sleep_until_eswitch_pressed() WDT_off(); ADC_off(); - // TODO: make sure switch isn't currently pressed + // make sure switch isn't currently pressed while (button_is_pressed()) {} PCINT_on(); // wake on e-switch event @@ -486,7 +609,7 @@ void sleep_until_eswitch_pressed() // something happened; wake up sleep_disable(); - //PCINT_off(); // we use this for more than just waking up + PCINT_on(); ADC_on(); WDT_on(); } @@ -579,8 +702,12 @@ int main() { // main loop while (1) { // TODO: update e-switch press state? - // TODO: if wait queue not empty, process and pop first item in queue? // 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