热敏电阻NTC、PTC


Chapter1 热敏电阻NTC、PTC

原文链接:https://blog.csdn.net/qq_54564988/article/details/130693394

前言

NTC、PTC是什么?对于没有接触过NTC、PTC或刚接触过NTC、PTC的人来说,NTC、PTC是什么也不知道,当然,了解NTC、PTC概念也是比较简单的,但是当你去搜索资料,看到许许多多的懵懵懂懂的专业名词时,以及看到一些硬件,可能会出现些许傻眼,毕竟没接触过,满脑问号。对于初学者或急于敢项目的软件工程师,尽快初步了解,学习到基本原理,代码跑出正确的数据就是最好的。毕竟学习是渐进的,不可一蹴而就就能深入其原理。

NTC是负温度系数的简写,全称是Negative temperature coefficient.意思是随着温度的升高,电阻值呈现下降趋势。常用作温度传感器。这里有一个式子表示负温度系数的电阻值:

RT=R0*exp(B (1/T-1/T0))

RT为周围温度为T (K) 时的电阻值,R0是周围温度为T0 (K) 时的电阻值,注意这里的温度是开尔文温度。B为B常数. 请记住这个B常数,它也是材料常数,一般在25摄氏度下测得。B值和电阻的温度系数正相关,也就是B值越大,电阻的温度系数越高。而温度系数是指每增加1℃,电阻值的变化率。也就是说,B值越大,电阻值的变化随着温度的增加越多,灵敏度越高.

在这里插入图片描述

一、NTC和PTC是什么?

NTC、PTC都是热敏电阻,是特殊的电阻,可以随着温度的变化阻值,也可以说是一种传感器。

区别是NTC就是负温度系数热敏电阻,PTC就是正温度系数热敏电阻。

正温度系数热敏电阻(PTC):电阻值随着温度的升高而增大;

负温度系数热敏电阻(NTC):电阻值随着温度的升高而减小;

二、NTC和PTC的用途

1.NTC的用途:
用于温度检测,一般是测温型NTC
用于浪涌抑制,一般是功率型的NTC
2.PTC的用途有:
保护电路中,如过温保护,过流保护
启动电路中

三、B值

B值:材料常数,是用来表示NTC在工作温度范围内阻值随温度变化幅度的参数,与材料的成分和烧结工艺有关。B值通常数值(3435K、3950K)。

B值越大表明阻值随温度的升高降低得越快,B值越小则相反。

B值在本文章没有用到,只是为了了解。求温度也可以用温度系数B值求值法,也可以称开尔文温度算法。

四、R25

R25:25℃时NTC本体的电阻值。

五、原理分析

以NTC为例,一般原理图如下:

在这里插入图片描述
原理分析:

ADC功能是用来采集电压的。

R1和R2是串联电路,通过串联电阻的分压公式,有:

R=R1+R2;

由I=U/R=U/(R1+R2)则:

U1=IR1=U(R1/(R1+R2))

U2=IR2=U(R2/(R1+R2))

我们用到的则是U2=IR2=U(R2/(R1+R2))这个就行了。

又由ADC采集的数据转换成电压就是U2的电压,所以

U(R2/(R1+R2))=ADC/1024*U

这里1024是我用的单片机的ADC的分辨率为10位,即1024

这里我们知道 U=3.3v 也就是图中的VCC,R1的值是10k,R2是NTC所以暂时不知道它的值。U是可以抵掉的。

最终公式为:R2=ADC*R1/1024-ADC

即R2=ADC*10000/1024-ADC

得到R2的电阻值后我们就可以通过对比阻值表来得到温度了。阻值对照表一般购买后商家都会给。
在这里插入图片描述
接下来上代码,这里采用NTC的查表方式进行温度转换。这个代码只要把你的ADC值加上去,就可以用了。

const unsigned int temp_tab[]={
119520,113300,107450,101930,96730,91830,87210,82850,78730,74850,//-30到-21,
71180,67710,64430,61330,58400,55620,53000,50510,48160,45930,//-20到-11,
43810,41810,39910,38110,36400,34770,33230,31770,30380,29050,//-10到-1,
27800,26600,25460,24380,23350,22370,21440,20550,19700,18900,18130,//0-10,
17390,16690,16020,15390,14780,14200,13640,13110,12610,12120,//11-20,
11660,11220,10790,10390,10000,9630,9270,8930,8610,8300,//21-30,
8000,7710,7430,7170,6920,6670,6440,6220,6000,5800,//31-40,
5600,5410,5230,5050,4880,4720,4570,4420,4270,4130,//49-50,
4000,3870,3750,3630,3510,3400,3300,3190,3090,3000,//51-60,
2910,2820,2730,2650,2570,2490,2420,2350,2280,2210,//61-70,
2150,2090,2030,1970,1910,1860,1800,1750,1700,1660,//71-80,
1610,1570,1520,1480,1440,1400,1370,1330,1290,1260,//81-90
1230,1190,1160,1130,1100,1070,1050,1020,990,//91-99,
};
 
 
short ADC;//获取ntc的ADC值
short NTC_R;//ntc的电阻值
#define R1 10000
 
void get_temp()
{  
  short temp;
  short cnt;
 
  ADC= adc_get_value(ADC_CH_0);//获取ADC值
  printf("-----------ADC:%d \n\n",ADC);   
  
  NTC_R=ADC*R1/(1024-ADC);
 
  cnt = 0;
  temp = -30;
  do{
      if(temp_tab[cnt] < NTC_R){  //表值小于计算出来的电阻值,退出 得出温度
        break;
      }
      ++temp;
  }while(++cnt < sizeof(temp_tab)/4);//循环表的大小,即次数
 
  printf("NTC_R:%d temp:%d \n\n",NTC_R,temp);
}

