The unified diff between revisions [23a3e9a5..] and [81e4dce2..] is displayed below. It can also be downloaded as a raw diff.

#
# old_revision [23a3e9a50b4034343e3bd217d2c225dcaec064dd]
# new_revision [81e4dce274e79dd9187ed4bd182e1d6fc0fdfb37]
#
# add_file "motor.c"
#  content [500d9a9173fe983ae7c50cf5fac1d37efa96d8ce]
# 
# add_file "motor.h"
#  content [2f9e49830e99e4252d190c55d3ca42dbd721cb1e]
# 
# add_file "stick.c"
#  content [b0c0cbffb3863a3992abc1ac1148fc90f019309b]
# 
# add_file "stick.h"
#  content [92351855dae47ede3f1743eee7bec18d064b3db1]
# 
# add_file "trig.c"
#  content [342aef33d7dce48c4ac212bc14db8b4e89d91e83]
# 
# add_file "trig.h"
#  content [f86596b4fa5504a309a82531eda202d0f52bbfdb]
# 
# patch "Makefile"
#  from [d61625cad3fe90d5ffd933b03b2f322afc177d10]
#    to [5665964a6034f95200be1f471778af03abfca1b5]
# 
# patch "dcm.c"
#  from [35a44d5b49a5c9bf56678ae7af0b611fde405621]
#    to [34486e2e7948509c6f2648d365feb84c22ab5b14]
# 
# patch "dcm.h"
#  from [91aed9ca7b3b59898347ab5f47e7aa364a483928]
#    to [801a64efefec7f36a5a97e191550b425a7f4def8]
# 
# patch "interrupt.h"
#  from [3735115d09df71f1da6b5ed670f9df7c4aafb82c]
#    to [04c04661f9d61779f82793ce9bc45c761f1ce63b]
# 
# patch "main.c"
#  from [e1a823b4962f3e8dc43b519a1f57745854ae6689]
#    to [b366dce1bedfd103e47161ada956b8569816f2f4]
# 
# patch "timer.c"
#  from [345db97155c057df90bcbbdc2386ab54a8a12be7]
#    to [88939ba8fd01747c78fddeb6d82012af7d61da7a]
# 
# patch "timer.h"
#  from [c2e75d36a6314f29f490f12d9d98f4bb50b843fc]
#    to [a1df137b32f7a24a94d0b016cb360bc10529bfa8]
# 
# patch "types.h"
#  from [00b92d607bdb71185e8326714de30eb16d3329ff]
#    to [36b3274ddc55e8a7879472b3977392bcb3da8521]
# 
# patch "uart.c"
#  from [37a2e0459886f7f9c4e4a5361e21d50902dbe3f7]
#    to [7a38c486dc1280695e1e62c6d3a76d6c9f849f67]
# 
# patch "uart.h"
#  from [4acdb1f160b933d1653b3ac765f110cd71332112]
#    to [86972bb7765797e0b5e660710beed2a9bbd9ec13]
# 
# patch "wmp.c"
#  from [1ab4017652548447277e83cf1697442cb7c6949b]
#    to [022e72f07353e280c050ae668cb3c02b318c72c4]
# 
# patch "wmp.h"
#  from [12983abdeae8f48c789d10feb223483cfd6e31eb]
#    to [6e0068d0e91c6fdc5f17e8569a6d05895be6cb65]
#
============================================================
--- Makefile	d61625cad3fe90d5ffd933b03b2f322afc177d10
+++ Makefile	5665964a6034f95200be1f471778af03abfca1b5
@@ -4,14 +4,23 @@ 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
+CSRCS+=fisqrt.c stick.c trig.c motor.c
 
+#PROJOPTS=-DUSE_UART -DSEND_DCM
+
 COPTIM?=-O1
-CFLAGS=-march=armv4t -msoft-float $(COPTIM) -Wall -Werror -Wextra
+CFLAGS=-march=armv4t -msoft-float $(COPTIM) -Wall -Werror -Wextra $(PROJOPTS)
 
 LDSCRIPT=lpc2103_flash.ld
+
+# To build with the Clang Static Analyzer, use
+#     scan-build --use-cc=arm-elf-gcc make
+# And uncomment the following line:
 CC=arm-elf-gcc
+
 OBJCOPY=arm-elf-objcopy
+LINT=splint
+LINTFLAGS=-booltype bool -nolib +charint
 
 CLEANOBJS=$(OBJS) $(NAME).hex $(NAME).elf $(NAME).bin $(NAME).map .depend
 
@@ -45,4 +54,7 @@ depend:
 depend:
 	$(CC) -MM $(CFLAGS) -nostdlib -nostartfiles $(CSRCS) >.depend
 
+lint:
+	$(LINT) $(LINTFLAGS) $(CSRCS)
+
 .sinclude ".depend"
============================================================
--- dcm.c	35a44d5b49a5c9bf56678ae7af0b611fde405621
+++ dcm.c	34486e2e7948509c6f2648d365feb84c22ab5b14
@@ -8,6 +8,7 @@
 #include "matrix.h"
 #include "dcm.h"
 #include "uart.h"
