From dda3f459eaff8d4e41cb44584c8ef77b8e2b3b1c Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sat, 29 Aug 2015 17:29:00 -0700 Subject: [PATCH] altos/telelco: Add drag race UI With the unit disarmed, press and hold the fire button for five seconds to enable drag race mode. The display will show 'dr' for five seconds and beep five times to indicate that drag race mode is enabled. The decimal points in the display will all be displayed as an additional visual aid. Once every five seconds, it will beep. With drag race mode enabled, you can select a box/pad pair and press the 'fire' button to add it to the drag race group. For the current box, all members of the drag race group will have their continuity LEDs blink slowly. There will be no indication of continuity in this mode; you'll want to check that before enabling drag race mode. If you want to de-select a member of the group, just press the fire button again. Each time you push the fire button, it will beep out the pad number added or removed. Arm the box and you will not be able to add or remove members from the drag race group. Firing will simultaneously fire all members of the drag race group. To disable drag race mode, press and hold the fire button for two seconds. It will beep twice and turn off the decimal points in the display. Signed-off-by: Keith Packard --- src/drivers/ao_lco.c | 310 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 287 insertions(+), 23 deletions(-) diff --git a/src/drivers/ao_lco.c b/src/drivers/ao_lco.c index 464e05ab..8180c49d 100644 --- a/src/drivers/ao_lco.c +++ b/src/drivers/ao_lco.c @@ -36,29 +36,42 @@ static uint8_t ao_lco_debug; #define AO_LCO_BOX_DIGIT_1 1 #define AO_LCO_BOX_DIGIT_10 2 +#define AO_LCO_DRAG_RACE_START_TIME AO_SEC_TO_TICKS(5) +#define AO_LCO_DRAG_RACE_STOP_TIME AO_SEC_TO_TICKS(2) + static uint8_t ao_lco_min_box, ao_lco_max_box; static uint8_t ao_lco_selected[AO_PAD_MAX_BOXES]; -static uint8_t ao_lco_armed; -static uint8_t ao_lco_firing; static uint8_t ao_lco_valid[AO_PAD_MAX_BOXES]; static uint8_t ao_lco_channels[AO_PAD_MAX_BOXES]; static uint16_t ao_lco_tick_offset[AO_PAD_MAX_BOXES]; +/* UI values */ +static uint8_t ao_lco_armed; +static uint8_t ao_lco_firing; +static uint16_t ao_lco_fire_tick; +static uint8_t ao_lco_fire_down; +static uint8_t ao_lco_drag_race; static uint8_t ao_lco_pad; static uint8_t ao_lco_box; static struct ao_pad_query ao_pad_query; +static uint8_t ao_lco_display_mutex; + static void ao_lco_set_pad(uint8_t pad) { - ao_seven_segment_set(AO_LCO_PAD_DIGIT, pad); + ao_mutex_get(&ao_lco_display_mutex); + ao_seven_segment_set(AO_LCO_PAD_DIGIT, pad | (ao_lco_drag_race << 4)); + ao_mutex_put(&ao_lco_display_mutex); } static void ao_lco_set_box(uint8_t box) { - ao_seven_segment_set(AO_LCO_BOX_DIGIT_1, box % 10); - ao_seven_segment_set(AO_LCO_BOX_DIGIT_10, box / 10); + ao_mutex_get(&ao_lco_display_mutex); + ao_seven_segment_set(AO_LCO_BOX_DIGIT_1, box % 10 | (ao_lco_drag_race << 4)); + ao_seven_segment_set(AO_LCO_BOX_DIGIT_10, box / 10 | (ao_lco_drag_race << 4)); + ao_mutex_put(&ao_lco_display_mutex); } static void @@ -69,9 +82,11 @@ ao_lco_set_voltage(uint16_t decivolts) tenths = decivolts % 10; ones = (decivolts / 10) % 10; tens = (decivolts / 100) % 10; + ao_mutex_get(&ao_lco_display_mutex); ao_seven_segment_set(AO_LCO_PAD_DIGIT, tenths); ao_seven_segment_set(AO_LCO_BOX_DIGIT_1, ones | 0x10); ao_seven_segment_set(AO_LCO_BOX_DIGIT_10, tens); + ao_mutex_put(&ao_lco_display_mutex); } static void @@ -85,6 +100,33 @@ ao_lco_set_display(void) } } +#define SEVEN_SEGMENT_d ((0 << 0) | \ + (0 << 1) | \ + (1 << 2) | \ + (1 << 3) | \ + (1 << 4) | \ + (1 << 5) | \ + (1 << 6)) + + +#define SEVEN_SEGMENT_r ((0 << 0) | \ + (0 << 1) | \ + (0 << 2) | \ + (1 << 3) | \ + (1 << 4) | \ + (0 << 5) | \ + (0 << 6)) + +static void +ao_lco_set_display_drag(void) +{ + ao_mutex_get(&ao_lco_display_mutex); + ao_seven_segment_direct(AO_LCO_BOX_DIGIT_10, SEVEN_SEGMENT_d | 0x80); + ao_seven_segment_direct(AO_LCO_BOX_DIGIT_1, SEVEN_SEGMENT_r | 0x80); + ao_mutex_put(&ao_lco_display_mutex); + ao_lco_set_pad(ao_lco_pad); +} + #define MASK_SIZE(n) (((n) + 7) >> 3) #define MASK_ID(n) ((n) >> 3) #define MASK_SHIFT(n) ((n) & 7) @@ -123,6 +165,188 @@ ao_lco_pad_first(uint8_t box) return 0; } +static struct ao_task ao_lco_drag_task; +static uint8_t ao_lco_drag_active; +static uint8_t ao_lco_drag_beep_count; +static uint8_t ao_lco_drag_beep_on; +static uint16_t ao_lco_drag_beep_time; +static uint16_t ao_lco_drag_warn_time; +static uint16_t ao_lco_drag_display_time; +static uint8_t ao_lco_drag_display_on; + +#define AO_LCO_DRAG_BEEP_TIME AO_MS_TO_TICKS(50) +#define AO_LCO_DRAG_WARN_TIME AO_SEC_TO_TICKS(5) + +static void +ao_lco_drag_beep_start(void) +{ + ao_beep(AO_BEEP_HIGH); + PRINTD("beep start\n"); + ao_lco_drag_beep_on = 1; + ao_lco_drag_beep_time = ao_time() + AO_LCO_DRAG_BEEP_TIME; +} + +static void +ao_lco_drag_beep_stop(void) +{ + ao_beep(0); + PRINTD("beep stop\n"); + ao_lco_drag_beep_on = 0; + if (ao_lco_drag_beep_count) { + --ao_lco_drag_beep_count; + if (ao_lco_drag_beep_count) + ao_lco_drag_beep_time = ao_time() + AO_LCO_DRAG_BEEP_TIME; + } +} + +static void +ao_lco_drag_beep(uint8_t beeps) +{ + PRINTD("beep %d\n", beeps); + if (!ao_lco_drag_beep_count) + ao_lco_drag_beep_start(); + ao_lco_drag_beep_count += beeps; +} + +static uint16_t +ao_lco_drag_beep_check(uint16_t now, uint16_t delay) +{ + PRINTD("beep check count %d delta %d\n", + ao_lco_drag_beep_count, + (int16_t) (now - ao_lco_drag_beep_time)); + if (ao_lco_drag_beep_count) { + if ((int16_t) (now - ao_lco_drag_beep_time) >= 0) { + if (ao_lco_drag_beep_on) + ao_lco_drag_beep_stop(); + else + ao_lco_drag_beep_start(); + } + } + + if (ao_lco_drag_beep_count) { + if (delay > AO_LCO_DRAG_BEEP_TIME) + delay = AO_LCO_DRAG_BEEP_TIME; + } + return delay; +} + +static void +ao_lco_drag_enable(void) +{ + PRINTD("Drag enable\n"); + ao_lco_drag_race = 1; + memset(ao_lco_selected, 0, sizeof (ao_lco_selected)); + ao_lco_drag_beep(5); + ao_lco_set_display_drag(); + ao_lco_fire_down = 0; + ao_lco_drag_display_on = 1; + ao_lco_drag_display_time = ao_time() + AO_SEC_TO_TICKS(5); +} + +static void +ao_lco_drag_disable(void) +{ + PRINTD("Drag disable\n"); + ao_lco_drag_race = 0; + memset(ao_lco_selected, 0, sizeof (ao_lco_selected)); + ao_lco_drag_beep(2); + ao_lco_set_display(); + ao_lco_fire_down = 0; +} + +static uint16_t +ao_lco_drag_button_check(uint16_t now, uint16_t delay) +{ + uint16_t button_delay = ~0; + + /* + * Check to see if the button has been held down long enough + * to switch in/out of drag race mode + */ + if (ao_lco_fire_down) { + if (ao_lco_drag_race) { + if ((int16_t) (now - ao_lco_fire_tick) >= AO_LCO_DRAG_RACE_STOP_TIME) + ao_lco_drag_disable(); + else + button_delay = ao_lco_fire_tick + AO_LCO_DRAG_RACE_STOP_TIME - now; + } else { + if ((int16_t) (now - ao_lco_fire_tick) >= AO_LCO_DRAG_RACE_START_TIME) + ao_lco_drag_enable(); + else + button_delay = ao_lco_fire_tick + AO_LCO_DRAG_RACE_START_TIME - now; + } + if (delay > button_delay) + delay = button_delay; + } + return delay; +} + +static uint16_t +ao_lco_drag_warn_check(uint16_t now, uint16_t delay) +{ + uint16_t warn_delay = ~0; + + if (ao_lco_drag_race) { + if ((int16_t) (now - ao_lco_drag_warn_time) >= 0) { + ao_lco_drag_beep(1); + ao_lco_drag_warn_time = now + AO_LCO_DRAG_WARN_TIME; + } + warn_delay = ao_lco_drag_warn_time - now; + } + if (delay > warn_delay) + delay = warn_delay; + return delay; +} + +static uint16_t +ao_lco_drag_display_check(uint16_t now, uint16_t delay) +{ + uint16_t display_delay; + + if (ao_lco_drag_display_on) { + if ((int16_t) (now - ao_lco_drag_display_time) >= 0) { + ao_lco_drag_display_on = 0; + ao_lco_set_display(); + } else { + display_delay = ao_lco_drag_display_time - now; + if (delay > display_delay) + delay = display_delay; + } + } + return delay; +} + +static void +ao_lco_drag_monitor(void) +{ + uint16_t delay = ~0; + uint16_t now; + + for (;;) { + PRINTD("Drag monitor active %d delay %d\n", ao_lco_drag_active, delay); + if (delay == (uint16_t) ~0) + ao_sleep(&ao_lco_drag_active); + else + ao_sleep_for(&ao_lco_drag_active, delay); + + delay = ~0; + if (!ao_lco_drag_active) + continue; + + now = ao_time(); + delay = ao_lco_drag_button_check(now, delay); + delay = ao_lco_drag_warn_check(now, delay); + delay = ao_lco_drag_beep_check(now, delay); + delay = ao_lco_drag_display_check(now, delay); + + /* check to see if there's anything left to do here */ + if (!ao_lco_fire_down && !ao_lco_drag_race && !ao_lco_drag_beep_count) { + delay = ~0; + ao_lco_drag_active = 0; + } + } +} + static void ao_lco_input(void) { @@ -184,17 +408,49 @@ ao_lco_input(void) case AO_BUTTON_ARM: ao_lco_armed = event.value; PRINTD("Armed %d\n", ao_lco_armed); - if (ao_lco_pad != 0) { - memset(ao_lco_selected, 0, sizeof (ao_lco_selected)); - ao_lco_selected[ao_lco_box] = (1 << (ao_lco_pad - 1)); - ao_wakeup(&ao_lco_armed); + if (ao_lco_armed) { + if (ao_lco_drag_race) { + uint8_t box; + + for (box = ao_lco_min_box; box <= ao_lco_max_box; box++) { + if (ao_lco_selected[box]) { + ao_wakeup(&ao_lco_armed); + break; + } + } + } else if (ao_lco_pad != 0) { + memset(ao_lco_selected, 0, sizeof (ao_lco_selected)); + ao_lco_selected[ao_lco_box] = (1 << (ao_lco_pad - 1)); + } } + ao_wakeup(&ao_lco_armed); break; case AO_BUTTON_FIRE: if (ao_lco_armed) { + ao_lco_fire_down = 0; ao_lco_firing = event.value; PRINTD("Firing %d\n", ao_lco_firing); ao_wakeup(&ao_lco_armed); + } else { + if (event.value) { + ao_lco_fire_down = 1; + ao_lco_fire_tick = ao_time(); + ao_lco_drag_active = 1; + /* ao_lco_fire_down will already be off if we just enabled drag mode */ + if (ao_lco_fire_down && ao_lco_drag_race) { + if (ao_lco_pad != 0) { + ao_lco_selected[ao_lco_box] ^= (1 << (ao_lco_pad - 1)); + PRINTD("Toggle box %d pad %d (pads now %x) to drag race\n", + ao_lco_pad, ao_lco_box, ao_lco_selected[ao_lco_box]); + ao_lco_drag_beep(ao_lco_pad); + } + } + ao_wakeup(&ao_lco_drag_active); + } else { + ao_lco_fire_down = 0; + if (ao_lco_drag_active) + ao_wakeup(&ao_lco_drag_active); + } } break; } @@ -241,7 +497,7 @@ ao_lco_get_channels(uint8_t box, struct ao_pad_query *query) ao_lco_valid[box] = 1; } else ao_lco_valid[box] = 0; - PRINTD("ao_lco_get_channels(%d) valid %d\n", box, ao_lco_valid[box]); + PRINTD("ao_lco_get_channels(%d) rssi %d valid %d ret %d\n", box, ao_radio_cmac_rssi, ao_lco_valid[box], r); ao_wakeup(&ao_pad_query); return ao_lco_valid[box]; } @@ -323,6 +579,7 @@ static void ao_lco_igniter_status(void) { uint8_t c; + uint8_t t = 0; for (;;) { ao_sleep(&ao_pad_query); @@ -343,18 +600,27 @@ ao_lco_igniter_status(void) ao_led_on(AO_LED_REMOTE_ARM); else ao_led_off(AO_LED_REMOTE_ARM); + for (c = 0; c < AO_LED_CONTINUITY_NUM; c++) { uint8_t status; - if (ao_pad_query.channels & (1 << c)) - status = ao_pad_query.igniter_status[c]; - else - status = AO_PAD_IGNITER_STATUS_NO_IGNITER_RELAY_OPEN; - if (status == AO_PAD_IGNITER_STATUS_GOOD_IGNITER_RELAY_OPEN) - ao_led_on(continuity_led[c]); - else - ao_led_off(continuity_led[c]); + if (ao_lco_drag_race) { + if (ao_lco_selected[ao_lco_box] & (1 << c) && t) + ao_led_on(continuity_led[c]); + else + ao_led_off(continuity_led[c]); + } else { + if (ao_pad_query.channels & (1 << c)) + status = ao_pad_query.igniter_status[c]; + else + status = AO_PAD_IGNITER_STATUS_NO_IGNITER_RELAY_OPEN; + if (status == AO_PAD_IGNITER_STATUS_GOOD_IGNITER_RELAY_OPEN) + ao_led_on(continuity_led[c]); + else + ao_led_off(continuity_led[c]); + } } + t = 1-t; } } @@ -379,12 +645,12 @@ ao_lco_monitor(void) { uint16_t delay; uint8_t box; - struct ao_pad_query pad_query; ao_lco_search(); ao_add_task(&ao_lco_input_task, ao_lco_input, "lco input"); ao_add_task(&ao_lco_arm_warn_task, ao_lco_arm_warn, "lco arm warn"); ao_add_task(&ao_lco_igniter_status_task, ao_lco_igniter_status, "lco igniter status"); + ao_add_task(&ao_lco_drag_task, ao_lco_drag_monitor, "drag race"); for (;;) { PRINTD("monitor armed %d firing %d\n", ao_lco_armed, ao_lco_firing); @@ -392,19 +658,17 @@ ao_lco_monitor(void) if (ao_lco_armed && ao_lco_firing) { ao_lco_ignite(); } else { + ao_lco_update(); if (ao_lco_armed) { - for (box = 0; box < AO_PAD_MAX_BOXES; box++) { + for (box = ao_lco_min_box; box <= ao_lco_max_box; box++) { if (ao_lco_selected[box]) { PRINTD("Arming box %d pads %x\n", box, ao_lco_selected[box]); - if (!ao_lco_valid[box]) - ao_lco_get_channels(box, &pad_query); if (ao_lco_valid[box]) ao_lco_arm(box, ao_lco_selected[box], ao_lco_tick_offset[ao_lco_box]); } } } - ao_lco_update(); } if (ao_lco_armed && ao_lco_firing) delay = AO_MS_TO_TICKS(100); -- 2.30.2