Chapter2 手把手教你使用热敏电阻NTC,产品级精度±0.1℃以内,简单明了,内附源码详解,方便移植

原文链接:https://blog.csdn.net/FutureStudio1994/article/details/112189049

一、背景

前一段疫情期间,就考虑到用NTC来做测温功能,写在这里记录自己的成长历程,也分享出去供大家参考!

NTC(Negative Temperature Coefficient)是指随温度上升电阻呈指数关系减小、具有负温度系数的热敏电阻现象和材料。(与此相反的有PTC)

与温度相关的大部分开发均可使用NTC,现在市面上的电子体温计多数用到的就是NTC,额温枪内部热电堆传感器就包含了一个NTC来做采集环境温度的功能,市面上不少智能手环为了降低成本也采用NTC,更不必论温控加热水壶,环境温度检测仪等等。NTC在生活中的应用不胜枚举。那么我们这次就来使用NTC测温度,这里用了黑体恒温水槽来检验精度。实验表明精度可以达到±0.1℃以内。

二、实验原理

本次实验采用了国产的BLE芯片,具体型号这里不公布,看官佬爷只需关心实现原理即可,与实验相关的ADC是10bit精度,无论是使用哪一款MCU都可以参考本例程。

首先必须大胆的明白热敏电阻就是电阻。所以它符合电阻的物理特性。

这里P上接线图:
在这里插入图片描述
通过上图,我们可以知道电阻NTC所分得的电压Vntc即为:Vntc = Vcc * ( Rntc / (Rntc + Rm) ),于是有了Vntc去换算出ADCntc是轻而易举的。只是换算的时候需要关注MCU内部ADC的参考电压是多少?
因为我这里使用的是10bit的ADC,该ADC引脚内部参考电压为3.6V,因此ADCntc = ( Vntc / 3.6 ) * 1024。

①:Vntc = Vcc * ( Rntc / (Rntc + Rm) )
②:ADCntc = ( Vntc / 3.6 ) * 1024
这里需要知道的是:ADCntc是ADC引脚采集到的数值,Vcc和Rm是已知量,只有Rntc是未知量。
故由式①②求得Rntc,然后拿着Rntc去查表,查什么表呢?

三、表的制作

这里附上产品规格,这个RT表是开发NTC过程中一定要使用的,问厂家或者客服索要。不同NTC的数据是存在差异的。
在这里插入图片描述
通过表格可以知道我选用的NTC的规格:R(37℃)= 30Kohm,也就是讲当温度在37℃的时候,该热敏电阻NTC阻值在30K,故而分压电阻也选为30K,以提高精度。
需要仔细阅读表格发现其中的规律,这里的规律就是
1.温度范围为0 - 60℃;
2.温度从0到 60℃,总共有251个数据,按照次序编一个序号从0到250,该序号将会作为数组的下标;
3.温度范围在[32℃,42℃]内,序号每增加1,温度就增加0.05℃;
温度范围在[0℃,32℃]和[42℃,60℃],序号每增加1,温度就增加1℃;

四、代码的实现

1.制作表格
我们要做的把上面的表格数据复制到编译器中,做成一维数组。

/*************************************************
NTC的R值数据表    
表的数值随序号的增加而减小   
*************************************************/
#define  NTCTABNum	251

static float NTCTAB[NTCTABNum]={
163.3,155.2,147.5,140.3,133.4,127.0,120.9,115.1,109.6,104.4,99.48,94.83,90.42,86.24,82.28,
78.52,74.96,71.57,68.36,65.31,62.41,59.66,57.04,54.56,52.19,49.94,47.80,45.76,43.82,41.98,
40.22,38.54,36.94,36.86,36.79,36.71,36.63,36.56,36.48,36.40,36.32,36.25,36.17,36.10,36.02,
35.94,35.87,35.79,35.72,35.64,35.57,35.49,35.42,35.35,35.27,35.20,35.12,35.05,34.98,34.90,
34.83,34.76,34.69,34.61,34.54,34.47,34.40,34.32,34.25,34.18,34.11,34.04,33.97,33.90,33.83,
33.76,33.68,33.61,33.54,33.48,33.41,33.34,33.27,33.20,33.13,33.06,32.99,32.92,32.85,32.79,
32.72,32.65,32.58,32.51,32.45,32.38,32.31,32.25,32.18,32.11,32.05,31.98,31.91,31.85,31.78,
31.72,31.65,31.59,31.52,31.46,31.39,31.33,31.26,31.20,31.13,31.07,31.00,30.94,30.88,30.81,
30.75,30.69,30.62,30.56,30.50,30.43,30.37,30.31,30.25,30.19,30.12,30.06,30.00,29.94,29.88,
29.82,29.76,29.69,29.63,29.57,29.51,29.45,29.39,29.33,29.27,29.21,29.15,29.09,29.03,28.97,
28.91,28.86,28.80,28.74,28.68,28.62,28.56,28.50,28.45,28.39,28.33,28.27,28.22,28.16,28.10,
28.04,27.99,27.93,27.87,27.82,27.76,27.70,27.65,27.59,27.54,27.48,27.42,27.37,27.31,27.26,
27.20,27.15,27.09,27.04,26.98,26.93,26.87,26.82,26.77,26.71,26.66,26.60,26.55,26.50,26.44,
26.39,26.34,26.28,26.23,26.18,26.13,26.07,26.02,25.97,25.92,25.86,25.81,25.76,25.71,25.66,
25.61,25.55,25.50,25.45,25.40,25.35,25.30,25.25,25.20,25.15,25.10,25.05,25.00,24.95,24.90,
24.85,24.80,24.75,24.70,24.65,24.60,24.55,24.50,23.54,22.63,21.76,20.92,20.12,19.35,18.62,
17.92,17.25,16.61,15.99,15.40,14.84,14.30,13.78,13.28,12.80,12.34};
/*
接下来的处理就是围绕着计算出来的Rntc去查表格。
*/

