Arduino驱动DS18B20温度传感器:从单总线协议到物联网应用实战
2026/9/14 1:12:41 网站建设 项目流程

简介:本资源是面向嵌入式初学者与实践爱好者的DS18B20温度传感器入门实验套件,聚焦Arduino平台下的单总线数字测温系统搭建与编程实现,解决硬件连接不明、OneWire通信配置复杂、温度数据解析困难等典型学习痛点。压缩包共19个文件(762KB),含5个核心.ino/.pde主程序文件(含串口显示与LCD扩展版本)、3个C++/头文件构成的Ds18b20+OneWire库支持模块、5个txt说明文档(含接线要点与寄存器配置注释)、1张清晰接线实物图(DS18B20_bb.jpg)及1个Fritzing电路图源文件(DS18B20.fzz),结构完整、即拿即用。已有52人下载学习,适合课堂实验、课程设计或DIY温控项目快速启动。读者可直接复现高精度(±0.5℃)、宽量程(-55℃~125℃)的实时温度采集,掌握1-Wire协议初始化、ROM搜索、分辨率配置及摄氏值转换全流程,并通过多版本代码对比理解不同显示方式与供电模式(寄生/外部)的工程取舍。

1. 项目概述:从零开始玩转DS18B20与Arduino

最近在整理旧项目资料时,翻出了一个尘封的压缩包,名字就叫“基于arduino的DS18B20温度传感器实验(源码+接线图).rar”。这让我想起了很多年前,第一次用Arduino Uno和这个小小的“一线总线”温度传感器搭建测温系统时的情景。对于刚接触嵌入式开发或物联网原型制作的朋友来说,DS18B20几乎是一个绕不开的经典器件。它价格低廉、精度尚可,最关键的是,其独特的单总线(1-Wire)通信协议,既带来了接线简洁的优势,也给初学者的代码调试带来了不小的挑战。这个实验包的核心,就是解决“如何让Arduino正确读取DS18B20的温度值”这个问题。它不仅仅是一份源码和一张接线图,更是一套完整的、可复现的实践方案,适合希望深入理解传感器通信协议、掌握Arduino外围设备驱动方法的开发者。无论你是想做一个室内温湿度监测站,还是为智能鱼缸、小型恒温箱制作控制核心,这个实验都能为你打下坚实的基础。接下来,我将结合多年的实操经验,为你深度拆解这个项目里的每一个技术细节、接线要点和代码逻辑,并分享那些在官方文档里找不到的“踩坑”实录。

2. 核心器件与通信协议深度解析

2.1 DS18B20温度传感器:一线总线的利与弊

DS18B20是Maxim(原Dallas)公司生产的一款数字温度传感器。它的核心魅力在于采用了单总线(1-Wire)协议进行通信。这意味着只需要一根数据线(外加电源和地线),就可以完成数据的双向传输。对于Arduino这类IO口资源有限的微控制器来说,这种节省引脚的设计非常友好。

从性能上看,DS18B20的测温范围通常在-55°C到+125°C之间,在-10°C到85°C范围内,精度可达±0.5°C。它内部直接将模拟温度信号转换为9位到12位的数字量,Arduino直接读取即可,省去了外部ADC(模数转换器)和复杂的校准过程。每个DS18B20都有一个全球唯一的64位ROM序列号,这使得在同一条单总线上挂载多个传感器成为可能,通过地址来区分它们。

然而,单总线协议也是一把双刃剑。其通信完全由精确的时序控制,对代码的时序要求极为苛刻。在Arduino上,如果中断处理不当,或者有其他耗时操作打断了时序,就极易导致通信失败,读取的数据全是0xFF(255)或0x00。这也是很多新手第一次使用DS18B20时最容易卡住的地方。

注意:市面上常见的DS18B20有三种封装:TO-92(像三极管)、不锈钢探头封装和贴片封装。实验中最常用的是TO-92封装,其引脚顺序(平面朝向自己,从左至右)通常是:1-GND, 2-DQ(数据), 3-VDD。务必确认你的传感器引脚定义,接反可能烧毁传感器。

2.2 Arduino作为主控器的角色与局限