+#include "motor.h"
 
 #define GRAVITY 9.80665f
 
@@ -27,13 +28,15 @@ float omega_i[3] = {0.0, 0.0, 0.0};
 float omega_p[3] = {0.0, 0.0, 0.0};
 float omega_i[3] = {0.0, 0.0, 0.0};
 
+float omega_x, omega_y, omega_z;
+
 float delta_t = 0.01;
 
 void dcm_update(float x, float y, float z)
 {
-	float omega_x = x + omega_i[0] + omega_p[0];
-	float omega_y = y + omega_i[1] + omega_p[1];
-	float omega_z = z + omega_i[2] + omega_p[2];
+	omega_x = x + omega_i[0] + omega_p[0];
+	omega_y = y + omega_i[1] + omega_p[1];
+	omega_z = z + omega_i[2] + omega_p[2];
 
 	float tx = delta_t * omega_x;
 	float ty = delta_t * omega_y;
@@ -182,9 +185,12 @@ void dcm_drift_correction(float x, float
 		omega_i[i] += error[i] * (KI_ROLLPITCH * weight);
 	}
 
+#if 0
 	putstr("w: ");
 	putint_s((int)(weight * 100000.0f));
 	putstr("\r\n");
+#endif
+
 #if 0
 	putstr("p: ");
 	putint_s((int)(omega_p[0] * 100000.0f));
@@ -202,6 +208,22 @@ void dcm_drift_correction(float x, float
 #endif
 }
 
+void dcm_attitude_error(float roll, float pitch, float yaw)
+{
+	/* dcm[6] = sine of pitch */
+	/* dcm[7] = sine of roll */
+
+	/* pitch error = pitch - dcm[6] */
+	/* roll error = roll - dcm[7] */
+
+	/* That was the theory. In practice, there appears to be some
+	   confusion over axes. Pitch and roll seem.. reversed. */
+
+	/* TODO: What if we are upside down? */
+
+	motor_pid_update(roll, dcm[6], pitch, -dcm[7], yaw, -omega_z);
+}
+
 void dcm_dump(void)
 {
 	putstr("dcm: ");
============================================================
--- dcm.h	91aed9ca7b3b59898347ab5f47e7aa364a483928
+++ dcm.h	801a64efefec7f36a5a97e191550b425a7f4def8
@@ -2,6 +2,8 @@
 
 #include "types.h"
 
+extern float delta_t;
+
 void dcm_update(float omega_x, float omega_y, float omega_z);
 void dcm_normalise(void);
 bool dcm_renormalise(float *v);
@@ -9,4 +11,4 @@ void dcm_drift_correction(float x, float
 void dcm_send_packet(void);
 void dcm_setvector(float x, float y, float z);
 void dcm_drift_correction(float x, float y, float z);
-
+void dcm_attitude_error(float x, float y, float z);
============================================================
--- interrupt.h	3735115d09df71f1da6b5ed670f9df7c4aafb82c
+++ interrupt.h	04c04661f9d61779f82793ce9bc45c761f1ce63b
@@ -35,6 +35,7 @@
 #define I_PRIORITY_I2C0		0
 #define I_PRIORITY_UART0	1
 #define I_PRIORITY_TIMER0	2
+#define I_PRIORITY_TIMER1	3
 
 #define interrupt_clear() do { VICVectAddr = 0xff; } while (0)
 
============================================================
--- main.c	e1a823b4962f3e8dc43b519a1f57745854ae6689
+++ main.c	b366dce1bedfd103e47161ada956b8569816f2f4
@@ -1,3 +1,4 @@
+/* main.c */
 
 #include "wmp.h"
 #include "i2c.h"
@@ -5,8 +6,10 @@
 #include "uart.h"
 #include "interrupt.h"
 #include "event.h"
+#include "stick.h"
 
-#define PINSEL0 (*((volatile unsigned char *) 0xE002C000))
+#define PINSEL0 (*((volatile unsigned int *) 0xE002C000))
+#define PINSEL1 (*((volatile unsigned int *) 0xE002C004))
 #define FP0XDIR (*((volatile unsigned int *) 0x3FFFC000))
 #define FP0XVAL (*((volatile unsigned int *) 0x3FFFC014))
 
@@ -15,18 +18,26 @@ void init_pins(void)
 
 void init_pins(void)
 {
-	PINSEL0 = 0x00000055; /* P0.0 and P0.1 assigned to UART */
+	PINSEL0 = 0x00a88055; /* P0.0 and P0.1 assigned to UART */
 			      /* P0.2 and P0.3 assigned to I2C  */
+	                      /* P0.7 and P0.9 assigned to MAT2.[02] */
+	                      /* P0.10 and P0.11 assigned to CAP1.[01] */
+	PINSEL1 = 0x20000828; /* P0.21 and P0.30 assigned to MAT3.[03] */
+	                      /* P0.17 and P0.18 assigned to CAP1.[23] */
 	SCS = 1;
 	FP0XDIR = 0x04000000; /* P0.26 is an output */
 	FP0XVAL = 0x0;
 }
 
+#ifdef USE_UART
 void reply(char *str)
 {
 	putstr(str);
 	putstr("\r\n");
 }
+#else
+#define reply(x) ((void)0)
+#endif
 
 unsigned int count = 0;
 
@@ -167,6 +178,8 @@ int main(void) {
 void menu_handler(void);
 
 int main(void) {
+	armed = FALSE;
+
 	init_interrupt();
 	init_uart();
 	init_i2c();
@@ -183,6 +196,21 @@ int main(void) {
 
 	putstr("prompt> ");
 
+	FP0XVAL &= ~0x04000000;
+	timer_delay_ms(1000);
+	FP0XVAL |= 0x04000000;
+	timer_delay_ms(1000);
+	FP0XVAL &= ~0x04000000;
+	if (!wmp_init())
+		putstr("WMP initialisation failed\r\n");
+
+	/* Flight is potentially live after this. */
+	timer_set_period(5*TIMER_MS);
+	wmp_start_zero();
+
+	FP0XVAL |= 0x04000000;
+
+	/* Good luck! */
 	while (1) {
 		FP0XVAL ^= 0x04000000;
 		event_dispatch();
@@ -197,6 +225,9 @@ void menu_handler(void)
 	char c;
 
 	while (getch(&c)) {
+#if 1
+		continue; /* Yes, let's just ignore UART input now. */
+#endif
 		if (c == 0x0a)
 			continue;
 		putch(c);
@@ -283,6 +314,58 @@ void menu_handler(void)
 			reply("done");
 			wmp_start_zero();
 			break;
+		case 'W':
+			for (i = 0; i < 4; i++) {
+				putstr("Width ");
+				putint(i);
+				putstr(": ");
+				putint(timer_input(i));
+				if (!timer_valid(i))
+					putstr(" (invalid)");
+				putstr("\r\n");
+			}
+			if (!timer_allvalid()) {
+				putstr("ALL INVALID!\r\n");
+			}
+			break;
+		case '0' & 0xdf:
+			timer_set_pwm_value(0, 0);
+			timer_set_pwm_value(1, 0);
+			timer_set_pwm_value(2, 0);
+			timer_set_pwm_value(3, 0);
+			break;
+#if 0
+		case '1' & 0xdf:
+			timer_set_pwm_value(0, PWM_MAX/2);
+			break;
+		case '2' & 0xdf:
+			timer_set_pwm_value(1, PWM_MAX/2);
+			break;
+		case '3' & 0xdf:
+			timer_set_pwm_value(2, PWM_MAX/2);
+			break;
+		case '4' & 0xdf:
+			timer_set_pwm_value(3, PWM_MAX/2);
+			break;
+		case '5' & 0xdf:
+			timer_set_pwm_value(0, PWM_MAX);
+			break;
+		case '6' & 0xdf:
+			timer_set_pwm_value(1, PWM_MAX);
+			break;
+		case '7' & 0xdf:
+			timer_set_pwm_value(2, PWM_MAX);
+			break;
+		case '8' & 0xdf:
+			timer_set_pwm_value(3, PWM_MAX);
+			break;
+#endif
+		case '9' & 0xdf:
+			timer_set_pwm_invalid(0);
+			timer_set_pwm_invalid(1);
+			timer_set_pwm_invalid(2);
+			timer_set_pwm_invalid(3);
+			break;
 		default:
 			reply("Unrecognised command.");
 			break;
============================================================
--- /dev/null	
+++ motor.c	500d9a9173fe983ae7c50cf5fac1d37efa96d8ce
@@ -0,0 +1,153 @@
+/* motor.c */
+
+#include "stick.h"
+#include "timer.h"
+#include "dcm.h"
+#include "uart.h"
+
+float integral[3] = {0.0f, 0.0f, 0.0f};
+float last[3];
+
+float throttle = 0.0f;
+
+#define Kp 0.2
+#define Ki 0.04
+#define Kd 0.08
+#define Ka 0.0
+
+#define Kp_y 0.2
+#define Ki_y 0.00
+#define Kd_y 0.00
+#define Ka_y 0.0
+
+
+/*
+ * Perform a PID loop iteration.
+ * roll and pitch are absolute values
+ * yaw is, currently, a rate.
+ */
+void motor_pid_update(float troll, float mroll,
+		      float tpitch, float mpitch,
+		      float tyaw, float myaw)
+{
+	float derivative[3];
+	float out[3];
+	float motor[3];
+	float roll, pitch, yaw;
+	float error, max_error;
+	float min_motor;
+	int i;
+
+	roll  = troll  - mroll;
+	pitch = tpitch - mpitch;
+	yaw   = tyaw   - myaw;
+
+#if 0
+        if ((stick_counter % 100) == 0) {
+		putstr("{");
+		putint_s((int)(tyaw * 10000));
+		putstr(", ");
+		putint_s((int)(myaw * 10000));
+		putstr("}\r\n");
+	}
+#endif
+
+	integral[0] += roll * delta_t;
+	integral[1] += pitch * delta_t;
+	integral[2] += yaw * delta_t;
+
+	/* The measurements are the opposite sign to the error */
+	derivative[0] = (-mroll  - last[0]) / delta_t;
+	derivative[1] = (-mpitch - last[1]) / delta_t;
+	derivative[2] = (-myaw   - last[2]) / delta_t;
+
+	last[0] = -mroll;
+	last[1] = -mpitch;
+	last[2] = -myaw;
+
+	out[0] = roll  * Kp   + integral[0] * Ki   + derivative[0] * Kd;
+	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) {
+		/* Front right */
+		motor[0] = throttle + out[0] + out[1] + out[2];
+		/* Front left */
+		motor[1] = throttle - out[0] + out[1] - out[2];
+		/* Rear left */
+		motor[2] = throttle - out[0] - out[1] + out[2];
+		/* Rear right */
+		motor[3] = throttle + out[0] - out[1] - out[2];
+	} else {
+		motor[0] = 0.0;
+		motor[1] = 0.0;
+		motor[2] = 0.0;
+		motor[3] = 0.0;
+	}
+
+	max_error = 0.0;
+	min_motor = 1.0;
+	for (i = 0; i < 3; i++) {
+		if (motor[i] < 0.0)
+			motor[i] = 0.0;
+		if (motor[i] > 1.0f) {
+			error = motor[i] - 1.0f;
+			if (error > max_error)
+				max_error = error;
+		}
+		if (motor[i] < min_motor)
+			min_motor = motor[i];
+	}
+
+	if (max_error > 0.0) {
+		for (i = 0; i < 3; i++) {
+			motor[i] -= max_error;
+			if (motor[i] < 0.0)
+				motor[i] = 0.0;
+		}
+	}
+
+	if (throttle <= 0.0) {
+		motor[0] = 0.0;
+		motor[1] = 0.0;
+		motor[2] = 0.0;
+		motor[3] = 0.0;
+		integral[0] = 0.0;
+		integral[1] = 0.0;
+		integral[2] = 0.0;
+	}
+	if (max_error < min_motor) {
+		float new_throttle2, new_out[3];
+		new_throttle2 = (motor[0] + motor[1] + motor[2] + motor[3])/2.0;
+		new_out[0] = (motor[0] + motor[3] - new_throttle2)/2.0;
+		new_out[1] = (motor[0] + motor[1] - new_throttle2)/2.0;
+		new_out[2] = (motor[0] + motor[2] - new_throttle2)/2.0;
+
+		/* Anti-windup */
+		for (i = 0; i < 3; i++) {
+			if (new_out[i] > 1.0)
+				integral[i] -= (new_out[i]-1.0) * Ka;
+			if (new_out[i] < 0.0)
+				integral[i] -= (new_out[i]) * Ka;
+		}
+	}
+
+	timer_set_pwm_value(0, (int)(motor[0] * PWM_MAX));
+	timer_set_pwm_value(1, (int)(motor[1] * PWM_MAX));
+	timer_set_pwm_value(2, (int)(motor[2] * PWM_MAX));
+	timer_set_pwm_value(3, (int)(motor[3] * PWM_MAX));
+}
+
+void motor_kill(void) {
+	throttle = 0.0;
+	timer_set_pwm_value(0, 0);
+	timer_set_pwm_value(1, 0);
+	timer_set_pwm_value(2, 0);
+	timer_set_pwm_value(3, 0);
+}
+
+void motor_set_throttle(float t) {
+	if (armed)
+		throttle = t;
+}
+
============================================================
--- /dev/null	
+++ motor.h	2f9e49830e99e4252d190c55d3ca42dbd721cb1e
@@ -0,0 +1,9 @@
+/* motor.h */
+
+extern float temp_kp;
+
+void motor_pid_update(float troll, float mroll,
+		      float tpitch, float mpitch,
+		      float tyaw, float myaw);
+void motor_kill(void);
+void motor_set_throttle(float t);
============================================================
--- /dev/null	
+++ stick.c	b0c0cbffb3863a3992abc1ac1148fc90f019309b
@@ -0,0 +1,147 @@
+/* stick.c */
+
+#ifdef WE_HAVE_SQRT
+#include <math.h>
+#else
+#include "fisqrt.h"
+#endif
+#include "matrix.h"
+#include "stick.h"
+#include "dcm.h"
+#include "uart.h"
+#include "timer.h"
+#include "trig.h"
+#include "motor.h"
+#include "wmp.h"
+
+#define TWO_PI 6.28318531f
+#define PI 3.14159265f
+
+#define MIN_X 15830
+#define MAX_X 28300
+#define CENTRE_X 22100
+
+#define MIN_Y 18530
+#define MAX_Y 28200
+#define CENTRE_Y 22100
+
+#define MIN_Z 15800
+#define MAX_Z 28304
+#define CENTRE_Z 22100
+
+#define MIN_THR 16500
+#define MAX_THR 28275
+
+#define MIN_REAL_THR 15830
+
+#define CENTRE_ZONE 100
+
+/* Full scale is a roll/pitch angle of 30 degrees from the vertical */
+#define SCALE_X (TWO_PI*30.0/360.0 / (MAX_X-CENTRE_X))
+#define SCALE_Y (TWO_PI*30.0/360.0 / (MAX_Y-CENTRE_Y))
+
+/* Full scale is a complete rotation in one second */
+#define SCALE_Z (TWO_PI / (MAX_Z-CENTRE_Z))
+
+/* 0 is min throttle, 1 is max throttle */
+#define SCALE_THROTTLE (1.0f/(MAX_THR - MIN_THR))
+
+float stick_yaw = 0.0f;
+
+unsigned int stick_counter;
+
+bool armed = FALSE;
+
+void stick_update(float x, float y, float z)
+{
+	float tz = delta_t * z;
+
+	stick_yaw += tz;
+
+	if (stick_yaw <= -PI)
+	    stick_yaw += TWO_PI;
+
+	if (stick_yaw > PI)
+	    stick_yaw -= TWO_PI;
+
+#if 0
+	z = stick_yaw;
+#endif
+
+	x = sine(x);
+	y = sine(y);
+#if 0
+	z = 1.0/fisqrt(1-x*x-y*y);
+#endif
+
+	dcm_attitude_error(x, y, z);
+}
+
+void stick_input(void) {
+	float x, y, z, throttle;
+	if (timer_allvalid()) {
+		x = timer_input(0);
+		y = timer_input(1);
+		throttle = timer_input(2);
+		z = timer_input(3);
+
+		if (!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;
+			}
+
+		}
+
+		x -= CENTRE_X;
+		y -= CENTRE_Y;
+		z -= CENTRE_Z;
+		x = x * SCALE_X;
+		y = y * SCALE_Y;
+		z = z * SCALE_Z;
+		throttle = (throttle - MIN_THR) * SCALE_THROTTLE;
+		if (throttle < 0.0)
+			throttle = 0.0;
+	} else {
+		x = 0.0f;
+		y = 0.0f;
+		z = 0.0f;
+		throttle = 0.0f;
+	}
+
+	motor_set_throttle(throttle);
+
+	/* So the controls are back to front. Let's fix that. */
+	x = -x;
+	y = -y;
+	z = -z;
+
+#if 0
+	if ((stick_counter % 100) == 0) {
+		putstr("[");
+		putint_s((int)(z * 10000));
+		putstr("] ");
+	}
+#endif
+
+#if 1
+	stick_update(x, y, z);
+#else
+	if ((stick_counter % 100) == 0)
+		stick_update(x, y, z);
+#endif
+
+/*
+	if ((stick_counter & 3) == 0)
+		stick_send_packet();
+*/
+	stick_counter++;
+}
+
============================================================
--- /dev/null	
+++ stick.h	92351855dae47ede3f1743eee7bec18d064b3db1
@@ -0,0 +1,10 @@
+/* stick.h */
+
+#include "types.h"
+
+extern bool armed;
+
+extern unsigned int stick_counter;
+
+void stick_input(void);
+void stick_update(float x, float y, float omega_z);
============================================================
--- timer.c	345db97155c057df90bcbbdc2386ab54a8a12be7
+++ timer.c	88939ba8fd01747c78fddeb6d82012af7d61da7a
@@ -3,8 +3,12 @@
 #include "uart.h"
 #include "event.h"
 
+#define FP0XVAL (*((volatile unsigned int *) 0x3FFFC014))
+
 #define TIMER0BASE  0xE0004000
 #define TIMER1BASE  0xE0008000
+#define TIMER2BASE  0xE0070000
+#define TIMER3BASE  0xE0074000
 
 #define IR    0x00
 #define TCR   0x04
@@ -28,6 +32,15 @@
 #define TREG(x) (((volatile unsigned char *)TIMER0BASE)[x])
 #define TWREG(x) (((volatile unsigned int *)TIMER0BASE)[(x)/sizeof(unsigned int)])
 
+#define T1REG(x) (((volatile unsigned char *)TIMER1BASE)[x])
+#define T1WREG(x) (((volatile unsigned int *)TIMER1BASE)[(x)/sizeof(unsigned int)])
+
+#define T2REG(x) (((volatile unsigned char *)TIMER2BASE)[x])
+#define T2WREG(x) (((volatile unsigned int *)TIMER2BASE)[(x)/sizeof(unsigned int)])
+
+#define T3REG(x) (((volatile unsigned char *)TIMER3BASE)[x])
+#define T3WREG(x) (((volatile unsigned int *)TIMER3BASE)[(x)/sizeof(unsigned int)])
+
 #define TCR_ENABLE (1<<0)
 #define TCR_RESET  (1<<1)
 
@@ -44,7 +57,14 @@
 #define MR3R (1<<10)
 #define MR3S (1<<11)
 
+
+volatile unsigned int timer1_rising[4];
+volatile unsigned int timer1_width[4];
+
+unsigned int timer_map[] = {0, 3, 2, 1};
+
 void __attribute__((interrupt("IRQ"))) timer_interrupt_handler(void);
+void __attribute__((interrupt("IRQ"))) timer1_interrupt_handler(void);
 
 void timer_event_handler(void);
 
@@ -58,6 +78,52 @@ void init_timer(void)
 	TWREG(PC) = 0;
 
 	TREG(TCR) = TCR_ENABLE;
+
+	interrupt_register(TIMER1, timer1_interrupt_handler);
+
+	T1REG(TCR) = TCR_ENABLE | TCR_RESET;
+
+	T1REG(CTCR) = 0; /* Use PCLK */
+	T1WREG(TC) = 0;
+	T1WREG(PR) = TIMER_PRESCALE ;
+	T1WREG(PC) = 0;
+
+	T1WREG(CCR) = 0x00000fff;
+
+	T1REG(TCR) = TCR_ENABLE;
+
+	T2REG(TCR) = TCR_ENABLE | TCR_RESET;
+	T2REG(CTCR) = 0; /* Use PCLK */
+	T2WREG(PR) = 0; // Prescaling
+	T2WREG(PC) = 0; // Reset the prescale counter
+	T2WREG(TC) = 0; // Reset the counter
+
+	T2WREG(MCR) = 0x0400; // Reset on MR3 match
+	T2WREG(PWM) = 0x0000000d; // Enable PWMs
+
+	T2WREG(MR3) = PWM_PERIOD; // Period duration
+
+	/* This is chosen to be an invalid output. */
+	T2WREG(MR1) = 1; // Pulse width
+	T2WREG(MR0) = 1; // Pulse width
+
+	T3REG(TCR) = TCR_ENABLE | TCR_RESET;
+	T3REG(CTCR) = 0; /* Use PCLK */
+	T3WREG(PR) = 0; // Prescaling
+	T3WREG(PC) = 0; // Reset the prescale counter
+	T3WREG(TC) = 0; // Reset the counter
+
+	T3WREG(MCR) = 0x0010; // Reset on MR1 match
+	T3WREG(PWM) = 0x0000000b; // Enable PWMs
+
+	T3WREG(MR1) = PWM_PERIOD; // Period duration
+
+	/* This is chosen to be an invalid output. */
+	T3WREG(MR3) = 1; // Pulse width
+	T3WREG(MR0) = 1; // Pulse width
+
+	T2REG(TCR) = TCR_ENABLE;
+	T3REG(TCR) = TCR_ENABLE;
 }
 
 unsigned int timer_read(void)
@@ -92,3 +158,109 @@ void __attribute__((interrupt("IRQ"))) t
 
 	interrupt_clear();
 }
+
+void __attribute__((interrupt("IRQ"))) timer1_interrupt_handler(void)
+{
+	unsigned int ir;
+	unsigned int gpio;
+	ir = T1REG(IR);
+	T1REG(IR) = ir;
+
+	gpio = FP0XVAL;
+
+	if (ir & (1<<4)) {
+		/* Capture channel 0 */
+		if (gpio & (1<<10)) {
+			timer1_rising[0] = T1WREG(CR0);
+		} else {
+			timer1_width[0] = T1WREG(CR0) - timer1_rising[0];
+		}
+	}
+	if (ir & (1<<5)) {
+		/* Capture channel 1 */
+		if (gpio & (1<<11)) {
+			timer1_rising[1] = T1WREG(CR1);
+		} else {
+			timer1_width[1] = T1WREG(CR1) - timer1_rising[1];
+		}
+	}
+	if (ir & (1<<6)) {
+		/* Capture channel 2 */
+		if (gpio & (1<<17)) {
+			timer1_rising[2] = T1WREG(CR2);
+		} else {
+			timer1_width[2] = T1WREG(CR2) - timer1_rising[2];
+		}
+	}
+	if (ir & (1<<7)) {
+		/* Capture channel 3 */
+		if (gpio & (1<<18)) {
+			timer1_rising[3] = T1WREG(CR3);
+		} else {
+			timer1_width[3] = T1WREG(CR3) - timer1_rising[3];
+		}
+	}
+
+	interrupt_clear();
+}
+
+bool timer_valid(int channel) {
+	channel = TIMER_CH(channel);
+	/* Be careful here to ensure that this can't be in the past */
+	unsigned int chtime = timer1_rising[channel];	/* Atomic */
+	unsigned int time = T1WREG(TC);			/* Atomic */
+	return (time - chtime) < TIMER_INPUT_TIMEOUT;
+}
+
+bool timer_allvalid(void) {
+	unsigned int time;
+	unsigned int chtime[4];
+	int i;
+	/* Be careful here to ensure that this can't be in the past */
+	for (i = 0; i < 4; i++)
+		chtime[i] = timer1_rising[i];
+	time = T1WREG(TC);
+	for (i = 0; i < 4; i++)
+		if ((time - chtime[i]) >= TIMER_INPUT_TIMEOUT)
+			return FALSE;
+	return TRUE;
+}
+
+void timer_set_pwm_value(int channel, int value)
+{
+	value = PWM_PERIOD - (PWM_MAX + value);
+	switch (channel) {
+	case 0:
+		T2WREG(MR2) = value;
+		break;
+	case 1:
+		T2WREG(MR0) = value;
+		break;
+	case 2:
+		T3WREG(MR3) = value;
+		break;
+	case 3:
+		T3WREG(MR0) = value;
+		break;
+	}
+}
+
+void timer_set_pwm_invalid(int channel)
+{
+	int value = 1;
+	switch (channel) {
+	case 0:
+		T2WREG(MR2) = value;
+		break;
+	case 1:
+		T2WREG(MR0) = value;
+		break;
+	case 2:
+		T3WREG(MR3) = value;
+		break;
+	case 3:
+		T3WREG(MR0) = value;
+		break;
+	}
+}
+
============================================================
--- timer.h	c2e75d36a6314f29f490f12d9d98f4bb50b843fc
+++ timer.h	a1df137b32f7a24a94d0b016cb360bc10529bfa8
@@ -1,6 +1,8 @@
 #ifndef __TIMER_H
 #define __TIMER_H
 
+#include "types.h"
+
 #define TIMER_PCLK 14745600
 #define TIMER_PRESCALE 0
 
@@ -8,12 +10,30 @@
 #define TIMER_MS (TIMER_SECOND/1000)
 #define TIMER_US (TIMER_SECOND/1000000)
 
+#define PWM_MAX 14745
+#if 0
+#define PWM_PERIOD 58980
+#endif
+#define PWM_PERIOD ((4*PWM_MAX)+1)
+
+#define TIMER_INPUT_TIMEOUT (TIMER_PCLK/10)
+
+#define TIMER_CH(x) (timer_map[x])
+
+extern volatile unsigned int timer1_width[];
+extern unsigned int timer_map[];
+
 void init_timer(void);
 unsigned int timer_read(void);
 void timer_delay_clocks(unsigned int clocks);
 void timer_set_period(unsigned int period);
+void timer_set_pwm_value(int channel, int value);
+void timer_set_pwm_invalid(int channel);
+bool timer_valid(int channel);
+bool timer_allvalid(void);
 
 #define timer_delay_us(x) timer_delay_clocks((x)*TIMER_US)
 #define timer_delay_ms(x) timer_delay_clocks((x)*TIMER_MS)
 
+#define timer_input(ch) (timer1_width[TIMER_CH(ch)])
 #endif /* __TIMER_H */
============================================================
--- /dev/null	
+++ trig.c	342aef33d7dce48c4ac212bc14db8b4e89d91e83
@@ -0,0 +1,44 @@
+/* trig.c */
+
+/* Cosine implementation from:
+ * http://www.ganssle.com/articles/atrig.htm
+ */
+
+double cosine(double x)
+{
+double p0,p1,p2,p3,p4,p5,y,t,absx,frac,pi2;
+int quad;
+p0= 0.999999999781;
+p1=-0.499999993585;
+p2= 0.041666636258;
+p3=-0.0013888361399;
+p4= 0.00002476016134;
+p5=-0.00000026051495;
+pi2=1.570796326794896; 		/* pi/2 */
+absx=x;
+if (x<0) absx=-absx; 	     /* absolute value of input */
+quad=(int) (absx/pi2);       	/* quadrant (0 to 3) */
+if (quad > 3) {
+	int q = quad & ~3; /* round down to the next multiple of 4 */
+	absx = absx / (pi2 * quad);
+	quad -= q;
+	t = 0.0; /* hello, compiler warnings */
+}
+frac= (absx/pi2) - quad;     	/* fractional part of input */
+if(quad==0) t=frac * pi2;
+if(quad==1) t=(1-frac) * pi2;
+if(quad==2) t=frac * pi2;
+if(quad==3) t=(frac-1) * pi2;
+t=t * t;
+y=p0 + (p1*t) + (p2*t*t) + (p3*t*t*t) + (p4*t*t*t*t) + (p5*t*t*t*t*t);
+if(quad==2 || quad==1) y=-y;  /* correct sign */
+return(y);
+};
+
+double sine(double x)
+{
+	double pi2=1.570796326794896; 		/* pi/2 */
+	x = x - pi2;
+	if (x < -pi2) x += pi2*4;
+	return cosine(x);
+}
============================================================
--- /dev/null	
+++ trig.h	f86596b4fa5504a309a82531eda202d0f52bbfdb
@@ -0,0 +1,5 @@
+/* trig.h */
+
+double cosine(double x);
+double sine(double x);
+
============================================================
--- types.h	00b92d607bdb71185e8326714de30eb16d3329ff
+++ types.h	36b3274ddc55e8a7879472b3977392bcb3da8521
@@ -7,6 +7,6 @@ typedef int bool;
 
 #define NULL 0
 
-typedef int size_t;
+typedef unsigned int size_t;
 
 #endif /* __TYPES_H */
============================================================
--- uart.c	37a2e0459886f7f9c4e4a5361e21d50902dbe3f7
+++ uart.c	7a38c486dc1280695e1e62c6d3a76d6c9f849f67
@@ -38,6 +38,8 @@ void __attribute__((interrupt("IRQ"))) u
 
 void __attribute__((interrupt("IRQ"))) uart_interrupt_handler(void);
 
+#ifdef USE_UART
+
 void init_uart(void)
 {
 	UREG(FDR) = 0x10; /* DivAddVal = 0, MulVal = 1 */
@@ -205,3 +207,4 @@ bool getch(char *c) {
 	uart_rxread = (uart_rxread + 1) % UART_RXBUFSIZE;
 	return TRUE;
 }
+#endif
============================================================
--- uart.h	4acdb1f160b933d1653b3ac765f110cd71332112
+++ uart.h	86972bb7765797e0b5e660710beed2a9bbd9ec13
@@ -3,6 +3,7 @@
 
 #include "types.h"
 
+#ifdef USE_UART
 void init_uart(void);
 void putch(char c);
 void putstr(char *s);
@@ -10,5 +11,14 @@ bool getch(char *c);
 void putint_s(int n);
 void puthex(unsigned int n);
 bool getch(char *c);
+#else
+#define init_uart() ((void)0)
+#define putch(x) ((void)0)
+#define putstr(x) ((void)0)
+#define putint(x) ((void)0)
+#define putint_s(x) ((void)0)
+#define puthex(x) ((void)0)
+#define getch(x) (FALSE)
+#endif
 
 #endif /* __UART_H */
============================================================
--- wmp.c	1ab4017652548447277e83cf1697442cb7c6949b
+++ wmp.c	022e72f07353e280c050ae668cb3c02b318c72c4
@@ -1,9 +1,11 @@
+/* wmp.c */
 
 #include "wmp.h"
 #include "i2c.h"
 #include "uart.h"
 #include "dcm.h"
 #include "fisqrt.h"
+#include "stick.h"
 
 #define WMP_ZERO_COUNT 100
 
@@ -115,6 +117,7 @@ bool wmp_zero;
 
 bool wmp_update;
 bool wmp_zero;
+unsigned int wmp_discard;
 
 #define TWO_PI 6.28318531f
 #define DEG_TO_RAD (TWO_PI/360.0f)
@@ -201,24 +204,28 @@ void wmp_process_gyro_sample(void)
 
 		wmp_generation++;
 
-#if 1
-		if ((wmp_generation % 2) == 0)
+#if SEND_DCM
+		if ((wmp_generation % 20) == 0)
 			dcm_send_packet();
 #endif
 
 	} else if (wmp_zero) {
-		wmp_yaw_zero += wmp_yaw;
-		wmp_pitch_zero += wmp_pitch;
-		wmp_roll_zero += wmp_roll;
-		wmp_generation++;
-		if (wmp_generation >= WMP_ZERO_COUNT) {
-			wmp_zero = FALSE;
-			wmp_update = TRUE;
-			wmp_generation = 0;
-			wmp_yaw_zero /= WMP_ZERO_COUNT;
-			wmp_pitch_zero /= WMP_ZERO_COUNT;
-			wmp_roll_zero /= WMP_ZERO_COUNT;
-			putstr("Zero finished\r\n");
+		if (wmp_discard) {
+			wmp_discard--;
+		} else {
+			wmp_yaw_zero += wmp_yaw;
+			wmp_pitch_zero += wmp_pitch;
+			wmp_roll_zero += wmp_roll;
+			wmp_generation++;
+			if (wmp_generation >= WMP_ZERO_COUNT) {
+				wmp_zero = FALSE;
+				wmp_update = TRUE;
+				wmp_generation = 0;
+				wmp_yaw_zero /= WMP_ZERO_COUNT;
+				wmp_pitch_zero /= WMP_ZERO_COUNT;
+				wmp_roll_zero /= WMP_ZERO_COUNT;
+				putstr("Zero finished\r\n");
+			}
 		}
 	}
 }
@@ -268,6 +275,7 @@ void wmp_process_accel_sample(void)
 	 * on that so we'll just fudge it here.
 	 */
 	dcm_drift_correction(x, -y, -z);
+	stick_input();
 }
 
 void wmp_event_handler(void)
@@ -287,6 +295,7 @@ void wmp_start_zero(void)
 {
 	wmp_zero = TRUE;
 	wmp_update = FALSE;
+	wmp_discard = 100;
 	wmp_generation = 0;
 	putstr("Starting zero\r\n");
 }
============================================================
--- wmp.h	12983abdeae8f48c789d10feb223483cfd6e31eb
+++ wmp.h	6e0068d0e91c6fdc5f17e8569a6d05895be6cb65
@@ -13,6 +13,8 @@ extern bool wmp_roll_fast;
 extern bool wmp_pitch_fast;
 extern bool wmp_roll_fast;
 
+extern bool wmp_zero;
+
 bool wmp_init(void);
 bool wmp_sample(void);
 bool wmp_read_calibration_data(void);