summaryrefslogtreecommitdiff
path: root/src/drivers
diff options
context:
space:
mode:
authorBdale Garbee <bdale@gag.com>2018-08-05 11:24:13 +0800
committerBdale Garbee <bdale@gag.com>2018-08-05 11:24:13 +0800
commita3b4f0d33e9d13fb5294397304ec0eb44ecaf1fc (patch)
tree69c563070ea7ca4a127f2ac925f18a36acb888bb /src/drivers
parent67da03d1382228a95d0414294703371cf32e666e (diff)
parent3a2a5a05bb6372d9003905cee7afdfcd6d38ae7e (diff)
Merge branch 'branch-1.8' into debian
Diffstat (limited to 'src/drivers')
-rw-r--r--src/drivers/ao_button.c69
-rw-r--r--src/drivers/ao_cc1120.c2
-rw-r--r--src/drivers/ao_lco.c550
-rw-r--r--src/drivers/ao_lco.h119
-rw-r--r--src/drivers/ao_lco_bits.c501
-rw-r--r--src/drivers/ao_lco_cmd.c11
-rw-r--r--src/drivers/ao_lco_two.c238
-rw-r--r--src/drivers/ao_pad.c254
-rw-r--r--src/drivers/ao_quadrature.c102
9 files changed, 1073 insertions, 773 deletions
diff --git a/src/drivers/ao_button.c b/src/drivers/ao_button.c
index 07e92c67..f6a9676b 100644
--- a/src/drivers/ao_button.c
+++ b/src/drivers/ao_button.c
@@ -69,6 +69,39 @@ _ao_button_get(uint8_t b)
#if AO_BUTTON_COUNT > 4
case 4: return ao_button_value(4);
#endif
+#if AO_BUTTON_COUNT > 5
+ case 5: return ao_button_value(5);
+#endif
+#if AO_BUTTON_COUNT > 6
+ case 6: return ao_button_value(6);
+#endif
+#if AO_BUTTON_COUNT > 7
+ case 7: return ao_button_value(7);
+#endif
+#if AO_BUTTON_COUNT > 8
+ case 8: return ao_button_value(8);
+#endif
+#if AO_BUTTON_COUNT > 9
+ case 9: return ao_button_value(9);
+#endif
+#if AO_BUTTON_COUNT > 10
+ case 10: return ao_button_value(10);
+#endif
+#if AO_BUTTON_COUNT > 11
+ case 11: return ao_button_value(11);
+#endif
+#if AO_BUTTON_COUNT > 12
+ case 12: return ao_button_value(12);
+#endif
+#if AO_BUTTON_COUNT > 13
+ case 13: return ao_button_value(13);
+#endif
+#if AO_BUTTON_COUNT > 14
+ case 14: return ao_button_value(14);
+#endif
+#if AO_BUTTON_COUNT > 15
+ case 15: return ao_button_value(15);
+#endif
}
return 0;
}
@@ -144,4 +177,40 @@ ao_button_init(void)
#if AO_BUTTON_COUNT > 4
init(4);
#endif
+#if AO_BUTTON_COUNT > 5
+ init(5);
+#endif
+#if AO_BUTTON_COUNT > 6
+ init(6);
+#endif
+#if AO_BUTTON_COUNT > 7
+ init(7);
+#endif
+#if AO_BUTTON_COUNT > 8
+ init(8);
+#endif
+#if AO_BUTTON_COUNT > 9
+ init(9);
+#endif
+#if AO_BUTTON_COUNT > 10
+ init(10);
+#endif
+#if AO_BUTTON_COUNT > 11
+ init(11);
+#endif
+#if AO_BUTTON_COUNT > 12
+ init(12);
+#endif
+#if AO_BUTTON_COUNT > 13
+ init(13);
+#endif
+#if AO_BUTTON_COUNT > 14
+ init(14);
+#endif
+#if AO_BUTTON_COUNT > 15
+ init(15);
+#endif
+#if AO_BUTTON_COUNT > 16
+ #error too many buttons
+#endif
}
diff --git a/src/drivers/ao_cc1120.c b/src/drivers/ao_cc1120.c
index efe5f996..2f091485 100644
--- a/src/drivers/ao_cc1120.c
+++ b/src/drivers/ao_cc1120.c
@@ -1447,7 +1447,9 @@ ao_radio_test_recv(void)
static void
ao_radio_aprs(void)
{
+#if PACKET_HAS_SLAVE
ao_packet_slave_stop();
+#endif
ao_aprs_send();
}
#endif
diff --git a/src/drivers/ao_lco.c b/src/drivers/ao_lco.c
index e1806ca3..e892c8c0 100644
--- a/src/drivers/ao_lco.c
+++ b/src/drivers/ao_lco.c
@@ -23,14 +23,8 @@
#include <ao_quadrature.h>
#include <ao_lco_func.h>
#include <ao_radio_cmac.h>
-
-#define DEBUG 1
-
-#if DEBUG
-static uint8_t ao_lco_debug;
-#define PRINTD(...) do { if (!ao_lco_debug) break; printf ("\r%5u %s: ", ao_tick_count, __func__); printf(__VA_ARGS__); flush(); } while(0)
-#else
-#define PRINTD(...)
+#if HAS_ADC_SINGLE
+#include <ao_adc_single.h>
#endif
#define AO_LCO_PAD_DIGIT 0
@@ -40,32 +34,16 @@ static uint8_t ao_lco_debug;
#define AO_LCO_DRAG_RACE_START_TIME AO_SEC_TO_TICKS(5)
#define AO_LCO_DRAG_RACE_STOP_TIME AO_SEC_TO_TICKS(2)
-#define AO_LCO_VALID_LAST 1
-#define AO_LCO_VALID_EVER 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_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];
+#define AO_LCO_BOX_DRAG 0x1000
/* 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 int16_t ao_lco_box;
-
-#define AO_LCO_BOX_DRAG 0x1000
-
-static struct ao_pad_query ao_pad_query;
static uint8_t ao_lco_display_mutex;
-static void
-ao_lco_set_pad(uint8_t pad)
+void
+ao_lco_show_pad(uint8_t pad)
{
ao_mutex_get(&ao_lco_display_mutex);
ao_seven_segment_set(AO_LCO_PAD_DIGIT, pad | (ao_lco_drag_race << 4));
@@ -89,8 +67,8 @@ ao_lco_set_pad(uint8_t pad)
(0 << 5) | \
(0 << 6))
-static void
-ao_lco_set_box(uint16_t box)
+void
+ao_lco_show_box(uint16_t box)
{
ao_mutex_get(&ao_lco_display_mutex);
if (box == AO_LCO_BOX_DRAG) {
@@ -104,7 +82,7 @@ ao_lco_set_box(uint16_t box)
}
static void
-ao_lco_set_voltage(uint16_t decivolts)
+ao_lco_show_voltage(uint16_t decivolts)
{
uint8_t tens, ones, tenths;
@@ -118,27 +96,21 @@ ao_lco_set_voltage(uint16_t decivolts)
ao_mutex_put(&ao_lco_display_mutex);
}
-static void
-ao_lco_set_display(void)
+void
+ao_lco_show(void)
{
- if (ao_lco_pad == 0 && ao_lco_box != AO_LCO_BOX_DRAG) {
- ao_lco_set_voltage(ao_pad_query.battery);
+ if (ao_lco_pad == AO_LCO_PAD_VOLTAGE && ao_lco_box != AO_LCO_BOX_DRAG) {
+ ao_lco_show_voltage(ao_pad_query.battery);
} else {
if (ao_lco_box == AO_LCO_BOX_DRAG)
- ao_lco_set_pad(ao_lco_drag_race);
+ ao_lco_show_pad(ao_lco_drag_race);
else
- ao_lco_set_pad(ao_lco_pad);
- ao_lco_set_box(ao_lco_box);
+ ao_lco_show_pad(ao_lco_pad);
+ ao_lco_show_box(ao_lco_box);
}
}
-#define MASK_SIZE(n) (((n) + 7) >> 3)
-#define MASK_ID(n) ((n) >> 3)
-#define MASK_SHIFT(n) ((n) & 7)
-
-static uint8_t ao_lco_box_mask[MASK_SIZE(AO_PAD_MAX_BOXES)];
-
-static uint8_t
+uint8_t
ao_lco_box_present(uint16_t box)
{
if (box == AO_LCO_BOX_DRAG)
@@ -146,117 +118,11 @@ ao_lco_box_present(uint16_t box)
if (box >= AO_PAD_MAX_BOXES)
return 0;
- return (ao_lco_box_mask[MASK_ID(box)] >> MASK_SHIFT(box)) & 1;
-}
-
-static uint8_t
-ao_lco_pad_present(uint8_t box, uint8_t pad)
-{
- /* voltage measurement is always valid */
- if (pad == 0)
- return 1;
- if (!ao_lco_channels[box])
- return 0;
- if (pad > AO_PAD_MAX_CHANNELS)
- return 0;
- return (ao_lco_channels[box] >> (pad - 1)) & 1;
-}
-
-static uint8_t
-ao_lco_pad_first(uint8_t box)
-{
- uint8_t pad;
-
- for (pad = 1; pad <= AO_PAD_MAX_CHANNELS; pad++)
- if (ao_lco_pad_present(box, pad))
- return pad;
- return 0;
+ return (ao_lco_box_mask[AO_LCO_MASK_ID(box)] >> AO_LCO_MASK_SHIFT(box)) & 1;
}
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;
-
-#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();
- ao_lco_fire_down = 0;
-}
-
-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)
@@ -269,13 +135,17 @@ ao_lco_drag_button_check(uint16_t now, uint16_t delay)
*/
if (ao_lco_fire_down) {
if (ao_lco_drag_race) {
- if ((int16_t) (now - ao_lco_fire_tick) >= AO_LCO_DRAG_RACE_STOP_TIME)
+ if ((int16_t) (now - ao_lco_fire_tick) >= AO_LCO_DRAG_RACE_STOP_TIME) {
ao_lco_drag_disable();
+ ao_lco_fire_down = 0;
+ }
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)
+ if ((int16_t) (now - ao_lco_fire_tick) >= AO_LCO_DRAG_RACE_START_TIME) {
ao_lco_drag_enable();
+ ao_lco_fire_down = 0;
+ }
else
button_delay = ao_lco_fire_tick + AO_LCO_DRAG_RACE_START_TIME - now;
}
@@ -285,35 +155,20 @@ ao_lco_drag_button_check(uint16_t now, uint16_t 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 void
ao_lco_drag_monitor(void)
{
uint16_t delay = ~0;
uint16_t now;
+ ao_beep_for(AO_BEEP_MID, AO_MS_TO_TICKS(200));
for (;;) {
- PRINTD("Drag monitor active %d delay %d\n", ao_lco_drag_active, delay);
+ PRINTD("Drag monitor count %d active %d delay %d\n",
+ ao_lco_drag_beep_count, ao_lco_drag_active, delay);
if (delay == (uint16_t) ~0)
- ao_sleep(&ao_lco_drag_active);
+ ao_sleep(&ao_lco_drag_beep_count);
else
- ao_sleep_for(&ao_lco_drag_active, delay);
+ ao_sleep_for(&ao_lco_drag_beep_count, delay);
delay = ~0;
if (!ao_lco_drag_active)
@@ -333,13 +188,33 @@ ao_lco_drag_monitor(void)
}
static void
+ao_lco_step_box(int8_t dir)
+{
+ int16_t new_box = ao_lco_box;
+ do {
+ if (new_box == AO_LCO_BOX_DRAG) {
+ if (dir < 0)
+ new_box = ao_lco_max_box;
+ else
+ new_box = ao_lco_min_box;
+ } else {
+ new_box += dir;
+ if (new_box > ao_lco_max_box)
+ new_box = AO_LCO_BOX_DRAG;
+ else if (new_box < ao_lco_min_box)
+ new_box = AO_LCO_BOX_DRAG;
+ }
+ if (new_box == ao_lco_box)
+ break;
+ } while (!ao_lco_box_present(new_box));
+ ao_lco_set_box(new_box);
+}
+
+static void
ao_lco_input(void)
{
static struct ao_event event;
- int8_t dir, new_pad;
- int16_t new_box;
- ao_beep_for(AO_BEEP_MID, AO_MS_TO_TICKS(200));
for (;;) {
ao_event_get(&event);
PRINTD("event type %d unit %d value %d\n",
@@ -348,106 +223,38 @@ ao_lco_input(void)
case AO_EVENT_QUADRATURE:
switch (event.unit) {
case AO_QUADRATURE_PAD:
- if (!ao_lco_armed) {
- dir = (int8_t) event.value;
- new_pad = ao_lco_pad;
- do {
- new_pad += dir;
- if (new_pad > AO_PAD_MAX_CHANNELS)
- new_pad = 0;
- if (new_pad < 0)
- new_pad = AO_PAD_MAX_CHANNELS;
- if (new_pad == ao_lco_pad)
- break;
- } while (!ao_lco_pad_present(ao_lco_box, new_pad));
- if (new_pad != ao_lco_pad) {
- ao_lco_pad = new_pad;
- ao_lco_set_display();
- }
- }
+ if (!ao_lco_armed)
+ ao_lco_step_pad((int8_t) event.value);
break;
case AO_QUADRATURE_BOX:
- if (!ao_lco_armed) {
- dir = (int8_t) event.value;
- new_box = ao_lco_box;
- do {
- if (new_box == AO_LCO_BOX_DRAG) {
- if (dir < 0)
- new_box = ao_lco_max_box;
- else
- new_box = ao_lco_min_box;
- } else {
- new_box += dir;
- if (new_box > ao_lco_max_box)
- new_box = AO_LCO_BOX_DRAG;
- else if (new_box < ao_lco_min_box)
- new_box = AO_LCO_BOX_DRAG;
- }
- if (new_box == ao_lco_box)
- break;
- } while (!ao_lco_box_present(new_box));
- if (ao_lco_box != new_box) {
- ao_lco_box = new_box;
- ao_lco_pad = 1;
- if (ao_lco_box != AO_LCO_BOX_DRAG)
- ao_lco_channels[ao_lco_box] = 0;
- ao_lco_set_display();
- }
- }
+ if (!ao_lco_armed)
+ ao_lco_step_box((int8_t) event.value);
break;
}
break;
case AO_EVENT_BUTTON:
switch (event.unit) {
case AO_BUTTON_ARM:
- ao_lco_armed = event.value;
- PRINTD("Armed %d\n", 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 {
- memset(ao_lco_selected, 0, sizeof (ao_lco_selected));
- if (ao_lco_pad != 0 && ao_lco_box != AO_LCO_BOX_DRAG)
- ao_lco_selected[ao_lco_box] = (1 << (ao_lco_pad - 1));
- else
- ao_lco_armed = 0;
- }
- }
- ao_wakeup(&ao_lco_armed);
+ ao_lco_set_armed(event.value);
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);
+ ao_lco_set_firing(event.value);
} else {
if (event.value) {
if (ao_lco_box == AO_LCO_BOX_DRAG) {
ao_lco_fire_down = 1;
ao_lco_fire_tick = ao_time();
ao_lco_drag_active = 1;
+ ao_wakeup(&ao_lco_drag_beep_count);
+ } else {
+ ao_lco_toggle_drag();
}
- if (ao_lco_drag_race) {
- if (ao_lco_pad != 0 && ao_lco_box != AO_LCO_BOX_DRAG) {
- 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);
+ ao_wakeup(&ao_lco_drag_beep_count);
}
}
break;
@@ -457,188 +264,36 @@ ao_lco_input(void)
}
}
-static AO_LED_TYPE continuity_led[AO_LED_CONTINUITY_NUM] = {
-#ifdef AO_LED_CONTINUITY_0
- AO_LED_CONTINUITY_0,
-#endif
-#ifdef AO_LED_CONTINUITY_1
- AO_LED_CONTINUITY_1,
-#endif
-#ifdef AO_LED_CONTINUITY_2
- AO_LED_CONTINUITY_2,
-#endif
-#ifdef AO_LED_CONTINUITY_3
- AO_LED_CONTINUITY_3,
-#endif
-#ifdef AO_LED_CONTINUITY_4
- AO_LED_CONTINUITY_4,
-#endif
-#ifdef AO_LED_CONTINUITY_5
- AO_LED_CONTINUITY_5,
-#endif
-#ifdef AO_LED_CONTINUITY_6
- AO_LED_CONTINUITY_6,
-#endif
-#ifdef AO_LED_CONTINUITY_7
- AO_LED_CONTINUITY_7,
-#endif
-};
-
-static uint8_t
-ao_lco_get_channels(uint8_t box, struct ao_pad_query *query)
-{
- int8_t r;
-
- r = ao_lco_query(box, query, &ao_lco_tick_offset[box]);
- if (r == AO_RADIO_CMAC_OK) {
- ao_lco_channels[box] = query->channels;
- ao_lco_valid[box] = AO_LCO_VALID_LAST | AO_LCO_VALID_EVER;
- } else
- ao_lco_valid[box] &= ~AO_LCO_VALID_LAST;
- PRINTD("ao_lco_get_channels(%d) rssi %d valid %d ret %d offset %d\n", box, ao_radio_cmac_rssi, ao_lco_valid[box], r, ao_lco_tick_offset[box]);
- ao_wakeup(&ao_pad_query);
- return ao_lco_valid[box];
-}
-
-static void
-ao_lco_update(void)
-{
- if (ao_lco_box != AO_LCO_BOX_DRAG) {
- uint8_t previous_valid = ao_lco_valid[ao_lco_box];
-
- if (ao_lco_get_channels(ao_lco_box, &ao_pad_query) & AO_LCO_VALID_LAST) {
- if (!(previous_valid & AO_LCO_VALID_EVER)) {
- if (ao_lco_pad != 0)
- ao_lco_pad = ao_lco_pad_first(ao_lco_box);
- ao_lco_set_display();
- }
- if (ao_lco_pad == 0)
- ao_lco_set_display();
- }
- }
-}
-
-static void
-ao_lco_box_reset_present(void)
-{
- ao_lco_min_box = 0xff;
- ao_lco_max_box = 0x00;
- memset(ao_lco_box_mask, 0, sizeof (ao_lco_box_mask));
-}
-
-static void
-ao_lco_box_set_present(uint8_t box)
-{
- if (box < ao_lco_min_box)
- ao_lco_min_box = box;
- if (box > ao_lco_max_box)
- ao_lco_max_box = box;
- if (box >= AO_PAD_MAX_BOXES)
- return;
- ao_lco_box_mask[MASK_ID(box)] |= 1 << MASK_SHIFT(box);
-}
-
+/*
+ * Light up everything for a second at power on to let the user
+ * visually inspect the system for correct operation
+ */
static void
-ao_lco_search(void)
+ao_lco_display_test()
{
- int8_t r;
- int8_t try;
- uint8_t box;
- uint8_t boxes = 0;
-
- ao_lco_box_reset_present();
- ao_lco_set_pad(0);
- for (box = 0; box < AO_PAD_MAX_BOXES; box++) {
- if ((box % 10) == 0)
- ao_lco_set_box(box);
- for (try = 0; try < 3; try++) {
- ao_lco_tick_offset[box] = 0;
- r = ao_lco_query(box, &ao_pad_query, &ao_lco_tick_offset[box]);
- PRINTD("box %d result %d offset %d\n", box, r, ao_lco_tick_offset[box]);
- if (r == AO_RADIO_CMAC_OK) {
- ++boxes;
- ao_lco_box_set_present(box);
- ao_lco_set_pad(boxes % 10);
- ao_delay(AO_MS_TO_TICKS(30));
- break;
- }
- }
- }
- if (ao_lco_min_box <= ao_lco_max_box)
- ao_lco_box = ao_lco_min_box;
- else
- ao_lco_min_box = ao_lco_max_box = ao_lco_box = 0;
- memset(ao_lco_valid, 0, sizeof (ao_lco_valid));
- memset(ao_lco_channels, 0, sizeof (ao_lco_channels));
- ao_lco_pad = 1;
- ao_lco_set_display();
+ ao_mutex_get(&ao_lco_display_mutex);
+ ao_seven_segment_set(AO_LCO_PAD_DIGIT, 8 | 0x10);
+ ao_seven_segment_set(AO_LCO_BOX_DIGIT_1, 8 | 0x10);
+ ao_seven_segment_set(AO_LCO_BOX_DIGIT_10, 8 | 0x10);
+ ao_mutex_put(&ao_lco_display_mutex);
+ ao_led_on(LEDS_AVAILABLE);
+ ao_delay(AO_MS_TO_TICKS(1000));
+ ao_led_off(LEDS_AVAILABLE);
}
+#if HAS_ADC_SINGLE
static void
-ao_lco_igniter_status(void)
+ao_lco_batt_voltage(void)
{
- uint8_t c;
- uint8_t t = 0;
-
- for (;;) {
- ao_sleep(&ao_pad_query);
- PRINTD("RSSI %d VALID %d\n", ao_radio_cmac_rssi, ao_lco_box == AO_LCO_BOX_DRAG ? -1 : ao_lco_valid[ao_lco_box]);
- if (ao_lco_box == AO_LCO_BOX_DRAG) {
- ao_led_off(AO_LED_RED|AO_LED_GREEN|AO_LED_AMBER);
- for (c = 0; c < AO_LED_CONTINUITY_NUM; c++)
- ao_led_off(continuity_led[c]);
- } else {
- if (!(ao_lco_valid[ao_lco_box] & AO_LCO_VALID_LAST)) {
- ao_led_on(AO_LED_RED);
- ao_led_off(AO_LED_GREEN|AO_LED_AMBER);
- continue;
- }
- if (ao_radio_cmac_rssi < -90) {
- ao_led_on(AO_LED_AMBER);
- ao_led_off(AO_LED_RED|AO_LED_GREEN);
- } else {
- ao_led_on(AO_LED_GREEN);
- ao_led_off(AO_LED_RED|AO_LED_AMBER);
- }
- if (ao_pad_query.arm_status)
- 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;
+ struct ao_adc packet;
+ int16_t decivolt;
- 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;
- }
- }
-}
-
-static void
-ao_lco_arm_warn(void)
-{
- for (;;) {
- while (!ao_lco_armed)
- ao_sleep(&ao_lco_armed);
- ao_beep_for(AO_BEEP_MID, AO_MS_TO_TICKS(200));
- ao_delay(AO_MS_TO_TICKS(200));
- }
+ ao_adc_single_get(&packet);
+ decivolt = ao_battery_decivolt(packet.v_batt);
+ ao_lco_show_voltage(decivolt);
+ ao_delay(AO_MS_TO_TICKS(1000));
}
+#endif
static struct ao_task ao_lco_input_task;
static struct ao_task ao_lco_monitor_task;
@@ -646,43 +301,18 @@ static struct ao_task ao_lco_arm_warn_task;
static struct ao_task ao_lco_igniter_status_task;
static void
-ao_lco_monitor(void)
+ao_lco_main(void)
{
- uint16_t delay;
- uint8_t box;
-
+ ao_lco_display_test();
+#if HAS_ADC_SINGLE
+ ao_lco_batt_voltage();
+#endif
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);
-
- if (ao_lco_armed && ao_lco_firing) {
- ao_lco_ignite(AO_PAD_FIRE);
- } else {
- ao_lco_update();
- if (ao_lco_armed) {
- 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_VALID_EVER) {
- ao_lco_arm(box, ao_lco_selected[box], ao_lco_tick_offset[box]);
- ao_delay(AO_MS_TO_TICKS(10));
- }
- }
- }
- }
- }
- if (ao_lco_armed && ao_lco_firing)
- delay = AO_MS_TO_TICKS(100);
- else
- delay = AO_SEC_TO_TICKS(1);
- ao_sleep_for(&ao_lco_armed, delay);
- }
+ ao_lco_monitor();
}
#if DEBUG
@@ -704,7 +334,7 @@ __code struct ao_cmds ao_lco_cmds[] = {
void
ao_lco_init(void)
{
- ao_add_task(&ao_lco_monitor_task, ao_lco_monitor, "lco monitor");
+ ao_add_task(&ao_lco_monitor_task, ao_lco_main, "lco monitor");
#if DEBUG
ao_cmd_register(&ao_lco_cmds[0]);
#endif
diff --git a/src/drivers/ao_lco.h b/src/drivers/ao_lco.h
index 5721eed5..2958fbcc 100644
--- a/src/drivers/ao_lco.h
+++ b/src/drivers/ao_lco.h
@@ -19,7 +19,126 @@
#ifndef _AO_LCO_H_
#define _AO_LCO_H_
+#include <ao_lco_func.h>
+
+#ifndef AO_LCO_DRAG
+#define AO_LCO_DRAG 1
+#endif
+
+#define DEBUG 1
+
+#if DEBUG
+extern uint8_t ao_lco_debug;
+#define PRINTD(...) do { if (!ao_lco_debug) break; printf ("\r%5u %s: ", ao_tick_count, __func__); printf(__VA_ARGS__); flush(); } while(0)
+#else
+#define PRINTD(...)
+#endif
+
+#if AO_LCO_DRAG
+extern uint8_t ao_lco_drag_race; /* TRUE when drag race mode enabled */
+#endif
+
+extern uint8_t ao_lco_pad; /* Currently selected pad */
+extern int16_t ao_lco_box; /* Currently selected box */
+
+extern uint8_t ao_lco_armed; /* armed mode active */
+extern uint8_t ao_lco_firing; /* fire button pressed */
+
+extern struct ao_pad_query ao_pad_query; /* Last received QUERY from pad */
+
+#define AO_LCO_PAD_VOLTAGE 0 /* Pad number to show box voltage */
+
+extern uint8_t ao_lco_min_box, ao_lco_max_box;
+
+#define AO_LCO_MASK_SIZE(n) (((n) + 7) >> 3)
+#define AO_LCO_MASK_ID(n) ((n) >> 3)
+#define AO_LCO_MASK_SHIFT(n) ((n) & 7)
+
+extern uint8_t ao_lco_box_mask[AO_LCO_MASK_SIZE(AO_PAD_MAX_BOXES)];
+
+/*
+ * Shared functions
+ */
+
+void
+ao_lco_igniter_status(void);
+
+void
+ao_lco_update(void);
+
+uint8_t
+ao_lco_pad_present(uint8_t box, uint8_t pad);
+
+uint8_t
+ao_lco_pad_first(uint8_t box);
+
+void
+ao_lco_set_pad(uint8_t new_pad);
+
+void
+ao_lco_step_pad(int8_t dir);
+
+void
+ao_lco_set_box(uint16_t new_box);
+
+void
+ao_lco_set_armed(uint8_t armed);
+
+void
+ao_lco_set_firing(uint8_t firing);
+
+void
+ao_lco_toggle_drag(void);
+
+void
+ao_lco_search(void);
+
+void
+ao_lco_monitor(void);
+
+extern uint8_t ao_lco_drag_beep_count;
+
+/* enable drag race mode */
+void
+ao_lco_drag_enable(void);
+
+/* disable drag race mode */
+void
+ao_lco_drag_disable(void);
+
+/* Handle drag beeps, return new delay */
+uint16_t
+ao_lco_drag_beep_check(uint16_t now, uint16_t delay);
+
+/* Check if it's time to beep during drag race. Return new delay */
+uint16_t
+ao_lco_drag_warn_check(uint16_t now, uint16_t delay);
+
+/* Request 'beeps' additional drag race beeps */
+void
+ao_lco_drag_add_beeps(uint8_t beeps);
+
+/* task function for beeping while arm is active */
+void
+ao_lco_arm_warn(void);
+
+/*
+ * Provided by the hw-specific driver code
+ */
+
+void
+ao_lco_show_pad(uint8_t pad);
+
+void
+ao_lco_show_box(uint16_t box);
+
+void
+ao_lco_show(void);
+
void
ao_lco_init(void);
+uint8_t
+ao_lco_box_present(uint16_t box);
+
#endif /* _AO_LCO_H_ */
diff --git a/src/drivers/ao_lco_bits.c b/src/drivers/ao_lco_bits.c
new file mode 100644
index 00000000..6e50e44d
--- /dev/null
+++ b/src/drivers/ao_lco_bits.c
@@ -0,0 +1,501 @@
+/*
+ * Copyright © 2018 Keith Packard <keithp@keithp.com>
+ *
+ * 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 2 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.
+ */
+
+#include <ao.h>
+#include <ao_lco.h>
+#include <ao_radio_cmac.h>
+
+uint8_t ao_lco_debug;
+
+uint8_t ao_lco_pad;
+int16_t ao_lco_box;
+
+uint8_t ao_lco_armed; /* arm active */
+uint8_t ao_lco_firing; /* fire active */
+
+uint8_t ao_lco_min_box, ao_lco_max_box;
+
+#if AO_LCO_DRAG
+uint8_t ao_lco_drag_race;
+#endif
+
+struct ao_pad_query ao_pad_query; /* latest query response */
+
+static uint8_t ao_lco_channels[AO_PAD_MAX_BOXES]; /* pad channels available on each box */
+static uint16_t ao_lco_tick_offset[AO_PAD_MAX_BOXES]; /* offset from local to remote tick count */
+static uint8_t ao_lco_selected[AO_PAD_MAX_BOXES]; /* pads selected to fire */
+
+#define AO_LCO_VALID_LAST 1
+#define AO_LCO_VALID_EVER 2
+
+static uint8_t ao_lco_valid[AO_PAD_MAX_BOXES]; /* AO_LCO_VALID bits per box */
+
+static const AO_LED_TYPE continuity_led[AO_LED_CONTINUITY_NUM] = {
+#ifdef AO_LED_CONTINUITY_0
+ AO_LED_CONTINUITY_0,
+#endif
+#ifdef AO_LED_CONTINUITY_1
+ AO_LED_CONTINUITY_1,
+#endif
+#ifdef AO_LED_CONTINUITY_2
+ AO_LED_CONTINUITY_2,
+#endif
+#ifdef AO_LED_CONTINUITY_3
+ AO_LED_CONTINUITY_3,
+#endif
+#ifdef AO_LED_CONTINUITY_4
+ AO_LED_CONTINUITY_4,
+#endif
+#ifdef AO_LED_CONTINUITY_5
+ AO_LED_CONTINUITY_5,
+#endif
+#ifdef AO_LED_CONTINUITY_6
+ AO_LED_CONTINUITY_6,
+#endif
+#ifdef AO_LED_CONTINUITY_7
+ AO_LED_CONTINUITY_7,
+#endif
+};
+
+/* Set LEDs to match remote box status */
+void
+ao_lco_igniter_status(void)
+{
+ uint8_t c;
+ uint8_t t = 0;
+
+ for (;;) {
+#if AO_LCO_DRAG
+ if (ao_lco_drag_race)
+ ao_sleep_for(&ao_pad_query, AO_MS_TO_TICKS(50));
+ else
+#endif
+ ao_sleep(&ao_pad_query);
+ PRINTD("RSSI %d VALID %d\n", ao_radio_cmac_rssi, ao_lco_valid[ao_lco_box]);
+ if (!(ao_lco_valid[ao_lco_box] & AO_LCO_VALID_LAST)) {
+ ao_led_on(AO_LED_RED);
+ ao_led_off(AO_LED_GREEN|AO_LED_AMBER);
+ continue;
+ }
+ if (ao_radio_cmac_rssi < -90) {
+ ao_led_on(AO_LED_AMBER);
+ ao_led_off(AO_LED_RED|AO_LED_GREEN);
+ } else {
+ ao_led_on(AO_LED_GREEN);
+ ao_led_off(AO_LED_RED|AO_LED_AMBER);
+ }
+ if (ao_pad_query.arm_status)
+ 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 AO_LCO_DRAG
+ if (ao_lco_drag_race && (ao_lco_selected[ao_lco_box] & (1 << c))) {
+ uint8_t on = 0;
+ if (status == AO_PAD_IGNITER_STATUS_GOOD_IGNITER_RELAY_OPEN) {
+ if (t)
+ on = 1;
+ } else {
+ if (t == 1)
+ on = 1;
+ }
+ if (on)
+ ao_led_on(continuity_led[c]);
+ else
+ ao_led_off(continuity_led[c]);
+ } else
+#endif
+ {
+ if (status == AO_PAD_IGNITER_STATUS_GOOD_IGNITER_RELAY_OPEN)
+ ao_led_on(continuity_led[c]);
+ else
+ ao_led_off(continuity_led[c]);
+ }
+ }
+ t = (t + 1) & 3;
+ }
+}
+
+uint8_t
+ao_lco_pad_present(uint8_t box, uint8_t pad)
+{
+ /* voltage measurement is always valid */
+ if (pad == AO_LCO_PAD_VOLTAGE)
+ return 1;
+ if (!ao_lco_channels[box])
+ return 0;
+ if (pad > AO_PAD_MAX_CHANNELS)
+ return 0;
+ return (ao_lco_channels[box] >> (pad - 1)) & 1;
+}
+
+uint8_t
+ao_lco_pad_first(uint8_t box)
+{
+ uint8_t pad;
+
+ for (pad = 1; pad <= AO_PAD_MAX_CHANNELS; pad++)
+ if (ao_lco_pad_present(box, pad))
+ return pad;
+ return 0;
+}
+
+static uint8_t
+ao_lco_get_channels(uint8_t box, struct ao_pad_query *query)
+{
+ int8_t r;
+
+ r = ao_lco_query(box, query, &ao_lco_tick_offset[box]);
+ if (r == AO_RADIO_CMAC_OK) {
+ ao_lco_channels[box] = query->channels;
+ ao_lco_valid[box] = AO_LCO_VALID_LAST | AO_LCO_VALID_EVER;
+ } else
+ ao_lco_valid[box] &= ~AO_LCO_VALID_LAST;
+ PRINTD("ao_lco_get_channels(%d) rssi %d valid %d ret %d offset %d\n", box, ao_radio_cmac_rssi, ao_lco_valid[box], r, ao_lco_tick_offset[box]);
+ ao_wakeup(&ao_pad_query);
+ return ao_lco_valid[box];
+}
+
+void
+ao_lco_update(void)
+{
+ uint8_t previous_valid = ao_lco_valid[ao_lco_box];
+
+ if (ao_lco_get_channels(ao_lco_box, &ao_pad_query) & AO_LCO_VALID_LAST) {
+ if (!(previous_valid & AO_LCO_VALID_EVER)) {
+ if (ao_lco_pad != AO_LCO_PAD_VOLTAGE)
+ ao_lco_set_pad(ao_lco_pad_first(ao_lco_box));
+ }
+ if (ao_lco_pad == AO_LCO_PAD_VOLTAGE)
+ ao_lco_show();
+ }
+}
+
+uint8_t ao_lco_box_mask[AO_LCO_MASK_SIZE(AO_PAD_MAX_BOXES)];
+
+static void
+ao_lco_box_reset_present(void)
+{
+ ao_lco_min_box = 0xff;
+ ao_lco_max_box = 0x00;
+ memset(ao_lco_box_mask, 0, sizeof (ao_lco_box_mask));
+}
+
+static void
+ao_lco_box_set_present(uint8_t box)
+{
+ if (box < ao_lco_min_box)
+ ao_lco_min_box = box;
+ if (box > ao_lco_max_box)
+ ao_lco_max_box = box;
+ if (box >= AO_PAD_MAX_BOXES)
+ return;
+ ao_lco_box_mask[AO_LCO_MASK_ID(box)] |= 1 << AO_LCO_MASK_SHIFT(box);
+}
+
+void
+ao_lco_set_pad(uint8_t new_pad)
+{
+ ao_lco_pad = new_pad;
+ ao_lco_show();
+}
+
+void
+ao_lco_set_box(uint16_t new_box)
+{
+ ao_lco_box = new_box;
+ if (ao_lco_box < AO_PAD_MAX_BOXES)
+ ao_lco_channels[ao_lco_box] = 0;
+ ao_lco_pad = 1;
+ ao_lco_show();
+}
+
+void
+ao_lco_step_pad(int8_t dir)
+{
+ int8_t new_pad;
+
+ new_pad = ao_lco_pad;
+ do {
+ new_pad += dir;
+ if (new_pad > AO_PAD_MAX_CHANNELS)
+ new_pad = AO_LCO_PAD_VOLTAGE;
+ if (new_pad < 0)
+ new_pad = AO_PAD_MAX_CHANNELS;
+ if (new_pad == ao_lco_pad)
+ break;
+ } while (!ao_lco_pad_present(ao_lco_box, new_pad));
+ ao_lco_set_pad(new_pad);
+}
+
+void
+ao_lco_set_armed(uint8_t armed)
+{
+ ao_lco_armed = armed;
+ PRINTD("Armed %d\n", ao_lco_armed);
+ if (ao_lco_armed) {
+#if AO_LCO_DRAG
+ 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])
+ break;
+ if (box > ao_lco_max_box)
+ ao_lco_armed = 0;
+ } else
+#endif
+ {
+ memset(ao_lco_selected, 0, sizeof (ao_lco_selected));
+ if (ao_lco_pad != 0)
+ ao_lco_selected[ao_lco_box] = (1 << (ao_lco_pad - 1));
+ else
+ ao_lco_armed = 0;
+ }
+ }
+ ao_wakeup(&ao_lco_armed);
+}
+
+void
+ao_lco_set_firing(uint8_t firing)
+{
+ ao_lco_firing = firing;
+ PRINTD("Firing %d\n", ao_lco_firing);
+ ao_wakeup(&ao_lco_armed);
+}
+
+void
+ao_lco_search(void)
+{
+ int8_t r;
+ int8_t try;
+ uint8_t box;
+ uint8_t boxes = 0;
+
+ ao_lco_box_reset_present();
+ ao_lco_show_box(0);
+ ao_lco_show_pad(0);
+ for (box = 0; box < AO_PAD_MAX_BOXES; box++) {
+ if ((box % 10) == 0)
+ ao_lco_show_box(box);
+ for (try = 0; try < 3; try++) {
+ ao_lco_tick_offset[box] = 0;
+ r = ao_lco_query(box, &ao_pad_query, &ao_lco_tick_offset[box]);
+ PRINTD("box %d result %d offset %d\n", box, r, ao_lco_tick_offset[box]);
+ if (r == AO_RADIO_CMAC_OK) {
+ ++boxes;
+ ao_lco_box_set_present(box);
+ ao_lco_show_pad(boxes % 10);
+ ao_delay(AO_MS_TO_TICKS(30));
+ break;
+ }
+ }
+ }
+ if (ao_lco_min_box <= ao_lco_max_box)
+ ao_lco_box = ao_lco_min_box;
+ else
+ ao_lco_min_box = ao_lco_max_box = ao_lco_box = 0;
+ memset(ao_lco_valid, 0, sizeof (ao_lco_valid));
+ memset(ao_lco_channels, 0, sizeof (ao_lco_channels));
+ ao_lco_set_box(ao_lco_min_box);
+}
+
+void
+ao_lco_monitor(void)
+{
+ uint16_t delay;
+ uint8_t box;
+
+ for (;;) {
+ PRINTD("monitor armed %d firing %d\n",
+ ao_lco_armed, ao_lco_firing);
+
+ if (ao_lco_armed && ao_lco_firing) {
+ ao_lco_ignite(AO_PAD_FIRE);
+ } else {
+ ao_lco_update();
+ if (ao_lco_armed) {
+ 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_VALID_EVER) {
+ ao_lco_arm(box, ao_lco_selected[box], ao_lco_tick_offset[box]);
+ ao_delay(AO_MS_TO_TICKS(10));
+ }
+ }
+ }
+ }
+ }
+ if (ao_lco_armed && ao_lco_firing)
+ delay = AO_MS_TO_TICKS(100);
+ else
+ delay = AO_SEC_TO_TICKS(1);
+ ao_sleep_for(&ao_lco_armed, delay);
+ }
+}
+
+#if AO_LCO_DRAG
+
+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;
+
+#define AO_LCO_DRAG_BEEP_TIME AO_MS_TO_TICKS(50)
+#define AO_LCO_DRAG_WARN_TIME AO_SEC_TO_TICKS(5)
+
+/* Request 'beeps' additional drag race beeps */
+void
+ao_lco_drag_add_beeps(uint8_t beeps)
+{
+ PRINTD("beep %d\n", beeps);
+ if (ao_lco_drag_beep_count == 0)
+ ao_lco_drag_beep_time = ao_time();
+ ao_lco_drag_beep_count += beeps;
+ ao_wakeup(&ao_lco_drag_beep_count);
+}
+
+/* Toggle current pad in drag set */
+void
+ao_lco_toggle_drag(void)
+{
+ if (ao_lco_drag_race && ao_lco_pad != AO_LCO_PAD_VOLTAGE) {
+ 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_add_beeps(ao_lco_pad);
+ }
+}
+
+/* Check whether it's time to change the beeper status, then either
+ * turn it on or off as necessary and bump the remaining beep counts
+ */
+
+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_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 = now + AO_LCO_DRAG_BEEP_TIME;
+ }
+ } else {
+ ao_beep(AO_BEEP_HIGH);
+ PRINTD("beep start\n");
+ ao_lco_drag_beep_on = 1;
+ ao_lco_drag_beep_time = now + AO_LCO_DRAG_BEEP_TIME;
+ }
+ }
+ }
+
+ if (ao_lco_drag_beep_count) {
+ uint16_t beep_delay = 0;
+
+ if (ao_lco_drag_beep_time > now)
+ beep_delay = ao_lco_drag_beep_time - now;
+
+ if (delay > beep_delay)
+ delay = beep_delay;
+ }
+ return delay;
+}
+
+void
+ao_lco_drag_enable(void)
+{
+ if (!ao_lco_drag_race) {
+ PRINTD("Drag enable\n");
+ ao_lco_drag_race = 1;
+ memset(ao_lco_selected, 0, sizeof (ao_lco_selected));
+#ifdef AO_LED_DRAG
+ ao_led_on(AO_LED_DRAG);
+#endif
+ ao_lco_drag_add_beeps(5);
+ ao_lco_show();
+ }
+}
+
+void
+ao_lco_drag_disable(void)
+{
+ if (ao_lco_drag_race) {
+ PRINTD("Drag disable\n");
+ ao_lco_drag_race = 0;
+#ifdef AO_LED_DRAG
+ ao_led_off(AO_LED_DRAG);
+#endif
+ memset(ao_lco_selected, 0, sizeof (ao_lco_selected));
+ ao_lco_drag_add_beeps(2);
+ ao_lco_show();
+ }
+}
+
+/* add a beep if it's time to warn the user that drag race mode is
+ * active
+ */
+
+uint16_t
+ao_lco_drag_warn_check(uint16_t now, uint16_t delay)
+{
+ if (ao_lco_drag_race) {
+ uint16_t warn_delay;
+
+ if ((int16_t) (now - ao_lco_drag_warn_time) >= 0) {
+ ao_lco_drag_add_beeps(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;
+}
+#endif /* AO_LCO_DRAG */
+
+/* task function for beeping while arm is active */
+void
+ao_lco_arm_warn(void)
+{
+ for (;;) {
+ while (!ao_lco_armed) {
+#ifdef AO_LED_FIRE
+ ao_led_off(AO_LED_FIRE);
+#endif
+ ao_sleep(&ao_lco_armed);
+ }
+#ifdef AO_LED_FIRE
+ ao_led_on(AO_LED_FIRE);
+#endif
+ ao_beep_for(AO_BEEP_MID, AO_MS_TO_TICKS(200));
+ ao_delay(AO_MS_TO_TICKS(200));
+ }
+}
diff --git a/src/drivers/ao_lco_cmd.c b/src/drivers/ao_lco_cmd.c
index 8de21fb6..dba9a76a 100644
--- a/src/drivers/ao_lco_cmd.c
+++ b/src/drivers/ao_lco_cmd.c
@@ -22,6 +22,10 @@
#include <ao_lco_func.h>
#include <ao_radio_cmac.h>
+#ifndef HAS_STATIC_TEST
+#define HAS_STATIC_TEST 1
+#endif
+
static __pdata uint16_t lco_box;
static __pdata uint8_t lco_channels;
static __pdata uint16_t tick_offset;
@@ -150,6 +154,7 @@ lco_fire_cmd(void) __reentrant
}
}
+#if HAS_STATIC_TEST
static void
lco_static_cmd(void) __reentrant
{
@@ -182,6 +187,7 @@ lco_static_cmd(void) __reentrant
ao_delay(AO_MS_TO_TICKS(100));
}
}
+#endif
static void
lco_arm_cmd(void) __reentrant
@@ -208,18 +214,21 @@ lco_ignite_cmd(void) __reentrant
}
+#if HAS_STATIC_TEST
static void
lco_endstatic_cmd(void) __reentrant
{
lco_ignite(AO_PAD_ENDSTATIC);
}
+#endif
static __code struct ao_cmds ao_lco_cmds[] = {
{ lco_report_cmd, "l <box> <channel>\0Get remote status" },
{ lco_fire_cmd, "F <box> <channel> <secs>\0Fire remote igniters" },
- { lco_fire_cmd, "F <box> <channel> <secs>\0Fire remote igniters" },
+#if HAS_STATIC_TEST
{ lco_static_cmd, "S <box> <channel> <secs>\0Initiate static test" },
{ lco_endstatic_cmd, "D\0End static test (and download someday)" },
+#endif
{ lco_arm_cmd, "a <box> <channel>\0Arm remote igniter" },
{ lco_ignite_cmd, "i <box> <channel>\0Pulse remote igniter" },
{ 0, NULL },
diff --git a/src/drivers/ao_lco_two.c b/src/drivers/ao_lco_two.c
index e2f86745..6f2d81ff 100644
--- a/src/drivers/ao_lco_two.c
+++ b/src/drivers/ao_lco_two.c
@@ -23,64 +23,13 @@
#include <ao_lco_func.h>
#include <ao_radio_cmac.h>
-#define DEBUG 1
-
-#if DEBUG
-static uint8_t ao_lco_debug;
-#define DEBUG_EVENT 1
-#define DEBUG_STATUS 2
-#define PRINTD(l, ...) do { if (!(ao_lco_debug & l)) break; printf ("\r%5u %s: ", ao_tick_count, __func__); printf(__VA_ARGS__); flush(); } while(0)
-#else
-#define PRINTD(l,...)
-#endif
-
-#define AO_LCO_VALID_LAST 1
-#define AO_LCO_VALID_EVER 2
-
static uint8_t ao_lco_suspended;
-static uint8_t ao_lco_valid;
-static uint8_t ao_lco_channels;
-static uint16_t ao_lco_tick_offset;
-
-/* UI values */
-static uint8_t ao_lco_armed;
-static uint8_t ao_lco_firing;
-static uint8_t ao_lco_box;
-
-static struct ao_pad_query ao_pad_query;
-
-/* TeleFireTwo boxes have a single pad */
-#define ao_lco_pad 0
-
-static void
-ao_lco_set_box(int box)
-{
- ao_lco_box = ao_config.pad_box + box;
- ao_lco_valid = 0;
- ao_lco_armed = 0;
- ao_wakeup(&ao_lco_armed);
-}
-
-static void
-ao_lco_set_armed(int armed)
-{
- uint8_t bit = (1 << ao_lco_pad);
-
- if (armed) {
- ao_lco_armed = bit;
- } else {
- ao_lco_armed = 0;
- }
- PRINTD(DEBUG_EVENT, "pad %d bit 0x%x armed %d ao_lco_armed 0x%x\n",
- ao_lco_pad, bit, armed, ao_lco_armed);
- ao_wakeup(&ao_lco_armed);
-}
static void
ao_lco_suspend(void)
{
if (!ao_lco_suspended) {
- PRINTD(DEBUG_EVENT, "suspend\n");
+ PRINTD("suspend\n");
ao_lco_suspended = 1;
ao_lco_armed = 0;
ao_wakeup(&ao_pad_query);
@@ -96,6 +45,23 @@ ao_lco_wakeup(void)
}
}
+void
+ao_lco_show_pad(uint8_t pad)
+{
+ (void) pad;
+}
+
+void
+ao_lco_show_box(uint16_t box)
+{
+ (void) box;
+}
+
+void
+ao_lco_show(void)
+{
+}
+
static void
ao_lco_input(void)
{
@@ -113,23 +79,21 @@ ao_lco_input(void)
ao_event_get(&event);
}
ao_lco_wakeup();
- PRINTD(DEBUG_EVENT, "event type %d unit %d value %d\n",
+ PRINTD("event type %d unit %d value %d\n",
event.type, event.unit, event.value);
switch (event.type) {
case AO_EVENT_BUTTON:
switch (event.unit) {
case AO_BUTTON_BOX:
ao_lco_set_box(event.value);
+ ao_lco_set_armed(0);
break;
case AO_BUTTON_ARM:
ao_lco_set_armed(event.value);
break;
case AO_BUTTON_FIRE:
- if (ao_lco_armed) {
- ao_lco_firing = event.value;
- PRINTD(DEBUG_EVENT, "Firing %d\n", ao_lco_firing);
- ao_wakeup(&ao_lco_armed);
- }
+ if (ao_lco_armed)
+ ao_lco_set_firing(event.value);
break;
}
break;
@@ -137,140 +101,14 @@ ao_lco_input(void)
}
}
-static AO_LED_TYPE continuity_led[AO_LED_CONTINUITY_NUM] = {
-#ifdef AO_LED_CONTINUITY_0
- AO_LED_CONTINUITY_0,
-#endif
-#ifdef AO_LED_CONTINUITY_1
- AO_LED_CONTINUITY_1,
-#endif
-#ifdef AO_LED_CONTINUITY_2
- AO_LED_CONTINUITY_2,
-#endif
-#ifdef AO_LED_CONTINUITY_3
- AO_LED_CONTINUITY_3,
-#endif
-#ifdef AO_LED_CONTINUITY_4
- AO_LED_CONTINUITY_4,
-#endif
-#ifdef AO_LED_CONTINUITY_5
- AO_LED_CONTINUITY_5,
-#endif
-#ifdef AO_LED_CONTINUITY_6
- AO_LED_CONTINUITY_6,
-#endif
-#ifdef AO_LED_CONTINUITY_7
- AO_LED_CONTINUITY_7,
-#endif
-};
-
-static uint8_t
-ao_lco_get_channels(void)
-{
- int8_t r;
-
- r = ao_lco_query(ao_lco_box, &ao_pad_query, &ao_lco_tick_offset);
- if (r == AO_RADIO_CMAC_OK) {
- ao_lco_channels = ao_pad_query.channels;
- ao_lco_valid = AO_LCO_VALID_LAST | AO_LCO_VALID_EVER;
- } else
- ao_lco_valid &= ~AO_LCO_VALID_LAST;
- PRINTD(DEBUG_STATUS, "ao_lco_get_channels() rssi %d valid %d ret %d offset %d\n", ao_radio_cmac_rssi, ao_lco_valid, r, ao_lco_tick_offset);
- ao_wakeup(&ao_pad_query);
- return ao_lco_valid;
-}
-
-static void
-ao_lco_igniter_status(void)
-{
- uint8_t c;
- uint8_t t = 0;
-
- for (;;) {
- uint8_t all_status;
- ao_sleep(&ao_pad_query);
- while (ao_lco_suspended) {
- ao_led_off(AO_LED_GREEN|AO_LED_AMBER|AO_LED_RED|AO_LED_REMOTE_ARM);
- for (c = 0; c < AO_LED_CONTINUITY_NUM; c++)
- ao_led_off(continuity_led[c]);
- ao_sleep(&ao_lco_suspended);
- }
- PRINTD(DEBUG_STATUS, "RSSI %d VALID %d channels %d arm_status %d\n",
- ao_radio_cmac_rssi, ao_lco_valid,
- ao_lco_channels, ao_pad_query.arm_status);
- if (!(ao_lco_valid & AO_LCO_VALID_LAST)) {
- ao_led_on(AO_LED_RED);
- ao_led_off(AO_LED_GREEN|AO_LED_AMBER);
- memset(&ao_pad_query, '\0', sizeof (ao_pad_query));
- } else if (ao_radio_cmac_rssi < -90) {
- ao_led_on(AO_LED_AMBER);
- ao_led_off(AO_LED_RED|AO_LED_GREEN);
- } else {
- ao_led_on(AO_LED_GREEN);
- ao_led_off(AO_LED_RED|AO_LED_AMBER);
- }
- if (ao_pad_query.arm_status)
- ao_led_on(AO_LED_REMOTE_ARM);
- else
- ao_led_off(AO_LED_REMOTE_ARM);
-
- all_status = AO_PAD_IGNITER_STATUS_NO_IGNITER_RELAY_OPEN;
- for (c = 0; c < 8; c++) {
- if (ao_pad_query.channels & (1 << c)) {
- uint8_t status = ao_pad_query.igniter_status[c];
- if (status > all_status)
- all_status = status;
- PRINTD(DEBUG_STATUS, "\tchannel %d status %d\n", c, status);
- }
- }
- for (c = 0; c < AO_LED_CONTINUITY_NUM; c++) {
- uint8_t on = 0;
- if (c == (ao_lco_box - ao_config.pad_box) % AO_LED_CONTINUITY_NUM) {
- switch (all_status) {
- case AO_PAD_IGNITER_STATUS_GOOD_IGNITER_RELAY_OPEN:
- on = 1;
- break;
- case AO_PAD_IGNITER_STATUS_NO_IGNITER_RELAY_CLOSED:
- case AO_PAD_IGNITER_STATUS_UNKNOWN:
- on = t & 1;
- }
- }
- if (on)
- ao_led_on(continuity_led[c]);
- else
- ao_led_off(continuity_led[c]);
- }
- t = 1-t;
- }
-}
-
-static void
-ao_lco_arm_warn(void)
-{
- int i;
- for (;;) {
- while (ao_lco_suspended)
- ao_sleep(&ao_lco_suspended);
- while (!ao_lco_armed)
- ao_sleep(&ao_lco_armed);
- for (i = 0; i < ao_lco_armed; i++) {
- ao_beep_for(AO_BEEP_MID, AO_MS_TO_TICKS(100));
- ao_delay(AO_MS_TO_TICKS(100));
- }
- ao_delay(AO_MS_TO_TICKS(300));
- }
-}
-
static struct ao_task ao_lco_input_task;
static struct ao_task ao_lco_monitor_task;
static struct ao_task ao_lco_arm_warn_task;
static struct ao_task ao_lco_igniter_status_task;
static void
-ao_lco_monitor(void)
+ao_lco_main(void)
{
- uint16_t delay;
-
ao_config_get();
ao_lco_set_box(ao_button_get(AO_BUTTON_BOX));
ao_add_task(&ao_lco_input_task, ao_lco_input, "lco input");
@@ -279,33 +117,7 @@ ao_lco_monitor(void)
ao_led_on(~0);
ao_beep_for(AO_BEEP_MID, AO_MS_TO_TICKS(200));
ao_led_off(~0);
- for (;;) {
- while (ao_lco_suspended)
- ao_sleep(&ao_lco_suspended);
-
- PRINTD(DEBUG_STATUS, "monitor armed %d firing %d\n",
- ao_lco_armed, ao_lco_firing);
-
- if (ao_lco_armed && ao_lco_firing) {
- ao_lco_ignite(AO_PAD_FIRE);
- } else {
- ao_lco_get_channels();
- if (ao_lco_armed) {
- PRINTD(DEBUG_STATUS, "Arming pads %x\n",
- ao_lco_armed);
- if (ao_lco_valid & AO_LCO_VALID_EVER) {
- ao_lco_arm(ao_lco_box, ao_lco_armed, ao_lco_tick_offset);
- ao_delay(AO_MS_TO_TICKS(10));
- }
- }
- }
- if (ao_lco_armed && ao_lco_firing)
- delay = AO_MS_TO_TICKS(100);
- else {
- delay = AO_SEC_TO_TICKS(1);
- }
- ao_sleep_for(&ao_lco_armed, delay);
- }
+ ao_lco_monitor();
}
#if DEBUG
@@ -326,7 +138,7 @@ __code struct ao_cmds ao_lco_cmds[] = {
void
ao_lco_init(void)
{
- ao_add_task(&ao_lco_monitor_task, ao_lco_monitor, "lco monitor");
+ ao_add_task(&ao_lco_monitor_task, ao_lco_main, "lco monitor");
#if DEBUG
ao_cmd_register(&ao_lco_cmds[0]);
#endif
diff --git a/src/drivers/ao_pad.c b/src/drivers/ao_pad.c
index 16b4ae60..c6efc311 100644
--- a/src/drivers/ao_pad.c
+++ b/src/drivers/ao_pad.c
@@ -17,6 +17,7 @@
*/
#include <ao.h>
+#include <ao_exti.h>
#include <ao_pad.h>
#include <ao_74hc165.h>
#include <ao_radio_cmac.h>
@@ -69,10 +70,20 @@ ao_strobe(uint8_t v)
#endif
}
+#ifdef AO_PAD_PORT_0
+#define pins_pad(pad) (*((AO_PAD_ ## pad ## _PORT) == AO_PAD_PORT_0 ? (&pins0) : (&pins1)))
+#else
+#define pins_pad(pad) pins0
+#define AO_PAD_PORT_0 AO_PAD_PORT
+#endif
+
static void
ao_pad_run(void)
{
- AO_PORT_TYPE pins;
+ AO_PORT_TYPE pins0;
+#ifdef AO_PAD_PORT_1
+ AO_PORT_TYPE pins1;
+#endif
for (;;) {
while (!ao_pad_ignite)
@@ -80,58 +91,116 @@ ao_pad_run(void)
/*
* Actually set the pad bits
*/
- pins = 0;
+ pins0 = 0;
+#ifdef AO_PAD_PORT_1
+ pins1 = 0;
+#endif
#if AO_PAD_NUM > 0
if (ao_pad_ignite & (1 << 0))
- pins |= (1 << AO_PAD_PIN_0);
+ pins_pad(0) |= (1 << AO_PAD_PIN_0);
#endif
#if AO_PAD_NUM > 1
if (ao_pad_ignite & (1 << 1))
- pins |= (1 << AO_PAD_PIN_1);
+ pins_pad(1) |= (1 << AO_PAD_PIN_1);
#endif
#if AO_PAD_NUM > 2
if (ao_pad_ignite & (1 << 2))
- pins |= (1 << AO_PAD_PIN_2);
+ pins_pad(2) |= (1 << AO_PAD_PIN_2);
#endif
#if AO_PAD_NUM > 3
if (ao_pad_ignite & (1 << 3))
- pins |= (1 << AO_PAD_PIN_3);
+ pins_pad(3) |= (1 << AO_PAD_PIN_3);
+#endif
+#if AO_PAD_NUM > 4
+ if (ao_pad_ignite & (1 << 4))
+ pins_pad(4) |= (1 << AO_PAD_PIN_4);
+#endif
+#if AO_PAD_NUM > 5
+ if (ao_pad_ignite & (1 << 5))
+ pins_pad(5) |= (1 << AO_PAD_PIN_5);
+#endif
+#if AO_PAD_NUM > 6
+ if (ao_pad_ignite & (1 << 6))
+ pins_pad(6) |= (1 << AO_PAD_PIN_6);
+#endif
+#if AO_PAD_NUM > 7
+ if (ao_pad_ignite & (1 << 7))
+ pins_pad(7) |= (1 << AO_PAD_PIN_7);
+#endif
+#ifdef AO_PAD_PORT_1
+ PRINTD("ignite pins 0x%x 0x%x\n", pins0, pins1);
+ ao_gpio_set_bits(AO_PAD_PORT_0, pins0);
+ ao_gpio_set_bits(AO_PAD_PORT_1, pins1);
+#else
+ PRINTD("ignite pins 0x%x\n", pins0);
+ ao_gpio_set_bits(AO_PAD_PORT_0, pins0);
#endif
- PRINTD("ignite pins 0x%x\n", pins);
- ao_gpio_set_bits(AO_PAD_PORT, pins);
while (ao_pad_ignite) {
ao_pad_ignite = 0;
ao_delay(AO_PAD_FIRE_TIME);
}
- ao_gpio_clr_bits(AO_PAD_PORT, pins);
- PRINTD("turn off pins 0x%x\n", pins);
+#ifdef AO_PAD_PORT_1
+ ao_gpio_clr_bits(AO_PAD_PORT_0, pins0);
+ ao_gpio_clr_bits(AO_PAD_PORT_1, pins1);
+ PRINTD("turn off pins 0x%x 0x%x\n", pins0, pins1);
+#else
+ ao_gpio_set_bits(AO_PAD_PORT_0, pins0);
+ PRINTD("turn off pins 0x%x\n", pins0);
+#endif
}
}
#define AO_PAD_ARM_SIREN_INTERVAL 200
-#ifndef AO_PYRO_R_PYRO_SENSE
-#define AO_PYRO_R_PYRO_SENSE 100
-#define AO_PYRO_R_SENSE_GND 27
-#define AO_FIRE_R_POWER_FET 100
-#define AO_FIRE_R_FET_SENSE 100
-#define AO_FIRE_R_SENSE_GND 27
-#endif
+/* Resistor values needed for various voltage test ratios:
+ *
+ * Net names involved:
+ *
+ * V_BATT Battery power, after the initial power switch
+ * V_PYRO Pyro power, after the pyro power switch (and initial power switch)
+ * PYRO_SENSE ADC input to sense V_PYRO voltage
+ * BATT_SENSE ADC input to sense V_BATT voltage
+ * IGNITER FET output to pad (the other pad lead hooks to V_PYRO)
+ * IGNITER_SENSE ADC input to sense igniter voltage
+ *
+ * AO_PAD_R_V_BATT_BATT_SENSE Resistor from battery rail to battery sense input
+ * AO_PAD_R_BATT_SENSE_GND Resistor from battery sense input to ground
+ *
+ * AO_PAD_R_V_BATT_V_PYRO Resistor from battery rail to pyro rail
+ * AO_PAD_R_V_PYRO_PYRO_SENSE Resistor from pyro rail to pyro sense input
+ * AO_PAD_R_PYRO_SENSE_GND Resistor from pyro sense input to ground
+ *
+ * AO_PAD_R_V_PYRO_IGNITER Optional resistors from pyro rail to FET igniter output
+ * AO_PAD_R_IGNITER_IGNITER_SENSE Resistors from FET igniter output to igniter sense ADC inputs
+ * AO_PAD_R_IGNITER_SENSE_GND Resistors from igniter sense ADC inputs to ground
+ */
+
+int16_t
+ao_pad_decivolt(int16_t adc, int16_t r_plus, int16_t r_minus)
+{
+ int32_t mul = (int32_t) AO_ADC_REFERENCE_DV * (r_plus + r_minus);
+ int32_t div = (int32_t) AO_ADC_MAX * r_minus;
+ return ((int32_t) adc * mul + mul/2) / div;
+}
static void
ao_pad_monitor(void)
{
uint8_t c;
uint8_t sample;
- __pdata uint8_t prev = 0, cur = 0;
+ __pdata AO_LED_TYPE prev = 0, cur = 0;
__pdata uint8_t beeping = 0;
__xdata volatile struct ao_data *packet;
__pdata uint16_t arm_beep_time = 0;
sample = ao_data_head;
+ ao_led_set(LEDS_AVAILABLE);
+ ao_delay(AO_MS_TO_TICKS(1000));
+ ao_led_set(0);
for (;;) {
__pdata int16_t pyro;
+
ao_arch_critical(
while (sample == ao_data_head)
ao_sleep((void *) DATA_TO_XDATA(&ao_data_head));
@@ -141,28 +210,18 @@ ao_pad_monitor(void)
packet = &ao_data_ring[sample];
sample = ao_data_ring_next(sample);
- pyro = packet->adc.pyro;
-
-#define VOLTS_TO_PYRO(x) ((int16_t) ((x) * ((1.0 * AO_PYRO_R_SENSE_GND) / \
- (1.0 * (AO_PYRO_R_SENSE_GND + AO_PYRO_R_PYRO_SENSE)) / 3.3 * AO_ADC_MAX)))
-
+ /* Reply battery voltage */
+ query.battery = ao_pad_decivolt(packet->adc.batt, AO_PAD_R_V_BATT_BATT_SENSE, AO_PAD_R_BATT_SENSE_GND);
-#define VOLTS_TO_FIRE(x) ((int16_t) ((x) * ((1.0 * AO_FIRE_R_SENSE_GND) / \
- (1.0 * (AO_FIRE_R_SENSE_GND + AO_FIRE_R_FET_SENSE)) / 3.3 * AO_ADC_MAX)))
+ /* Current pyro voltage */
+ pyro = ao_pad_decivolt(packet->adc.pyro,
+ AO_PAD_R_V_PYRO_PYRO_SENSE,
+ AO_PAD_R_PYRO_SENSE_GND);
- /* convert ADC value to voltage in tenths, then add .2 for the diode drop */
- query.battery = (packet->adc.batt + 96) / 192 + 2;
cur = 0;
- if (pyro > VOLTS_TO_PYRO(10)) {
+ if (pyro > query.battery * 7 / 8) {
query.arm_status = AO_PAD_ARM_STATUS_ARMED;
cur |= AO_LED_ARMED;
-#if AO_FIRE_R_POWER_FET
- } else if (pyro > VOLTS_TO_PYRO(5)) {
- if ((ao_time() % 100) < 50)
- cur |= AO_LED_ARMED;
- query.arm_status = AO_PAD_ARM_STATUS_UNKNOWN;
- arm_beep_time = 0;
-#endif
} else {
query.arm_status = AO_PAD_ARM_STATUS_DISARMED;
arm_beep_time = 0;
@@ -175,54 +234,50 @@ ao_pad_monitor(void)
cur |= AO_LED_GREEN;
for (c = 0; c < AO_PAD_NUM; c++) {
- int16_t sense = packet->adc.sense[c];
+ int16_t sense = ao_pad_decivolt(packet->adc.sense[c],
+ AO_PAD_R_IGNITER_IGNITER_SENSE,
+ AO_PAD_R_IGNITER_SENSE_GND);
uint8_t status = AO_PAD_IGNITER_STATUS_UNKNOWN;
/*
- * pyro is run through a divider, so pyro = v_pyro * 27 / 127 ~= v_pyro / 20
- * v_pyro = pyro * 127 / 27
+ * Here's the resistor stack on each
+ * igniter channel. Note that
+ * AO_PAD_R_V_PYRO_IGNITER is optional
*
- * v_pyro \
- * 100k igniter
- * output /
- * 100k \
- * sense relay
- * 27k /
- * gnd ---
+ * v_pyro \
+ * AO_PAD_R_V_PYRO_IGNITER igniter
+ * output /
+ * AO_PAD_R_IGNITER_IGNITER_SENSE \
+ * sense relay
+ * AO_PAD_R_IGNITER_SENSE_GND /
+ * gnd ---
*
- * v_pyro \
- * 200k igniter
- * output /
- * 200k \
- * sense relay
- * 22k /
- * gnd ---
- *
- * If the relay is closed, then sense will be 0
- * If no igniter is present, then sense will be v_pyro * 27k/227k = pyro * 127 / 227 ~= pyro/2
- * If igniter is present, then sense will be v_pyro * 27k/127k ~= v_pyro / 20 = pyro
*/
-#if AO_FIRE_R_POWER_FET
+#ifdef AO_PAD_R_V_PYRO_IGNITER
if (sense <= pyro / 8) {
+ /* close to zero → relay is closed */
status = AO_PAD_IGNITER_STATUS_NO_IGNITER_RELAY_CLOSED;
if ((ao_time() % 100) < 50)
cur |= AO_LED_CONTINUITY(c);
- } else
- if (pyro / 8 * 3 <= sense && sense <= pyro / 8 * 5)
- status = AO_PAD_IGNITER_STATUS_NO_IGNITER_RELAY_OPEN;
- else if (pyro / 8 * 7 <= sense) {
- status = AO_PAD_IGNITER_STATUS_GOOD_IGNITER_RELAY_OPEN;
- cur |= AO_LED_CONTINUITY(c);
- }
-#else
- if (sense >= pyro / 8 * 5) {
- status = AO_PAD_IGNITER_STATUS_GOOD_IGNITER_RELAY_OPEN;
- cur |= AO_LED_CONTINUITY(c);
- } else {
- status = AO_PAD_IGNITER_STATUS_NO_IGNITER_RELAY_OPEN;
}
+ else
#endif
+ {
+ if (sense >= (pyro * 7) / 8) {
+
+ /* sense close to pyro voltage; igniter is good
+ */
+ status = AO_PAD_IGNITER_STATUS_GOOD_IGNITER_RELAY_OPEN;
+ cur |= AO_LED_CONTINUITY(c);
+ } else {
+
+ /* relay not shorted (if we can tell),
+ * and igniter not obviously present
+ */
+ status = AO_PAD_IGNITER_STATUS_NO_IGNITER_RELAY_OPEN;
+ }
+ }
query.igniter_status[c] = status;
}
if (cur != prev) {
@@ -284,6 +339,23 @@ ao_pad_read_box(void)
}
#endif
+#ifdef AO_PAD_SELECTOR_PORT
+static int ao_pad_read_box(void) {
+ AO_PORT_TYPE value = ao_gpio_get_all(AO_PAD_SELECTOR_PORT);
+ unsigned pin;
+ int select = 1;
+
+ for (pin = 0; pin < sizeof (AO_PORT_TYPE) * 8; pin++) {
+ if (AO_PAD_SELECTOR_PINS & (1 << pin)) {
+ if ((value & (1 << pin)) == 0)
+ return select;
+ select++;
+ }
+ }
+ return ao_config.pad_box;
+}
+#else
+
#if HAS_FIXED_PAD_BOX
#define ao_pad_read_box() ao_config.pad_box
#endif
@@ -292,6 +364,8 @@ ao_pad_read_box(void)
#define ao_pad_read_box() PAD_BOX
#endif
+#endif
+
static void
ao_pad(void)
{
@@ -299,7 +373,6 @@ ao_pad(void)
int8_t ret;
ao_pad_box = 0;
- ao_led_set(0);
for (;;) {
FLUSHD();
while (ao_pad_disabled)
@@ -497,20 +570,51 @@ __code struct ao_cmds ao_pad_cmds[] = {
{ 0, NULL }
};
+#ifndef AO_PAD_PORT_1
+#define AO_PAD_0_PORT AO_PAD_PORT
+#define AO_PAD_1_PORT AO_PAD_PORT
+#define AO_PAD_2_PORT AO_PAD_PORT
+#define AO_PAD_3_PORT AO_PAD_PORT
+#define AO_PAD_4_PORT AO_PAD_PORT
+#define AO_PAD_5_PORT AO_PAD_PORT
+#define AO_PAD_6_PORT AO_PAD_PORT
+#define AO_PAD_7_PORT AO_PAD_PORT
+#endif
+
void
ao_pad_init(void)
{
+#ifdef AO_PAD_SELECTOR_PORT
+ unsigned pin;
+
+ for (pin = 0; pin < sizeof (AO_PORT_TYPE) * 8; pin++) {
+ if (AO_PAD_SELECTOR_PINS & (1 << pin))
+ ao_enable_input(AO_PAD_SELECTOR_PORT, pin, AO_EXTI_MODE_PULL_UP);
+ }
+#endif
#if AO_PAD_NUM > 0
- ao_enable_output(AO_PAD_PORT, AO_PAD_PIN_0, AO_PAD_0, 0);
+ ao_enable_output(AO_PAD_0_PORT, AO_PAD_PIN_0, AO_PAD_0, 0);
#endif
#if AO_PAD_NUM > 1
- ao_enable_output(AO_PAD_PORT, AO_PAD_PIN_1, AO_PAD_1, 0);
+ ao_enable_output(AO_PAD_1_PORT, AO_PAD_PIN_1, AO_PAD_1, 0);
#endif
#if AO_PAD_NUM > 2
- ao_enable_output(AO_PAD_PORT, AO_PAD_PIN_2, AO_PAD_2, 0);
+ ao_enable_output(AO_PAD_2_PORT, AO_PAD_PIN_2, AO_PAD_2, 0);
#endif
#if AO_PAD_NUM > 3
- ao_enable_output(AO_PAD_PORT, AO_PAD_PIN_3, AO_PAD_3, 0);
+ ao_enable_output(AO_PAD_3_PORT, AO_PAD_PIN_3, AO_PAD_3, 0);
+#endif
+#if AO_PAD_NUM > 4
+ ao_enable_output(AO_PAD_4_PORT, AO_PAD_PIN_4, AO_PAD_4, 0);
+#endif
+#if AO_PAD_NUM > 5
+ ao_enable_output(AO_PAD_5_PORT, AO_PAD_PIN_5, AO_PAD_5, 0);
+#endif
+#if AO_PAD_NUM > 5
+ ao_enable_output(AO_PAD_6_PORT, AO_PAD_PIN_6, AO_PAD_6, 0);
+#endif
+#if AO_PAD_NUM > 7
+ ao_enable_output(AO_PAD_7_PORT, AO_PAD_PIN_7, AO_PAD_7, 0);
#endif
#ifdef AO_STROBE
ao_enable_output(AO_STROBE_PORT, AO_STROBE_PIN, AO_STROBE, 0);
diff --git a/src/drivers/ao_quadrature.c b/src/drivers/ao_quadrature.c
index deecfb79..250e035f 100644
--- a/src/drivers/ao_quadrature.c
+++ b/src/drivers/ao_quadrature.c
@@ -23,6 +23,10 @@
#include <ao_event.h>
__xdata int32_t ao_quadrature_count[AO_QUADRATURE_COUNT];
+#ifndef AO_QUADRATURE_SINGLE_CODE
+static int8_t ao_quadrature_step[AO_QUADRATURE_COUNT];
+#endif
+
static uint8_t ao_quadrature_state[AO_QUADRATURE_COUNT];
struct ao_debounce {
@@ -39,20 +43,25 @@ static struct ao_debounce ao_debounce_state[AO_QUADRATURE_COUNT][2];
#define pinb(q) AO_QUADRATURE_ ## q ## _B ## _PIN
#define isr(q) ao_quadrature_isr_ ## q
-#define DEBOUNCE 10
+#ifndef AO_QUADRATURE_DEBOUNCE
+#error must define AO_QUADRATURE_DEBOUNCE
+#endif
static uint8_t
ao_debounce(uint8_t cur, struct ao_debounce *debounce)
{
- if (cur == debounce->state)
- debounce->count = 0;
- else {
- if (++debounce->count == DEBOUNCE) {
- debounce->state = cur;
- debounce->count = 0;
- }
+#if AO_QUADRATURE_DEBOUNCE > 0
+ if (debounce->count > 0) {
+ debounce->count--;
+ } else if (cur != debounce->state) {
+ debounce->state = cur;
+ debounce->count = AO_QUADRATURE_DEBOUNCE;
}
return debounce->state;
+#else
+ (void) debounce;
+ return cur;
+#endif
}
static uint16_t
@@ -70,8 +79,21 @@ ao_quadrature_read(struct stm_gpio *gpio, uint8_t pin_a, uint8_t pin_b, struct a
#define _ao_quadrature_get(q) ao_quadrature_read(port(q), bita(q), bitb(q), ao_debounce_state[q])
static void
-_ao_quadrature_queue(uint8_t q, int8_t step)
+_ao_quadrature_step(uint8_t q, int8_t step)
{
+#ifndef AO_QUADRATURE_SINGLE_CODE
+ ao_quadrature_step[q] += step;
+ if (ao_quadrature_state[q] != 0)
+ return;
+ if (ao_quadrature_step[q] >= 4) {
+ ao_quadrature_step[q] = 0;
+ step = 1;
+ } else if (ao_quadrature_step[q] <= -4) {
+ ao_quadrature_step[q] = 0;
+ step = -1;
+ } else
+ return;
+#endif
ao_quadrature_count[q] += step;
#if AO_EVENT
ao_event_put_isr(AO_EVENT_QUADRATURE, q, step);
@@ -79,17 +101,29 @@ _ao_quadrature_queue(uint8_t q, int8_t step)
ao_wakeup(&ao_quadrature_count[q]);
}
+static const struct {
+ uint8_t prev, next;
+} ao_quadrature_steps[4] = {
+ [0] { .prev = 2, .next = 1 },
+ [1] { .prev = 0, .next = 3 },
+ [3] { .prev = 1, .next = 2 },
+ [2] { .prev = 3, .next = 0 },
+};
+
static void
-_ao_quadrature_set(uint8_t q, uint8_t new) {
- uint8_t old = ao_quadrature_state[q];
-
- if (old != new && new == 0) {
- if (old & 2)
- _ao_quadrature_queue(q, 1);
- else if (old & 1)
- _ao_quadrature_queue(q, -1);
- }
+_ao_quadrature_set(uint8_t q, uint8_t new)
+{
+ uint8_t old;
+
+ ao_arch_block_interrupts();
+ old = ao_quadrature_state[q];
ao_quadrature_state[q] = new;
+ ao_arch_release_interrupts();
+
+ if (new == ao_quadrature_steps[old].next)
+ _ao_quadrature_step(q, 1);
+ else if (new == ao_quadrature_steps[old].prev)
+ _ao_quadrature_step(q, -1);
}
static void
@@ -103,6 +137,14 @@ ao_quadrature_isr(void)
#endif
}
+static void
+_ao_quadrature_start_one(uint8_t q, uint8_t new)
+{
+ ao_arch_block_interrupts();
+ ao_quadrature_state[q] = new;
+ ao_arch_release_interrupts();
+}
+
int32_t
ao_quadrature_poll(uint8_t q)
{
@@ -124,21 +166,32 @@ ao_quadrature_test(void)
uint8_t q;
int32_t c;
uint8_t s;
+#ifndef AO_QUADRATURE_SINGLE_CODE
+ int8_t t = 0;
+#endif
ao_cmd_decimal();
q = ao_cmd_lex_i;
- if (q >= AO_QUADRATURE_COUNT) {
+ if (q >= AO_QUADRATURE_COUNT)
ao_cmd_status = ao_cmd_syntax_error;
+ if (ao_cmd_status != ao_cmd_success)
return;
- }
c = -10000;
s = 0;
while (ao_quadrature_count[q] != 10) {
if (ao_quadrature_count[q] != c ||
- ao_quadrature_state[q] != s) {
+#ifndef AO_QUADRATURE_SINGLE_CODE
+ ao_quadrature_step[q] != t ||
+#endif
+ ao_quadrature_state[q] != s)
+ {
c = ao_quadrature_count[q];
s = ao_quadrature_state[q];
+#ifndef AO_QUADRATURE_SINGLE_CODE
+ t = ao_quadrature_step[q];
+ printf("step %3d ", t);
+#endif
printf ("count %3d state %2x\n", c, s);
flush();
}
@@ -160,9 +213,10 @@ static const struct ao_cmds ao_quadrature_cmds[] = {
{ 0, NULL }
};
-#define init(q) do { \
- ao_enable_input(port(q), bita(q), 0); \
- ao_enable_input(port(q), bitb(q), 0); \
+#define init(q) do { \
+ ao_enable_input(port(q), bita(q), 0); \
+ ao_enable_input(port(q), bitb(q), 0); \
+ _ao_quadrature_start_one(q, _ao_quadrature_get(q)); \
} while (0)
void