Correction and Supplement for Blog “An Arduino Sample Project Based on Temperature Sensor”
Introduction
之前在学习温度传感器模块的时候(博客An Arduino Sample Project Based on Temperature Sensor - What a starry night~,后简称为“博客”),有一个问题没有解决,没有搞清楚Arduino端口的输出值与电阻值之间的转换关系。但其实问题不止于此,我忽略了很多重要的东西,因此这篇博客意在对上一篇博客进行纠正和补充。
The Different Versions of Temperature Sonsors
首先,在博客的学习中,我参考的文档以及所采用的代码都来自文档:Grove - Temperature Sensor,但实际上这篇文档介绍的是V1.0版本的温度模块,而我购买的是V1.2版本,应当参考文档Grove - Temperature Sensor V1.2(这篇wiki同样适用于V1.1版本)。V1.2版本的温度模块实际运行的代码应该是这样的:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// Demo code for Grove - Temperature Sensor V1.1/1.2
// Loovee @ 2015-8-26
#include <math.h>
const int B = 4275; // B value of the thermistor
const int R0 = 100000; // R0 = 100k
const int pinTempSensor = A0; // Grove - Temperature Sensor connect to A0
#if defined(ARDUINO_ARCH_AVR)
#define debug Serial
#elif defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_SAM)
#define debug SerialUSB
#else
#define debug Serial
#endif
void setup()
{
Serial.begin(9600);
}
void loop()
{
int a = analogRead(pinTempSensor);
float R = 1023.0/a-1.0;
R = R0*R;
float temperature = 1.0/(log(R/R0)/B+1/298.15)-273.15; // convert to temperature via datasheet
Serial.print("temperature = ");
Serial.println(temperature);
delay(100);
}
From Arduino Output Value to Resistance
上述代码中:
1
2
float R = 1023.0/a-1.0;
R = R0*R;
实现Arduino输出值与电阻值之间的转换
1
float temperature = 1.0/(log(R/R0)/B+1/298.15)-273.15;
实现电阻值与温度值之间的转换。
后者(即NTC电阻的RT特性)可以完全按照博客中所述进行理解,没有问题;重点还是理解Arduino输出值与电阻值的转换。
实际上,该模块中集成了一个voltage divider(Lesson 11 : Temperature sensor Grove module. - YouTube):(选用100k电阻是因为NTC在25摄氏度时的电阻值就是100k)
因此端口的输出电压$V$与工作电压$V_{CC}$之间存在比例关系:
\[V = V_{CC}\dfrac{R_0}{R_0+NTC}\notag\] \[\dfrac{V}{V_{CC}} = \dfrac{R_0}{R_0+NTC}\notag\] \[NTC=V_{CC}\dfrac{R_0}{V}-R_0\notag\] \[NTC=(\dfrac{V_{CC}}{V}-1)R_0\notag\]具体而言,NTC与输出电压的转换关系应该是:
\[NTC=(\dfrac{5}{V}-1)R_0\label{eq1}\]但是,Arduino端口的输出值a
并不是电压值。根据Arduino官方关于ananologRead()
函数的文档(analogRead() - Arduino Reference),Arduino板实际上包含了一个10-bit模数转换器(10-bit analog to digital converter),这意味着Arduino会将工作电压(即5V,下文中将再次强调这一电压值)范围内的值映射为0~1023之间的整数输出:
Reads the value from the specified analog pin. Arduino boards contain a multichannel, 10-bit analog to digital converter. This means that it will map input voltages between 0 and the operating voltage(5V or 3.3V) into integer values between 0 and 1023.
注:这也意味着resolution为 5 volts / 1024 units,即0.0049 volts (4.9 mV) per unit.
因此,式$\eqref{eq1}$中的电压(模拟量)比值$\dfrac5V$可以改为实际输出(数字量)比值$\dfrac{1023}{a}$:
\[NTC=(\dfrac{1023}{a}-1)R_0\]这就是上面代码计算的原理。
此时再观察博客中使用的转换代码:
1
2
a = analogRead(0); // Read analog A0 output value
resistance = (float)(1023-a)*10000/a; //get the resistance of the sensor;
可以发现它与本文所采用的代码:
1
2
float R = 1023.0/a-1.0;
R = R0*R;
完全是等效的,只是两个版本热敏电阻的Zero Power Resistance at 25 $^\circ \mathrm{C}$是不同的而已!!!更进一步地讲,博客中运行的代码与本博客中的代码在实现原理上是一致的,差别就在于NTC电阻的$B$值和$R_0$值不同
Operating Voltage
第二个问题是Arduino工作电压的问题。我所使用的扩展板上Grove Base Shield V2有一个调整工作电压的开关(可选3.3V或5.0V):
我之前并不在意这个开关,但这个工作很重要。仍然是ananologRead()
函数的文档(analogRead() - Arduino Reference):
这里明确指出Arduino Uno的工作电压是5 Volts,因此需要始终把它调整到5 V的位置。如果将其调整到3.3V的位置,就会出现错误。对于LED灯而言,它的亮度会变暗;而对于本文所使用的温度传感器,则会导致明显的输出错误。
工作电压为5 V时打印的环境温度值:
工作电压为3.3 V时打印的环境温度值: