blob: 14c176c8f3e0850d4ce454e5634ad6314690e26a [file] [log] [blame]
Brian Silverman95244d82013-12-14 12:15:46 -08001#include "cape/analog.h"
2
3#include <string.h>
4
Brian Silverman95244d82013-12-14 12:15:46 -08005#include "cape/util.h"
Brian Silverman176c6762013-12-19 16:28:09 -08006#include "cape/led.h"
Brian Silverman95244d82013-12-14 12:15:46 -08007
8#define SPI SPI2
Brian Silverman176c6762013-12-19 16:28:09 -08009#define SPI_IRQHandler SPI2_IRQHandler
Brian Silverman95244d82013-12-14 12:15:46 -080010#define SPI_IRQn SPI2_IRQn
11#define RCC_APB1ENR_SPIEN RCC_APB1ENR_SPI2EN
12#define TIM TIM14
13#define TIM_IRQHandler TIM8_TRG_COM_TIM14_IRQHandler
14#define TIM_IRQn TIM8_TRG_COM_TIM14_IRQn
15#define RCC_APB1ENR_TIMEN RCC_APB1ENR_TIM14EN
16#define CSEL_GPIO GPIOB
17#define CSEL_NUM 12
18
19#define NUM_CHANNELS 8
20
Brian Silvermanf482b4c2014-03-17 19:44:20 -070021// This file handles reading values from the MCP3008-I/SL ADC.
22
Brian Silverman95244d82013-12-14 12:15:46 -080023uint16_t analog_readings[NUM_CHANNELS] __attribute__((aligned(8)));
24static volatile int current_channel;
Brian Silverman38188d62014-01-01 13:17:35 -080025static volatile int partial_reading;
26static volatile int frame;
Brian Silvermanf482b4c2014-03-17 19:44:20 -070027static volatile int analog_errors;
Brian Silverman95244d82013-12-14 12:15:46 -080028
29void SPI_IRQHandler(void) {
30 uint32_t status = SPI->SR;
31 if (status & SPI_SR_RXNE) {
32 uint16_t value = SPI->DR;
Brian Silverman38188d62014-01-01 13:17:35 -080033 if (frame == 0) {
34 frame = 1;
35 partial_reading = value;
36 } else {
Brian Silvermanf482b4c2014-03-17 19:44:20 -070037 frame = 2;
Brian Silverman38188d62014-01-01 13:17:35 -080038 // Masking off the high bits is important because there's nothing driving
39 // the MISO line during the time the MCU receives them.
Brian Silvermanf482b4c2014-03-17 19:44:20 -070040 analog_readings[current_channel] =
41 (partial_reading << 16 | value) & 0x3FF;
Brian Silverman38188d62014-01-01 13:17:35 -080042 for (int i = 0; i < 100; ++i) gpio_off(CSEL_GPIO, CSEL_NUM);
43 gpio_on(CSEL_GPIO, CSEL_NUM);
Brian Silverman95244d82013-12-14 12:15:46 -080044
Brian Silvermanf482b4c2014-03-17 19:44:20 -070045 current_channel = (current_channel + 1) % NUM_CHANNELS;
Brian Silverman38188d62014-01-01 13:17:35 -080046 }
Brian Silverman95244d82013-12-14 12:15:46 -080047 }
48}
49
50void TIM_IRQHandler(void) {
Brian Silvermanb4c08822014-03-23 15:28:23 -070051 TIM->SR = ~TIM_SR_UIF;
Brian Silverman95244d82013-12-14 12:15:46 -080052
Brian Silvermanf482b4c2014-03-17 19:44:20 -070053 if (frame != 2) {
54 // We're not done with the previous reading yet, so we're going to reset and
55 // try again.
56 // 270ns*120MHz = 32.4
57 for (int i = 0; i < 33; ++i) gpio_on(CSEL_GPIO, CSEL_NUM);
58 ++analog_errors;
59 }
60
61 // This needs to wait 13 cycles between enabling the CSEL pin and starting to
62 // send data.
63 // (100ns+8ns)*120MHz = 12.96
64
65 // Clear the CSEL pin to select it.
66 for (int i = 0; i < 9; ++i) gpio_off(CSEL_GPIO, CSEL_NUM);
67 partial_reading = 0;
68 frame = 0;
69 SPI->DR = 1; // start bit
70 uint16_t data = (1 << 15) /* not differential */ |
71 (current_channel << 12);
72 while (!(SPI->SR & SPI_SR_TXE));
73 SPI->DR = data;
Brian Silverman95244d82013-12-14 12:15:46 -080074}
75
76void analog_init(void) {
77 memset(analog_readings, 0xFF, sizeof(analog_readings));
78
79 RCC->APB1ENR |= RCC_APB1ENR_SPIEN;
80 RCC->APB1ENR |= RCC_APB1ENR_TIMEN;
81
82 gpio_setup_out(CSEL_GPIO, CSEL_NUM, 3);
Brian Silverman391beca2013-12-28 22:32:48 -080083 gpio_on(CSEL_GPIO, CSEL_NUM); // deselect it
Brian Silverman95244d82013-12-14 12:15:46 -080084
85 gpio_setup_alt(GPIOB, 13, 5); // SCK
86 gpio_setup_alt(GPIOB, 14, 5); // MISO
87 gpio_setup_alt(GPIOB, 15, 5); // MOSI
88
89 NVIC_SetPriority(SPI_IRQn, 6);
90 NVIC_EnableIRQ(SPI_IRQn);
91 NVIC_SetPriority(TIM_IRQn, 6);
92 NVIC_EnableIRQ(TIM_IRQn);
93
Brian Silverman45ee39e2014-03-24 15:07:17 -070094 // We set it up to trigger at 4.44KHz (each sensor at just over 500Hz).
95 // 1/(1.875MHz)*32 = 17067ns (58.6Khz), and we don't want to be close (other
96 // interrupts do get in the way, and there's no reason to be).
Brian Silvermanf482b4c2014-03-17 19:44:20 -070097 TIM->CR1 = 0;
Brian Silvermanb4c08822014-03-23 15:28:23 -070098 TIM->DIER = TIM_DIER_UIE;
Brian Silverman45ee39e2014-03-24 15:07:17 -070099 // Make each tick take 45000ns.
100 TIM->PSC = (60 * 45000 / 1000) - 1;
101 // Only count to 5 before triggering the interrupt and wrapping around.
102 TIM->ARR = 5;
Brian Silverman95244d82013-12-14 12:15:46 -0800103
104 SPI->CR1 = 0; // make sure it's disabled
105 SPI->CR1 =
106 SPI_CR1_DFF /* 16 bit frame */ |
Brian Silverman176c6762013-12-19 16:28:09 -0800107 SPI_CR1_SSM | SPI_CR1_SSI | /* don't watch for other masters */
Brian Silverman38188d62014-01-01 13:17:35 -0800108 3 << 3 /* 30MHz/16 = 1.875MHz */ |
Brian Silverman95244d82013-12-14 12:15:46 -0800109 SPI_CR1_MSTR /* master mode */;
110 SPI->CR2 = SPI_CR2_RXNEIE;
111 SPI->CR1 |= SPI_CR1_SPE; // enable it
112
Brian Silvermanf482b4c2014-03-17 19:44:20 -0700113 current_channel = 0;
114 analog_errors = 0;
115
116 TIM->EGR = TIM_EGR_UG;
117 TIM->CR1 |= TIM_CR1_CEN;
118}
119
120int analog_get_errors(void) {
121 NVIC_DisableIRQ(TIM_IRQn);
122 int r = analog_errors;
123 analog_errors = 0;
124 NVIC_EnableIRQ(TIM_IRQn);
125 return r;
Brian Silverman95244d82013-12-14 12:15:46 -0800126}