NTC测温——查表计算vs公式计算
[原文PDF下载]
NTC测温,尤其面对通过ADC转换得到的数值时,你是否会犹豫:用什么方式更为合理地将这个ADC值转换为温度T?
假设我们已经对ADC的分辨率、准确度,以及所选的NTC精度范围都有了选择。对于绝大多数的应用而言,通过电阻分压方式的配置就可以满足我们所需的配置及所需的精度;个别高精度的NTC测温会用到4线制以消除导线电阻带来的误差,这些暂不在我们今天讨论的话题范围。
因为提供的NTC的参数内容不一样,本文以我们之前除雾传感器中的测量前挡风玻璃温度的NTC为例作为比较和说明。
- 查表方式的特点
- 查表方式的操作:二分法查找和线性插值;
- 通过R-T表获取拟合NTC的R(T)特性公式;
- 公式计算方式的特点
- 对于查表和R-T公式计算方式测温的两种方法比较
查表方式的特点
相对的,对于测量精度要求不是太高,或者只提供了R-T表的,而且相应温度测量范围不是太宽情况下的应用,用查表方式一般可以解决问题。这里表内存储的元素为int类型的ADC值,而不是其他字节数长度更多的float或者double数,否则无法达到节省代码的目的,从而表占用的代码空间就不会太大。当利用查表方式使用NTC测温时,最好将制表的温度分辨率不低于需要的测温精度。例如,如果测温精度要求为+/-1°C,那么表中的温度参数间隔就至少不低于1°C;而位于表内两个邻近温度值之间的数值,则采用线性插值的方式进行。
以除雾传感器中的NTC为例。下侧图中:
- R_NTC为当前的NTC
- Rs为串联分压电阻(9kohm)
- Vcc为驱动电压(5V)
- V_ADC为12位的分压ADC数值。
在以上这种设定情况下,我们通过使用excel可以得到像以下一样的表格:
TEMP(°C) |
R_NTC (kohm) |
Vout |
ADC(12bit) |
0 |
27.76 |
3.757009 |
3078 |
1 |
26.57 |
3.716083 |
3044 |
2 |
25.438 |
3.674521 |
3010 |
3 |
24.361 |
3.632344 |
2976 |
4 |
23.335 |
3.589651 |
2941 |
5 |
22.358 |
3.546371 |
2905 |
6 |
21.427 |
3.502596 |
2869 |
7 |
20.54 |
3.458323 |
2833 |
8 |
19.695 |
3.41365 |
2796 |
9 |
18.89 |
3.368501 |
2759 |
10 |
18.121 |
3.323023 |
2722 |
11 |
17.389 |
3.277182 |
2685 |
12 |
16.69 |
3.230993 |
2647 |
13 |
16.022 |
3.184605 |
2609 |
14 |
15.386 |
3.13788 |
2571 |
… |
… |
… |
… |
这样,通过这个表,我们就可以建立温度和分压电路ADC之间的映射。从表中任意两个相邻温度对应的ADC值可以看到,还有许多ADC值将面对“空白”。
如果允许,一方面我们可以在两个温度值对应的ADC之间进行线性插值的方式进行换算;另一方面,如果允许,还可以生成更多的数值对,以实现更高的查表分辨率。
另外,如果限于所使用器件(比如MCU的代码空间尺寸)的限制,不得不使用当前表格的分辨率,甚至需要减小表格的温度分辨率时(比如从间隔1°C增加到2°C),如果允许,就更需要使用线性插值的方式来计算当ADC值落入中间空白值时对应的温度,比如在上表中如果ADC=2700时,那么对应的温度值将处于10°C和11°C之间。
我们看一下基于分压电路的ADC值和温度T之间的关系图(如下)。
再比较一下NTC的原始R-T图(如下)。
由于ADC值/T图中的曲线更加平直,在横轴上取温度间隔∆T很小时,对应的ADC—T曲线将非常近似于直线,这也是为什么可以在相邻的表格温度T与ADC值之间可以采用线性插值的理由。
查表方式的操作:二分法查找+线性插值
两个步骤:(1)二分法找到当前ADC对应的表中索引位置;(2)线性插值计算温度值。
相比于线性查找时间复杂度O(N),二分法查找的时间复杂度是O(),即总体上二分法所花的时间会更短。这都是在假设所查找值为一个随机值情况下的统计。实际上由于NTC温度探头的响应时间的存在,或者环境温度的平缓变化,或者ADC采集频率的提高,那么如果程序中记录了前一次的查找值,那么在后一次根据ADC值查找过程中,是可以将前一次的查找索引值作为参考值来处理从而缩小查找范围的,不过在实现上会增加一些代码。
二分法查找的python示意代码(C和Python代码都可以在我们网站对应文章页面下载):
def binary_search(array, target): low, high = 0, len(array) - 1 while low <= high: mid = low + (high - low) // 2 mid_val = array[mid]
if mid_val == target: return mid, mid elif mid_val < target: low = mid + 1 else: high = mid - 1
return high, low
|
if __name__ == '__main__': array = [1, 2, 4, 5, 6, 8, 11, 15, 20, 30] target = 7 low, high = binary_search(array, target)
if low == high: print(f'Element {target} is at index {low}') else: print(f'Element {target} is between indexes {low} and {high}')
上面的二分法查找代码中有个小bug,读者可以尝试查找,或者在我们的网站文章页面链接中下载修正之后的代码。
|
以上代码中,当low==high,即二分法查找函数返回的数组索引值相等时,此时ADC值刚好有对应表值,否则,返回值表示当前的ADC结果位于表中array [low]和array [high]两个值之间。
当ADC的值位于表中array [low]和array [high]两个元素值之间时,就可以考虑线性插值了。
如图,在对应的2个ADC值之间出现一个中间值ADC时(橙色线表示的坐标点为待求值,图中温度间隔为1℃ ),由于ADC(n)和ADC(n+1)之间近于直线,则根据简单的相似原理计算出∆T后就可以得出当前ADC(t)值对应的温度值。
得到:
[式-1]
所以,ADC(t)对应的温度值就是(T+∆T)。这里ADC(n)和T之间存在简单映射。读者需要根据自己的温度间隔设置来调整上面的公式。
在一些小型应用中,如果因为MCU的代码空间限制问题导致无法使用更多的代码,则可以简单估算中间值,或者只好牺牲一些表格精度了。
可能会有人问,为什么一直在说MCU的代码空间?如果有R-T公式,直接调用<math.h>中的公式是不是会更加节省代码?比如像下面的这种公式:
[式-2]
通过R-T表获取拟合NTC的R(T)特性公式
由于手边只有R-T表,现在考虑通过拟合的方式获取R-T特性公式。
[式-3]
其中,T(K)=t+273.15,T0=273.15+25=298.15(K),R_To在这里也是常数,取25℃时的R值,可以从R-T表中读取。通过处理lnR和1/T之间的线性关系,可得下图所示的坐标图:
从拟合的公式中看到,可以取材料常数β=3365.8K。
余数部分经核算也基本符合,和上图拟合公式中的2.1166相差0.04。
到这一步,[式-2]中常数项都已经得到,R-T特性公式也就定了。如果知道了NTC的当前R值,就可以根据[式-2]计算温度值。
公式计算方式的特点
由公式计算温度值通过以下几步完成:
- 读取分压电路的ADC值;
- 通过ADC值计算NTC的R(T)值;
- 将R(T)值代入[式-2]计算获取温度值。
直接代码如下:
#include<math.h>
float CalWindyScreenTemp(int inputTempADC) { float belta = 3365.8; float R25 = 100000.0; //float_Serial=9000.0; //float Vcc = 5.0;
float Vout = 5.0 * inputTempADC/4096.0; //Get Vout if(Vout>=5.0) return(-100.0); //Error indicator float R_NTC = Vout * 90000.0/(5.0-Vout); //R_NTC
float temperature = log(R_NTC/R25)/belta + 1/(273.15+25.0); temperature = 1/temperature - 273.15; return(temperature); }
|
使用公式计算似乎更为方便。不着急,我们先看一下后续的比较。
对于查表和R-T公式计算方式测温的两种方法比较
单从表面的代码长度上比较的话,用公式计算的代码要比要查表的代码简单得多。不过我们还是先比较一下实际编译后的可执行代码尺寸。
以下的数据基于STM32F103和STM32CubeIDE。
Text | Data | Bss | Dec | 代码内容 | 代码变化 |
---|---|---|---|---|---|
写入到flash | 初始化后数据 | 未初始化数据 | 前三部分总和 | ||
3512 | 20 | 1572 | 5104 | 无NTC测温 | |
5888 | 20 | 1572 | 7480 | 查表测温 | 2376 |
8188 | 100 | 1884 | 10172 | 公式测温 | 4676 |
从上面的表中可以看到,看似代码少的公式计算法反而需要更多的代码空间。而查表的方法(+插值运算)所需的代码空间却要更少些。
实际上,公式方式中单纯调用log的计算所用到的函数,就会占用1~2k的字节。如果用于计算我们另外一款SC30系列高精度互换型NTC,则在额外使用指数函数(可以用连乘方式替代)的时候,代码还会再额外增加2k字节的样子。
所以真要比较两种计算方法,我们可以简单罗列如下表:
比较内容 | 查表插值法 | 公式法 |
---|---|---|
优势 |
|
|
劣势 |
|
|
[1]查表插值法[点击下载Python源代码]
[2]查表插值法[点击下载C源代码]
总结
应用的选择考虑,总是成本和效能的博弈结果。穷则迂回穿插,达则全域轰炸。
关于NTC,我们在公司网站及公众号还有其他相关的文档可以参考。
- NTC R-T表生成软件
- 如何进一步提高局部测温精度和灵敏度的方法
- NTC热敏电阻的热特性
- 如何使用可互换性高精度热敏电阻NTC传感器的参数
- NTC测温电路灵敏度问题简析
- 除雾传感器露点检测和应用
- 红外温度传感器在额温枪测温计中的应用