altos/telelco: Add drag race UI
[fw/altos] / src / drivers / ao_lco.c
index 464e05ab363c1bf115a7321cf0859b5d976eccb1..8180c49de1137dc69af68f59702d9aa571e3cd1f 100644 (file)
@@ -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);