在这个实验中,Arduino(通常是Uno型号)扮演着主控器(Master)的角色。它需要严格按照单总线协议的时序要求,向DS18B20发送复位脉冲、读写命令,并读取传感器返回的数据。Arduino的digitalWritedigitalRead函数虽然简单易用,但其执行速度并非纳秒级,在实现微秒级精确延时时,直接使用delayMicroseconds()函数有时并不够精确,尤其是在没有关闭全局中断的情况下。

因此,一个健壮的DS18B20驱动代码,往往需要直接操作端口寄存器来拉高拉低数据线,或者非常谨慎地管理中断。对于Arduino Uno,数据线推荐连接在带有外部中断功能的数字引脚(如D2或D3)上,但这并非强制,更重要的是代码中对时序的严格把控。

2.3 单总线(1-Wire)协议通信原理解读

单总线协议可以理解为一套在单根线上实现半双工通信的“摩尔斯电码”。它定义了几种特定的信号:复位脉冲、存在脉冲、写0、写1、读0、读1。所有通信均由主设备(Arduino)发起。

一次完整的温度读取流程如下:

  1. 初始化(复位与存在脉冲):Master拉低总线至少480μs,然后释放。如果总线上有DS18B20,它会在Master释放总线后的15-60μs内,拉低总线60-240μs作为应答,这个信号称为“存在脉冲”。Master检测到这个脉冲,才知道总线有设备。
  2. 发送ROM命令(如跳过ROM搜索):由于我们通常单总线上只挂一个传感器,最常用的命令是0xCC(跳过ROM)。如果需要寻址多个,则需先发送0x55(匹配ROM)命令,后跟64位地址。
  3. 发送功能命令:对于温度转换,发送0x44命令。发送后,DS18B20开始进行温度转换,对于12位精度,转换时间最长可达750ms。在此期间,如果Master执行读时序,总线会读到0,转换完毕则读到1。
  4. 再次初始化、发送ROM命令
  5. 发送读取暂存器命令0xBE
  6. 连续读取9个字节:DS18B20的暂存器共9字节,前两个字节就是温度值(LSB和MSB)。

整个过程中,每一个“写位”或“读位”的时序窗口都非常窄,通常以60μs或120μs为一个周期。这就是为什么“软件模拟”单总线协议时,必须关闭中断或使用高度优化的代码。

3. 硬件连接与电路设计要点

3.1 接线图详解与电源模式选择

一份清晰的接线图是成功的一半。DS18B20有三种基本的电源接线模式:外部供电模式、寄生供电模式和强上拉模式。实验中最常用、最稳定的是外部供电模式

外部供电模式接线(推荐)

  • DS18B20的VDD引脚->Arduino的5V引脚
  • DS18B20的GND引脚->Arduino的GND引脚
  • DS18B20的DQ(数据)引脚->Arduino的某个数字引脚(如D2),同时,在该数字引脚与5V之间连接一个4.7kΩ的上拉电阻

这个4.7kΩ的上拉电阻至关重要。单总线在空闲状态时需要被拉高至高电平。没有这个电阻,总线电平可能处于不确定的悬浮状态,导致通信完全失败。电阻值在4.7kΩ到10kΩ之间均可,4.7kΩ是典型值。

寄生供电模式可以节省一根电源线(只接GND和DQ),但时序要求更严苛,且在执行温度转换时,需要Master通过总线提供足够的“强上拉”电流,实现起来更复杂,稳定性不如外部供电,不推荐初学者使用。

3.2 上拉电阻的作用与选型考量

上拉电阻在这里扮演了两个角色:一是确定总线空闲时的电平(高电平);二是在Master释放总线(设置为输入模式)后,由它将总线电平从低迅速拉高,为DS18B20的输出提供电流路径。

为什么是4.7kΩ?这是一个折衷值。电阻太小,当Master主动拉低总线时,会流过太大电流,增加功耗甚至损坏IO口。电阻太大,则总线从低电平恢复到高电平的速度太慢(RC时间常数大),可能无法满足协议规定的上升时间要求,导致传感器检测不到正确的时序边缘。在总线长度较短(1-2米)、挂载设备少的情况下,4.7kΩ是一个可靠的选择。如果总线很长或设备多,可能需要减小电阻值,如2.2kΩ。

