diff --git a/stm-firmware/adc-meas.c b/stm-firmware/adc-meas.c index 9b50903..fe7dcd3 100644 --- a/stm-firmware/adc-meas.c +++ b/stm-firmware/adc-meas.c @@ -12,8 +12,11 @@ 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 dma_flag_ptr = NULL; +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) @@ -50,6 +53,54 @@ static inline void adc_pt1000_disable_adc() 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)); @@ -76,8 +127,8 @@ void adc_pt1000_setup_meas() /* Set channel as 1st element in sequence */ ADC1->SQR3 = (ADC_PT1000_CHANNEL<<0); - ADC1->CR1 = ADC_CR1_OVRIE | ADC_CR1_AWDEN | ADC_CR1_EOCIE; - ADC1->CR2 = ADC_CR2_EXTEN_0 | ADC_CR2_EXTSEL_2 | ADC_CR2_EXTSEL_1 | ADC_CR2_ADON; + 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); @@ -85,7 +136,7 @@ void adc_pt1000_setup_meas() NVIC_EnableIRQ(ADC_IRQn); - RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN; + adc_pt1000_enable_dma_stream(); adc_pt1000_setup_sample_frequency_timer(); } @@ -170,21 +221,49 @@ 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 (dma_flag_ptr) { - *dma_flag_ptr = -3; - dma_flag_ptr = NULL; + if (streaming_flag_ptr) { + *streaming_flag_ptr = -3; + streaming_flag_ptr = NULL; } } -static inline __attribute__((optimize("O3"))) void adc_pt1000_filter(uint16_t adc_value) +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-filter_alpha) * pt1000_res_raw_lf + filter_alpha * ADC_TO_RES((float)adc_value); + 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) @@ -192,19 +271,15 @@ void ADC_IRQHandler(void) uint32_t adc1_sr; adc1_sr = ADC1->SR; - if (ADC1->SR & ADC_SR_EOC) { - ADC1->SR &= ~ADC_SR_EOC; - adc_pt1000_filter(ADC1->DR); - } 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 (dma_flag_ptr) { - *dma_flag_ptr = -1; - dma_flag_ptr = NULL; + if (streaming_flag_ptr) { + *streaming_flag_ptr = -1; + streaming_flag_ptr = NULL; } } @@ -217,18 +292,23 @@ void ADC_IRQHandler(void) void DMA2_Stream0_IRQHandler() { uint32_t lisr; + float adc_val; lisr = DMA2->LISR; DMA2->LIFCR = lisr; - if (lisr & DMA_LISR_TCIF0 && dma_flag_ptr) { - *dma_flag_ptr = 1; - dma_flag_ptr = NULL; + 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 && dma_flag_ptr) { - *dma_flag_ptr = -2; - dma_flag_ptr = NULL; + if (lisr & DMA_LISR_TEIF0) { + /* Wait for watchdog to kick in */ + while(1); } } diff --git a/stm-firmware/include/adc-meas.h b/stm-firmware/include/adc-meas.h index 053e07b..37b8f91 100644 --- a/stm-firmware/include/adc-meas.h +++ b/stm-firmware/include/adc-meas.h @@ -12,30 +12,32 @@ /** * @brief ADC channel number of PT1000 sensor input */ -#define ADC_PT1000_CHANNEL 2 +#define ADC_PT1000_CHANNEL 2U #define ADC_PT1000_PORT GPIOA #define ADC_PT1000_PORT_RCC_MASK RCC_AHB1ENR_GPIOAEN -#define ADC_PT1000_PIN 2 +#define ADC_PT1000_PIN 2U -#define ADC_FILTER_STARTUP_CYCLES 800 +#define ADC_FILTER_STARTUP_CYCLES 800U -#define ADC_PT1000_SAMPLE_CNT_DELAY 2000 +#define ADC_PT1000_SAMPLE_CNT_DELAY 1000U + +#define ADC_PT1000_DMA_AVG_SAMPLES 6U /** * @brief Lower value for valid input range for PT1000 measurement * * If the input of the PT1000 sensor is below this value, an error is thrown. This is used to disable the temperature control loop */ -#define ADC_PT1000_LOWER_WATCHDOG 100 +#define ADC_PT1000_LOWER_WATCHDOG 200U /** * @brief Upper value for valid input range for PT1000 measurement * * If the input of the PT1000 sensor is above this value, an error is thrown. This is used to disable the temperature control loop */ -#define ADC_PT1000_UPPER_WATCHDOG 4000 +#define ADC_PT1000_UPPER_WATCHDOG 4000U enum adc_pt1000_error {ADC_PT1000_NO_ERR= 0, ADC_PT1000_WATCHDOG_ERROR=(1UL<<0), ADC_PT1000_OVERFLOW=(1UL<<1)}; diff --git a/stm-firmware/include/uart/dma-ring-buffer.h b/stm-firmware/include/uart/dma-ring-buffer.h new file mode 100644 index 0000000..4b4bb93 --- /dev/null +++ b/stm-firmware/include/uart/dma-ring-buffer.h @@ -0,0 +1,4 @@ +#ifndef __DMA_RING_BUFFER_H__ +#define __DMA_RING_BUFFER_H__ + +#endif /* __DMA_RING_BUFFER_H__ */ diff --git a/stm-firmware/main.c b/stm-firmware/main.c index 42751a2..a842acb 100644 --- a/stm-firmware/main.c +++ b/stm-firmware/main.c @@ -20,8 +20,8 @@ static void setup_nvic_priorities() NVIC_SetPriorityGrouping(2); /* Setup Priorities */ - NVIC_SetPriority(ADC_IRQn, 1); - NVIC_SetPriority(DMA2_Stream0_IRQn, 2); + NVIC_SetPriority(ADC_IRQn, 2); + NVIC_SetPriority(DMA2_Stream0_IRQn, 1); } static float pt1000_value; diff --git a/stm-firmware/uart/dma-ring-buffer.c b/stm-firmware/uart/dma-ring-buffer.c new file mode 100644 index 0000000..48e68d9 --- /dev/null +++ b/stm-firmware/uart/dma-ring-buffer.c @@ -0,0 +1,2 @@ +#include +