got talking to the ADC working (theoretically)
diff --git a/bbb_cape/src/cape/Makefile b/bbb_cape/src/cape/Makefile
index 5955c6b..35aeb0a 100644
--- a/bbb_cape/src/cape/Makefile
+++ b/bbb_cape/src/cape/Makefile
@@ -39,6 +39,7 @@
 	crc \
 	gyro \
 	led \
+	analog \
 
 OBJECTS_bootloader := bootloader \
 	uart_common \
diff --git a/bbb_cape/src/cape/analog.c b/bbb_cape/src/cape/analog.c
new file mode 100644
index 0000000..2f2602b
--- /dev/null
+++ b/bbb_cape/src/cape/analog.c
@@ -0,0 +1,94 @@
+#include "cape/analog.h"
+
+#include <string.h>
+
+#include <STM32F2XX.h>
+
+#include "cape/util.h"
+
+#define SPI SPI2
+#define SPI_IRQHander SPI2_IRQHandler
+#define SPI_IRQn SPI2_IRQn
+#define RCC_APB1ENR_SPIEN RCC_APB1ENR_SPI2EN
+#define TIM TIM14
+#define TIM_IRQHandler TIM8_TRG_COM_TIM14_IRQHandler
+#define TIM_IRQn TIM8_TRG_COM_TIM14_IRQn
+#define RCC_APB1ENR_TIMEN RCC_APB1ENR_TIM14EN
+#define CSEL_GPIO GPIOB
+#define CSEL_NUM 12
+
+#define NUM_CHANNELS 8
+
+uint16_t analog_readings[NUM_CHANNELS] __attribute__((aligned(8)));
+static volatile int current_channel;
+
+static void start_read(int channel) {
+  // This needs to wait 13 cycles between enabling the CSEL pin and starting to
+  // send data.
+  // (100ns+8ns)*120MHz = 12.96
+
+  // Clear the CSEL pin to select it.
+  for (int i = 0; i < 9; ++i) CSEL_GPIO->BSRRH = 1 << CSEL_NUM;
+  current_channel = channel;
+  uint16_t data = 1 << 8 /* start bit */ |
+      0 << 7 /* not differential */ |
+      channel << 4;
+  SPI->DR = data;
+}
+
+void SPI_IRQHandler(void) {
+  uint32_t status = SPI->SR;
+  if (status & SPI_SR_RXNE) {
+    uint16_t value = SPI->DR;
+    // Masking off the high bits is important because there's nothing driving
+    // the MISO line during the time the MCU receives them.
+    analog_readings[current_channel] = value & 0x3FF;
+    CSEL_GPIO->BSRRL = 1 << CSEL_NUM;
+
+    TIM->CR1 = TIM_CR1_UDIS;
+    TIM->CCR1 = 1;
+    TIM->EGR = TIM_EGR_UG;
+  }
+}
+
+void TIM_IRQHandler(void) {
+  TIM->CR1 &= ~TIM_CR1_CEN;
+  TIM->SR = TIM_SR_CC1IF;
+
+  start_read((current_channel + 1) % NUM_CHANNELS);
+}
+
+void analog_init(void) {
+  memset(analog_readings, 0xFF, sizeof(analog_readings));
+
+  RCC->APB1ENR |= RCC_APB1ENR_SPIEN;
+  RCC->APB1ENR |= RCC_APB1ENR_TIMEN;
+
+  gpio_setup_out(CSEL_GPIO, CSEL_NUM, 3);
+  CSEL_GPIO->BSRRL = 1 << CSEL_NUM;  // make sure it's deselected
+
+  gpio_setup_alt(GPIOB, 13, 5);  // SCK
+  gpio_setup_alt(GPIOB, 14, 5);  // MISO
+  gpio_setup_alt(GPIOB, 15, 5);  // MOSI
+
+  NVIC_SetPriority(SPI_IRQn, 6);
+  NVIC_EnableIRQ(SPI_IRQn);
+  NVIC_SetPriority(TIM_IRQn, 6);
+  NVIC_EnableIRQ(TIM_IRQn);
+
+  TIM->CR1 = TIM_CR1_UDIS;
+  TIM->DIER = TIM_DIER_CC1IE;
+  TIM->CCMR1 = 0;
+  // Make each tick take 500ns.
+  TIM->PSC = (30 * 500 / 1000) - 1;
+
+  SPI->CR1 = 0;  // make sure it's disabled
+  SPI->CR1 =
+      SPI_CR1_DFF /* 16 bit frame */ |
+      1 << 3 /* 30MHz/4 = 7.5MHz */ |
+      SPI_CR1_MSTR /* master mode */;
+  SPI->CR2 = SPI_CR2_RXNEIE;
+  SPI->CR1 |= SPI_CR1_SPE;  // enable it
+
+  start_read(0);
+}
diff --git a/bbb_cape/src/cape/analog.h b/bbb_cape/src/cape/analog.h
new file mode 100644
index 0000000..50038d5
--- /dev/null
+++ b/bbb_cape/src/cape/analog.h
@@ -0,0 +1,17 @@
+#ifndef CAPE_ANALOG_H_
+#define CAPE_ANALOG_H_
+
+#include <stdint.h>
+
+// Starts up constantly reading analog values and storing them in an array to
+// be retrieved by analog_get.
+void analog_init(void);
+
+static inline uint16_t analog_get(int num) {
+  if (num < 0 || num > 7) return 0xFFFF;
+
+  extern uint16_t analog_readings[8] __attribute__((aligned(8)));
+  return analog_readings[num];
+}
+
+#endif  // CAPE_ANALOG_H_
diff --git a/bbb_cape/src/cape/fill_packet.c b/bbb_cape/src/cape/fill_packet.c
index 5779ae3..0f49bd4 100644
--- a/bbb_cape/src/cape/fill_packet.c
+++ b/bbb_cape/src/cape/fill_packet.c
@@ -12,6 +12,7 @@
 #include "cape/bootloader_handoff.h"
 #include "cape/gyro.h"
 #include "cape/led.h"