3.3 多传感器组网与布线实践

单总线的优势在于可以挂载多个设备。理论上,一条总线上可以挂载无数个DS18B20,因为它们有唯一地址。但实际上,受限于总线驱动能力和信号完整性,一般建议不超过10个。

多传感器接线时,所有传感器的VDD接5V,GND共地,DQ全部并联在一起,接到Arduino的同一个数字引脚,并且该引脚仍然只需要一个4.7kΩ上拉电阻到5V。

这里最大的挑战是ROM地址的获取。你需要先运行一个“总线搜索”算法来获取每个传感器的64位地址。有现成的库(如OneWire库)提供search()函数来完成这个工作。获取到地址后,在读取温度时,就需要先发送0x55命令,再紧跟对应的8字节地址,然后才能发送读取命令。

实操心得:在给多个DS18B20编号时,最好在获取到地址后,用传感器实际测量一个已知温度(比如手握升温),根据读到的温度值来物理确认每个地址对应的是哪个传感器,并记录在案。否则,一旦布线固定,再想区分就麻烦了。

4. 软件驱动与代码逐行分析

4.1 不使用库的“裸机”驱动实现

理解单总线协议最好的方式就是自己动手实现一遍时序。下面是一个简化的、用于单个DS18B20的驱动代码片段,并附上详细注释。

// 定义引脚 #define ONE_WIRE_BUS 2 // 初始化,拉高总线 void setup() { pinMode(ONE_WIRE_BUS, INPUT_PULLUP); // 先设置为输入上拉,让电阻将总线拉高 Serial.begin(9600); } // 生成1-Wire复位脉冲,返回是否存在设备 bool oneWireReset() { pinMode(ONE_WIRE_BIRE, OUTPUT); digitalWrite(ONE_WIRE_BUS, LOW); delayMicroseconds(480); // 保持低电平至少480us pinMode(ONE_WIRE_BUS, INPUT_PULLUP); // 释放总线,改为输入 delayMicroseconds(70); // 等待15-60us后检测 bool presence = !digitalRead(ONE_WIRE_BUS); // 检测到低电平表示存在 delayMicroseconds(410); // 等待存在脉冲结束 return presence; } // 向总线写一位 void writeBit(bool bitVal) { pinMode(ONE_WIRE_BUS, OUTPUT); digitalWrite(ONE_WIRE_BUS, LOW); delayMicroseconds(bitVal ? 10 : 65); // 写1保持低电平10us,写0保持65us pinMode(ONE_WIRE_BUS, INPUT_PULLUP); // 释放总线 delayMicroseconds(bitVal ? 55 : 5); // 恢复时间,补足一个时隙60us } // 从总线读一位 bool readBit() { bool bitVal; pinMode(ONE_WIRE_BUS, OUTPUT); digitalWrite(ONE_WIRE_BUS, LOW); delayMicroseconds(3); // 拉低3us后立即释放 pinMode(ONE_WIRE_BUS, INPUT_PULLUP); delayMicroseconds(10); // 等待10us后采样 bitVal = digitalRead(ONE_WIRE_BUS); delayMicroseconds(50); // 补足一个读时隙 return bitVal; } // 写一个字节 void writeByte(byte data) { for (int i = 0; i < 8; i++) { writeBit(bitRead(data, i)); // 低位先传 } } // 读一个字节 byte readByte() { byte data = 0; for (int i = 0; i < 8; i++) { bitWrite(data, i, readBit()); // 低位先收 } return data; }

这段代码清晰地展示了每一个时序。delayMicroseconds()的精度在16MHz的Arduino Uno上大约为4us的倍数,对于要求不严苛的场景勉强可用,但并非最佳。

4.2 利用成熟库(OneWire & DallasTemperature)简化开发

对于绝大多数应用,我们更推荐使用成熟的库,它们经过了充分优化和测试。最经典的组合是OneWire库和DallasTemperature库。OneWire库实现了底层的单总线通信,而DallasTemperature在其基础上封装了针对DS18B20等Dallas温度传感器的高级命令,让操作变得极其简单。

