219 lines
5.5 KiB
C
219 lines
5.5 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"
|
|
|
|
// DMA ADC-buffer
|
|
#define OVERSAMPLE 6
|
|
static uint16_t BSS adc_buffer[OVERSAMPLE][NUM_TEMP_SENSORS];
|
|
|
|
// 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() {
|
|
|
|
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN; //Enable clock
|
|
|
|
#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
|
|
|
|
/* 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
|
|
tmp_CR &= ~(DMA_SxCR_CHSEL);
|
|
|
|
// 7. priority
|
|
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 |
|
|
DMA_SxCR_TCIE;
|
|
|
|
DMA2_Stream4->CR = tmp_CR;
|
|
|
|
// 10. Enable DMA-Stream
|
|
DMA2_Stream4->CR |= DMA_SxCR_EN;
|
|
while(!(DMA2_Stream4->CR & DMA_SxCR_EN));
|
|
|
|
NVIC_SetPriority(DMA2_Stream4_IRQn,
|
|
NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 3, 3));
|
|
NVIC_EnableIRQ(DMA2_Stream4_IRQn); // Enable interrupt generally.
|
|
|
|
}
|
|
|
|
/**
|
|
DMA2 Stream4 interrupt.
|
|
|
|
Happens every time the complete stream is written.
|
|
In that case we stop the continious conversion and clear the DMA bit.
|
|
|
|
Must have the same name as in cmsis-startup_stm32f411xe.s.
|
|
*/
|
|
void DMA2_Stream4_IRQHandler(void) {
|
|
DMA2->HIFCR = DMA_HIFCR_CTCIF4;
|
|
ADC1->CR2 &= ~(ADC_CR2_CONT | ADC_CR2_DMA);
|
|
}
|
|
|
|
/** 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[i][index];
|
|
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;
|
|
}
|
|
}
|
|
|
|
/**
|
|
Start a new ADC conversion.
|
|
*/
|
|
void start_adc() {
|
|
/* To restart the DMA, clear (done in the TCI) and set the DMA bit.
|
|
Then enable the continious conversion and start the ADC again.
|
|
*/
|
|
ADC1->CR2 |= ADC_CR2_DMA |
|
|
ADC_CR2_CONT |
|
|
ADC_CR2_SWSTART;
|
|
}
|
|
|
|
#endif /* defined TEACUP_C_INCLUDE && defined __ARM_STM32F411__ */
|