STM32 ADC NTC热敏电阻二分(折半)查表法实现测温功能

本文主要描述 - STM32 ADC NTC热敏电阻二分(折半)查表法测温功能的思路和代码实现

NTC的相关属性:R25=10K±3% B25/50=4100K±3% 10K上拉

STM32 ADC实现NTC测温的电路示意图如下:

STM32 ADC NTC热敏电阻二分(折半)查表法实现测温功能_第1张图片

STM32的ADC分辨率为12位,模数转换的范围 0~4095(0x000~0xFFF)

STM32 ADC NTC热敏电阻二分(折半)查表法实现测温功能_第2张图片

针对以上描述的NTC属性以及电路,对应的温度和测量的数字量的关系表:

static const uint16 R10K_TAB[] = { //R25=10K±3% B25/50=4100K±3% 10K上拉
 3738,3719,3698,3677,3655,3631,3607,3582,3556,3530,   //-20℃ ... -11℃
  3502,3473,3443,3412,3381,3348,3314,3280,3244,3208,   //-10℃ ...  -1℃
  3170,3132,3093,3053,3012,2970,2928,2885,2842,2797,   //  0℃ ...   9℃
  2752,2707,2661,2615,2568,2522,2474,2427,2379,2332,   // 10℃ ...  19℃
  2284,2237,2189,2142,2095,2048,2001,1954,1908,1863,   // 20℃ ...  29℃
  1818,1773,1729,1685,1642,1600,1558,1517,1477,1437,   // 30℃ ...  39℃
  1398,1360,1323,1286,1250,1215,1180,1147,1114,1082,   // 40℃ ...  49℃
  1051,1020, 990, 961, 933, 905, 878, 852, 827, 802,   // 50℃ ...  59℃
   778, 755, 732, 710, 688, 668, 647, 628, 609, 590,   // 60℃ ...  69℃
   572, 555, 538, 522, 506, 491, 476, 461, 447, 434,   // 70℃ ...  79℃
   421, 408, 395, 384, 372, 361, 350, 340, 330, 320,   // 80℃ ...  89℃
   311, 302, 293, 284, 276, 268, 261, 253, 246, 239,   // 90℃ ...  99℃
   233, 226, 220, 214, 209, 203                         //100℃ ... 105℃
};

将数据表的数据放到Excel中查看曲线的形状

STM32 ADC NTC热敏电阻二分(折半)查表法实现测温功能_第3张图片

从图中可以看出,NTC的温度与数字量的关系比较接近一元线性关系,数字量越大,温度值越低,负相关。那么将两度之间的小数度数几乎可以认为就是线性的,下面介绍计算小数精度的方法按照线性处理。

在数据表中给出的数据,是整数温度对应的数字量,本次实验测量的精度为0.1℃,测量的数字量值很可能是在两个整数度中间,如ADC采样的数字量为 0x80C,十进制是2060,对应在数据表的2048(25℃)和2095(24℃)中间,计算方式按照线性处理如下:

STM32 ADC NTC热敏电阻二分(折半)查表法实现测温功能_第4张图片

关于二分(折半)查找的方法,肯定是要比逐次对比效率要高很多,至于效率高多少就不分析了,这里我按照二分查找的思路来进行。由于采样的ADC数字量可能在表中查找不到,但是可以查找出在哪个范围内,就如我上述举例的情况,采样是 2060,在2048和2095之间,至于怎么实现二分查找,参考一下下面的代码。

函数方法实现的思路,在第一图电路示意图中如果没有接NTC传感器,相当于是开路,ADC采样的值相当于电源电压;如果将NTC的两个端子用导线短接,ADC采样的值相当于GND;考虑到极限的情况温度低于-20℃,采样的数字量值不在数据表范围内,即大于3738;同样,如果实际测量温度高于105度,采样的数字量值不在数据表范围内,即小于203;其他的情况就属于正常可以查表的数字量。开路测量电源电压时,可能会稍低于0XFFF,我这里留出一些余量0X00F;短路测GND的电压数字量时,可能会稍微高于0X000,也留出余量0X00F。

用图来表示

STM32 ADC NTC热敏电阻二分(折半)查表法实现测温功能_第5张图片

下面是具体的代码实现:(代码中小数部分,通过*10的倍率来表示,即247代表24.7℃)

z_hardware_adc.c

#ifndef __Z_HARDWARE_ADC_H
#define __Z_HARDWARE_ADC_H
#include "z_hardware_adc.h"
#endif

#define SHORT_CIRCUIT_THRESHOLD 15
#define OPEN_CIRCUIT_THRESHOLD 4080

static const u16 R10K_TAB[] = { //R25=10K±3% B25/50=4100K±3% 10K??
  3738,3719,3698,3677,3655,3631,3607,3582,3556,3530,   //-20? ... -11?
  3502,3473,3443,3412,3381,3348,3314,3280,3244,3208,   //-10? ...  -1?
  3170,3132,3093,3053,3012,2970,2928,2885,2842,2797,   //  0? ...   9?
  2752,2707,2661,2615,2568,2522,2474,2427,2379,2332,   // 10? ...  19?
  2284,2237,2189,2142,2095,2048,2001,1954,1908,1863,   // 20? ...  29?
  1818,1773,1729,1685,1642,1600,1558,1517,1477,1437,   // 30? ...  39?
  1398,1360,1323,1286,1250,1215,1180,1147,1114,1082,   // 40? ...  49?
  1051,1020, 990, 961, 933, 905, 878, 852, 827, 802,   // 50? ...  59?
   778, 755, 732, 710, 688, 668, 647, 628, 609, 590,   // 60? ...  69?
   572, 555, 538, 522, 506, 491, 476, 461, 447, 434,   // 70? ...  79?
   421, 408, 395, 384, 372, 361, 350, 340, 330, 320,   // 80? ...  89?
   311, 302, 293, 284, 276, 268, 261, 253, 246, 239,   // 90? ...  99?
   233, 226, 220, 214, 209, 203                         //100? ... 105?
};