2.写查表函数

/*================================================================================
*Function	Name 	:LookupTable
*Description  		:查表函数
*parameter			:1.*p 		:表头,即表的首地址
*					 2.tableNum :表格的元素的个数
*					 3.data 	:该变量在这里传入的是当前温度下NTC的阻值
*Return				:当前NTC阻值对应在表中的位置
================================================================================*/
//这里提供两种较易理解的查表方法
#if  1 
//第一种方法
uint8_t LookupTable(float *p , uint8_t tableNum , float data)
{
		uint16_t 	begin  = 0;   
		uint16_t 	end    = 0; 
		uint16_t 	middle = 0;  
		uint8_t 	i      = 0; 
		end = tableNum-1; 
		
		if(data >= p[begin])        	return begin;
		else if(data <= p[end])     	return end; 
		
		while(begin < end)  
		{
				middle = (begin+end)/2; 
				
				if(data == p[middle]) 							break; 
				if(data <  p[middle] && data > p[middle+1]) 	break;   
				if(data >  p[middle])  	end   = middle ;                      
				else                  	begin = middle ;      
				if(i++ > tableNum) 								break; 
		}
		if(begin > end)   				return 0;   
		
		return middle;
}

#else 
//第二种方法
uint8_t LookupTable(float *p,uint8_t tableNum,float data)
{
		uint8_t	i,index	= 0;
	
		for(i=0;i<(tableNum-1);i++)
		{
				if((data<p[i]) && (data>p[i+1]))
				index = i;	
		}
		return index;
}
#endif

3.获取AD值或R值

/*================================================================================
*Function	Name 	:GetADCAverage/GetRkohmAverage
*Description  		:获取多次采样的平均值
*parameter			:无
*Return				:平均的AD值
================================================================================*/
/* 这里附上伪代码,只走一个思路,每个parameter都有自己的想法
*  Get_Single_ADC_Value(); 是针对不同MCU的ADC单次采集接口函数
*/
float  GetADCAverage(void)
{
/*times是样本采样次数
* adc_average 是均值
*/
		for(t=0;t<times;t++)
		{
			temp_val += Get_Single_ADC_Value();
		}		
		adc_average = temp_val/times;

		return adc_average;	
}
float  GetRkohmAverage(void)
{
/*
①:Vntc = Vcc * ( Rntc  / (Rntc + Rm) )
②:ADCntc = ( Vntc / 3.6 ) * 1024
由公式①②得出Rntc的表达式,
其中ADCntc = GetADCAverage();
可以求出Rntc,拿着这个值去查表即可!
*/
}

4.获取温度粗值

/*================================================================================
*Function Name 		:GetRoughTemperature
*Description  		:由序号转化得出温度粗值
*parameter			:serialNum	:表的序号值
*Return				:roughTemp	:温度粗值
================================================================================*/
float GetRoughTemperature(uint8_t serialNum)
{
		float  roughTemp = 0;
	
		if(serialNum <= 32)			roughTemp = serialNum;
		else if(serialNum >= 232)	roughTemp = serialNum - 190;
		else						roughTemp = 0.05 * (serialNum - 32) + 32;  
		/*   eg:132-32=100  100*0.05=5  5+32=37  */
		
		return roughTemp;
}
/*该函数是观察RT表的规律得出的*/

5.获取温度精值

/*================================================================================
*Function	Name 	:GetAccuraryTemperature
*Description  		:由温度粗值得到温度精值
*parameter			:readRKohm		:读取到的电阻值
*Return				:accuraryTemp	:温度精值
================================================================================*/
/*== 可以精确计算到±0.1℃ ,例如36.57℃ ==*/
float GetAccuraryTemperature(float readRKohm) 	   //这里的返回值数据是要拿出去显示出来的
{
		float  	t0   = 0;
		float  	temp = 0;		
		float  	accuraryTemp = 0;
		uint8_t serialNum    = 0;  //查表得到的 AD值 或 R值 所在的位置

		if((readRKohm <= NTCTAB[0]) && (readRKohm > NTCTAB[NTCTABNum-1]))
		{
				serialNum = LookupTable(NTCTAB,NTCTABNum,readRKohm);
				t0 = GetRoughTemperature(serialNum);
				/*== 温度范围在32℃ -- 42℃ ==*/
				if((readRKohm <= NTCTAB[32]) && (readRKohm > NTCTAB[232]))
				temp = 0.05*(readRKohm-NTCTAB[serialNum])/(NTCTAB[serialNum+1]-NTCTAB[serialNum])+t0;
				/*== 温度范围在0℃ -- 32℃  以及  42℃ -- 60℃ ==*/	
				else	
				temp = 1*(readRKohm-NTCTAB[serialNum])/(NTCTAB[serialNum+1]-NTCTAB[serialNum])+t0;
		}
		
		accuraryTemp = temp;
		
		return accuraryTemp;
}
/****************************************************************
三个点,在坐标上的顺序依次为(X1,Y1),(X,Y),(X2,Y2)
已知(X1,Y1),(X2,Y2),求(X,Y)
两点式:(X-X1)/(Y-Y1) = (X2-X1)/(Y2-Y1)       
则:X = [(X2-X1)/(Y2-Y1 )]* (Y-Y1) + X1
由于已知(X1,Y1),(X2,Y2)为相邻两温度点  X2-X1 = 0.05
故:X = [0.05/(Y2-Y1 )]* (Y-Y1) + X1
或者X = 0.05 * (Y-Y1) / (Y2-Y1 ) + X1
其中X对应温度值 Y对应R值    这样可以把精度从RT表上的0.05提高到0.01
下图中的(Xi,Yi)就是这里描述的(X,Y);
****************************************************************/

在这里插入图片描述
6.温度数值送显

