#include <ao_74hc165.h>
#include <ao_radio_cmac.h>
-static __xdata uint8_t ao_pad_ignite;
-static __xdata struct ao_pad_command command;
-static __xdata struct ao_pad_query query;
-static __pdata uint8_t ao_pad_armed;
-static __pdata uint16_t ao_pad_arm_time;
-static __pdata uint8_t ao_pad_box;
-static __xdata uint8_t ao_pad_disabled;
-static __pdata uint16_t ao_pad_packet_time;
+static uint8_t ao_pad_ignite;
+static struct ao_pad_command command;
+static struct ao_pad_query query;
+static uint8_t ao_pad_armed;
+static uint16_t ao_pad_arm_time;
+static uint8_t ao_pad_box;
+static uint8_t ao_pad_disabled;
+static uint16_t ao_pad_packet_time;
#ifndef AO_PAD_RSSI_MINIMUM
#define AO_PAD_RSSI_MINIMUM -90
#define DEBUG 1
#if DEBUG
-static __pdata uint8_t ao_pad_debug;
+static uint8_t ao_pad_debug;
#define PRINTD(...) (ao_pad_debug ? (printf(__VA_ARGS__), 0) : 0)
#define FLUSHD() (ao_pad_debug ? (flush(), 0) : 0)
#else
#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 uint8_t beeping = 0;
- __xdata volatile struct ao_data *packet;
- __pdata uint16_t arm_beep_time = 0;
+ AO_LED_TYPE prev = 0, cur = 0;
+ uint8_t beeping = 0;
+ volatile struct ao_data *packet;
+ 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;
+ int16_t pyro;
+
ao_arch_critical(
while (sample == ao_data_head)
- ao_sleep((void *) DATA_TO_XDATA(&ao_data_head));
+ ao_sleep((void *) &ao_data_head);
);
packet = &ao_data_ring[sample];
sample = ao_data_ring_next(sample);
- pyro = packet->adc.pyro;
+ /* 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_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)))
+ /* Current pyro voltage */
+ pyro = ao_pad_decivolt(packet->adc.pyro,
+ AO_PAD_R_V_PYRO_PYRO_SENSE,
+ AO_PAD_R_PYRO_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)))
-
- /* 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;
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
- *
- * v_pyro \
- * 100k igniter
- * output /
- * 100k \
- * sense relay
- * 27k /
- * gnd ---
+ * Here's the resistor stack on each
+ * igniter channel. Note that
+ * AO_PAD_R_V_PYRO_IGNITER is optional
*
- * v_pyro \
- * 200k igniter
- * output /
- * 200k \
- * sense relay
- * 22k /
- * 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 ---
*
- * 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) {
int8_t ret;
ao_pad_box = 0;
- ao_led_set(0);
for (;;) {
FLUSHD();
while (ao_pad_disabled)
}
}
-static __xdata struct ao_task ao_pad_task;
-static __xdata struct ao_task ao_pad_ignite_task;
-static __xdata struct ao_task ao_pad_monitor_task;
+static struct ao_task ao_pad_task;
+static struct ao_task ao_pad_ignite_task;
+static struct ao_task ao_pad_monitor_task;
#if DEBUG
void
}
#endif
-__code struct ao_cmds ao_pad_cmds[] = {
+const struct ao_cmds ao_pad_cmds[] = {
{ ao_pad_test, "t\0Test pad continuity" },
{ ao_pad_manual, "i <key> <n>\0Fire igniter. <key> is doit with D&I" },
#if DEBUG