272 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			272 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| #include <adc-meas.h>
 | |
| #include <stm32f4xx.h>
 | |
| #include <core_cm4.h>
 | |
| #include <stm32-gpio-macros.h>
 | |
| #include <stdlib.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 streaming_active;
 | |
| static volatile bool filter_ready;
 | |
| 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)
 | |
| {
 | |
| 	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;
 | |
| 	}
 | |
| 
 | |
| 	if (streaming_active) {
 | |
| 		ret_val = 1;
 | |
| 		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)
 | |
| {
 | |
| 	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)
 | |
| {
 | |
| 
 | |
| }
 | |
| 
 | |
| 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();
 | |
| 	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)
 | |
| {
 | |
| 	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 (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;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 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;
 | |
| 	}
 | |
| 
 | |
| }
 |