/*================================================================================
*Function	Name 	:GetDisplayTempValue
*Description  		:送显的温度数值
*parameter			:accuraryTemp :读取到的温度精值
*Return				:temp		  :温度精值*100
================================================================================*/
uint32_t GetDisplayTempValue(float accuraryTemp)
{
   uint32_t temp = 0;
	
   temp = GetAccuraryTemperature(accuraryTemp)*100;
	 
   return temp;
}
/****************************************************************
作用:我这里是拿着数据显示到OLED屏幕上的,设计上是要显示到小数点后两位的,
eg:36.57℃,   例如:exempli gratia → eg
而采集到的也是小数点后两位,为了方便处理显示函数这里将温度值乘以100,
拿着3657去取整取余分别将每一位显示出来,温度值在上一个函数(第5步)已经实现,这里只是为了送显; 
输入参数:float readRKohm这个参变量将代表  GetADCAverage(); 或者 GetRkohmAverage();
****************************************************************/

创建变量TempValue作为求得的目标温度值
TempValue= GetDisplayTempValue(GetAccuraryTemperature(GetRkohmAverage()));
这里调用的是GetRkohmAverage();故而查表的表格是NTC的RT表格;
或者
TempValue = GetDisplayTempValue(GetAccuraryTemperature(GetADCAverage()));
这里调用的是GetADCAverage();故而查表的表格是NTC的ADC表格;
本文中只设计了RT表没有制作对应的AD表,可以用Excel表格将RT表换算得出对应的AD表,原理是一样的。

到此温度值就已经得到了,精度至少可以保证在±0.3以内,为什么这么讲,
我们要明白影响温度精度的要素有什么?
1.Vcc:取决于LDO,可以用四位半以上万用表测试一下电压等;
2.30K:取决于精密电阻,一般选用1‰,不同的NTC配置不同的大小的电阻;
3.NTC:NTC也是电阻,故NTC的精度和表格的精度是有误差的;一般情况下采购是分等级的;
4.MCU:批量生产的时候,可能会发现芯片是有差的,每一个芯片的ADC采集到的数值是不一样的,原因很多,其中影响较大的是芯片设计的时候内部的参考电压是否是稳定的;
5.计算过程中的误差,比如浮点型转整型,求取平均值的时候的误差;
等等诸多因素
其中影响最大的就是第4条,芯片之间的差异;批量生产的时候需要注意,如果存在这个问题那么设计电路的时候也许就要换个思路了,后面会讲如何改变消除这种影响,抛开第四条因素之外(如果你选用的芯片没有第四条这个问题),其他的只要不是很差劲,精度一般可以做到±0.1以内;

五、消除芯片ADC误差的影响,在上述四的基础上进行代码的扩展实现(另一个方法:采用电压基准供电)

要明白一点,这个误差是来自于不同芯片之间内部的参考电压不一致,为了排除掉这个误差,可以用下图的设计,避开掉内部的参考电压对数据结果的影响。
在这里插入图片描述
那么根据新的电路设计,使用双ADC,为了方便阅读,我们在这里先约定:
1.AN1引脚的ADC值命名为MaxADC;
2.AN2引脚的ADC值命名为MinADC;
3.精密电阻依然是30K,命名为Rm;
4.NTC的电阻值命名为Rntc,程序中为:RKohmValue 见后面程序;
求取的Rntc = Rm*MinADC/(MaxADC-MinADC);
这样无论是MaxADC还是MinADC都是以该芯片的参考电压为比例得到的数值;分式就抵消掉了这部分的影响,其实仔细思考会发现抵消掉的还有Vcc的影响,毕竟随着电池使用,电量降低,其实LDO出来的数值也是会有影响的,不仅如此,LDO的精度也受制于选择的型号及品牌,不过没关系这部分在这里也将抵消掉!
为了进一步提高精度,要在多次采样之后均值的处理上做一些改变!
为什么要这么做,上图
在这里插入图片描述
这里只是我在Excel上随机写了100组数据,实际上我们采集到的ADC的数值离散分布也是如此的,那么我们最好能够摒弃掉上下两个绿色框内的数据,限制幅度,其实样本大了以后会发现,中位值是最接近真值的。

那么我们要怎么处理这些离散数据呢?
获取一定样本的数据,放在一维数组中,对该数值的元素进行从小到大排序,取中间一定数量的元素求和取平均值,但是因为冒泡排序是比较耗费资源的,再求和取平均势必影响出值速度,因此这里我取中位值作为有效值去计算NTC的电阻值!
我将其称为限幅滤波,或者是中位值滤波!

7.滤波