#include <OneWire.h> #include <DallasTemperature.h> #define ONE_WIRE_BUS 2 OneWire oneWire(ONE_WIRE_BUS); DallasTemperature sensors(&oneWire); void setup() { Serial.begin(9600); sensors.begin(); // 初始化总线 } void loop() { sensors.requestTemperatures(); // 向所有传感器发送温度转换命令 // 转换需要时间,这里简单延时。更好的做法是检查转换完成标志。 delay(750); // 12位精度下等待750ms float tempC = sensors.getTempCByIndex(0); // 获取第一个传感器的摄氏温度 Serial.print("Temperature: "); Serial.print(tempC); Serial.println(" °C"); delay(1000); }

使用库的好处是显而易见的:代码简洁,支持多传感器,自动处理地址搜索和温度值转换(将两个字节的整数转换为浮点数)。getTempCByIndex(0)中的索引0,对应通过sensors.getDeviceCount()找到的第一个传感器。

4.3 温度数据读取与格式转换算法

DS18B20返回的温度数据是两个字节(16位),存储在一个带符号的整数中。其格式是固定的:

  • 低字节(LSB)的低4位是小数部分。
  • 高字节(MSB)的高5位是符号位和整数部分。

例如,读取到的两个字节为0x500x05(即0x0550)。转换为二进制:0000 0101 0101 0000

  • 符号位是0,正数。
  • 整数部分:0000 0101(高字节低4位与低字节高4位组合?这里需要仔细解析)。实际上,数据格式是:MSB的bit4-bit0是温度的整数部分(二进制),LSB的bit7-bit4是温度的小数部分(二进制,每个位代表0.0625°C)。

标准转换代码如下:

int16_t rawTemp = (data[1] << 8) | data[0]; // data[1]是MSB, data[0]是LSB float celsius = (float)rawTemp / 16.0; // 12位分辨率下,最小单位是0.0625°C,即1/16

DallasTemperature库内部就是做了这样的转换。理解这个转换过程,有助于你在不依赖库的时候自己处理数据,或者在调试时直接解析从总线读出的原始字节。

5. 实验步骤与调试全流程

5.1 开发环境搭建与库安装

首先确保你安装了Arduino IDE。打开IDE,进入“工具”->“管理库...”,在库管理器中搜索“OneWire”,找到由Paul Stoffregen开发的版本进行安装。这个库非常稳定且高效。然后,同样搜索并安装“DallasTemperature”库,作者是Miles Burton。

安装完成后,在代码中通过#include <OneWire.h>#include <DallasTemperature.h>即可引入。库的安装包通常存储在Arduino IDE安装目录下的libraries文件夹里。如果遇到安装失败,可以手动下载ZIP库文件,在IDE中选择“项目”->“加载库”->“添加.ZIP库...”进行手动安装。

5.2 从接线到上传的完整操作流程

  1. 硬件连接:按照前述的“外部供电模式”连接电路。务必检查4.7kΩ上拉电阻是否接在数据线和5V之间。接好后,可以用万用表测量一下数据线引脚对地的电压,在空闲时应为接近5V的高电平。
  2. 代码编写:在Arduino IDE中新建一个项目,将4.2节中使用库的示例代码复制进去。注意将ONE_WIRE_BUS定义为实际连接的引脚号。
  3. 编译与上传:选择正确的板卡型号(如Arduino Uno)和端口,点击上传。
  4. 打开串口监视器:上传成功后,打开IDE的串口监视器(右上角放大镜图标),将波特率设置为9600。你应该能看到每隔一秒打印出的温度值。

如果一切正常,你会看到类似“Temperature: 25.37 °C”的输出。用手捏住传感器,温度值应该会缓慢上升。

5.3 串口调试与数据验证方法

