got all the digital inputs hooked up
diff --git a/bbb_cape/src/cape/Makefile b/bbb_cape/src/cape/Makefile
index 710cfcf..5046615 100644
--- a/bbb_cape/src/cape/Makefile
+++ b/bbb_cape/src/cape/Makefile
@@ -40,6 +40,7 @@
 	gyro \
 	led \
 	analog \
+	digital \
 
 OBJECTS_bootloader := bootloader \
 	uart_common \
diff --git a/bbb_cape/src/cape/digital.c b/bbb_cape/src/cape/digital.c
new file mode 100644
index 0000000..a15b00b
--- /dev/null
+++ b/bbb_cape/src/cape/digital.c
@@ -0,0 +1,213 @@
+#include "cape/digital.h"
+
+#include <STM32F2XX.h>
+
+#include "cape/util.h"
+
+static void digital_capture_default(void) {}
+
+void digital_capture_0P(void) ALIAS_WEAK(digital_capture_default);
+void digital_capture_0N(void) ALIAS_WEAK(digital_capture_default);
+void digital_capture_1P(void) ALIAS_WEAK(digital_capture_default);
+void digital_capture_1N(void) ALIAS_WEAK(digital_capture_default);
+void digital_capture_2P(void) ALIAS_WEAK(digital_capture_default);
+void digital_capture_2N(void) ALIAS_WEAK(digital_capture_default);
+void digital_capture_3P(void) ALIAS_WEAK(digital_capture_default);
+void digital_capture_3N(void) ALIAS_WEAK(digital_capture_default);
+void digital_capture_4P(void) ALIAS_WEAK(digital_capture_default);
+void digital_capture_4N(void) ALIAS_WEAK(digital_capture_default);
+void digital_capture_5P(void) ALIAS_WEAK(digital_capture_default);
+void digital_capture_5N(void) ALIAS_WEAK(digital_capture_default);
+void digital_capture_6P(void) ALIAS_WEAK(digital_capture_default);
+void digital_capture_6N(void) ALIAS_WEAK(digital_capture_default);
+void digital_capture_7P(void) ALIAS_WEAK(digital_capture_default);
+void digital_capture_7N(void) ALIAS_WEAK(digital_capture_default);
+void digital_capture_8P(void) ALIAS_WEAK(digital_capture_default);
+void digital_capture_8N(void) ALIAS_WEAK(digital_capture_default);
+void digital_capture_9P(void) ALIAS_WEAK(digital_capture_default);
+void digital_capture_9N(void) ALIAS_WEAK(digital_capture_default);
+void digital_capture_10P(void) ALIAS_WEAK(digital_capture_default);
+void digital_capture_10N(void) ALIAS_WEAK(digital_capture_default);
+void digital_capture_11P(void) ALIAS_WEAK(digital_capture_default);
+void digital_capture_11N(void) ALIAS_WEAK(digital_capture_default);
+void digital_capture_12P(void) ALIAS_WEAK(digital_capture_default);
+void digital_capture_12N(void) ALIAS_WEAK(digital_capture_default);
+
+typedef void (*EXTIHandler)(uint32_t);
+
+void EXTI2_IRQHandler(void) {
+  uint32_t inputs = GPIOB->IDR;
+  EXTI->PR = EXTI_PR_PR2;
+  if (inputs & (1 << 2)) {
+    digital_capture_11N();
+  } else {
+    digital_capture_11P();
+  }
+}
+
+void EXTI4_IRQHandler(void) {
+  uint32_t inputs = GPIOC->IDR;
+  EXTI->PR = EXTI_PR_PR4;
+  if (inputs & (1 << 4)) {
+    digital_capture_0N();
+  } else {
+    digital_capture_0P();
+  }
+}
+
+static void EXTI5_Handler(uint32_t inputs) {
+  if (inputs & (1 << 5)) {
+    digital_capture_1N();
+  } else {
+    digital_capture_1P();
+  }
+}
+
+static void EXTI7_Handler(uint32_t inputs) {
+  if (inputs & (1 << 7)) {
+    digital_capture_10N();
+  } else {
+    digital_capture_10P();
+  }
+}
+
+static void EXTI8_Handler(uint32_t inputs) {
+  if (inputs & (1 << 7)) {
+    digital_capture_7N();
+  } else {
+    digital_capture_7P();
+  }
+}
+
+static void EXTI9_Handler(uint32_t inputs) {
+  if (inputs & (1 << 9)) {
+    digital_capture_6N();
+  } else {
+    digital_capture_6P();
+  }
+}
+
+void EXTI9_5_IRQHandler(void) {
+  uint32_t inputs = GPIOC->IDR;
+  uint32_t exti = __clz(EXTI->PR);
+  EXTI->PR = (1 << 31) >> exti;
+  switch (exti) {
+    case 31 - 5:
+      EXTI5_Handler(inputs);
+      break;
+    case 31 - 7:
+      EXTI7_Handler(inputs);
+      break;
+    case 31 - 8:
+      EXTI8_Handler(inputs);
+      break;
+    case 31 - 9:
+      EXTI9_Handler(inputs);
+      break;
+  }
+}
+
+static void EXTI10_Handler(uint32_t inputs) {
+  if (inputs & (1 << 10)) {
+    digital_capture_5N();
+  } else {
+    digital_capture_5P();
+  }
+}
+
+static void EXTI11_Handler(uint32_t inputs) {
+  if (inputs & (1 << 11)) {
+    digital_capture_9N();
+  } else {
+    digital_capture_9P();
+  }
+}
+
+static void EXTI12_Handler(uint32_t inputs) {
+  if (inputs & (1 << 12)) {
+    digital_capture_8N();
+  } else {
+    digital_capture_8P();
+  }
+}
+
+static void EXTI13_Handler(uint32_t inputs) {
+  if (inputs & (1 << 13)) {
+    digital_capture_2N();
+  } else {
+    digital_capture_2P();
+  }
+}
+
+static void EXTI14_Handler(uint32_t inputs) {
+  if (inputs & (1 << 14)) {
+    digital_capture_3N();
+  } else {
+    digital_capture_3P();
+  }
+}
+
+static void EXTI15_Handler(uint32_t inputs) {
+  if (inputs & (1 << 15)) {
+    digital_capture_4N();
+  } else {
+    digital_capture_4P();
+  }
+}
+
+void EXTI15_10_IRQHandler(void) {
+  uint32_t inputs = GPIOC->IDR;
+  uint32_t exti = __clz(EXTI->PR);
+  EXTI->PR = (1 << 31) >> exti;
+  switch (exti) {
+    case 31 - 10:
+      EXTI10_Handler(inputs);
+      break;
+    case 31 - 11:
+      EXTI11_Handler(inputs);
+      break;
+    case 31 - 12:
+      EXTI12_Handler(inputs);
+      break;
+    case 31 - 13:
+      EXTI13_Handler(inputs);
+      break;
+    case 31 - 14:
+      EXTI14_Handler(inputs);
+      break;
+    case 31 - 15:
+      EXTI15_Handler(inputs);
+      break;
+  }
+}
+
+static void init_exti(int exti, int port) {
+  EXTI_set(exti, port);
+  EXTI->IMR |= 1 << exti;
+  EXTI->RTSR |= 1 << exti;
+  EXTI->FTSR |= 1 << exti;
+}
+
+void digital_init(void) {
+  init_exti(2, 1);
+  init_exti(4, 2);
+  init_exti(5, 2);
+  init_exti(7, 0);
+  init_exti(8, 1);
+  init_exti(9, 1);
+  init_exti(10, 1);
+  init_exti(11, 0);
+  init_exti(12, 0);
+  init_exti(13, 2);
+  init_exti(14, 2);
+  init_exti(15, 2);
+
+  NVIC_SetPriority(EXTI2_IRQn, 1);
+  NVIC_EnableIRQ(EXTI2_IRQn);
+  NVIC_SetPriority(EXTI4_IRQn, 1);
+  NVIC_EnableIRQ(EXTI4_IRQn);
+  NVIC_SetPriority(EXTI9_5_IRQn, 1);
+  NVIC_EnableIRQ(EXTI9_5_IRQn);
+  NVIC_SetPriority(EXTI15_10_IRQn, 1);
+  NVIC_EnableIRQ(EXTI15_10_IRQn);
+}
diff --git a/bbb_cape/src/cape/digital.h b/bbb_cape/src/cape/digital.h
new file mode 100644
index 0000000..4905c23
--- /dev/null
+++ b/bbb_cape/src/cape/digital.h
@@ -0,0 +1,47 @@
+#ifndef CAPE_DIGITAL_H_
+#define CAPE_DIGITAL_H_
+
+#include <STM32F2XX.h>
+
+void digital_init(void);
+
+static inline int digital_read(int num) {
+  switch (num) {
+    case 0:
+      return GPIOC->IDR & (1 << 4);
+    case 1:
+      return GPIOC->IDR & (1 << 5);
+    case 2:
+      return GPIOC->IDR & (1 << 13);
+    case 3:
+      return GPIOC->IDR & (1 << 14);
+    case 4:
+      return GPIOC->IDR & (1 << 15);
+    case 5:
+      return GPIOB->IDR & (1 << 10);
+    case 6:
+      return GPIOB->IDR & (1 << 9);
+    case 7:
+      return GPIOB->IDR & (1 << 8);
+    case 8:
+      return GPIOA->IDR & (1 << 12);
+    case 9:
+      return GPIOA->IDR & (1 << 11);
+    case 10:
+      return GPIOA->IDR & (1 << 7);
+    case 11:
+      return GPIOB->IDR & (1 << 2);
+    default:
+      return 0;
+  }
+}
+
+// These are the functions for handling edges on the inputs. They have
+// default (weak symbol) implementations that do nothing.
+//void digital_capture_0P(void);
+//void digital_capture_0N(void);
+//void digital_capture_1P(void);
+//void digital_capture_1N(void);
+//...
+
+#endif  // CAPE_DIGITAL_H_
diff --git a/bbb_cape/src/cape/encoder.c b/bbb_cape/src/cape/encoder.c
index 45b7d16..31374db 100644
--- a/bbb_cape/src/cape/encoder.c
+++ b/bbb_cape/src/cape/encoder.c
@@ -19,8 +19,8 @@
 
 // 1.A
 void EXTI0_IRQHandler(void) {
-  EXTI->PR = EXTI_PR_PR0;
   uint32_t inputs = GPIOA->IDR;
+  EXTI->PR = EXTI_PR_PR0;
   // This looks like a weird way to XOR the 2 inputs, but it compiles down to
   // just 2 instructions, which is hard to beat.
   if (((inputs >> 1) ^ inputs) & (1 << 0)) {
@@ -32,8 +32,8 @@
 
 // 1.B
 void EXTI1_IRQHandler(void) {
-  EXTI->PR = EXTI_PR_PR1;
   uint32_t inputs = GPIOA->IDR;
+  EXTI->PR = EXTI_PR_PR1;
   if (((inputs >> 1) ^ inputs) & (1 << 0)) {
     --encoder1_value;
   } else {
@@ -42,9 +42,9 @@
 }
 
 // 3.A
-void EXTI2_IRQHandler(void) {
-  EXTI->PR = EXTI_PR_PR2;
+void TIM1_TRG_COM_TIM11_IRQHandler(void) {
   uint32_t inputs = GPIOC->IDR;
+  TIM9->SR = TIM_SR_CC1IF;
   if (((inputs >> 1) ^ inputs) & (1 << 2)) {
     ++encoder3_value;
   } else {
@@ -54,8 +54,8 @@
 
 // 3.B
 void EXTI3_IRQHandler(void) {
-  EXTI->PR = EXTI_PR_PR3;
   uint32_t inputs = GPIOC->IDR;
+  EXTI->PR = EXTI_PR_PR3;
   if (((inputs >> 1) ^ inputs) & (1 << 2)) {
     --encoder3_value;
   } else {
@@ -64,7 +64,9 @@
 }
 
 static void encoder_setup(TIM_TypeDef *timer) {
-  timer->CR1 = TIM_CR1_UDIS;
+  timer->CR1 = TIM_CR1_UDIS /* wait until we tell it to do anything */ |
+               TIM_CR1_URS /* don't generate spurious update interrupts that
+                              might be shared with other timers */;
   timer->SMCR = 3;  // 4x quadrature encoder mode
   timer->CCMR1 =
       TIM_CCMR1_CC2S_0 | /* input pin 2 -> timer input 2 */
@@ -74,19 +76,28 @@
 }
 
 void encoder_init(void) {
-  SYSCFG->EXTICR[0] =
-      SYSCFG_EXTICR1_EXTI0_PC |
-      SYSCFG_EXTICR1_EXTI1_PC |
-      SYSCFG_EXTICR1_EXTI2_PA |
-      SYSCFG_EXTICR1_EXTI3_PA;
-  EXTI->IMR |= EXTI_IMR_MR0 | EXTI_IMR_MR1 | EXTI_IMR_MR2 | EXTI_IMR_MR3;
-  EXTI->RTSR |= EXTI_RTSR_TR0 | EXTI_RTSR_TR1 | EXTI_RTSR_TR2 | EXTI_RTSR_TR3;
-  EXTI->FTSR |= EXTI_FTSR_TR0 | EXTI_FTSR_TR1 | EXTI_FTSR_TR2 | EXTI_FTSR_TR3;
+  // Set up the 3 simple software encoder inputs.
+  EXTI_set(0, 2);
+  EXTI_set(1, 2);
+  EXTI_set(3, 0);
+  EXTI->IMR |= EXTI_IMR_MR0 | EXTI_IMR_MR1 | EXTI_IMR_MR3;
+  EXTI->RTSR |= EXTI_RTSR_TR0 | EXTI_RTSR_TR1 | EXTI_RTSR_TR3;
+  EXTI->FTSR |= EXTI_FTSR_TR0 | EXTI_FTSR_TR1 | EXTI_FTSR_TR3;
   NVIC_EnableIRQ(EXTI0_IRQn);
   NVIC_EnableIRQ(EXTI1_IRQn);
-  NVIC_EnableIRQ(EXTI2_IRQn);
   NVIC_EnableIRQ(EXTI3_IRQn);
 
+  // Set up the A2 software encoder input through TIM9.
+  gpio_setup_alt(GPIOA, 2, 3);
+  RCC->APB2ENR |= RCC_APB2ENR_TIM9EN;
+  TIM9->CR1 = TIM_CR1_UDIS;
+  TIM9->DIER = TIM_DIER_CC1IE;
+  TIM9->CCMR1 = TIM_CCMR1_CC1S_0; /* input pin 1 -> timer input 1 */
+  TIM9->CCER = TIM_CCER_CC1NP | TIM_CCER_CC1P | TIM_CCER_CC1E;
+  TIM9->EGR = TIM_EGR_UG;
+  TIM9->CR1 |= TIM_CR1_CEN;
+  NVIC_EnableIRQ(TIM1_BRK_TIM9_IRQn);
+
   gpio_setup_alt(GPIOA, 8, 1);
   gpio_setup_alt(GPIOB, 0, 1);
   RCC->APB2ENR |= RCC_APB2ENR_TIM1EN;
diff --git a/bbb_cape/src/cape/fill_packet.c b/bbb_cape/src/cape/fill_packet.c
index b525c2f..700df1d 100644
--- a/bbb_cape/src/cape/fill_packet.c
+++ b/bbb_cape/src/cape/fill_packet.c
@@ -14,6 +14,7 @@
 #include "cape/led.h"
 #include "cape/analog.h"
 #include "cape/robot.h"
+#include "cape/digital.h"
 
 #define TIMESTAMP_TIM TIM6
 #define RCC_APB1ENR_TIMESTAMP_TIMEN RCC_APB1ENR_TIM6EN
@@ -76,6 +77,7 @@
   analog_init();
   encoder_init();
   gyro_init();
+  digital_init();
 
   uint8_t *flash_end = &__etext + (&__data_start__ - &__data_end__) + 8;
   flash_checksum = crc_calculate((void *)MAIN_FLASH_START,
diff --git a/bbb_cape/src/cape/hardware.notes b/bbb_cape/src/cape/hardware.notes
index 05b0dfe..73593b6 100644
--- a/bbb_cape/src/cape/hardware.notes
+++ b/bbb_cape/src/cape/hardware.notes
@@ -56,11 +56,10 @@
 [GPIOs]
 C0  enc
 C1  enc
-A2  enc
+A2  enc (as TIM9.1)
 A3  enc
 
 B2
-#A2  TIM9.1
 C4
 C5
 A7
diff --git a/bbb_cape/src/cape/peripherial_usage.notes b/bbb_cape/src/cape/peripherial_usage.notes
index 987113e..5980be0 100644
--- a/bbb_cape/src/cape/peripherial_usage.notes
+++ b/bbb_cape/src/cape/peripherial_usage.notes
@@ -32,9 +32,15 @@
 [encoders]
 encoder
   TIM1,TIM2,TIM3,TIM4,TIM5,TIM8
-  EXTI0,EXTI1,EXTI2,EXTI3
-  EXTI0_IRQ:0,EXTI1_IRQ:0,EXTI2_IRQ:0,EXTI3_IRQ:0
+  EXTI0,EXTI1,EXTI3
+  EXTI0_IRQ:0,EXTI1_IRQ:0,EXTI3_IRQ:0
+  TIM9 (for its input capture 1)
+  TIM9_IRQ:0 (aka TIM1_BRK)
 
+[digital inputs]
+digital
+  EXTI2,EXTI4-15
+  EXTI2_IRQ:1,EXTI4-15_IRQ:1
 
 [sensor packet sending]
 fill_packet
diff --git a/bbb_cape/src/cape/util.h b/bbb_cape/src/cape/util.h
index 893c95b..02e72fb 100644
--- a/bbb_cape/src/cape/util.h
+++ b/bbb_cape/src/cape/util.h
@@ -13,6 +13,14 @@
   __asm__ __volatile__("" ::: "memory");
 }
 
+// Count leading zeros.
+// Returns 0 if bit 31 is set etc.
+__attribute__((always_inline)) static __INLINE uint32_t __clz(uint32_t value) {
+  uint32_t result;
+  __asm__("clz %0, %1" : "=r" (result) : "r" (value));
+  return result;
+}
+
 // Sets number_of_bits (shifted left shift number of slots) to value in
 // variable.
 // This means that the total shift is number_bits*shift.
@@ -43,4 +51,10 @@
   SET_BITS(port->OSPEEDR, 2, speed, pin);
 }
 
+// exti is which EXTI line to set
+// port is 0 for A, 1 for B, etc
+static inline void EXTI_set(int exti, int port) {
+  SET_BITS(SYSCFG->EXTICR[exti / 4], 4, port, exti % 4);
+}
+
 #endif  // CAPE_UTIL_H_