/*======================以下是对数据进行滤波处理============================*/
/*说明:代码中使用了malloc和free,用malloc来申请空间自身是有弊端的,它会将空间分成很多个碎片,
但在本实验中没有太大影响,*/
/*================================================================================
*Function	Name 	:GetMaxADCValue
*Description  		:获取供电端ADC的数值
*parameter			:无
*Return				:MaxADCFilterValue 
================================================================================*/
float GetMaxADCValue(void)
{
	/*== 变量定义 ==*/
	float	 MaxADCFilterValue = 0;		
	uint32_t *MaxADCArray;//数组首元素的地址
    uint32_t i,j,m=0;
	uint32_t times = 501;//样本大小	
	/*== 获得样本数据 ==*/	
	MaxADCArray = (uint32_t *)malloc(times);
	for(m=0;m<times;m++)
	{		  
		MaxADCArray[m] = Get_MaxADC_Single_ADC_Value();			
	}			
	/*== 样本数据从小到大排列 ==*/			
	for (j=0;j<times-1;j++)
	{
		for (i=0;i<times-1-j;i++)
		{
			if (MaxADCArray[i] > MaxADCArray[i+1])
			{
				MaxADCArray[i]	 ^= MaxADCArray[i+1];
				MaxADCArray[i+1] ^= MaxADCArray[i];
				MaxADCArray[i]	 ^= MaxADCArray[i+1];
			}
		}
	}	
	/*== 滤除远离目标值的无效值 ==*/	
	//这里只取了排序之后的中间的值作为有效值,也就是中位值			
	MaxADCFilterValue = MaxADCArray[250];
	free(MaxADCArray);
			
	return MaxADCFilterValue;			
}
/*================================================================================
*Function	Name 	:GetMinADCValue
*Description  		:获取NTC端ADC的数值
*parameter			:无
*Return				:MinADCFilterValue
================================================================================*/
float GetMinADCValue(void)
{
	/*== 变量定义 ==*/
	float    MinADCFilterValue = 0;
	uint32_t *MinADCArray;//数组首元素的地址
    uint32_t i,j,m=0;
	uint32_t times = 801; //样本大小	
	/*== 获得样本数据 ==*/
	MinADCArray = (uint32_t *)malloc(times);
	for(m=0;m<times;m++)
	{
		MinADCArray[m] = Get_MinADC_Single_ADC_Value();
	}
	/*== 样本数据从小到大排列 ==*/
	for (j=0;j<times-1;j++)
	{
		for (i=0;i<times-1-j;i++)
		{
			if (MinADCArray[i] > MinADCArray[i+1])
			{
				MinADCArray[i] 	 ^= MinADCArray[i+1];
				MinADCArray[i+1] ^= MinADCArray[i];
				MinADCArray[i] 	 ^= MinADCArray[i+1];
			}
		}
	}
	/*== 滤除远离目标值的无效值 ==*/
	//这里只取了排序之后的中间的值作为有效值,也就是中位值
	MinADCFilterValue = MinADCArray[400];
	free(MinADCArray);

	return MinADCFilterValue;
}

8.获取NTC阻值

/*================================================================================
*Function	Name 	:GetRKohmValve
*Description  		:获取当前温度下NTC阻值
*parameter			:无
*Return				:NTC的阻值
================================================================================*/
float GetRKohmValve(void)
{
	float RKohmValue = 0;
	float MaxADC,MinADC = 0;
	
	MaxADC = GetMaxADCValue();
	MinADC = GetMinADCValue();
	RKohmValue = 30*MinADC/(MaxADC-MinADC);
	
	return RKohmValue;
}

创建变量TempValue作为求得的目标温度值
TempValue= GetDisplayTempValue(GetAccuraryTemperature(GetRKohmValve()));
这里调用的是GetRKohmValve();故而查表的表格是NTC的RT表格;

最后,设计上如果对功耗有要求,在第二种设计的基础上可以用一个单独的IO口作为供电端Vcc,
使用的时候拉高,不用的时候拉低,这样可以降低功耗!

Chapter3 stm32通过NTC采集温度,二分法查表,精度0.1℃

原文链接

NTC是指负温度系数的电阻器,电阻值会随着温度上升而减少,我们可以利用该特性,对温度进行采集和计算。

下面是NTC的规格和温度阻值表

标称阻值:10kΩ @ 25℃

精度公差:±1%

B值:3435K at 25/85℃

B值公差:±1%
在这里插入图片描述
我用的是查表法,所以在计算中不使用B值,B值直接忽略,只关心精度和温度阻值表。

我们先看看电路
在这里插入图片描述
STM32的ADC是12位的,我们根据电路图可以得到公式
在这里插入图片描述
我们把温度阻值表代入公式,可借助EXCEL计算,可以得到温度对应的ADC值大小,然后定义数组,用于查表计算温度。

我选取的温度范围是-20℃~120℃,生成下列数组。

#ifndef _NTC_H
#define _NTC_H
#include "user_define.h"
 
#define NTC_HIGH_PRECISION 	1//1=0.1℃ 0=1℃
 
#define NUM 141
 
//-20℃~120℃ 
static sc16 NTC3435_10K[NUM]=
{
	3581,3559,3535,3511,3486,3461,3435,3408,3381,3353,
	3324,3295,3266,3234,3203,3171,3138,3105,3072,3037,
	3003,                                             //0                                      
	2968,2932,2896,2859,2823,2785,2748,2710,2672,2633,
	2595,2556,2517,2478,2438,2399,2360,2321,2281,2242,
	2203,2164,2125,2086,2048,2009,1971,1933,1896,1858,
	1822,1785,1749,1713,1678,1643,1896,1896,1540,1507,
	1475,1436,1411,1380,1349,1319,1289,1260,1231,1203,
	1176,1149,1122,1096,1070,1045,1021,997,974,951,
	928,906,885,864,843,823,803,784,766,747,
	729,712,695,678,662,646,631,616,601,587,
	573,559,546,533,520,508,496,484,473,462,
	451,440,430,420,410,400,391,382,373,365,
	356,348,340,332,325,317,310,303,296,290,
	283,277,271,265,259,253,247,242,237,231,
};
 
extern s16 Read_NTC_Temperature(sc16 *list,u16 rADC,s16 BaseValue);
 
#endif

二分法查表,可以用循环的方法或者递归函数查找,我采用的是循环的方法,查找出ADC在列表中的位置,就可以知道对应的索引号,进而计算温度值,代码如下:

//输入参数:ADC值表  ADC值
//返回值:查表后的索引号
u16 NTC_Lookup(sc16 *list,u16 data)
{
	u16 middle=0;
	u16 indexL=0;
	u16 indexR=NUM-1;
	if(data>=*(list+0))
    	return 0;
	if(data<=*(list+NUM-1))
   		return NUM-1;
   	
	while((indexR-indexL)>1)
	{
		middle=(indexL+indexR)>>1;
		if(data==*(list+middle))
			return middle;
		else if(data>*(list+middle))
       indexR=middle;
    else if(data<*(list+middle))
    	indexL=middle;
	}
	return indexL;
}
 
