Teacup_Firmware/analog-stm32.c

201 lines
5.0 KiB
C

/** \file
\brief Analog subsystem, ARM specific part.
STM32F4 goes a different way. The ADC don't have a register for
each channel. We are using DMA instead.
*/
#if defined TEACUP_C_INCLUDE && defined __ARM_STM32F411__
#include "cmsis-stm32f4xx.h"
#include "arduino.h"
#include "pinio.h"
#include "delay.h"
#include "temp.h"
#include <string.h>
// DMA ADC-buffer
#define OVERSAMPLE 6
static uint16_t BSS adc_buffer[NUM_TEMP_SENSORS * OVERSAMPLE];
// Private functions
void init_analog(void);
void init_dma(void);
/** Initialize the analog subsystem.
Initialize the ADC and start hardware scan for all sensors.
*/
void analog_init() {
if (NUM_TEMP_SENSORS) { // At least one channel in use.
init_dma();
init_analog();
}
}
/** Initialize all analog pins from config
Initialize the pins to analog mode, no pullup/no pulldown, highspeed
*/
void init_analog() {
#undef DEFINE_TEMP_SENSOR
/*
config analog pins
1. analog mode
2. no pullup
3. high speed
*/
#define DEFINE_TEMP_SENSOR(name, type, pin, additional) \
SET_MODE(pin, 0x3); \
PULL_OFF(pin); \
SET_OSPEED(pin, 0x3);
#include "config_wrapper.h"
#undef DEFINE_TEMP_SENSOR
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN; //Enable clock
/* Set ADC parameters */
/* Set the ADC clock prescaler */
ADC->CCR |= ADC_CCR_ADCPRE;
ADC1->CR1 &= ~(ADC_CR1_RES);
ADC1->CR1 |= ADC_CR1_RES_0;
ADC1->CR1 |= ADC_CR1_SCAN | ADC_CR1_OVRIE;
ADC1->CR2 |= ADC_CR2_DMA;
/* Set ADC number of conversion */
// ((NUM_TEMP_SENSORS) - 1) << 20
ADC1->SQR1 &= ~(ADC_SQR1_L);
ADC1->SQR1 |= (NUM_TEMP_SENSORS - 1) << 20;
// for loop over each channel (0..15) for sequence
#undef DEFINE_TEMP_SENSOR
// for PIO ## ADC >= 10 SRPR1 and ADC -10, else SMPR 2
// 0x06 = 144 cycles
// subt line is to keep compiler happy
#define DEFINE_TEMP_SENSOR(name, type, pin, additional) \
if (NUM_TEMP_SENSORS) { \
uint8_t subt = (pin ## _ADC >= 10) ? 10 : 0; \
if (pin ## _ADC >= 10) { \
ADC1->SMPR1 |= (uint32_t)0x06 << (3 * ((pin ## _ADC) - subt)); \
} else { \
ADC1->SMPR2 |= (uint32_t)0x06 << (3 * ((pin ## _ADC) - subt)); \
} \
subt = (TEMP_SENSOR_ ## name <= 5) ? 0 : (TEMP_SENSOR_ ## name <= 11) ? 6 : 12; \
if (TEMP_SENSOR_ ## name <= 5) { \
ADC1->SQR3 |= pin ## _ADC << (5 * TEMP_SENSOR_ ## name - subt); \
} else \
if (TEMP_SENSOR_ ## name <= 11) { \
ADC1->SQR2 |= pin ## _ADC << (5 * (TEMP_SENSOR_ ## name - subt)); \
} else { \
ADC1->SQR1 |= pin ## _ADC << (5 * (TEMP_SENSOR_ ## name - subt)); \
} \
}
#include "config_wrapper.h"
#undef DEFINE_TEMP_SENSOR
ADC1->CR2 |= ADC_CR2_CONT;
ADC1->CR2 |= ADC_CR2_ADON; // A/D Converter ON / OFF
ADC1->CR2 |= ADC_CR2_SWSTART;
}
/** Init the DMA for ADC
*/
void init_dma() {
RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN; // Enable clock
/**
We have two DMA streams for ADC1. (DMA2_Stream0 and DMA2_Stream4)
We take DMA2 Stream4.
See reference manual 9.3.3 channel selection (p. 166)
*/
// 1. Disable DMA-Stream
DMA2_Stream4->CR &= ~DMA_SxCR_EN;
while(DMA2_Stream4->CR & DMA_SxCR_EN); // we wait until it is disabled.
uint32_t tmp_CR = 0; //DMA2_Stream4->CR;
// 2. perihperal port register address
DMA2_Stream4->PAR = (uint32_t)&ADC1->DR;
// 3. memory address
DMA2_Stream4->M0AR = (uint32_t)&adc_buffer;
// 4. total number of data items
DMA2_Stream4->NDTR = NUM_TEMP_SENSORS * OVERSAMPLE;
// 5. DMA channel
// channel 0
tmp_CR &= ~(DMA_SxCR_CHSEL);
// 6.
// 7. priority
// Very high
tmp_CR |= DMA_SxCR_PL;
// 8. FIFO
DMA2_Stream4->FCR &= ~(DMA_SxFCR_DMDIS);
// 9. config the rest
/*
* halfword for memory and periphal: the 12bit ADC is 16bit right aligned
* memory inc.: we read any adc and doing a step of 16bits after each conversion
* circular mode: repeat until inf
*/
tmp_CR &= ~(DMA_SxCR_DIR);
tmp_CR |= DMA_SxCR_MSIZE_0 |
DMA_SxCR_PSIZE_0 |
DMA_SxCR_MINC |
DMA_SxCR_CIRC;
DMA2_Stream4->CR = tmp_CR;
// 10. Enable DMA-Stream
DMA2_Stream4->CR |= DMA_SxCR_EN;
while(!(DMA2_Stream4->CR & DMA_SxCR_EN));
}
/** Read analog value.
\param channel Channel to be read.
\return Analog reading, 10-bit right aligned.
*/
uint16_t analog_read(uint8_t index) {
if (NUM_TEMP_SENSORS > 0) {
uint16_t r = 0;
uint16_t temp;
uint16_t max_temp = 0;
uint16_t min_temp = 0xffff;
for (uint8_t i = 0; i < OVERSAMPLE; i++) {
temp = adc_buffer[index + NUM_TEMP_SENSORS * i];
max_temp = max_temp > temp ? max_temp : temp;
min_temp = min_temp < temp ? min_temp : temp;
r += temp;
}
r = (r - max_temp - min_temp) / (OVERSAMPLE - 2);
return r;
} else {
return 0;
}
// memset(adc_buffer[!(DMA2_Stream4->CR & DMA_SxCR_CT)], 0, sizeof(adc_buffer[0]));
}
/**
Start a new ADC conversion.
*/
void start_adc() {
ADC1->CR2 &= ~(ADC_CR2_DMA);
ADC1->CR2 |= ADC_CR2_DMA;
}
#endif /* defined TEACUP_C_INCLUDE && defined __ARM_STM32F411__ */