diff --git a/stm-firmware/adc-meas.c b/stm-firmware/adc-meas.c index 657ba81..f52e173 100644 --- a/stm-firmware/adc-meas.c +++ b/stm-firmware/adc-meas.c @@ -1,30 +1,98 @@ #include #include +#include #include +#include static float pt1000_offset; static float pt1000_sens_dev; static bool calibration_active; static float filter_alpha; -static float pt1000_adc_raw_lf; -static bool streaming_active; +static volatile float pt1000_res_raw_lf; +static volatile bool streaming_active; static volatile bool filter_ready; -static volatile enum adc_p1000_error pt1000_error; - +static volatile enum adc_pt1000_error pt1000_error; +static volatile uint8_t *dma_flag_ptr = NULL; +static uint32_t filter_startup_cnt; #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->APB1ENR &= ~RCC_APB1ENR_TIM2EN; +} + +static inline void adc_pt1000_setup_sample_frequency_timer() +{ + RCC->APB1ENR |= 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->APB2ENR &= ~RCC_APB2ENR_ADC1EN; +} + void adc_pt1000_setup_meas() { RCC->APB2ENR |= RCC_APB2ENR_ADC1EN; RCC->AHB1ENR |= 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_EOCIE; + ADC1->CR2 = ADC_CR2_EXTEN_0 | ADC_CR2_EXTSEL_2 | ADC_CR2_EXTSEL_1 | ADC_CR2_ADON; + + adc_pt1000_set_moving_average_filter_param(ADC_PT1000_FILTER_WEIGHT); + adc_pt1000_set_resistance_calibration(0, 0, false); + streaming_active = false; + pt1000_res_raw_lf = 0.0f; + + NVIC_EnableIRQ(ADC_IRQn); + + RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN; + + 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) @@ -44,6 +112,15 @@ void adc_pt1000_get_resistance_calibration(float *offset, float *sensitivity_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; @@ -51,24 +128,22 @@ int adc_pt1000_get_current_resistance(float *resistance) if (!resistance) return -1001; - if (calibration_active) - *resistance = ADC_TO_RES(pt1000_adc_raw_lf) * (1 + pt1000_sens_dev) + pt1000_offset; - else - *resistance = ADC_TO_RES(pt1000_adc_raw_lf); + *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; + } + if (streaming_active) { ret_val = 1; goto return_value; } - if (!filter_ready) - { - ret_val = 2; - } return_value: return ret_val; @@ -76,7 +151,44 @@ return_value: int adc_pt1000_stream_raw_value_to_memory(uint16_t *adc_array, uint32_t length, volatile uint8_t *flag_to_set) { + static volatile uint8_t alt_flag; + int ret_val = 0; + if (!(ADC1->CR2 & ADC_CR2_ADON)) + return -1; + + if (!adc_array || !length) + return -1000; + + if (flag_to_set) + dma_flag_ptr = flag_to_set; + else + dma_flag_ptr = &alt_flag; + + *dma_flag_ptr = 0; + streaming_active = true; + + ADC1->CR2 &= ~ADC_CR2_ADON; + DMA2_Stream0->CR &= ~DMA_SxCR_EN; + + DMA2_Stream0->M0AR = (uint32_t)adc_array; + DMA2_Stream0->PAR = (uint32_t)&ADC1->DR; + + DMA2_Stream0->CR = DMA_SxCR_PL_1 | DMA_SxCR_MSIZE_0 | DMA_SxCR_PSIZE_0 | DMA_SxCR_MINC | DMA_SxCR_TCIE; + DMA2_Stream0->NDTR = length; + NVIC_EnableIRQ(DMA2_Stream0_IRQn); + + DMA2_Stream0->CR |= DMA_SxCR_EN; + ADC1->CR2 |= ADC_CR2_ADON | ADC_CR2_DMA; + + if (!flag_to_set) { + while(!alt_flag); + + if (alt_flag < 0) + ret_val = -alt_flag; + } + + return ret_val; } void adc_pt1000_convert_raw_value_array_to_resistance(float *resistance_dest, uint16_t *raw_source, uint32_t count) @@ -84,7 +196,7 @@ void adc_pt1000_convert_raw_value_array_to_resistance(float *resistance_dest, ui } -enum adc_p1000_error adc_pt1000_check_error() +enum adc_pt1000_error adc_pt1000_check_error() { return pt1000_error; } @@ -94,9 +206,20 @@ void adc_pt1000_clear_error() pt1000_error = ADC_PT1000_NO_ERR; } -static void adc_pt1000_filter(uint16_t adc_value) +void adc_pt1000_disable() { - pt1000_adc_raw_lf = (1-filter_alpha) * pt1000_adc_raw_lf + filter_alpha * (float)adc_value; + adc_pt1000_disable_adc(); + adc_pt1000_stop_sample_frequency_timer(); + filter_ready = false; + pt1000_res_raw_lf = 0.0f; +} + +static inline __attribute__((optimize("O3"))) void adc_pt1000_filter(uint16_t adc_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); } void ADC_IRQHandler(void) @@ -111,13 +234,38 @@ void ADC_IRQHandler(void) if (adc1_sr & ADC_SR_OVR) { ADC1->SR &= ~ADC_SR_OVR; - pt1000_error = ADC_PT1000_OVERFLOW; + pt1000_error |= ADC_PT1000_OVERFLOW; /* Disable ADC in case of overrrun*/ - ADC1->CR2 &= ADC_CR2_ADON; + adc_pt1000_disable(); + if (streaming_active) { + streaming_active = false; + *dma_flag_ptr = -1; + } } if (adc1_sr & ADC_SR_AWD) { ADC1->SR &= ~ADC_SR_AWD; - pt1000_error = ADC_PT1000_WATCHDOG_ERROR; + pt1000_error |= ADC_PT1000_WATCHDOG_ERROR; } } + +void DMA2_Stream0_IRQHandler() +{ + uint32_t lisr; + + lisr = DMA2->LISR; + DMA2->LIFCR = lisr; + + if (lisr & DMA_LISR_TCIF0) { + *dma_flag_ptr = 1; + ADC1->CR2 &= ~ADC_CR2_DMA; + streaming_active = false; + } + + if (lisr & DMA_LISR_TEIF0) { + *dma_flag_ptr = -2; + ADC1->CR2 &= ~ADC_CR2_DMA; + streaming_active = false; + } + +} diff --git a/stm-firmware/include/adc-meas.h b/stm-firmware/include/adc-meas.h index c0b8cd0..053e07b 100644 --- a/stm-firmware/include/adc-meas.h +++ b/stm-firmware/include/adc-meas.h @@ -19,7 +19,9 @@ #define ADC_PT1000_PIN 2 -#define ADC_FILTER_STARTUP_CYCLES 200 +#define ADC_FILTER_STARTUP_CYCLES 800 + +#define ADC_PT1000_SAMPLE_CNT_DELAY 2000 /** * @brief Lower value for valid input range for PT1000 measurement @@ -35,7 +37,7 @@ */ #define ADC_PT1000_UPPER_WATCHDOG 4000 -enum adc_p1000_error {ADC_PT1000_NO_ERR= 0, ADC_PT1000_WATCHDOG_ERROR=-1, ADC_PT1000_OVERFLOW=2}; +enum adc_pt1000_error {ADC_PT1000_NO_ERR= 0, ADC_PT1000_WATCHDOG_ERROR=(1UL<<0), ADC_PT1000_OVERFLOW=(1UL<<1)}; /** * @brief This function sets up the ADC measurement fo the external PT1000 temperature sensor @@ -86,8 +88,7 @@ void adc_pt1000_get_resistance_calibration(float *offset, float *sensitivity_dev * If the reistance calibration is enabled, this function applies the calculations of the raw reistance reading and * returns the corrected value. * - * If an ADC error is set, the status is negative. If the ADC is in streaming mode, the reistance reading is disabled and - * the status is 1. The status is 2 during the first measurements with a given filter setting. Technically, the resistance value is + * If an ADC error is set, the status is negative. The status is 2 during the first measurements with a given filter setting. Technically, the resistance value is * correct but the filter is not stable yet. * Use adc_pt1000_check_error to check the error and reinitialize the ADC. * @@ -102,7 +103,6 @@ int adc_pt1000_get_current_resistance(float *resistance); * * Streaming is done using DMA2 Stream0. * This function is used for gathering fullspeed sampling data for external interfaces or calibration - * During streaming, the adc_pt1000_get_current_resistance() function will return an error * * @param adc_array Array to stream data to * @param length Amount of data points to be measured @@ -118,8 +118,10 @@ void adc_pt1000_convert_raw_value_array_to_resistance(float *resistance_dest, ui * * In case of an error, it may be necessary to call adc_pt1000_setup_meas() again in order to recover from the error */ -enum adc_p1000_error adc_pt1000_check_error(); +enum adc_pt1000_error adc_pt1000_check_error(); void adc_pt1000_clear_error(); +void adc_pt1000_disable(); + #endif // __ADCMEAS_H__ diff --git a/stm-firmware/main.c b/stm-firmware/main.c index 4288212..dfe9fa6 100644 --- a/stm-firmware/main.c +++ b/stm-firmware/main.c @@ -7,120 +7,39 @@ #include #include //#include +#include #include #include #include #include -#define OUTPUT(pin) (0b01 << (pin * 2)) -#define ANALOG(pin) (0x03 << (pin * 2)) - -struct adc_conversions { - uint16_t pa2_raw; - uint16_t ref_raw; - uint16_t temp_raw; - uint16_t vbat_raw; -}; - -static volatile struct adc_conversions adc_results; - -volatile uint64_t sample_count = 0; -volatile uint8_t new_data = 0; - - -void DMA2_Stream0_IRQHandler() +static void setup_nvic_priorities() { - uint32_t lisr; - - lisr = DMA2->LISR; - DMA2->LIFCR = lisr; - - if (lisr & DMA_LISR_TCIF0) { - if (new_data) - new_data = 2; - new_data = 1; - sample_count++; - - GPIOB->ODR ^= (1<<3); - } + /* No sub priorities */ + NVIC_SetPriorityGrouping(2); + /* Setup Priorities */ + NVIC_SetPriority(ADC_IRQn, 1); + NVIC_SetPriority(DMA2_Stream0_IRQn, 2); } -void setup_dma(void *dest, size_t size) -{ - RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN; - - DMA2_Stream0->M0AR = (uint32_t)dest; - DMA2_Stream0->PAR = (uint32_t)&ADC1->DR; - DMA2_Stream0->CR = DMA_SxCR_PL_1 | DMA_SxCR_MSIZE_0 | DMA_SxCR_PSIZE_0 | DMA_SxCR_MINC | - DMA_SxCR_CIRC | DMA_SxCR_TCIE; - DMA2_Stream0->NDTR = size; - NVIC_EnableIRQ(DMA2_Stream0_IRQn); - - DMA2_Stream0->CR |= DMA_SxCR_EN; - new_data = 0; -} - -float ext_lf_corr; - -float temp_lf_corr; - -float ref_lf; -float vdd_calculated = 3.3f; - -float vbat_lf_corr; - - - -static void setup_timers(void) -{ -} - +static float pt1000_value; +static volatile int pt1000_value_status; int main() { - struct adc_conversions working; - - RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN; - RCC->APB2ENR |= RCC_APB2ENR_ADC1EN; RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN; __DSB(); GPIOB->MODER = OUTPUT(2) | OUTPUT(3); - GPIOA->MODER |= ANALOG(2); GPIOB->ODR |= (1<<2); - ADC1->SMPR2 = (7U<<(3*2)); - ADC1->SMPR1 = (7U<<18) | (7U<<21) | (7U<<24); - ADC1->SQR1 = (2<<20); - ADC1->SQR3 = (2<<0) | (16<<(5*2)) | (17<<(5*1)); - - ADC->CCR |= (0x2<<16) | ADC_CCR_TSVREFE; - ADC1->CR1 = ADC_CR1_SCAN; - ADC1->CR2 = ADC_CR2_ADON | ADC_CR2_CONT | ADC_CR2_DMA | ADC_CR2_DDS; - - //while(1); - + setup_nvic_priorities(); systick_setup(); - setup_dma(&adc_results, 3); + //setup_dma(&adc_results, 3); adc_pt1000_setup_meas(); - ADC1->CR2 |= ADC_CR2_SWSTART; - while(1) { - if (!new_data) { - continue; - } - - - memcpy(&working, &adc_results, sizeof(adc_results)); - new_data = 0; - //ref_lf = 0.995f * ref_lf + 0.005f * (float)working.ref_raw; - //vdd_calculated = ((1.21f * 4096)/ ref_lf); - - //temp_lf_corr = 0.99f * temp_lf_corr + 0.01 * (float)working.temp_raw * vdd_calculated / 2.495f; - ext_lf_corr = 0.995f * ext_lf_corr + 0.005f * (float)working.pa2_raw / 4096 * 2500.0f; // * vdd_calculated / 2.495f; - - //vbat_lf_corr = 0.99 * vbat_lf_corr + 0.01 * (float)working.vbat_raw / 4096 * vdd_calculated * 2.0f; + pt1000_value_status = adc_pt1000_get_current_resistance(&pt1000_value); } }