串口监视器是最基本的调试工具。如果看不到数据,或者数据固定为-127850等异常值,请按以下步骤排查:

  1. 检查电源:用万用表测量DS18B20的VDD和GND之间电压,确保在4.5V-5.5V之间。
  2. 检查上拉电阻:确认电阻已正确连接。可以尝试暂时用一根杜邦线将数据线直接短接到5V(上电状态短暂短接一般不会损坏),如果此时能读到数据(可能是错的),说明问题出在上拉电阻未接或接触不良。
  3. 检查接线:再三确认VDD、GND、DQ没有接错。TO-92封装的引脚顺序容易看错。
  4. 检查代码引脚定义:确认代码中的ONE_WIRE_BUS与实物连接完全一致。
  5. 简化代码:使用最基础的、不带任何复杂逻辑的示例代码进行测试。
  6. 尝试不同的Arduino引脚:有时某个引脚可能存在硬件问题。

如果读取到的温度值固定为85.00,这通常是DS18B20的默认上电值,说明温度转换命令可能没有成功执行,或者读取的不是温度寄存器。重点检查requestTemperatures()函数后是否有足够的延时,以及发送的命令序列是否正确。

6. 常见问题排查与性能优化技巧

6.1 典型故障现象与根因分析

故障现象可能原因排查方法
串口无输出或输出乱码1. 串口波特率不匹配
2. 代码未执行到打印语句
3. Arduino未正确连接
1. 确认串口监视器波特率与代码中Serial.begin()一致
2. 在setup()开头加Serial.println("Start");测试
3. 检查板卡和端口选择
温度值始终为-127°C1. 传感器连接错误或损坏
2. 通信完全失败,库返回错误值
1. 检查硬件连接和电源
2. 使用oneWire.reset()检查是否存在脉冲
3. 尝试更换传感器
温度值始终为85.00°C1. 未成功启动温度转换
2. 读取了错误的寄存器
1. 确保调用了sensors.requestTemperatures()
2. 在requestTemperatures()后增加足够延时(>750ms)
3. 检查是否误发送了0x44以外的命令
温度值跳动剧烈,噪声大1. 电源噪声
2. 总线受到干扰
3. 上拉电阻过大或接触不良
1. 在VDD和GND之间并联一个0.1uF的陶瓷电容滤波
2. 缩短总线长度,远离电机等干扰源
3. 确保上拉电阻可靠连接,尝试减小阻值
多传感器时,只能读到一个或地址混乱1. ROM地址搜索算法失败
2. 总线驱动能力不足
3. 传感器地址未正确绑定
1. 使用库的sensors.getDeviceCount()检查识别数量
2. 尝试降低上拉电阻值(如2.2kΩ)
3. 重新运行地址搜索程序并物理标记传感器

6.2 时序干扰与中断处理的应对策略

Arduino的delay()函数和delayMicroseconds()函数在延时期间,中断仍然是使能的。如果系统中存在串口接收、定时器中断等,它们可能会打断单总线通信的微妙时序,导致读写出错。

