The unified diff between revisions [d0420ebd..] and [64de686d..] is displayed below. It can also be downloaded as a raw diff.

#
# old_revision [d0420ebd87c820e33a32b29727989516e15980a8]
# new_revision [64de686d701acb9539dc52fe0bff299405612ab0]
#
# add_file "abs.h"
#  content [c484ad4af3d7c487cd3ff1d3be16c6841ba01429]
# 
# add_file "panic.c"
#  content [d33cc5af7411880a40d87b7d56a9086705b49112]
# 
# add_file "panic.h"
#  content [4ed7a80d4d14e77a557b91a5dcc39e621f765d6e]
# 
# add_file "status.c"
#  content [31db654aeccf16a2aa7a73255e38f2dbe05034aa]
# 
# add_file "status.h"
#  content [2cf551e442b9d0a472c5be2684540045a994652b]
# 
# add_file "watchdog.c"
#  content [8398952deb37d8242fc858143c5e201321343c27]
# 
# add_file "watchdog.h"
#  content [e38eb63c762343f8c2221d67501b11e9ac92ebb6]
# 
# patch "Makefile"
#  from [682a220b957308023bd4b69a74f1bdbd6cc62025]
#    to [3c0587750b990cb4dc04f8caccec5088db10298c]
# 
# patch "dcm.c"
#  from [34486e2e7948509c6f2648d365feb84c22ab5b14]
#    to [90d94d47a262c72d472ee7aca36bb55e2b328e66]
# 
# patch "led.c"
#  from [2244b2b9d6abf2cf49ad8123596633912b1f1a90]
#    to [c5ff1936310b87f8ddb877b8d60f17617f4daabd]
# 
# patch "led.h"
#  from [d877be81bb449ce8dbe9db28a85ab4129ec25fc2]
#    to [b0da2e9767cc7415414c01afa7ba79bc8adb4509]
# 
# patch "main.c"
#  from [958bdc83e26a4a7dda991daabf259c44079bc7c5]
#    to [7098e8a2414c2d385b9a43d19c2e3b2461ae00c0]
# 
# patch "motor.c"
#  from [500d9a9173fe983ae7c50cf5fac1d37efa96d8ce]
#    to [b9920bb4788532a73cb93301787e4d0540cc3d56]
# 
# patch "stick.c"
#  from [b0c0cbffb3863a3992abc1ac1148fc90f019309b]
#    to [ad24bc7d9d8c95c497a1b5bd7a13d8e1619ca539]
# 
# patch "wmp.c"
#  from [022e72f07353e280c050ae668cb3c02b318c72c4]
#    to [e6627850f4ae970c9aee0dbb4c6f1933f0cb31c7]
#
============================================================
--- Makefile	682a220b957308023bd4b69a74f1bdbd6cc62025
+++ Makefile	3c0587750b990cb4dc04f8caccec5088db10298c
@@ -4,7 +4,7 @@ CSRCS=main.c i2c.c wmp.c timer.c interru
 
 SSRCS=crt0.s
 CSRCS=main.c i2c.c wmp.c timer.c interrupt.c uart.c event.c matrix.c dcm.c
-CSRCS+=fisqrt.c stick.c trig.c motor.c led.c
+CSRCS+=fisqrt.c stick.c trig.c motor.c led.c watchdog.c panic.c status.c
 
 #PROJOPTS=-DUSE_UART -DSEND_DCM
 
============================================================
--- /dev/null	
+++ abs.h	c484ad4af3d7c487cd3ff1d3be16c6841ba01429
@@ -0,0 +1,3 @@
+/* abs.h */
+
+#define abs(x) (((x) < 0)? (-(x)) : (x))
============================================================
--- dcm.c	34486e2e7948509c6f2648d365feb84c22ab5b14
+++ dcm.c	90d94d47a262c72d472ee7aca36bb55e2b328e66
@@ -9,6 +9,8 @@
 #include "dcm.h"
 #include "uart.h"
 #include "motor.h"
+#include "status.h"
+#include "abs.h"
 
 #define GRAVITY 9.80665f
 
