blob: 14c176c8f3e0850d4ce454e5634ad6314690e26a [file] [log] [blame]
#include "cape/analog.h"
#include <string.h>
#include "cape/util.h"
#include "cape/led.h"
#define SPI SPI2
#define SPI_IRQHandler 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
// This file handles reading values from the MCP3008-I/SL ADC.
uint16_t analog_readings[NUM_CHANNELS] __attribute__((aligned(8)));
static volatile int current_channel;
static volatile int partial_reading;
static volatile int frame;
static volatile int analog_errors;
void SPI_IRQHandler(void) {
uint32_t status = SPI->SR;
if (status & SPI_SR_RXNE) {
uint16_t value = SPI->DR;
if (frame == 0) {
frame = 1;
partial_reading = value;
} else {
frame = 2;
// 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] =
(partial_reading << 16 | value) & 0x3FF;
for (int i = 0; i < 100; ++i) gpio_off(CSEL_GPIO, CSEL_NUM);
gpio_on(CSEL_GPIO, CSEL_NUM);
current_channel = (current_channel + 1) % NUM_CHANNELS;
}
}
}
void TIM_IRQHandler(void) {
TIM->SR = ~TIM_SR_UIF;
if (frame != 2) {
// We're not done with the previous reading yet, so we're going to reset and
// try again.
// 270ns*120MHz = 32.4
for (int i = 0; i < 33; ++i) gpio_on(CSEL_GPIO, CSEL_NUM);
++analog_errors;
}
// 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) gpio_off(CSEL_GPIO, CSEL_NUM);
partial_reading = 0;
frame = 0;
SPI->DR = 1; // start bit
uint16_t data = (1 << 15) /* not differential */ |
(current_channel << 12);
while (!(SPI->SR & SPI_SR_TXE));
SPI->DR = data;
}
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);
gpio_on(CSEL_GPIO, CSEL_NUM); // deselect it
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);
// We set it up to trigger at 4.44KHz (each sensor at just over 500Hz).
// 1/(1.875MHz)*32 = 17067ns (58.6Khz), and we don't want to be close (other
// interrupts do get in the way, and there's no reason to be).
TIM->CR1 = 0;
TIM->DIER = TIM_DIER_UIE;
// Make each tick take 45000ns.
TIM->PSC = (60 * 45000 / 1000) - 1;
// Only count to 5 before triggering the interrupt and wrapping around.
TIM->ARR = 5;
SPI->CR1 = 0; // make sure it's disabled
SPI->CR1 =
SPI_CR1_DFF /* 16 bit frame */ |
SPI_CR1_SSM | SPI_CR1_SSI | /* don't watch for other masters */
3 << 3 /* 30MHz/16 = 1.875MHz */ |
SPI_CR1_MSTR /* master mode */;
SPI->CR2 = SPI_CR2_RXNEIE;
SPI->CR1 |= SPI_CR1_SPE; // enable it
current_channel = 0;
analog_errors = 0;
TIM->EGR = TIM_EGR_UG;
TIM->CR1 |= TIM_CR1_CEN;
}
int analog_get_errors(void) {
NVIC_DisableIRQ(TIM_IRQn);
int r = analog_errors;
analog_errors = 0;
NVIC_EnableIRQ(TIM_IRQn);
return r;
}