解决方案

  1. 在关键时序操作期间关闭全局中断:这是最直接有效的方法。在oneWireReset(),writeBit(),readBit()等函数的开头加上noInterrupts(),在函数返回前加上interrupts()。但要注意,关闭中断时间不能过长,否则会影响其他功能(如串口接收丢失数据)。
    void writeBit(bool bitVal) { noInterrupts(); // 关闭中断 // ... 精确时序操作 ... interrupts(); // 恢复中断 }
  2. 使用硬件定时器产生精确延时:对于追求极致可靠性的应用,可以配置一个硬件定时器(如Timer1)来产生精确的微秒级延时,这不受中断影响。
  3. 使用优化过的底层库:前面提到的Paul Stoffregen的OneWire库,其底层实现已经考虑了中断问题,并可能使用了汇编或直接端口操作来保证时序,可靠性远高于自己用digitalWrite写的代码。因此,强烈建议在正式项目中使用该库

6.3 提高测量精度与稳定性的工程实践

  1. 电源去耦:在DS18B20的VDD和GND引脚之间,尽可能靠近传感器放置一个0.1μF(104)的陶瓷电容,用于滤除电源线上的高频噪声。这对于提高读数稳定性,尤其是环境中有开关电源或电机时,效果显著。
  2. 总线保护与屏蔽:如果传感器需要通过较长导线(>3米)连接,数据线最好使用双绞线,并与电源线分开走线。在工业环境或强干扰场合,可以考虑使用屏蔽线,屏蔽层单点接地。
  3. 软件滤波:不要只依赖单次读数。可以在代码中实现简单的软件滤波,例如连续读取5次温度,去掉最大最小值后取平均,或者使用一阶滞后滤波(指数加权平均)。
    float filteredTemp = 0.0; float alpha = 0.2; // 滤波系数,越小越平滑,响应越慢 void loop() { sensors.requestTemperatures(); delay(750); float newTemp = sensors.getTempCByIndex(0); filteredTemp = (alpha * newTemp) + ((1 - alpha) * filteredTemp); Serial.println(filteredTemp); }
  4. 分辨率选择:DS18B20支持9-12位分辨率,精度越高,转换时间越长。通过库可以设置:sensors.setResolution(12)。对于变化缓慢的环境温度监测,使用12位分辨率;对于需要快速读数的场合,可以降低到9位,转换时间将缩短至约100ms。
  5. 定期校准(可选):虽然DS18B20出厂已校准,但对于精度要求极高的场合,可以将传感器与一个经过计量的高精度温度计置于同一恒温环境(如冰水混合物、沸水,注意沸点受气压影响),记录读数偏差,在软件中进行补偿。

7. 项目拓展与高级应用构想

7.1 构建本地温度监测网络

单个传感器只能监测一个点的温度。利用单总线的多设备能力,可以轻松构建一个分布式的温度监测网络。例如,在一个大型温室的不同区域布置多个DS18B20探头,用一个Arduino作为数据采集器,定期轮询所有传感器,将数据通过串口发送给树莓派或电脑上的上位机软件,绘制出温场分布图,甚至实现超温报警。

关键点在于可靠地获取并管理每个传感器的64位ROM地址。建议在系统初始化时,运行一次地址搜索程序,将找到的地址列表保存在Arduino的EEPROM中,这样下次上电就不需要重新搜索了。

7.2 接入物联网平台与远程监控

让Arduino联网,温度数据就拥有了更大的价值。你可以使用ESP8266或ESP32这类自带Wi-Fi的Arduino兼容板。

  • 硬件升级:将主控从Arduino Uno替换为NodeMCU(ESP8266)或ESP32开发板。DS18B20的接线方式完全不变。
  • 代码调整:在原有温度读取代码的基础上,加入Wi-Fi连接逻辑(使用WiFi.h库)和HTTP客户端逻辑(使用HTTPClientWiFiClient库)。
  • 数据上传:将读取到的温度数据,按照物联网平台(如阿里云物联网平台、ThingsBoard、Blynk,或自建的MQTT服务器)要求的协议格式(通常是JSON),通过HTTP POST或MQTT协议发送出去。
  • 远程查看:在平台端配置仪表盘,就可以在手机或电脑上实时查看温度曲线和历史数据。

7.3 结合执行机构实现闭环控制

测温的最终目的往往是控制。我们可以将DS18B20作为反馈元件,构建一个简单的闭环温控系统。

示例:恒温风扇控制器

  • 硬件:Arduino Uno + DS18B20 + 继电器模块 + 小型风扇。
  • 逻辑:设定一个目标温度(如28°C)。DS18B20实时监测环境温度。当温度高于28.5°C时,Arduino控制继电器闭合,风扇启动降温;当温度低于27.5°C时,继电器断开,风扇停止。这中间0.5°C的差值称为“回差”(Hysteresis),用于防止继电器在临界点频繁开关。
  • 代码核心
    float targetTemp = 28.0; float hysteresis = 0.5; bool fanState = false; void loop() { float currentTemp = readTemperature(); if (currentTemp > (targetTemp + hysteresis) && !fanState) { digitalWrite(FAN_PIN, HIGH); // 打开风扇 fanState = true; } else if (currentTemp < (targetTemp - hysteresis) && fanState) { digitalWrite(FAN_PIN, LOW); // 关闭风扇 fanState = false; } delay(5000); // 每5秒检查一次 }

这个简单的PID(比例、积分、微分)控制相比,回差控制简单粗暴但有效,非常适合对控制精度要求不高的场合,如孵化器、酸奶机、小型植物暖房等。通过这个项目,你就能将感知(传感器)、决策(Arduino程序)、执行(继电器)完整地串联起来,实现一个真正的嵌入式系统应用。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询