单片机
返回首页

Arduino STM32用NTC热敏电阻 OLED显示屏制作温度计

2025-10-29 来源:bilibili

1.材料清单

STM32F103C8T6开发板(黑色板)、NTC热敏电阻、12864OLED显示屏(四脚)、microUSB数据线、导线及面包板

2.电路连接

NTC热敏电阻连接电路:GND->10k电阻->PB1->NTC热敏电阻->3.3VOLED显示屏连接电路: * GND->GND * VCC->3.3V * SCL->PB6 * SCL->PB7

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!

arduino代码如下:

/* 

   ===热敏电阻演示代码===

   为了消除噪声读数,采样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-100

    for j=1:100

        sum = sum+data(i+j);

    end

    res(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

 * SCL->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!");

#endif

void 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 done

  delay(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_平衡。


进入单片机查看更多内容>>
相关视频
  • 【TI MSPM0 应用实战】智能小车+工业角度编码器+血氧仪+烟雾探测器!硬核参考设计详解!

  • 2022 Digi-Key KOL 系列: 你见过1GHz主频的单片机吗?Teensy 4.1开发板介绍

  • TI 新一代 C2000™ 微控制器:全方位助力伺服及马达驱动应用

  • MSP430电容触摸技术 - 防水Demo演示

  • 直播回放: Microchip Timberwolf™ 音频处理器在线研讨会

  • 基于灵动MM32W0系列MCU的指夹血氧仪控制及OTA升级应用方案分享

精选电路图
  • 1瓦线性调频增强器

  • 家用电器遥控器

  • 12V 转 28V DC-DC 变换器(基于 LM2585)

  • 红外开关

  • DS1669数字电位器

  • HA1377 桥式放大器 BCL 电容 17W(汽车音频)

    相关电子头条文章