//例如我们采集到的ADC值是3000,经过二分法查找到的索引号是20,我们就可以知道温度在NTC3435_10K[20](0℃)和NTC3435_10K[21](1℃)之间。

二分法查找的值是表中的索引号,我们还需要根据索引号和ADC值进一步计算温度值(精度0.1℃),代码如下:

//输入参数:ADC表  采集的ADC值   ADC表的起始温度值(-20℃=-200)
//返回值:温度值 单位0.1℃ 例如返回值是100,对应的就是100*0.1℃=10℃。
s16 Read_NTC_Temperature(sc16 *list,u16 rADC,s16 BaseValue)
{
	u16 index=0;
	u16 deta=0;
	u16 t=0;
	s16 result=0;
	if(rADC>=list[0])
		return BaseValue;
	if(rADC<=*(list+NUM-1))
	{
		result=((NUM-1)*10+BaseValue);
		return result;
	}
	index=NTC_Lookup(list,rADC);
#if NTC_HIGH_PRECISION
	deta=list[index]-list[index+1];
	t=10*(list[index]-rADC)/deta;
#endif
	result=(BaseValue+index*10+t);
	return result;
}

main.c代码

int main()
{
  int temp;//
  temp=Read_NTC_Temperature(NTC3435_10K,3010,-200);//温度ADC表 ADC值 ADC表的起始温度(-20℃)
  printf("ntc = %d\n",temp);
  return 0;
}

我输入ADC值3010,输出值为-3,也就是-3*0.1℃=-0.3℃,
在这里插入图片描述
我用ADC换算成电阻值再去推算温度也是-0.3℃,算法正确,大家放心使用。

Chapter4 Simulink查表法实现NTC温度计算模型

原文链接:https://blog.csdn.net/weixin_42665184/article/details/134141109

前言

在实际项目中需要对NTC对某些区域进行温度采样和做一些系统层面的保护等等,比如过温降载,过温保护,这时就需要对NTC或者其他的温度传感器进行采样,计算实时温度。而NTC的数据表提供的温度和阻值的对应关系点数太少,计算的温度误差比较大。这里记录下所使用的方法,方便日后回顾。

把NTC数据导入到excel

如下图是开发板上的一个NTC电路。MCU采样NTC_Temp的AD值并进行温度的计算
在这里插入图片描述
拟合NTC温度曲线
输入以下代码

 
Coef1=polyfit(ADC_Table_T,Tem_Table_T,5);
z1=polyval(Coef1,ADC_Table_T);
plot(ADC_Table_T,Tem_Table_T,'r*',ADC_Table_T,z1,'b')

第一句的意思是把ADC数组作为输入,把温度作为输出,拟合成一个5次多项式的形式,并获取其系数。

第二句就是把ADC数组作为输入,用拟合得到的系数方程计算得到温度Z1

第三句就是比较拟合出来的曲线和数组曲线作对比
在这里插入图片描述
查表实现温度计算
把计算公式输入到excel里,和之前的数据做对比,这里设置了从-40-125度一共191个点。可根据实际需要进行适当调整,精度越高占用空间越多,合理选择就可以。
在这里插入图片描述

Chapter5 【STC32G应用】NTC测温还在用查表法?

原文链接:https://blog.csdn.net/lunzilx/article/details/131888957

前言

测温是单片机经常应用的一项功能,记得早期在学校用DS18B20这种单总线传感器,后面还有温湿度一体的传感器。后面到了一些应用领域,尤其是养殖和种植行业,NTC电阻这种方式还是更多一点。一个是价格相对便宜,再一个应用领域里基本都是分布式,传感器与控制器之间可能间距百米,NTC电阻就更有优势一点。

一、NTC测温原理

NTC其实就是一个随温度变化的电阻,有正温度系数和负温度系数,以前51在做NTC测温的时候,通常会选择查表法来推算温度。查表法也就是把各个温度对应的NTC阻值组成一个数组,单片机计算出NTC阻值后,在数组中进行查找,找到对应的温度值。这种方式的优点是速度快,缺点就是误差比较大,尤其是数组数量越小,误差越大,数组数量多的话,又占用存储空间。

其实NTC还有另外一种测试的方式,就是通过公式进行推算。
NTC电阻有一个参数,B值。B值与NTC电阻的制作材料有关系。
NTC热敏电阻B值公式为:B=T1T2 Ln(RT1/RT2)/(T2-T1)

其中的B:NTC热敏电阻的B值,由厂家提供;RT1、RT2:热敏电阻在温度分别为T1、T2时的电阻值; T1、T2:绝对温标。

之前51查表法用的多,也有一个原因是通过公式来计算效率比较低,但是现在STC32G出来以后,计算量来说就并不是问题了。

二、NTC测温函数

代码如下(示例):

const float Rp=10000.0; //100K
const float T2 = (273.15+25.0);//T2
const float B0 = 3950.0;//B
const float B1 = 3435.0;//B
const float Ka = 273.15;

float Get_Temp(float Rt,float Bx)
{
    float temp;
    //like this R=5000, T2=273.15+25,B=3470, RT=5000*EXP(3470*(1/T1-1/(273.15+25)),
    temp = Rt/Rp;
    temp = log(temp);//ln(Rt/Rp)
    temp/=Bx;//ln(Rt/Rp)/B
    temp+=(1/T2);
    temp = 1/(temp);
    temp-=Ka;
    return temp;
}

总结

通过公式来计算NTC阻值,相对来说更加精确。但是计算前和计算后一般还需要进行一些滤波操作,来让温度值更加平滑。

Chapter6 NTC查表法,采用二分法

原文链接:https://blog.csdn.net/xuechengchang/article/details/89399125