void init_adc(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	ADC_InitTypeDef ADC_InitStructure;
	ErrorStatus HSEStartUpStatus;
	
	RCC_DeInit();
	RCC_HSEConfig(RCC_HSE_ON);
	HSEStartUpStatus = RCC_WaitForHSEStartUp();
	if(HSEStartUpStatus == SUCCESS) 
  {
		RCC_HCLKConfig(RCC_SYSCLK_Div1);
		RCC_PCLK2Config(RCC_HCLK_Div1);
		RCC_PCLK1Config(RCC_HCLK_Div2);
		RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);
		RCC_PLLCmd(ENABLE);
		while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
		RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
		while(RCC_GetSYSCLKSource() != 0x08);
	}
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;//PC4
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	ADC_DeInit(ADC1);
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
	ADC_InitStructure.ADC_ScanConvMode = DISABLE;
	ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
	ADC_InitStructure.ADC_NbrOfChannel = 1;
	ADC_Init(ADC1, &ADC_InitStructure);
	ADC_Cmd(ADC1, ENABLE);
	
	ADC_ResetCalibration(ADC1);
	while(ADC_GetResetCalibrationStatus(ADC1));
	ADC_StartCalibration(ADC1);
	while(ADC_GetCalibrationStatus(ADC1));
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);
}


u16 func_get_adc_valve_ch7(void)
{	
	ADC_RegularChannelConfig(ADC1, ADC_Channel_7, 1, ADC_SampleTime_55Cycles5);
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);
	while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));
	return ADC_GetConversionValue(ADC1);
}

u8 func_get_ntc_temp(u16 value_adc, s16* value_temp)
{
	u8 index_l, index_r;
	u8 r10k_tab_size = 126;
	s32 temp = 0;
	if(value_adc <= SHORT_CIRCUIT_THRESHOLD)
	{
		return 1;
	}
	else if(value_adc >= OPEN_CIRCUIT_THRESHOLD)
	{
		return 2;
	}
	else if(value_adc > R10K_TAB[0])
	{
		return 3;
	}
	else if(value_adc < R10K_TAB[r10k_tab_size - 1])
	{
		return 4;
	}
	index_l = 0;
	index_r = r10k_tab_size - 1;
	for(;index_r - index_l > 1;)
	{
		if((value_adc <= R10K_TAB[index_l]) && (value_adc > R10K_TAB[(index_r + index_l)%2 == 0 ? (index_r + index_l)/2 : (index_r + index_l)/2 + 1]))
		{
			index_r = (index_r + index_l) % 2 == 0 ? (index_r + index_l)/2 : (index_r + index_l)/2 + 1;
		}
		else
		{
			index_l = (index_r + index_l)/2;
		}
	}
	if(R10K_TAB[index_l] == value_adc)
	{
		temp = (((s16)index_l) - 20)*10;//rate *10
	}
	else if(R10K_TAB[index_r] == value_adc)
	{
		temp = (((s16)index_r) - 20)*10;//rate *10
	}
	else
	{
		if(R10K_TAB[index_l] - R10K_TAB[index_r] == 0)
		{
			temp = (((s16)index_l) - 20)*10;//rate *10
		}
		else
		{
			temp = (((s16)index_l) - 20)*10 + ((R10K_TAB[index_l] - value_adc)*100 + 5)/10/(R10K_TAB[index_l] - R10K_TAB[index_r]);
		}		
	}
	*value_temp = temp;
	return 0;
}

z_hardware_adc.h

#ifndef __STM32F10X_H
#define __STM32F10X_H
#include "stm32f10x.h"
#endif

void init_adc(void);
u16 func_get_adc_valve_ch7(void);

u8 func_get_ntc_temp(u16 value_adc, s16* value_temp);

测试的主函数代码:

#ifndef __STM32F10X_H
#define __STM32F10X_H
#include "stm32f10x.h"
#endif

#ifndef __Z_UTIL_TIME_H
#define __Z_UTIL_TIME_H
#include "z_util_time.h"
#endif

#ifndef __Z_HARDWARE_USART2_H
#define __Z_HARDWARE_USART2_H
#include "z_hardware_usart2.h"
#endif

#ifndef __Z_HARDWARE_ADC_H
#define __Z_HARDWARE_ADC_H
#include "z_hardware_adc.h"
#endif

#ifndef __Z_HARDWARE_LED_H
#define __Z_HARDWARE_LED_H
#include "z_hardware_led.h"
#endif

int main()
{
	u8 buf[8];
	u16 val;
	s16 value_temp;
	u8 res;
	
	init_adc();
	init_hardware_usart2_dma(9600);
	init_led();
	
	val = func_get_adc_valve_ch7();
	
	for(;;)
	{ 
		val = func_get_adc_valve_ch7();
		res = func_get_ntc_temp(val, &value_temp);
		if(res == 0)
		{
			buf[0] = value_temp >> 8;
			buf[1] = value_temp;
			func_usart2_dma_send_bytes(buf, 2);
		}
				
		func_led1_on();
		delay_ms(1000);
		func_led1_off();
		delay_ms(1000);
	}
	
}

测试的效果,通过串口将16进制值打印出来如下:

STM32 ADC NTC热敏电阻二分(折半)查表法实现测温功能_第6张图片

你可能感兴趣的:(STM32,ADC,NTC,STM32,ADC,NTC,二分法,测温)