diff options
| author | Selene ToyKeeper | 2017-08-19 02:14:57 -0600 |
|---|---|---|
| committer | Selene ToyKeeper | 2017-08-19 02:14:57 -0600 |
| commit | cee85486b04e970e2eb0bf912f319cbdf1b3d5b1 (patch) | |
| tree | d024c73c528011b755efa8ff5e04387bb68cb304 | |
| parent | First RoundTable example actually works (simple momentary 1-mode UI). (diff) | |
| download | anduril-cee85486b04e970e2eb0bf912f319cbdf1b3d5b1.tar.gz anduril-cee85486b04e970e2eb0bf912f319cbdf1b3d5b1.tar.bz2 anduril-cee85486b04e970e2eb0bf912f319cbdf1b3d5b1.zip | |
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.
Diffstat (limited to '')
| -rw-r--r-- | RoundTable/momentary.c | 40 | ||||
| -rw-r--r-- | RoundTable/round-table.c | 199 |
2 files changed, 177 insertions, 62 deletions
diff --git a/RoundTable/momentary.c b/RoundTable/momentary.c index 4394f46..a40363a 100644 --- a/RoundTable/momentary.c +++ b/RoundTable/momentary.c @@ -22,53 +22,44 @@ #define RT_EMISAR_D4_LAYOUT #define USE_LVP #define USE_DEBUG_BLINK -#define OWN_DELAY -#define USE_DELAY_MS #include "round-table.c" -#include "tk-delay.h" volatile uint8_t brightness; +volatile uint8_t on_now; void light_on() { + on_now = 1; PWM1_LVL = brightness; PWM2_LVL = 0; } void light_off() { + on_now = 0; PWM1_LVL = 0; PWM2_LVL = 0; } -//State momentary_state { uint8_t momentary_state(EventPtr event, uint16_t arg) { if (event == EV_click1_press) { brightness = 255; light_on(); - // reset current event queue + // don't attempt to parse multiple clicks empty_event_sequence(); return 0; } else if (event == EV_release) { light_off(); - // reset current event queue + // don't attempt to parse multiple clicks empty_event_sequence(); return 0; } - /* - // LVP / low-voltage protection - //else if ((event == EV_voltage_low) || (event == EV_voltage_critical)) { - else if (event == EV_voltage_low) { - if (brightness > 0) brightness >>= 1; - else { - light_off(); - standby_mode(); - } - return 0; + else if (event == EV_debug) { + //PWM1_LVL = arg&0xff; + DEBUG_FLASH; } - */ // event not handled return 1; @@ -76,21 +67,18 @@ uint8_t momentary_state(EventPtr event, uint16_t arg) { // LVP / low-voltage protection void low_voltage() { - if (brightness > 0) brightness >>= 1; - else { + debug_blink(3); + if (brightness > 0) { + brightness >>= 1; + if (on_now) light_on(); + } else { light_off(); standby_mode(); } } void setup() { - //debug_blink(1); - /* - brightness = 255; - light_on(); - delay_ms(10); - light_off(); - */ + debug_blink(2); push_state(momentary_state); } 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<EMISSION_QUEUE_LEN) && (emissions[i].event != NULL); + i++) { } + // add new entry + if (i < EMISSION_QUEUE_LEN) { + emissions[i].event = event; + emissions[i].arg = arg; + } else { + // TODO: if queue full, what should we do? + } +} + +void delete_first_emission() { + uint8_t i; + for(i=0; i<EMISSION_QUEUE_LEN-1; i++) { + emissions[i].event = emissions[i+1].event; + emissions[i].arg = emissions[i+1].arg; + } + emissions[i].event = NULL; + emissions[i].arg = 0; +} + // TODO: stack for states, to allow shared utility states like "input a number" // and such, which return to the previous state after finishing #define STATE_STACK_SIZE 8 @@ -225,8 +266,8 @@ uint8_t state_stack_len = 0; // 255: error (not sure what this would even mean though, or what difference it would make) // TODO: function to call stacked callbacks until one returns "handled" // Call stacked callbacks for the given event until one handles it. -uint8_t emit(EventPtr event, uint16_t arg) { - // TODO: implement +//#define emit_now emit +uint8_t emit_now(EventPtr 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; @@ -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; i++) { // check current value - measurement = (measurement << 1) | ((PINB & (1<<SWITCH_PIN)) == 0); + uint8_t bit = ((PINB & (1<<SWITCH_PIN)) == 0); + highcount += bit; // wait a moment _delay_loop_2(BOGOMIPS/64); } - // 0 bits mean "pressed", 1 bits mean "not pressed" - // only return "pressed" if all bits match - return (measurement != 0); + // use most common value + uint8_t result = (highcount > (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<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 @@ -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(); + } } } |