做温控器,传感器采用NTC热敏电阻,前几年做的代码,为了省事方便,直接采用查询方法,从头到尾查询一边,一个200个元素的一维数组,例如NTC_ADC_TAB[200],最多要查询200次!方法很笨!

对于强迫症的工程师来说,这是个打心眼里别扭的事情,就像穿西装,打领带这么别扭!

最近项目不忙,对温控器的代码做了优化重构,顺便把温度查表函数做了优化更改,采用二分法,效率显著提升,废话不多,SHOW THE CODE:

以下代码采用编程平台:IAR FOR STM8
硬件平台:stm8l152
ADC分辨率:12位
已经在产品上验证并批量出货!PS:做一个靠谱的网友!
 
/*********************** 宏定义 *********************************/
#define NTC_ADC_MAX    201
 
/**************** NTC热敏电阻转换成ADC的值 ***********************/
const uint16_t NTC_ADC_Tab[NTC_ADC_MAX]={
    2559 ,2555 ,2551 ,2546 ,2542 ,2538 ,2534 ,2529 ,2525 ,2521 ,// 25.0~25.9 ℃
    2517 ,2513 ,2508 ,2504 ,2500 ,2496 ,2491 ,2487 ,2483 ,2479 ,// 26.0~26.9 ℃
    2474 ,2470 ,2466 ,2461 ,2457 ,2453 ,2449 ,2444 ,2440 ,2436 ,// 27.0~27.9 ℃
    2432 ,2427 ,2423 ,2419 ,2415 ,2410 ,2406 ,2402 ,2397 ,2393 ,// 28.0~28.9 ℃
    2389 ,2385 ,2380 ,2376 ,2372 ,2368 ,2363 ,2359 ,2355 ,2350 ,// 29.0~29.9 ℃
    2346 ,2342 ,2338 ,2333 ,2329 ,2325 ,2320 ,2316 ,2312 ,2308 ,// 30.0~30.9 ℃
    2303 ,2299 ,2295 ,2291 ,2286 ,2282 ,2278 ,2273 ,2269 ,2265 ,// 31.0~31.9 ℃
    2261 ,2256 ,2252 ,2248 ,2243 ,2239 ,2235 ,2231 ,2226 ,2222 ,// 32.0~32.9 ℃
    2218 ,2214 ,2209 ,2205 ,2201 ,2196 ,2192 ,2188 ,2184 ,2179 ,// 33.0~33.9 ℃
    2175 ,2171 ,2167 ,2162 ,2158 ,2154 ,2150 ,2145 ,2141 ,2137 ,// 34.0~34.9 ℃
    2133 ,2128 ,2124 ,2120 ,2116 ,2111 ,2107 ,2103 ,2099 ,2095 ,// 35.0~35.9 ℃
    2090 ,2086 ,2082 ,2078 ,2073 ,2069 ,2065 ,2061 ,2057 ,2052 ,// 36.0~36.9 ℃
    2048 ,2044 ,2040 ,2036 ,2031 ,2027 ,2023 ,2019 ,2015 ,2010 ,// 37.0~37.9 ℃
    2006 ,2002 ,1998 ,1994 ,1990 ,1985 ,1981 ,1977 ,1973 ,1969 ,// 38.0~38.9 ℃
    1965 ,1960 ,1956 ,1952 ,1948 ,1944 ,1940 ,1936 ,1932 ,1927 ,// 39.0~39.9 ℃
    1923 ,1919 ,1915 ,1911 ,1907 ,1903 ,1899 ,1895 ,1890 ,1886 ,// 40.0~40.9 ℃
    1882 ,1878 ,1874 ,1870 ,1866 ,1862 ,1858 ,1854 ,1850 ,1846 ,// 41.0~41.9 ℃
    1842 ,1838 ,1833 ,1829 ,1825 ,1821 ,1817 ,1813 ,1809 ,1805 ,// 42.0~42.9 ℃
    1801 ,1797 ,1793 ,1789 ,1785 ,1781 ,1777 ,1773 ,1769 ,1765 ,// 43.0~43.9 ℃
    1761 ,1757 ,1753 ,1750 ,1746 ,1742 ,1738 ,1734 ,1730 ,1726 ,// 44.0~44.9 ℃
    1722 ,// 45 ℃
};
 
/**
  * @brief  二分法查表法,得出单路温度值
  * @param  pos
  * @retval TempSingleValue
  */
