diff options
Diffstat (limited to 'src/drivers')
| -rw-r--r-- | src/drivers/ao_button.c | 69 | ||||
| -rw-r--r-- | src/drivers/ao_cc1120.c | 2 | ||||
| -rw-r--r-- | src/drivers/ao_lco.c | 550 | ||||
| -rw-r--r-- | src/drivers/ao_lco.h | 119 | ||||
| -rw-r--r-- | src/drivers/ao_lco_bits.c | 501 | ||||
| -rw-r--r-- | src/drivers/ao_lco_cmd.c | 11 | ||||
| -rw-r--r-- | src/drivers/ao_lco_two.c | 238 | ||||
| -rw-r--r-- | src/drivers/ao_pad.c | 254 | ||||
| -rw-r--r-- | src/drivers/ao_quadrature.c | 102 | 
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  | 
