blob: 73459168ca62bd00681258e08e6b738f7acd3d74 [file] [log] [blame]
Brian Silverman95244d82013-12-14 12:15:46 -08001#include "cape/analog.h"
2
3#include <string.h>
4
5#include <STM32F2XX.h>
6
7#include "cape/util.h"
Brian Silverman176c6762013-12-19 16:28:09 -08008#include "cape/led.h"
Brian Silverman95244d82013-12-14 12:15:46 -08009
10#define SPI SPI2
Brian Silverman176c6762013-12-19 16:28:09 -080011#define SPI_IRQHandler SPI2_IRQHandler
Brian Silverman95244d82013-12-14 12:15:46 -080012#define SPI_IRQn SPI2_IRQn
13#define RCC_APB1ENR_SPIEN RCC_APB1ENR_SPI2EN
14#define TIM TIM14
15#define TIM_IRQHandler TIM8_TRG_COM_TIM14_IRQHandler
16#define TIM_IRQn TIM8_TRG_COM_TIM14_IRQn
17#define RCC_APB1ENR_TIMEN RCC_APB1ENR_TIM14EN
18#define CSEL_GPIO GPIOB
19#define CSEL_NUM 12
20
21#define NUM_CHANNELS 8
22
23uint16_t analog_readings[NUM_CHANNELS] __attribute__((aligned(8)));
24static volatile int current_channel;
25
26static void start_read(int channel) {
27 // This needs to wait 13 cycles between enabling the CSEL pin and starting to
28 // send data.
29 // (100ns+8ns)*120MHz = 12.96
30
31 // Clear the CSEL pin to select it.
Brian Silvermaned183e62013-12-18 15:51:16 -080032 for (int i = 0; i < 9; ++i) CSEL_GPIO->BSRRL = 1 << CSEL_NUM;
Brian Silverman95244d82013-12-14 12:15:46 -080033 current_channel = channel;
34 uint16_t data = 1 << 8 /* start bit */ |
35 0 << 7 /* not differential */ |
36 channel << 4;
37 SPI->DR = data;
38}
39
40void SPI_IRQHandler(void) {
41 uint32_t status = SPI->SR;
42 if (status & SPI_SR_RXNE) {
43 uint16_t value = SPI->DR;
44 // Masking off the high bits is important because there's nothing driving
45 // the MISO line during the time the MCU receives them.
46 analog_readings[current_channel] = value & 0x3FF;
Brian Silvermaned183e62013-12-18 15:51:16 -080047 CSEL_GPIO->BSRRH = 1 << CSEL_NUM;
Brian Silverman95244d82013-12-14 12:15:46 -080048
Brian Silvermanffeef3f2013-12-22 14:06:23 -080049 TIM->CR1 = TIM_CR1_OPM;
Brian Silverman95244d82013-12-14 12:15:46 -080050 TIM->EGR = TIM_EGR_UG;
Brian Silvermanc58872f2013-12-15 16:27:53 -080051 TIM->CR1 |= TIM_CR1_CEN;
Brian Silverman95244d82013-12-14 12:15:46 -080052 }
53}
54
55void TIM_IRQHandler(void) {
Brian Silverman176c6762013-12-19 16:28:09 -080056 TIM->SR = ~TIM_SR_CC1IF;
Brian Silverman95244d82013-12-14 12:15:46 -080057
58 start_read((current_channel + 1) % NUM_CHANNELS);
59}
60
61void analog_init(void) {
62 memset(analog_readings, 0xFF, sizeof(analog_readings));
63
64 RCC->APB1ENR |= RCC_APB1ENR_SPIEN;
65 RCC->APB1ENR |= RCC_APB1ENR_TIMEN;
66
67 gpio_setup_out(CSEL_GPIO, CSEL_NUM, 3);
Brian Silvermaned183e62013-12-18 15:51:16 -080068 CSEL_GPIO->BSRRH = 1 << CSEL_NUM; // make sure it's deselected
Brian Silverman95244d82013-12-14 12:15:46 -080069
70 gpio_setup_alt(GPIOB, 13, 5); // SCK
71 gpio_setup_alt(GPIOB, 14, 5); // MISO
72 gpio_setup_alt(GPIOB, 15, 5); // MOSI
73
74 NVIC_SetPriority(SPI_IRQn, 6);
75 NVIC_EnableIRQ(SPI_IRQn);
76 NVIC_SetPriority(TIM_IRQn, 6);
77 NVIC_EnableIRQ(TIM_IRQn);
78
Brian Silvermanffeef3f2013-12-22 14:06:23 -080079 TIM->CR1 = TIM_CR1_OPM;
Brian Silverman95244d82013-12-14 12:15:46 -080080 TIM->DIER = TIM_DIER_CC1IE;
81 TIM->CCMR1 = 0;
82 // Make each tick take 500ns.
Brian Silverman176c6762013-12-19 16:28:09 -080083 TIM->PSC = (60 * 500 / 1000) - 1;
84 // Call the interrupt after 1 tick.
85 TIM->CCR1 = 1;
Brian Silverman95244d82013-12-14 12:15:46 -080086
87 SPI->CR1 = 0; // make sure it's disabled
88 SPI->CR1 =
89 SPI_CR1_DFF /* 16 bit frame */ |
Brian Silverman176c6762013-12-19 16:28:09 -080090 SPI_CR1_SSM | SPI_CR1_SSI | /* don't watch for other masters */
Brian Silverman95244d82013-12-14 12:15:46 -080091 1 << 3 /* 30MHz/4 = 7.5MHz */ |
92 SPI_CR1_MSTR /* master mode */;
93 SPI->CR2 = SPI_CR2_RXNEIE;
94 SPI->CR1 |= SPI_CR1_SPE; // enable it
95
96 start_read(0);
97}