uint16_t get_single_temp(uint8_t pos)
{
    uint16_t    End = NTC_ADC_MAX - 1;// 数组下标最后一个数
    uint16_t    Front = 0;// 数组第一个数
    uint8_t     half = 0;
    uint16_t    TempSingleADC = 0;// 单次转换的ADC值
    uint16_t    TempSingleValue = 0;// 单次换算成的温度值,用来函数返回值;
 
    ADC_Channel_TypeDef    ADC_Channel_X = ADC_Channel_0;
    
    switch(pos)
    {
        case 0:
          ADC_Channel_X = ADC_Channel_0;
          break;
        case 1:
          ADC_Channel_X = ADC_Channel_1;
          break;
        case 2:
          ADC_Channel_X = ADC_Channel_2;
          break;
        default :
          break;
    }
    
    /* Enable ADC1 clock */
    CLK_PeripheralClockConfig(CLK_Peripheral_ADC1, ENABLE);
 
    /* Initialize and configure ADC1 */
    ADC_Init(ADC1, ADC_ConversionMode_Continuous, ADC_Resolution_12Bit, ADC_Prescaler_2);
 
    /* ADC channel used for IDD measurement */
    ADC_SamplingTimeConfig(ADC1, ADC_Group_FastChannels, ADC_SamplingTime_192Cycles);
 
    /* Enable ADC1 */
    ADC_Cmd(ADC1, ENABLE);
    Delay(500);
 
    /* Disable SchmittTrigger for ADC_Channel, to save power */
    ADC_SchmittTriggerConfig(ADC1, ADC_Channel_X, DISABLE);
 
    /* Enable ADC1 Channel used for LAD measurement */
    ADC_ChannelCmd(ADC1, ADC_Channel_X, ENABLE);
    Delay(500);
    ADC_SoftwareStartConv (ADC1);//开启软件转换
 
    while(!ADC_GetFlagStatus (ADC1,ADC_FLAG_EOC));//等待转换结束
    ADC_ClearFlag (ADC1,ADC_FLAG_EOC);//清除相关标识
    TempSingleADC = ADC_GetConversionValue (ADC1);//获取转换值;
    //TempSingleADC = 1722;// 调试二分法查表用的临时赋值,调试代码用;
    
    /* DeInitialize ADC1 */
    ADC_DeInit(ADC1);
    /* Disable ADC1 clock */
    CLK_PeripheralClockConfig(CLK_Peripheral_ADC1, DISABLE);
    ADC_ChannelCmd(ADC1, ADC_Channel_X, DISABLE); 
 
    /**********二分法查表求温度值*********/
    if((TempSingleADC <= NTC_ADC_Tab[0])&&(TempSingleADC >= NTC_ADC_Tab[NTC_ADC_MAX-1]))
    {
        for(half=100;End-Front != 1;)
        {
            if(TempSingleADC > NTC_ADC_Tab[half])
            {
                End = half;
                half = (End + Front)/2;
            }
            else if(TempSingleADC < NTC_ADC_Tab[half])
            {
                Front = half;
                half = (Front + End)/2;
            }
            else// 正好等于表格中的值
            {
                TempSingleValue = 2500+half*10+(NTC_ADC_Tab[half]-TempSingleADC)*10/(NTC_ADC_Tab[half]-NTC_ADC_Tab[half+1]);
                break;
            }
        }
        if(End-Front == 1)// 在表格两个值之间的数
        {
            TempSingleValue = 2500+half*10+(NTC_ADC_Tab[half]-TempSingleADC)*10/(NTC_ADC_Tab[half]-NTC_ADC_Tab[half+1]);
        }
    }
    else
    {
        TempSingleValue = 0;          // 温度超出数组范围,就返回0度
    }
    return TempSingleValue;
}

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

原文链接:https://blog.csdn.net/lnniyunlong99/article/details/105802834

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

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

STM32 ADC实现NTC测温的电路示意图如下:
在这里插入图片描述
STM32的ADC分辨率为12位,模数转换的范围 0~ 4095(0x000~0xFFF)

在这里插入图片描述
针对以上描述的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中查看曲线的形状
在这里插入图片描述
从图中可以看出,NTC的温度与数字量的关系比较接近一元线性关系,数字量越大,温度值越低,负相关。那么将两度之间的小数度数几乎可以认为就是线性的,下面介绍计算小数精度的方法按照线性处理。

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

在这里插入图片描述

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

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

用图来表示
在这里插入图片描述
下面是具体的代码实现:(代码中小数部分,通过*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进制值打印出来如下:
在这里插入图片描述

Chapter8 个人实验,R25=10k, B3950,公式直接计算法(也可以采用二分查找法)

开发板:正点原子或者神舟号STM32F4开发板,PA5引脚作为ADC输入,VREF=VCC=3.3V
在这里插入图片描述
在这里插入图片描述
NTC: R25=10k, B3950
在这里插入图片描述

计算公式:

在这里插入图片描述

测试结果:
在这里插入图片描述
在这里插入图片描述

代码:

/**
 ****************************************************************************************************
 * @file        main.c
 * @author      正点原子团队(ALIENTEK)
 * @version     V1.0
 * @date        2021-10-18
 * @brief       单通道ADC采集 实验
 * @license     Copyright (c) 2020-2032, 广州市星翼电子科技有限公司
 ****************************************************************************************************
 * @attention
 *
 * 实验平台:正点原子 探索者 F407开发板
 * 在线视频:www.yuanzige.com
 * 技术论坛:www.openedv.com
 * 公司网址:www.alientek.com
 * 购买地址:openedv.taobao.com
 *
 ****************************************************************************************************
 */
#include "stdio.h"
#include "math.h"
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include "./BSP/ADC/adc.h"

    uint16_t adcx;
    float temp;
	float Rntc = 10000.0;


	
	const float Rp=10000.0; //10K
	const float T2 = (273.15+25.0);//T2
	const float B0 = 3950.0;//B
	const float B1 = 3435.0;//B
	const float Ka = 273.15;
	
	float Get_Temp(float Rt,float Bx)
	{
		float temp;
		//like this R=5000, T2=273.15+25,B=3470, RT=5000*EXP(3470*(1/T1-1/(273.15+25)),
		temp = Rt/Rp;
		temp = log(temp);//ln(Rt/Rp)
		temp/=Bx;//ln(Rt/Rp)/B
		temp+=(1/T2);
		temp = 1/(temp);
		temp-=Ka;
		return temp;
	}
	
		

int main(void)
{


    HAL_Init();                             /* 初始化HAL库 */
    sys_stm32_clock_init(336, 8, 2, 7);     /* 设置时钟,168Mhz */
    delay_init(168);                        /* 延时初始化 */
    usart_init(115200);                     /* 串口初始化为115200 */
    led_init();                             /* 初始化LED */
    adc_init();                             /* 初始化ADC */


    while (1)
    {
        adcx = adc_get_result_average(ADC_ADCX_CHY, 10);    /* 获取通道5的转换值,10次取平均 */

		Rntc = (float)10000.0*adcx/(4096-adcx);
 
        temp = Get_Temp(Rntc, B0);
		
		printf("ADC:%d, Rntc:%f, temp:%.2f\n", adcx, Rntc, temp);

        LED0_TOGGLE();
        delay_ms(500);
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值