@@ -17,6 +19,10 @@
 
 #define ERROR_LIMIT 1.17f
 
+/* Maximum allowed error for arming */
+#define ERROR_THRESHOLD 0.20f
+
+
 /* Implementation of the DCM IMU concept as described by Premerlani
  * and Bizard
  */
@@ -173,6 +179,15 @@ void dcm_drift_correction(float x, float
 	error[1] = dcm[8]*x - dcm[6]*z;
 	error[2] = dcm[6]*y - dcm[7]*x;
 
+	if (!status_armed()) {
+		if ((abs(error[0]) < ERROR_THRESHOLD) &&
+		    (abs(error[1]) < ERROR_THRESHOLD) &&
+		    (abs(error[2]) < ERROR_THRESHOLD))
+			status_set_ready(STATUS_MODULE_DCM_ERROR, TRUE);
+		else
+			status_set_ready(STATUS_MODULE_DCM_ERROR, FALSE);
+	}
+
 	for (i = 0; i < 3; i++) {
 		if (error[i] > ERROR_LIMIT)
 			error[i] = ERROR_LIMIT;
@@ -208,6 +223,9 @@ void dcm_drift_correction(float x, float
 #endif
 }
 
+/* Maximum angle to the horizontal for arming: 30 degrees */
+#define ATTITUDE_THRESHOLD (0.5)
+
 void dcm_attitude_error(float roll, float pitch, float yaw)
 {
 	/* dcm[6] = sine of pitch */
@@ -221,6 +239,14 @@ void dcm_attitude_error(float roll, floa
 
 	/* TODO: What if we are upside down? */
 
+	if (!status_armed()) {
+		if ((abs(dcm[6]) < ATTITUDE_THRESHOLD) &&
+		    (abs(dcm[7]) < ATTITUDE_THRESHOLD))
+			status_set_ready(STATUS_MODULE_ATTITUDE, TRUE);
+		else
+			status_set_ready(STATUS_MODULE_ATTITUDE, FALSE);
+	}
+
 	motor_pid_update(roll, dcm[6], pitch, -dcm[7], yaw, -omega_z);
 }
 
============================================================
--- led.c	2244b2b9d6abf2cf49ad8123596633912b1f1a90
+++ led.c	c5ff1936310b87f8ddb877b8d60f17617f4daabd
@@ -11,13 +11,14 @@ led_pattern led_pattern_active[] = {250,
 bool led_next_state;
 
 led_pattern led_pattern_active[] = {250, 250, 0};
+led_pattern led_pattern_unknown[] = {100, 100, 0};
 
 void led_set(bool on)
 {
 	if (on)
-		FP0XVAL |= 0x04000000;
+		FP0XVAL &= ~0x04000000;
 	else
-		FP0XVAL &= ~0x04000000;
+		FP0XVAL |= 0x04000000;
 }
 
 void led_update(void)
@@ -43,8 +44,15 @@ void led_set_pattern(led_pattern *patter
 
 void led_set_pattern(led_pattern *pattern)
 {
+	if (led_current_pattern == pattern)
+		return;
 	led_current_pattern = pattern;
-	led_current_pointer = pattern;
+}
+
+void led_init(void)
+{
+	led_current_pattern = led_pattern_unknown;
+	led_current_pointer = led_pattern_unknown;
 	led_next_state = TRUE;
 	led_next_time = timer_read();
 	led_update();
============================================================
--- led.h	d877be81bb449ce8dbe9db28a85ab4129ec25fc2
+++ led.h	b0da2e9767cc7415414c01afa7ba79bc8adb4509
@@ -5,7 +5,9 @@ extern led_pattern led_pattern_active[];
 typedef unsigned int led_pattern;
 
 extern led_pattern led_pattern_active[];
+extern led_pattern led_pattern_unknown[];
 
+void led_init(void);
 void led_set(bool on);
 void led_update(void);
 void led_set_pattern(led_pattern *pattern);
============================================================
--- main.c	958bdc83e26a4a7dda991daabf259c44079bc7c5
+++ main.c	7098e8a2414c2d385b9a43d19c2e3b2461ae00c0
@@ -6,8 +6,9 @@
 #include "uart.h"
 #include "interrupt.h"
 #include "event.h"
-#include "stick.h"
 #include "led.h"
+#include "status.h"
+#include "watchdog.h"
 
 #define PINSEL0 (*((volatile unsigned int *) 0xE002C000))
 #define PINSEL1 (*((volatile unsigned int *) 0xE002C004))
@@ -179,13 +180,12 @@ int main(void) {
 void menu_handler(void);
 
 int main(void) {
-	armed = FALSE;
-
 	init_interrupt();
 	init_uart();
 	init_i2c();
 	init_pins();
 	init_timer();
+	init_status();
 
 	event_register(EVENT_UART_INPUT, menu_handler);
 
@@ -197,11 +197,7 @@ int main(void) {
 
 	putstr("prompt> ");
 
-	led_set(FALSE);
 	timer_delay_ms(1000);
-	led_set(TRUE);
-	timer_delay_ms(1000);
-	led_set(FALSE);
 	if (!wmp_init())
 		putstr("WMP initialisation failed\r\n");
 
@@ -209,12 +205,15 @@ int main(void) {
 	timer_set_period(5*TIMER_MS);
 	wmp_start_zero();
 
-	led_set_pattern(led_pattern_active);
+	led_init();
 
+	init_watchdog();
+
 	/* Good luck! */
 	while (1) {
 		led_update();
 		event_dispatch();
+		watchdog_check();
 	}
 
 	return 0;
============================================================
--- motor.c	500d9a9173fe983ae7c50cf5fac1d37efa96d8ce
+++ motor.c	b9920bb4788532a73cb93301787e4d0540cc3d56
@@ -4,6 +4,7 @@
 #include "timer.h"
 #include "dcm.h"
 #include "uart.h"
+#include "status.h"
 
 float integral[3] = {0.0f, 0.0f, 0.0f};
 float last[3];
@@ -69,7 +70,7 @@ void motor_pid_update(float troll, float
 	out[1] = pitch * Kp   + integral[1] * Ki   + derivative[1] * Kd;
 	out[2] = yaw   * Kp_y + integral[2] * Ki_y + derivative[2] * Kd_y;
 
-	if (armed) {
+	if (status_armed()) {
 		/* Front right */
 		motor[0] = throttle + out[0] + out[1] + out[2];
 		/* Front left */
@@ -147,7 +148,7 @@ void motor_set_throttle(float t) {
 }
 
 void motor_set_throttle(float t) {
-	if (armed)
+	if (status_armed())
 		throttle = t;
 }
 
============================================================
--- /dev/null	
+++ panic.c	d33cc5af7411880a40d87b7d56a9086705b49112
@@ -0,0 +1,58 @@
+/* panic.c */
+
+/*
+ * Something has gone horribly, horribly wrong.
+ *
+ * If we are in the air, we are going to crash. This could be nasty.
+ * Try to limit the damage by turning off all of the motors.
+ * There's not much else we can do at this point.
+ *
+ */
+
+#include "panic.h"
+#include "motor.h"
+#include "led.h"
+
+led_pattern led_pattern_panic[] = {100, 100, 100, 100, 100, 100, 100, 100,
+				   100, 100, 100, 100, 100, 100, 100, 100,
+				   100, 100, 100, 100, 100, 100, 100, 100,
+				   100, 100, 100, 100, 100, 100, 100, 3000, 0};
+
+/* Take the lower 16 bits and make a pattern of them, MSB first */
+static void panic_create_pattern(led_pattern *pattern, unsigned int reason)
+{
+	int i;
+	for (i = 0; i < 16; i++) {
+		if (reason & (1<<(15-i))) {
+			pattern[2*i] = 400;
+			pattern[2*i+1] = 100;
+		} else {
+			pattern[2*i] = 100;
+			pattern[2*i+1] = 400;
+		}
+		if ((i % 4) == 3)
+			pattern[2*i+1] += 500;
+		if (i == 15)
+			pattern[2*i+1] += 2500;
+	}
+}
+
+void panic(unsigned int reason)
+{
+	/*
+	 * We may one day be able to do something with the reason, like emit
+	 * a final deathbed confession. So we'll provide the reasons in the
+	 * caller and just ignore them for now.
+	 */
+	(void)reason;
+
+	motor_kill();
+
+	panic_create_pattern(led_pattern_panic, reason);
+
+	led_set_pattern(led_pattern_panic);
+
+	/* Wait for the inevitable plunge to the death */
+	for (;;)
+		led_update();
+}
============================================================
--- /dev/null	
+++ panic.h	4ed7a80d4d14e77a557b91a5dcc39e621f765d6e
@@ -0,0 +1,8 @@
+/* panic.h */
+
+/* OMG we're all going to die!!!! */
+
+void panic(unsigned int reason);
+
+/* Panic code goes in the low 8 bits */
+#define PANIC_WATCHDOG_TIMEOUT 0x100
============================================================
--- /dev/null	
+++ status.c	31db654aeccf16a2aa7a73255e38f2dbe05034aa
@@ -0,0 +1,91 @@
+/* status.c */
+
+#include "status.h"
+#include "led.h"
+
+static bool armed = FALSE;
+
+static unsigned int module_ready[STATUS_MODULES];
+static const unsigned int module_count[STATUS_MODULES] = STATUS_COUNT;
+
+led_pattern led_pattern_stick[] = {100, 1000, 0};
+led_pattern led_pattern_gyro_zero[]  = {100, 200, 100, 1000, 0};
+led_pattern led_pattern_gyro_rate[] = {100, 200, 100, 200, 100, 1000, 0};
+led_pattern led_pattern_attitude[] = {100, 200, 100, 200, 100, 200, 100,
+							1000, 0};
+led_pattern led_pattern_dcm_error[] = {100, 200, 100, 200, 100, 200,
+						100, 200, 100, 1000, 0};
+
+bool status_armed(void)
+{
+	return armed;
+}
+
+void status_set_ready(unsigned int module, bool ready)
+{
+	int i;
+	int all_ready;
+
+	if (module >= STATUS_MODULES)
+		return;
+
+	if (ready) {
+		if (module_ready[module] < module_count[module])
+			module_ready[module]++;
+	} else {
+		module_ready[module] = 0;
+	}
+
+	/* We can't un-arm. */
+	if (armed)
+		return;
+
+	all_ready = TRUE;
+
+	for (i = 0; i < STATUS_MODULES; i++)
+		if (module_ready[i] < module_count[i]) {
+			if (all_ready) {
+				status_set_led_pattern(i);
+				all_ready = FALSE;
+			}
+		}
+
+	if (all_ready) {
+		armed = TRUE;
+		led_set_pattern(led_pattern_active);
+	}
+}
+
+void status_set_led_pattern(unsigned int module)
+{
+	switch (module) {
+	case STATUS_MODULE_STICK:
+		led_set_pattern(led_pattern_stick);
+		break;
+	case STATUS_MODULE_GYRO_ZERO:
+		led_set_pattern(led_pattern_gyro_zero);
+		break;
+	case STATUS_MODULE_GYRO_RATE:
+		led_set_pattern(led_pattern_gyro_rate);
+		break;
+	case STATUS_MODULE_ATTITUDE:
+		led_set_pattern(led_pattern_attitude);
+		break;
+	case STATUS_MODULE_DCM_ERROR:
+		led_set_pattern(led_pattern_dcm_error);
+		break;
+	default:
+		led_set_pattern(led_pattern_unknown);
+		break;
+	}
+}
+
+void init_status(void)
+{
+	int i;
+
+	armed = FALSE;
+	for (i = 0; i < STATUS_MODULES; i++)
+		module_ready[i] = 0;
+	led_set_pattern(led_pattern_unknown);
+}
============================================================
--- /dev/null	
+++ status.h	2cf551e442b9d0a472c5be2684540045a994652b
@@ -0,0 +1,36 @@
+/* status.h */
+
+#include "types.h"
+
+bool status_armed(void);
+void status_set_ready(unsigned int module, bool ready);
+void status_set_led_pattern(unsigned int module);
+void init_status(void);
+
+
+#define STATUS_MODULE_GYRO_ZERO 0
+#define STATUS_MODULE_GYRO_RATE 1
+#define STATUS_MODULE_ATTITUDE  2
+#define STATUS_MODULE_DCM_ERROR 3
+#define STATUS_MODULE_STICK     4
+
+#define STATUS_MODULES		5
+
+#define STATUS_COUNT {				\
+		STATUS_COUNT_GYRO_ZERO,		\
+		STATUS_COUNT_GYRO_RATE,		\
+		STATUS_COUNT_ATTITUDE,		\
+		STATUS_COUNT_DCM_ERROR,		\
+		STATUS_COUNT_STICK		\
+	}
+
+/*
+ * Each condition must be valid for so many samples, typically once
+ * per 100Hz loop
+ */
+#define STATUS_COUNT_STICK     100
+#define STATUS_COUNT_GYRO_ZERO 1
+#define STATUS_COUNT_GYRO_RATE 100
+#define STATUS_COUNT_ATTITUDE  100
+#define STATUS_COUNT_DCM_ERROR 100
+
============================================================
--- stick.c	b0c0cbffb3863a3992abc1ac1148fc90f019309b
+++ stick.c	ad24bc7d9d8c95c497a1b5bd7a13d8e1619ca539
@@ -13,6 +13,8 @@
 #include "trig.h"
 #include "motor.h"
 #include "wmp.h"
+#include "status.h"
+#include "watchdog.h"
 
 #define TWO_PI 6.28318531f
 #define PI 3.14159265f
@@ -50,8 +52,6 @@ unsigned int stick_counter;
 
 unsigned int stick_counter;
 
-bool armed = FALSE;
-
 void stick_update(float x, float y, float z)
 {
 	float tz = delta_t * z;
@@ -85,18 +85,17 @@ void stick_input(void) {
 		throttle = timer_input(2);
 		z = timer_input(3);
 
-		if (!armed) {
+		if (!status_armed()) {
 			if ((throttle < MIN_THR) &&
 			    (x > (CENTRE_X - CENTRE_ZONE)) &&
 			    (x < (CENTRE_X + CENTRE_ZONE)) &&
 			    (y > (CENTRE_Y - CENTRE_ZONE)) &&
 			    (y < (CENTRE_Y + CENTRE_ZONE)) &&
 			    (z > (CENTRE_Z - CENTRE_ZONE)) &&
-			    (z < (CENTRE_Z + CENTRE_ZONE)) &&
-			    (wmp_zero == FALSE)) {
-				putstr("ARMED!!!\r\n");
-				armed = TRUE;
-			}
+			    (z < (CENTRE_Z + CENTRE_ZONE)))
+				    status_set_ready(STATUS_MODULE_STICK, TRUE);
+			else
+				    status_set_ready(STATUS_MODULE_STICK,FALSE);
 
 		}
 
@@ -114,10 +113,13 @@ void stick_input(void) {
 		y = 0.0f;
 		z = 0.0f;
 		throttle = 0.0f;
+		status_set_ready(STATUS_MODULE_STICK,FALSE);
 	}
 
 	motor_set_throttle(throttle);
 
+	watchdog_kick(WATCHDOG_STICK);
+
 	/* So the controls are back to front. Let's fix that. */
 	x = -x;
 	y = -y;
============================================================
--- /dev/null	
+++ watchdog.c	8398952deb37d8242fc858143c5e201321343c27
@@ -0,0 +1,50 @@
+/* watchdog.c */
+
+#include "watchdog.h"
+#include "panic.h"
+#include "timer.h"
+
+/* There are two watchdogs to worry about. The hardware one, and watchdogs
+   to make sure that critical parts of the software are running.
+ */
+
+/*
+ * This is about 10 times round the main loop. If we haven't had a kick by
+ * now, something's gone horribly wrong.
+ */
+
+#define WATCHDOG_TIMEOUT (100 * TIMER_MS)
+
+static unsigned int watchdog_last_seen[WATCHDOG_MODULES];
+
+void watchdog_kick(unsigned int module)
+{
+	if (module > WATCHDOG_MODULES)
+		return;
+	watchdog_last_seen[module] = timer_read();
+}
+
+void watchdog_check(void)
+{
+	unsigned int time = timer_read();
+	int i;
+
+	/* XXX not yet */
+/*	return; */
+	for (i = 0; i < WATCHDOG_MODULES; i++) {
+		if ((signed int)(watchdog_last_seen[i] + WATCHDOG_TIMEOUT
+					- time) < 0) {
+			panic(PANIC_WATCHDOG_TIMEOUT + i);
+		}
+	}
+}
+
+void init_watchdog(void)
+{
+	unsigned int time = timer_read();
+	int i;
+
+	for (i = 0; i < WATCHDOG_MODULES; i++) {
+		watchdog_last_seen[i] = time;
+	}
+}
============================================================
--- /dev/null	
+++ watchdog.h	e38eb63c762343f8c2221d67501b11e9ac92ebb6
@@ -0,0 +1,11 @@
+/* watchdog.h */
+
+void watchdog_kick(unsigned int module);
+void watchdog_check(void);
+void init_watchdog(void);
+
+#define WATCHDOG_STICK 0
+#define WATCHDOG_GYRO  1
+#define WATCHDOG_ACCEL 2
+
+#define WATCHDOG_MODULES 3
============================================================
--- wmp.c	022e72f07353e280c050ae668cb3c02b318c72c4
+++ wmp.c	e6627850f4ae970c9aee0dbb4c6f1933f0cb31c7
@@ -6,6 +6,9 @@
 #include "dcm.h"
 #include "fisqrt.h"
 #include "stick.h"
+#include "watchdog.h"
+#include "status.h"
+#include "abs.h"
 
 #define WMP_ZERO_COUNT 100
 
@@ -134,6 +137,9 @@ unsigned int wmp_discard;
 #define FAST_PITCH_STEP (4 / DEG_TO_RAD)
 #define FAST_ROLL_STEP  (4 / DEG_TO_RAD)
 
+/* The gyro has to stay within this limit in each axis in order to arm */
+#define GYRO_RATE_THRESHOLD (0.01 / DEG_TO_RAD)
+
 bool wmp_sample(void)
 {
 	if (!i2c_start_transaction(&wmp_sample_transaction))
@@ -202,6 +208,16 @@ void wmp_process_gyro_sample(void)
 
 		dcm_update(roll, pitch, yaw);
 
+		if (!status_armed()) {
+			if (  (abs(roll)  < GYRO_RATE_THRESHOLD) &&
+			      (abs(pitch) < GYRO_RATE_THRESHOLD) &&
+			      (abs(yaw)   < GYRO_RATE_THRESHOLD)  ) {
+			    status_set_ready(STATUS_MODULE_GYRO_RATE, TRUE);
+			} else {
+			    status_set_ready(STATUS_MODULE_GYRO_RATE, FALSE);
+			}
+		}
+
 		wmp_generation++;
 
 #if SEND_DCM
@@ -225,9 +241,11 @@ void wmp_process_gyro_sample(void)
 				wmp_pitch_zero /= WMP_ZERO_COUNT;
 				wmp_roll_zero /= WMP_ZERO_COUNT;
 				putstr("Zero finished\r\n");
+				status_set_ready(STATUS_MODULE_GYRO_ZERO, TRUE);
 			}
 		}
 	}
+	watchdog_kick(WATCHDOG_GYRO);
 }
 
 void wmp_process_accel_sample(void)
@@ -275,6 +293,7 @@ void wmp_process_accel_sample(void)
 	 * on that so we'll just fudge it here.
 	 */
 	dcm_drift_correction(x, -y, -z);
+	watchdog_kick(WATCHDOG_ACCEL);
 	stick_input();
 }