+#include "cape/analog.h"
 
 #define TIMESTAMP_TIM TIM6
 #define RCC_APB1ENR_TIMESTAMP_TIMEN RCC_APB1ENR_TIM6EN
@@ -47,6 +48,10 @@
   packet->main.encoders[5] = encoder_read(5);
   packet->main.encoders[6] = encoder_read(6);
   packet->main.encoders[7] = encoder_read(7);
+
+  for (int i = 0; i < 8; ++i) {
+    packet->main.analogs[i] = analog_get(i);
+  }
 }
 
 // Fills the new packet with data.
@@ -78,6 +83,7 @@
 
   crc_init();
   led_init();
+  analog_init();
   encoder_init();
   gyro_init();
 
diff --git a/bbb_cape/src/cape/gyro.c b/bbb_cape/src/cape/gyro.c
index a663e16..55c3e5f 100644
--- a/bbb_cape/src/cape/gyro.c
+++ b/bbb_cape/src/cape/gyro.c
@@ -11,10 +11,12 @@
 
 #define SPI SPI3
 #define SPI_IRQHandler SPI3_IRQHandler
+#define SPI_IRQn SPI3_IRQn
 #define RCC_APB1ENR_SPIEN RCC_APB1ENR_SPI3EN
-#define TIM TIM10
-#define TIM_IRQHandler TIM1_UP_TIM10_IRQHandler
-#define RCC_APB2ENR_TIMEN RCC_APB2ENR_TIM10EN
+#define TIM TIM13
+#define TIM_IRQHandler TIM8_UP_TIM13_IRQHandler
+#define TIM_IRQn TIM8_UP_TIM13_IRQn
+#define RCC_APB1ENR_TIMEN RCC_APB1ENR_TIM13EN
 #define CSEL_GPIO GPIOA
 #define CSEL_NUM 4
 // The header file also contains references to TIM in gyro_read.
