reflow-oven-control-sw/stm-firmware/adc-meas.c

315 lines
7.7 KiB
C

#include <adc-meas.h>
#include <stm32f4xx.h>
#include <core_cm4.h>
#include <stm32-gpio-macros.h>
#include <stdlib.h>
#include <clock-enable-manager.h>
static float pt1000_offset;
static float pt1000_sens_dev;
static bool calibration_active;
static float filter_alpha;
static volatile float pt1000_res_raw_lf;
static volatile bool filter_ready;
static volatile enum adc_pt1000_error pt1000_error;
static volatile uint8_t * volatile streaming_flag_ptr = NULL;
static uint32_t filter_startup_cnt;
static volatile float adc_pt1000_raw_reading_hf;
static volatile uint16_t dma_sample_buffer[ADC_PT1000_DMA_AVG_SAMPLES];
#define ADC_TO_RES(adc) ((float)(adc) / 4096.0f * 2500.0f)
static inline void adc_pt1000_stop_sample_frequency_timer()
{
TIM2->CR1 &= ~TIM_CR1_CEN;
rcc_manager_disable_clock(&RCC->APB1ENR, BITMASK_TO_BITNO(RCC_APB1ENR_TIM2EN));
}
static inline void adc_pt1000_setup_sample_frequency_timer()
{
rcc_manager_enable_clock(&RCC->APB1ENR, BITMASK_TO_BITNO(RCC_APB1ENR_TIM2EN));
/* Divide 42 MHz peripheral clock by 42 */
TIM2->PSC = (42UL-1UL);
/* Reload value */
TIM2->ARR = ADC_PT1000_SAMPLE_CNT_DELAY;
/* Trigger output at update event */
TIM2->CR2 = TIM_CR2_MMS_1;
/* Start Timer in downcounting mode with autoreload */
TIM2->CR1 = TIM_CR1_DIR | TIM_CR1_CEN;
}
static inline void adc_pt1000_disable_adc()
{
ADC1->CR2 &= ~ADC_CR2_ADON;
DMA2_Stream0->CR = 0;
rcc_manager_disable_clock(&RCC->APB2ENR, BITMASK_TO_BITNO(RCC_APB2ENR_ADC1EN));
rcc_manager_disable_clock(&RCC->AHB1ENR, BITMASK_TO_BITNO(ADC_PT1000_PORT_RCC_MASK));
}
/**
* @brief Enable DMA Stream for ADC
*
* DMA2 Stream 0 is used. It will capture @ref ADC_PT1000_DMA_AVG_SAMPLES measurement values,
* delete the two most extreme values
* and calculate the avereage over the remaining values. This ensures, that one time errors are
* not included in the measurement.
*
* After that, the moving average filter is fed with the values.
*
*/
static inline void adc_pt1000_enable_dma_stream()
{
/* Enable peripheral clock for DMA2 */
rcc_manager_enable_clock(&RCC->AHB1ENR, BITMASK_TO_BITNO(RCC_AHB1ENR_DMA2EN));
/* Destination is the DMA sample buffer */
DMA2_Stream0->M0AR = (uint32_t)dma_sample_buffer;
/* Source is the ADC data register */
DMA2_Stream0->PAR = (uint32_t)&ADC1->DR;
/* Transfer size is ADC_PT1000_DMA_AVG_SAMPLES */
DMA2_Stream0->NDTR = ADC_PT1000_DMA_AVG_SAMPLES;
NVIC_EnableIRQ(DMA2_Stream0_IRQn);
/* Enable the stream in Peripheral-to-Memory mode with 16 bit data and a circular destination buffer
* Enable interrupt generation on transfer complete
*
* Todo: Maybe use twice as big of a buffer and also use half-fill interrupt in order to prevent overruns
*/
DMA2_Stream0->CR = DMA_SxCR_PL_1 | DMA_SxCR_MSIZE_0 | DMA_SxCR_PSIZE_0 | DMA_SxCR_MINC |
DMA_SxCR_CIRC | DMA_SxCR_TCIE | DMA_SxCR_EN;
}
static inline void adc_pt1000_disable_dma_stream()
{
/* Disable the stream */
DMA2_Stream0->CR = 0;
/* Disable clock if necessary */
rcc_manager_disable_clock(&RCC->AHB1ENR, BITMASK_TO_BITNO(RCC_AHB1ENR_DMA2EN));
/* Disable interrupt */
NVIC_DisableIRQ(DMA2_Stream0_IRQn);
}
void adc_pt1000_setup_meas()
{
rcc_manager_enable_clock(&RCC->APB2ENR, BITMASK_TO_BITNO(RCC_APB2ENR_ADC1EN));
rcc_manager_enable_clock(&RCC->AHB1ENR, BITMASK_TO_BITNO(ADC_PT1000_PORT_RCC_MASK));
ADC_PT1000_PORT->MODER |= ANALOG(ADC_PT1000_PIN);
/* Set S&H time for PT1000 ADC channel */
#if ADC_PT1000_CHANNEL < 10
ADC1->SMPR2 |= (7U << (3*ADC_PT1000_CHANNEL));
#else
ADC1->SMPR1 |= (7U << (3*(ADC_PT1000_CHANNEL-10)));
#endif
ADC->CCR |= (0x2<<16);
/* Set watchdog limits */
ADC1->HTR = ADC_PT1000_UPPER_WATCHDOG;
ADC1->LTR = ADC_PT1000_LOWER_WATCHDOG;
/* Set length of sequence to 1 */
ADC1->SQR1 = (0UL<<20);
/* Set channel as 1st element in sequence */
ADC1->SQR3 = (ADC_PT1000_CHANNEL<<0);
ADC1->CR1 = ADC_CR1_OVRIE | ADC_CR1_AWDEN | ADC_CR1_AWDIE;
ADC1->CR2 = ADC_CR2_EXTEN_0 | ADC_CR2_EXTSEL_2 | ADC_CR2_EXTSEL_1 | ADC_CR2_ADON | ADC_CR2_DMA | ADC_CR2_DDS;
adc_pt1000_set_moving_average_filter_param(ADC_PT1000_FILTER_WEIGHT);
adc_pt1000_set_resistance_calibration(0, 0, false);
pt1000_res_raw_lf = 0.0f;
NVIC_EnableIRQ(ADC_IRQn);
adc_pt1000_enable_dma_stream();
adc_pt1000_setup_sample_frequency_timer();
}
void adc_pt1000_set_moving_average_filter_param(float alpha)
{
filter_alpha = alpha;
filter_ready = false;
filter_startup_cnt = ADC_FILTER_STARTUP_CYCLES;
}
void adc_pt1000_set_resistance_calibration(float offset, float sensitivity_deviation, bool active)
{
pt1000_offset = offset;
pt1000_sens_dev = sensitivity_deviation;
calibration_active = active;
}
void adc_pt1000_get_resistance_calibration(float *offset, float *sensitivity_deviation, bool *active)
{
if (!offset || !sensitivity_deviation || !active)
return;
*offset = pt1000_offset;
*sensitivity_deviation = pt1000_sens_dev;
*active = calibration_active;
}
static inline float adc_pt1000_apply_calibration(float raw_resistance)
{
if (calibration_active)
return pt1000_res_raw_lf * (1.0f + pt1000_sens_dev) + pt1000_offset;
else
return raw_resistance;
}
int adc_pt1000_get_current_resistance(float *resistance)
{
int ret_val = 0;
if (!resistance)
return -1001;
*resistance = adc_pt1000_apply_calibration(pt1000_res_raw_lf);
if (adc_pt1000_check_error()) {
ret_val = -100;
goto return_value;
}
if (!filter_ready) {
ret_val = 2;
goto return_value;
}
return_value:
return ret_val;
}
int adc_pt1000_stream_raw_value_to_memory(uint16_t *adc_array, uint32_t length, volatile uint8_t *flag_to_set)
{
}
void adc_pt1000_convert_raw_value_array_to_resistance(float *resistance_dest, uint16_t *raw_source, uint32_t count)
{
}
enum adc_pt1000_error adc_pt1000_check_error()
{
return pt1000_error;
}
void adc_pt1000_clear_error()
{
pt1000_error = ADC_PT1000_NO_ERR;
}
void adc_pt1000_disable()
{
adc_pt1000_disable_adc();
adc_pt1000_stop_sample_frequency_timer();
adc_pt1000_disable_dma_stream();
filter_ready = false;
pt1000_res_raw_lf = 0.0f;
if (streaming_flag_ptr) {
*streaming_flag_ptr = -3;
streaming_flag_ptr = NULL;
}
}
static inline __attribute__((optimize("O3"))) void adc_pt1000_filter(float adc_prefiltered_value)
{
if (!filter_ready && --filter_startup_cnt <= 0)
filter_ready = true;
pt1000_res_raw_lf = (1.0f-filter_alpha) * pt1000_res_raw_lf + filter_alpha * ADC_TO_RES(adc_prefiltered_value);
}
static inline __attribute__((optimize("O3"))) float adc_pt1000_dma_avg_pre_filter()
{
int i;
uint32_t sum = 0;
uint16_t max_val = 0U;
uint16_t min_val = 65535U;
uint16_t sample;
for (i = 0; i < ADC_PT1000_DMA_AVG_SAMPLES; i++) {
sample = dma_sample_buffer[i];
/* Update min and max trackers */
max_val = (sample > max_val ? sample : max_val);
min_val = (sample < min_val ? sample : min_val);
/* Sum up all values (for average) */
sum += sample;
}
/* Delete max and min vals from sum */
sum = sum - (uint32_t)max_val - (uint32_t)min_val;
/* Divide to get average and return */
return (float)(sum / (ADC_PT1000_DMA_AVG_SAMPLES-2));
}
void ADC_IRQHandler(void)
{
uint32_t adc1_sr;
adc1_sr = ADC1->SR;
if (adc1_sr & ADC_SR_OVR) {
ADC1->SR &= ~ADC_SR_OVR;
pt1000_error |= ADC_PT1000_OVERFLOW;
/* Disable ADC in case of overrrun*/
adc_pt1000_disable();
if (streaming_flag_ptr) {
*streaming_flag_ptr = -1;
streaming_flag_ptr = NULL;
}
}
if (adc1_sr & ADC_SR_AWD) {
ADC1->SR &= ~ADC_SR_AWD;
pt1000_error |= ADC_PT1000_WATCHDOG_ERROR;
}
}
void DMA2_Stream0_IRQHandler()
{
uint32_t lisr;
float adc_val;
lisr = DMA2->LISR;
DMA2->LIFCR = lisr;
if (lisr & DMA_LISR_TCIF0) {
/* Samples Transfered */
adc_val = adc_pt1000_dma_avg_pre_filter();
adc_pt1000_raw_reading_hf = adc_val;
/* Call moving average filter */
adc_pt1000_filter(adc_val);
}
if (lisr & DMA_LISR_TEIF0) {
/* Wait for watchdog to kick in */
while(1);
}
}