内容简介
专业的温度采集测量仪价格不菲,主要特点是精度高,配套完整的上位机绘制成温度曲线,方便观察。原理上来说,是通过一个温度传感器(NTC,即热敏电阻)来测量,经过MCU的AD采集经过处理后,传输到上位机并显示出来。
既然知道原理,为何不自己动手做一个简单的测温仪器呢?
开发环境
Arduino UNO+电阻(10kΩ)+NTC(10kΩ@25℃,B=3950)。
NTC与R1串联,并由5V供电。通过采集NTC与R1之间的压差来实现温度测量。本用AD通道 2 来进行测量。
Arduino范例代码如下
int sensorPin = A2;
void setup() {
pinMode(sensorPin, INPUT); // 设置传感器引脚为输入模式
Serial.begin(9600);// 初始化串口通信
}
void loop() {
int value = analogRead(sensorPin);// 读取传感器数值
//发送的格式是{CH1,value}
Serial.print ("{AD,");//头码+通道
Serial.print (value, DEC);//以 10 进制格式发送数值
Serial.print ("}");//结束码
delay(1000); // 延迟1000毫秒
}
此时可以看到,上传到上位机的是AD值,并不是实际的温度值。所以需要将AD值转换成对应的温度。
JXChart(查看使用方法) 采集如下图
2024/06/07 08:52:21.168(接收)ascii:{AD,540}
2024/06/07 08:52:22.168(接收)ascii:{AD,540}
2024/06/07 08:52:23.169(接收)ascii:{AD,540}
2024/06/07 08:52:24.169(接收)ascii:{AD,539}
2024/06/07 08:52:25.169(接收)ascii:{AD,539}
2024/06/07 08:52:26.169(接收)ascii:{AD,539}
2024/06/07 08:52:27.170(接收)ascii:{AD,539}
2024/06/07 08:52:28.170(接收)ascii:{AD,539}
2024/06/07 08:52:29.170(接收)ascii:{AD,538}
2024/06/07 08:52:30.170(接收)ascii:{AD,538}
2024/06/07 08:52:31.170(接收)ascii:{AD,538}
公式法
T1 = 1 / ( ln( Rt/R ) / B + 1/T2 )
(1) T1和T2指的是K度,即开尔文温度。
(2) Rt 是热敏电阻在T1温度下的阻值。
(3) R是热敏电阻在T2常温下的标称阻值。10K的热敏电阻25℃的值为10K(即R=10K)
具体可以参照这篇文章 热敏电阻NTC103、PT100温度计算公式
#define NTC_B 3950 // B值
#define NTC_T25 298.15 //25摄氏度的开尔文温度
#define NTC_R25 10000 //10 kΩ
float fGet_NTC_Temp(float Rntc)
{
float temp;
temp = 1/(((log(Rntc/NTC_R25) / NTC_B) + 1/298.15))-273.15;
return temp ;
}
问题又来了,Rntc是电阻值,而MCU采集到的是电压值,所以需要再次进行转换。首先确认一下,Arduino UNO的AD是10 bit,所以测量到的
AD = 1024R1/(Rntc+R1)
因此 Rntc = (1024R1/AD) - R1
fGet_NTC_Temp 函数要改成
//---------公式法----------------------------
#define NTC_B 3950 // B值
#define NTC_T25 298.15 //25摄氏度的开尔文温度
#define NTC_R25 10000 //10 kΩ 25℃时的NTC电阻值
#define NTC_R1 10000 //10 kΩ 分压电阻R1
#define NTC_MAX 1017 // 最大温度AD值
#define NTC_MIN 8 // 最小温度AD值
float fGet_NTC_Temp(float AD_Rntc)
{
float temp;
float Rntc;
if(AD_Rntc<=NTC_MIN)
AD_Rntc=NTC_MIN;//对最低温进行限制,维持在-55℃以上
else if(AD_Rntc>=NTC_MAX)
AD_Rntc=NTC_MAX;//对最高温进行限制,维持在204℃以以下
Rntc= NTC_R1*(1024/AD_Rntc) - NTC_R1 ;
temp = 1/(((log(Rntc/NTC_R25) / NTC_B) + 1/298.15))-273.15;
return temp ;
}
JXChart(查看使用方法) 采集如下图。
当前室温约26℃,手捏上去后,温度会缓慢上升
2024/06/07 08:56:41.864(接收)ascii:{formula,26.6804809570}
2024/06/07 08:56:42.866(接收)ascii:{formula,26.8586425781}
2024/06/07 08:56:43.868(接收)ascii:{formula,27.0371093750}
2024/06/07 08:56:44.869(接收)ascii:{formula,27.3052673339}
2024/06/07 08:56:45.871(接收)ascii:{formula,27.4844360351}
2024/06/07 08:56:46.873(接收)ascii:{formula,27.7537231445}
2024/06/07 08:56:47.875(接收)ascii:{formula,27.8436279296}
2024/06/07 08:56:48.876(接收)ascii:{formula,28.1138000488}
2024/06/07 08:56:49.878(接收)ascii:{formula,28.2943115234}
2024/06/07 08:56:50.879(接收)ascii:{formula,28.4751281738}
2024/06/07 08:56:51.881(接收)ascii:{formula,28.5656738281}
2024/06/07 08:56:52.883(接收)ascii:{formula,28.7469787597}
2024/06/07 08:56:53.884(接收)ascii:{formula,28.9286499023}
2024/06/07 08:56:54.886(接收)ascii:{formula,29.0195922851}
2024/06/07 08:56:55.888(接收)ascii:{formula,29.2017822265}
2024/06/07 08:56:56.889(接收)ascii:{formula,29.3842773437}
2024/06/07 08:56:57.891(接收)ascii:{formula,29.3842773437}
2024/06/07 08:56:58.893(接收)ascii:{formula,29.5671691894}
2024/06/07 08:56:59.894(接收)ascii:{formula,29.6587524414}
公式法的缺点是,计算量太大,不适合与低运算能力的MCU。因此,可以采用查表法
查表法
int sensorPin = A2;
void setup() {
pinMode(sensorPin, INPUT); // 设置传感器引脚为输入模式
Serial.begin(9600);// 初始化串口通信
}
void loop() {
int value = analogRead(sensorPin);// 读取传感器数值
//发送的格式是{CH1,value}
Serial.print ("{table,");//头码+通道
Serial.print (tsp_BinaryTableSearch(value), DEC);//以 10 进制格式发送数值
Serial.print ("}");//结束码
delay(1000); // 延迟1000毫秒
}
//---------查表法----------------------------
#define NTC_ADC_MAX 260
#define TSP_SHORT_CIRCUIT_THRESHOLD 1024U /*!<NTC short-circuit */
#define TSP_OPEN_CIRCUIT_THRESHOLD 8U /*!<NTC open-circuit */
#define TSP_MAX_T 204.0F // 最大温度值
#define TSP_MIN_T -55.0F // 最小温度值
const uint16_t NTC_adc_table[NTC_ADC_MAX] = {
8 ,9 ,9 ,10 ,11 ,12 ,13 ,14 ,15 ,16, //-55~-46℃
17 ,19 ,20 ,22 ,23 ,25 ,27 ,29 ,31 ,33, //-45~-36℃
35 ,38 ,40 ,43 ,46 ,49 ,52 ,55 ,59 ,62, //-35~-26℃
66 ,70 ,75 ,79 ,84 ,89 ,94 ,99 ,105 ,110, //-25~-16℃
116 ,123 ,129 ,136 ,143 ,150 ,157 ,165 ,173 ,181, //-15~-6℃
190 ,198 ,207 ,216 ,225 ,235 ,244 ,254 ,264 ,275, //-5~4℃
285 ,296 ,306 ,317 ,328 ,339 ,351 ,362 ,373 ,385, //5~14℃
396 ,408 ,420 ,431 ,443 ,454 ,466 ,478 ,489 ,501, //15~24℃
512 ,523 ,535 ,546 ,557 ,568 ,579 ,589 ,600 ,610, //25~34℃
620 ,630 ,640 ,650 ,660 ,669 ,678 ,688 ,696 ,705, //35~44℃
714 ,722 ,730 ,738 ,746 ,754 ,761 ,768 ,775 ,782, //45~54℃
789 ,796 ,802 ,808 ,814 ,820 ,826 ,831 ,837 ,842, //55~64℃
847 ,852 ,857 ,862 ,866 ,871 ,875 ,879 ,883 ,887, //65~74℃
891 ,895 ,898 ,902 ,905 ,909 ,912 ,915 ,918 ,921, //75~84℃
924 ,926 ,929 ,932 ,934 ,937 ,939 ,941 ,943 ,946, //85~94℃
948 ,950 ,952 ,954 ,955 ,957 ,959 ,961 ,962 ,964, //95~104℃
965 ,967 ,968 ,970 ,971 ,973 ,974 ,975 ,976 ,978, //105~114℃
979 ,980 ,981 ,982 ,983 ,984 ,985 ,986 ,987 ,988, //115~124℃
989 ,989 ,990 ,991 ,992 ,993 ,993 ,994 ,995 ,995, //125~134℃
996 ,997 ,997 ,998 ,998 ,999 ,1000 ,1000 ,1001 ,1001, //135~144℃
1002 ,1002 ,1003 ,1003 ,1004 ,1004 ,1004 ,1005 ,1005 ,1006, //145~154℃
1006 ,1006 ,1007 ,1007 ,1007 ,1008 ,1008 ,1008 ,1009 ,1009, //155~164℃
1009 ,1010 ,1010 ,1010 ,1010 ,1011 ,1011 ,1011 ,1012 ,1012, //165~174℃
1012 ,1012 ,1012 ,1013 ,1013 ,1013 ,1013 ,1014 ,1014 ,1014, //175~184℃
1014 ,1014 ,1014 ,1015 ,1015 ,1015 ,1015 ,1015 ,1015 ,1016, //185~194℃
1016 ,1016 ,1016 ,1016 ,1016 ,1016 ,1017 ,1017 ,1017 ,1017, //195~204℃
};
float tsp_BinaryTableSearch( float adc_val )
{
int start = 0U, end = 0U, mid = 0U;
end = ( sizeof( NTC_adc_table )/ sizeof( NTC_adc_table[0] ) ) - 1U;
if( adc_val > TSP_SHORT_CIRCUIT_THRESHOLD )
{
return 204.0F;
}
else if( adc_val < TSP_OPEN_CIRCUIT_THRESHOLD )
{
return -55.0F;
}
else if( adc_val < NTC_adc_table[0] )
{
return -55.0F;
}
else if( adc_val > NTC_adc_table[end - 1U] )
{
return 204.0F;
}
else
{
}
while ( start <= end )
{
mid = (start + end) >> 1;
if( adc_val == NTC_adc_table[mid] )
{
break;
}
if( ( adc_val > NTC_adc_table[mid] ) && ( adc_val < NTC_adc_table[mid+1U] ) )
{
break;
}
if( adc_val > NTC_adc_table[mid] )
{
start = mid + 1U;
}
else if( adc_val < NTC_adc_table[mid] )
{
end = mid - 1U;
}
else
{
}
}
return (mid-55) + (float)(adc_val-NTC_adc_table[mid] )/(float)(NTC_adc_table[mid+1]-NTC_adc_table[mid]);
}
JXChart(查看使用方法) 采集如下图
最后在对比一下“公式法”和“查表法”的温度曲线,从图中可以看到,两条曲线基本吻合。
2024/06/07 09:18:10.785(接收)ascii:{formula,31.0439453125}
2024/06/07 09:18:10.836(接收)ascii:{00table,31.0000000000}
2024/06/07 09:18:11.838(接收)ascii:{formula,31.3237304687}
2024/06/07 09:18:11.889(接收)ascii:{00table,31.2999992370}
2024/06/07 09:18:12.891(接收)ascii:{formula,31.5108032226}
2024/06/07 09:18:12.943(接收)ascii:{00table,31.5000000000}
2024/06/07 09:18:13.944(接收)ascii:{formula,31.7922363281}
2024/06/07 09:18:13.996(接收)ascii:{00table,31.7999992370}
2024/06/07 09:18:14.997(接收)ascii:{formula,31.9804077148}
2024/06/07 09:18:15.049(接收)ascii:{00table,32.0000000000}
2024/06/07 09:18:16.050(接收)ascii:{formula,32.1690673828}
2024/06/07 09:18:16.102(接收)ascii:{00table,32.1818199157}
2024/06/07 09:18:17.104(接收)ascii:{formula,32.2635498046}
2024/06/07 09:18:17.155(接收)ascii:{00table,32.2727279663}
2024/06/07 09:18:18.157(接收)ascii:{formula,32.3581542968}
2024/06/07 09:18:18.209(接收)ascii:{00table,32.3636360168}
2024/06/07 09:18:19.210(接收)ascii:{formula,32.4528808593}