@@ -355,27 +357,28 @@
   gyro_output.zeroed = 0;
 
   RCC->APB1ENR |= RCC_APB1ENR_SPIEN;
-  RCC->APB2ENR |= RCC_APB2ENR_TIMEN;
+  RCC->APB1ENR |= RCC_APB1ENR_TIMEN;
 
   // Set up CSEL.
   // It's is just a GPIO pin because we're the master (it would be special if we
   // were a slave).
   gpio_setup_out(CSEL_GPIO, CSEL_NUM, 3);
+  CSEL_GPIO->BSRRL = 1 << CSEL_NUM;  // make sure it's deselected
 
   // Set up SCK, MISO, and MOSI.
   gpio_setup_alt(GPIOC, 10, 6);  // SCK
   gpio_setup_alt(GPIOC, 11, 6);  // MISO
   gpio_setup_alt(GPIOC, 12, 6);  // MOSI
 
-  NVIC_SetPriority(SPI3_IRQn, 4);
-  NVIC_EnableIRQ(SPI3_IRQn);
-  NVIC_SetPriority(TIM1_UP_TIM10_IRQn, 5);
-  NVIC_EnableIRQ(TIM1_UP_TIM10_IRQn);
+  NVIC_SetPriority(SPI_IRQn, 4);
+  NVIC_EnableIRQ(SPI_IRQn);
+  NVIC_SetPriority(TIM_IRQn, 5);
+  NVIC_EnableIRQ(TIM_IRQn);
 
   TIM->CR1 = TIM_CR1_UDIS;
   TIM->DIER = TIM_DIER_CC1IE;
   TIM->CCMR1 = 0;
-  TIM->PSC = 60000 - 1;
+  TIM->PSC = 30000 - 1;
 
   SPI->CR1 = 0;  // make sure it's disabled
   SPI->CR1 =
diff --git a/bbb_cape/src/cape/peripherial_usage.notes b/bbb_cape/src/cape/peripherial_usage.notes
index bf55182..987113e 100644
--- a/bbb_cape/src/cape/peripherial_usage.notes
+++ b/bbb_cape/src/cape/peripherial_usage.notes
@@ -11,7 +11,7 @@
   USART1_IRQ:3
 uart_dma
   DMA2.7:2
-  DMA2.7_IRQ:6
+  DMA2.7_IRQ:8
 uart_byte
   TIM7
 
@@ -19,11 +19,15 @@
 gyro
   SPI3
   SPI3_IRQ:4
-  TIM10
-  TIM10_IRQ:5 (aka TIM1_UP)
+  TIM13
+  TIM13_IRQ:5 (aka TIM8_UP)
 
 [ADC communication]
-SPI2
+analog
+  SPI2
+  SPI2_IRQ:6
+  TIM14
+  TIM14_IRQ:6 (aka TIM8_TRG_COM)
 
 [encoders]
 encoder
diff --git a/bbb_cape/src/cape/uart_dma.c b/bbb_cape/src/cape/uart_dma.c
index 4d4da1c..f5c1967 100644
--- a/bbb_cape/src/cape/uart_dma.c
+++ b/bbb_cape/src/cape/uart_dma.c
@@ -61,7 +61,7 @@
       DMA_SxFCR_DMDIS /* disable direct mode (enable the FIFO) */ |
       1 /* 1/2 full threshold */;
   DMA_Stream->CR |= DMA_SxCR_EN;  // enable it
-  NVIC_SetPriority(DMA_Stream_IRQn, 6);
+  NVIC_SetPriority(DMA_Stream_IRQn, 8);
   NVIC_EnableIRQ(DMA_Stream_IRQn);
 
   uart_dma_callback(buffer2);