1.材料清单
STM32F103C8T6开发板(黑色板)、NTC热敏电阻、12864OLED显示屏(四脚)、microUSB数据线、导线及面包板
2.电路连接
NTC热敏电阻连接电路:GND->10k电阻->PB1->NTC热敏电阻->3.3V
OLED显示屏连接电路:
* GND->GND
* VCC->3.3V
* SCL->PB6
* SDA->PB7
STM32配置Arduino环境:https://blog.csdn.net/weixin_42101470/article/details/103411081
3.NTC热敏电阻测温
连接好电路后,先来读取热敏电阻的阻值,并发送到串口。
以上是NTC热敏电阻的接口电路,其中输出电压V_out连接PB1,VCC=3.3V。每次热敏电阻检测到温度变化时,输出电压都会随之变化。通常,我们使用分压器,其公式如下:
V_out = VCC ⋅[R_平衡 / (R_NTC + R_平衡)]
但是,我们不希望V_out作为答案,我们想要热敏电阻的阻值R_NTC。对上式整理得:
R_NTC = R_平衡 ⋅ (VCC / V_out – 1)
从上式可以看出我们需要测量电压输出和电源电压,这就是ADC的用武之地。ADC可以将电压表示为一定范围内的数字,所以上式最终结果如下:
R_NTC = R_平衡 ⋅(D_max / D_测量 – 1)
这在数学上是成功的,因为无论我们如何表示电压(以伏特或数字为单位),这些单位抵消了分数中的分子和分母,留下无量纲数。在那之后,乘以一个阻值,以欧姆为单位得出答案。
在STM32F103C8T6中ADC的范围是0~4095,所以D_max=4095,D_测量为PB1引脚读取到的值,R_平衡可通过万用表精准测得,由此我们便可以求得R_NTC的值。那么,R_NTC与温度的关系是怎样的呢?
幸运的是我们可以查得NTC热敏电阻的拟合曲线方程如下:
其中,R0为T0温度(单位:K)下的阻值,beta为常数,三个变量均由厂家给出。
所以如果已知热敏电阻阻值,我们便可以通过上式求得对应的温度。
Let's get started!
/* ===热敏电阻演示代码===为了消除噪声读数,采样ADC几次,然后平均样本以获得更稳定的测量值,用readThermistor函数实现。http://www.thermistors.cn/news/293.html
*/
const int sampleNumber = 10; //采样次数
const double balanceR = 9700.0; //参考电阻阻值,越精确越好
const double ADC_max = 4095.0;
/*使用beta方程计算阻值。*/
const double beta = 3950.0; //商家给出的电阻对应25°C下的bata值
const double roomTemp = 298.15; //以开尔文为单位的室温25°C
const double roomTempR = 10000.0; //NTC热敏电阻在室温25°C下具有典型的电阻
double currentTemperature = 0; //保存当前温度const int thermistorPin = PB1; // ADC采样电阻分压器的输出引脚void setup(){//设置串口窗口消息的端口速度Serial.begin(9600);
}
void loop(){ currentTemperature = readThermistor();
// Serial.print("当前温度:"); Serial.println(currentTemperature);
// Serial.println("°C");delay(3000);
} /* 函数功能:读取模拟引脚,如下所示。通过模数转换将电压信号转换为数字表示。但是,这样做了多次,因此我们可以对其进行平均以消除测量误差。然后使用该平均数来计算热敏电阻的电阻。此后,电阻用于计算热敏电阻的温度。最后,温度转换为摄氏度。有关此过程的详细信息和一般理论,请参阅allaboutcircuits.com文章。原理图:(地面)----====------- | ---------====--------3.3V R_balance | R_thermistor | ADC引脚
*/ double readThermistor(){double rThermistor = 0; //保存热敏电阻的电阻值double tKelvin = 0; //以开尔文温度保存温度double tCelsius = 0; //以摄氏温度保存温度double adcAverage = 0; //保存平均电压测量值double adcSamplesi = 0 ;//保存当前采样值for(int i = 0; i<sampleNumber; i ++){ adcSamplesi = analogRead(thermistorPin); //从引脚和存储
// Serial.println(adcSamplesi);delay(10); //等待10毫秒adcAverage += adcSamplesi; //添加所有样本}adcAverage /= sampleNumber; //平均值w= sum/sampleNumber
// Serial.println(adcAverage);/*公式计算热敏电阻的电阻。*/ rThermistor = balanceR *((ADC_max / adcAverage) - 1); tKelvin =(beta * roomTemp)/ (beta +(roomTemp * log(rThermistor / roomTempR))); tCelsius = tKelvin - 273.15; //将开尔文转换为摄氏温度return tCelsius;//以摄氏度返回温度
}
串口输出效果:
由于ADC的偶然误差,每次测得的数值会有变化,将测得结果绘制图像如下:
故采用均值滤波来平滑所测的结果,由readThermistor中for循环实现。
MATLAB均值滤波代码
clear;clc;
load('tempData.mat');
n=length(data);
sum = 0;
% res = zeros(n,1);
x=1:n-100;
for i =1:n-100for j=1:100sum = sum+data(i+j);endres(i,1)=sum/100;sum=0;
end
figure(1);
clf
plot(x, data(1:n-100), '.-b', x, res, '-.r','linewidth',3);
set(gca,'FontSize',12); set(gcf,'Color','White');
xlabel('时间'); ylabel('温度(℃)');
legend('滤波前', '滤波后');
滤波前后对比图如下,可见均值滤波可以很好的去除噪点,能够提高OLED显示的稳定性!
4.OLED显示温度
温度℃的显示用zimoV2.2软件取模,软件下载网盘链接:
链接:https://pan.baidu.com/s/1G7McxB00iCCCeBg9yUUg5w
提取码:qg3d
这里还需要下载Adafruit GFX和Adafruit SSD1306两个库。
Arduino IDE操作方法:项目>>加载库>>管理库…,查找安装
加上OLED显示屏的完整程序:
/*NTC OLED显示屏制作温度计* NTC热敏电阻连接:GND->10k电阻->PB1->NTC热敏电阻->3.3V* OLED屏连接:* GND->GND* VCC->3.3V* SCL->PB6* SDA->PB7
*/
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306_STM32.h>
#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);const int sampleNumber = 100; //采样次数,const对象放在只读内存中
const double balanceR = 9982.0; //参考电阻阻值,越精确越好
const double ADC_max = 4095.0;
/*使用beta方程计算阻值。*/
const double beta = 3950.0; //商家给出的电阻对应25°C下的bata值
const double roomTemp = 298.15; //以开尔文为单位的室温25°C
const double roomTempR = 10000.0; //NTC热敏电阻在室温25°C下具有典型的电阻
double currentTemperature = 0; //保存当前温度const int thermistorPin = PB1; // ADC采样电阻分压器的输出引脚//*--文字: ℃--*/
static const unsigned char PROGMEM strC[] =
{/*-- 宽度x高度=40x35 --*/
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x07,0xC0,0x0C,0x00,0x00,0x0F,0xC0,0xFF,0xC0,0x00,0x1C,0xE3,
0xFF,0xE4,0x00,0x1C,0xE7,0xC0,0x7E,0x00,0x1C,0xE7,0x00,0x3E,0x00,0x0F,0xCF,0x00,
0x1E,0x00,0x07,0x9E,0x00,0x0E,0x00,0x00,0x1E,0x00,0x0E,0x00,0x00,0x3C,0x00,0x0E,
0x00,0x00,0x3C,0x00,0x0E,0x00,0x00,0x3C,0x00,0x04,0x00,0x00,0x3C,0x00,0x00,0x00,
0x00,0x3C,0x00,0x00,0x00,0x00,0x3C,0x00,0x00,0x00,0x00,0x3C,0x00,0x00,0x00,0x00,
0x3C,0x00,0x00,0x00,0x00,0x3C,0x00,0x00,0x00,0x00,0x3C,0x00,0x00,0x00,0x00,0x1C,
0x00,0x00,0x00,0x00,0x1E,0x00,0x06,0x00,0x00,0x1E,0x00,0x0C,0x00,0x00,0x0F,0x00,
0x1C,0x00,0x00,0x07,0x80,0x78,0x00,0x00,0x07,0xE1,0xF0,0x00,0x00,0x01,0xFF,0xE0,
0x00,0x00,0x00,0xFF,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
};#if (SSD1306_LCDHEIGHT != 64)
#error("Height incorrect, please fix Adafruit_SSD1306.h!");
#endifvoid setup()
{Serial.begin(9600);display.begin(SSD1306_SWITCHCAPVCC, 0x3C); // initialize with the I2C addr 0x3D (for the 128x64)display.display();delay(1000);display.begin(SSD1306_SWITCHCAPVCC, 0x3C);display.display();// init donedelay(1000);
}
void loop()
{//*-- 以下开始显示内容 --*///清理1306屏幕,准备显示:display.clearDisplay();// temp&Duty 显示://display.setTextSize(3); //设置字体大小display.setTextColor(WHITE); //设置字体颜色白色//中文字符显示display.setCursor(2,25); //设置字体的起始位置currentTemperature = readThermistor();display.println(currentTemperature); //输出字符display.drawBitmap(98,18, strC, 40, 35, 1); //℃,宽*高点阵display.display(); //把缓存都显示
}double readThermistor(){double rThermistor = 0; //保存热敏电阻的电阻值double tKelvin = 0; //以开尔文温度保存温度double tCelsius = 0; //以摄氏温度保存温度double adcAverage = 0; //保存平均电压测量值double adcSamplesi = 0 ;//保存当前采样值for(int i = 0; i<sampleNumber; i ++){ adcSamplesi = analogRead(thermistorPin); //从引脚和存储
// Serial.println(adcSamplesi);delay(10); //等待10毫秒adcAverage += adcSamplesi; //添加所有样本}adcAverage /= sampleNumber; //平均值w= sum/sampleNumber
// Serial.println(ADC_max / adcAverage - 1);/*公式计算热敏电阻的电阻。*/ rThermistor = balanceR *((ADC_max / adcAverage) - 1); tKelvin =(beta * roomTemp)/ (beta +(roomTemp * log(rThermistor / roomTempR))); tCelsius = tKelvin - 273.15; //将开尔文转换为摄氏温度return tCelsius;//以摄氏度返回温度
}
5.实现效果
6.误差分析
由于beta值、R0、T0、ADC测量值、R_平衡均会有测量误差 ,所以需要对参数进行校准。又因为R_平衡为个人测量结果,测量误差较大,对结果影响最大。这里笔者采用体温计对体温测量,得到较精确的体温36.8℃,再用STM32得到此时的R_NTC值,通过上面的公式反推出R_平衡。
参考链接
http://www.thermistors.cn/news/293.html