news 2026/5/10 13:28:53

51单片机实战:DS18B20温度传感器的单总线通信与精准测温

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
51单片机实战:DS18B20温度传感器的单总线通信与精准测温

1. 从零认识DS18B20:你的第一个数字温度计

如果你刚开始玩51单片机,想做个温度计或者温控小风扇,那DS18B20绝对是你绕不开的一个“神器”。我第一次用它的时候,感觉特别神奇,就这么一个小黑疙瘩,三根线(甚至两根线)接上,写几行代码,就能读到精确到小数点后好几位的温度,比那种需要接ADC的模拟传感器方便太多了。

DS18B20本质上是一个数字温度传感器。什么叫数字呢?就是说它内部自己就把温度测量好了,并且转换成了我们单片机可以直接理解的二进制数据,通过一根线传给我们。这就好比一个会说话的 thermometer,你问它“现在多少度?”,它直接告诉你“26.5摄氏度”,而不是给你一个需要你自己去换算的电阻值或者电压值。这对我们单片机开发者来说,省去了设计模拟信号调理电路、进行模数转换的麻烦,硬件电路极其简单,抗干扰能力也强。

它的核心卖点就是那个“单总线”(1-Wire)通信协议。顾名思义,只用一根数据线(通常叫DQ线)就能完成双向通信,既用它给传感器发命令,也用它从传感器读数据。如果采用“寄生供电”模式,连电源线都可以省掉,直接数据线和地线两根线就能工作,这在布线空间紧张或者需要远程测温的场景下非常有用。它的测温范围是-55°C 到 +125°C,精度在常温下能达到±0.5°C,分辨率最高可以调到0.0625°C,对于我们日常的电子制作和大多数工业监控场景,完全够用。

那么,它适合谁呢?我觉得最适合两类朋友:一是正在学习51单片机的学生或爱好者,想找一个有代表性、能实战的传感器项目来练手;二是需要快速实现温度监测功能的电子工程师或创客,DS18B20能让你用最小的硬件成本和时间成本,得到一个稳定可靠的结果。接下来,我就带你从硬件连接到代码调试,完整体验一遍用51单片机“驾驭”这个传感器的全过程,过程中我踩过的坑、总结的技巧,都会毫无保留地分享给你。

2. 硬件连接:两种供电模式的实战选择

拿到DS18B20,第一步就是把它和我们的51单片机开发板连起来。别小看接线,这里面的选择直接影响系统的稳定性和复杂度。DS18B20通常有三种引脚:红色的VDD(电源正极)、黑色的GND(地线)、以及黄色(或蓝色)的DQ(数据线)。对应到我们的51单片机,就是接电源(通常是5V或3.3V)、接地、以及接一个任意的I/O口(比如P3^7)。

第一种接法,是标准的外部供电模式。这也是我最推荐新手使用的模式,因为它最稳定,不容易出幺蛾子。接法很简单:DS18B20的VDD引脚接到开发板的5V引脚,GND接GND,DQ引脚接单片机的一个I/O口(例如P3.7)。关键一步来了:必须在DQ引脚和电源(VCC)之间,连接一个4.7kΩ左右的上拉电阻。这个电阻的作用至关重要,因为DS18B20的数据口是“开漏输出”结构,它自己只能把总线拉低(输出0),而不能主动拉高(输出1)。这个上拉电阻就是负责在传感器不拉低总线时,将总线电平保持在高电平(1),为数据传输提供稳定的“高电平”基准。很多朋友第一次做发现通信不上,十有八九是忘了焊这个电阻。

第二种接法,是炫酷的寄生供电模式。这种模式下,DS18B20的VDD引脚直接和GND引脚接在一起,都接地。它所需要的电能,全部通过那根DQ数据线“偷”过来。具体怎么“偷”呢?这需要我们的单片机配合:在传感器进行温度转换这种耗电较大的操作时,单片机需要把DQ引脚通过一个MOS管强行拉到电源(称为“强上拉”),给传感器内部的电容充电,供其完成转换。转换结束后,再恢复成普通的开漏模式加4.7kΩ上拉进行通信。这种模式省了一根电源线,在需要多点测温、布线复杂的场合很有优势。但是,它对时序和电源管理的要求更苛刻,如果强上拉的时间不够或者电流不足,可能导致转换失败。我个人的经验是,新手项目或者单点测温,老老实实用外部供电,等玩熟了再挑战寄生供电。

为了让你更清楚,我画一个简单的对比表格:

特性外部供电模式寄生供电模式
接线VDD接电源,GND接地,DQ接IO并上拉VDD与GND共接接地,仅DQ接IO并上拉
上拉电阻必需,通常4.7kΩ必需,通常4.7kΩ,且需额外强上拉电路
稳定性,电源独立,不受通信影响,依赖总线供电,大电流操作时需特别处理
复杂度低,接线简单,程序无需特殊处理高,需在代码中控制强上拉时序
适用场景新手入门、单点测温、稳定性要求高的项目多点测温、布线受限、需要极致简化连线的场合

在实际焊接时,我习惯使用面包板先搭建电路,确认一切工作正常后,再焊接到万用板或者集成到自己的PCB上。一定要确保连接牢固,虚焊是调试过程中最让人头疼的问题之一。另外,如果你买的DS18B20是那种不锈钢封装探头型的,它的三根线可能颜色不标准,一定要用万用表测一下,区分出VDD、GND和DQ,通常红线是VDD,黑线是GND,黄/白/蓝线是DQ。

3. 单总线通信协议:像摩尔斯电码一样的对话规则

硬件接好了,接下来就要让单片机和DS18B20“对话”了。它们之间的语言,就是单总线协议。你可以把它理解成一套非常严格的“摩尔斯电码”规则,所有通信都通过控制DQ这根线电平的高低和持续时间来完成。协议规定了四种基本操作:初始化(复位)、写一位、读一位、以及由它们衍生出的写一个字节和读一个字节。

首先是初始化,也叫复位脉冲。这是每次通信序列的开始,目的是让总线上的所有DS18B20“醒一醒,准备听命令”。具体过程是:主机(我们的单片机)把DQ线拉低至少480微秒,然后释放(也就是让DQ线变回高电平,靠那个4.7kΩ电阻拉上去)。释放之后,主机会等待大约15到60微秒,然后去“听”总线。此时,如果总线上有DS18B20,它会在主机释放总线后的15到60微秒内,主动把总线拉低大约60到240微秒,以此来回应主机:“嗨,我在这儿呢!”。主机检测到这个由从机产生的低电平脉冲,就知道初始化成功,有设备在线。如果超过一定时间没检测到,那就是通信失败了。这个“一问一答”的过程,是建立联系的基础。

然后是发送一位数据。在单总线上,数据是靠控制低电平的持续时间来区分的。要发送一个逻辑“0”,主机需要把总线拉低60到120微秒,然后释放。要发送一个逻辑“1”,操作就“短促”得多:主机把总线拉低仅仅1到15微秒,然后马上释放。DS18B20会在主机拉低总线后大约30微秒的时刻去采样总线电平。如果主机发的是“1”,此时总线早已恢复高电平,从机就读到“1”;如果发的是“0”,此时总线还被主机拉着低电平,从机就读到“0”。每个这样的“位时间片”总长度必须大于60微秒。

接收一位数据则反过来。主机要读取从机发来的一个位,需要先主动发起一个“读时隙”:主机把总线拉低1到15微秒,然后释放。释放之后,主机必须赶紧在15微秒内去读取总线上的电平。这里有个关键:DS18B20会在主机拉低总线的那一刻开始准备数据,并在主机释放总线后,将数据位保持到总线上一段时间。如果从机想输出“0”,它就会持续拉低总线;如果输出“1”,它就释放总线(由上拉电阻拉高)。所以,主机在释放总线后大约15微秒时读取,读到低电平就是0,读到高电平就是1。

基于这一位一位的读写,我们就能组合成字节的读写。发送一个字节,就是循环8次“发送一位”的操作,通常从字节的最低位(LSB)开始发送。接收一个字节,则是循环8次“接收一位”的操作,同样从最低位开始组装。理解了这个位时序,再看代码就会豁然开朗。写代码时最要紧的就是精确的延时,因为DS18B20对时序非常敏感。在12MHz晶振的51单片机上,一个_nop_()空操作指令耗时1微秒,这是我们构建所有延时函数的基础。我后面会给出用循环精确计算延时的代码,这是确保通信稳定的核心。

4. 驱动代码编写:从底层时序到高层API

理论懂了,现在我们来动手写代码。我会按照自底向上的顺序,先实现最核心的单总线底层时序函数,然后基于它们实现DS18B20的驱动,最后完成一个温度读取的主程序。这样结构清晰,也方便你以后把代码移植到其他项目。

我们先来写单总线协议的核心文件OneWire.cOneWire.h。这里假设DS18B20接在单片机的P3.7引脚。

// OneWire.h #ifndef __ONEWIRE_H__ #define __ONEWIRE_H__ unsigned char OneWire_Init(void); void OneWire_SendBit(unsigned char Bit); unsigned char OneWire_ReceiveBit(void); void OneWire_SendByte(unsigned char Byte); unsigned char OneWire_ReceiveByte(void); #endif

头文件定义了五个函数:初始化、发送一位、接收一位、发送一个字节、接收一个字节。下面是具体的实现,我加了详细的注释,特别是延时部分,这是根据12MHz晶振计算出来的:

// OneWire.c #include <REGX52.H> #include <INTRINS.H> // 使用_nop_()函数 // 引脚定义 sbit OneWire_DQ = P3^7; /** * @brief 单总线初始化(复位脉冲) * @param 无 * @retval 从机响应位:0表示有设备响应,1表示无设备响应 */ unsigned char OneWire_Init(void) { unsigned char i; unsigned char AckBit; OneWire_DQ = 1; // 先确保释放总线 _nop_(); // 短暂延时 OneWire_DQ = 0; // 主机拉低总线,开始复位脉冲 i = 247; while (--i); // 精确延时约500us,远超480us最小值 OneWire_DQ = 1; // 主机释放总线 i = 32; while (--i); // 延时约70us,等待15-60us后检测 AckBit = OneWire_DQ; // 读取总线电平,此时若从机存在会拉低 i = 247; while (--i); // 再延时约500us,等待从机释放总线 return AckBit; // 返回应答位,0为成功 } /** * @brief 向单总线发送一位数据 * @param Bit 要发送的位,0或1 * @retval 无 */ void OneWire_SendBit(unsigned char Bit) { unsigned char i; OneWire_DQ = 0; // 主机拉低总线,开始一个时间片 i = 4; while (--i); // 拉低约10us // 根据要发送的值,决定释放总线的时机 OneWire_DQ = Bit; // 如果Bit=1,则很快拉高;如果Bit=0,则保持低电平 i = 24; while (--i); // 保持约50us,确保整个时间片>60us OneWire_DQ = 1; // 最终释放总线,等待下一个时间片 } /** * @brief 从单总线接收一位数据 * @param 无 * @retval 读取到的位,0或1 */ unsigned char OneWire_ReceiveBit(void) { unsigned char i; unsigned char Bit; OneWire_DQ = 0; // 主机拉低总线,启动读时隙 i = 2; while (--i); // 拉低约5us OneWire_DQ = 1; // 主机迅速释放总线 i = 2; while (--i); // 延时约5us,等待总线稳定 Bit = OneWire_DQ; // 在拉低后约10us处采样总线电平 i = 24; while (--i); // 延时约50us,等待该时间片结束 return Bit; } /** * @brief 向单总线发送一个字节数据(低位在先) * @param Byte 要发送的字节 * @retval 无 */ void OneWire_SendByte(unsigned char Byte) { unsigned char i; for(i=0; i<8; i++) { // 依次发送字节的每一位,从最低位开始 OneWire_SendBit(Byte & (0x01 << i)); } } /** * @brief 从单总线接收一个字节数据(低位在先) * @param 无 * @retval 接收到的字节 */ unsigned char OneWire_ReceiveByte(void) { unsigned char i; unsigned char Byte = 0x00; for(i=0; i<8; i++) { if(OneWire_ReceiveBit()) { Byte |= (0x01 << i); // 如果读到1,则设置对应位 } } return Byte; }

有了坚固的底层,DS18B20的驱动就变得非常简单了。我们只需要按照它的命令集来操作。两个最常用的命令是:0xCC(跳过ROM,适用于总线上只有一个传感器时),0x44(开始温度转换),0xBE(读取暂存器,即温度数据)。

// DS18B20.h #ifndef __DS18B20_H__ #define __DS18B20_H__ void DS18B20_ConvertT(void); float DS18B20_ReadT(void); #endif
// DS18B20.c #include <REGX52.H> #include "OneWire.h" // DS18B20指令定义 #define DS18B20_SKIP_ROM 0xCC #define DS18B20_CONVERT_T 0x44 #define DS18B20_READ_SCRATCHPAD 0xBE /** * @brief 启动DS18B20进行一次温度转换 * @param 无 * @retval 无 */ void DS18B20_ConvertT(void) { OneWire_Init(); // 初始化总线 OneWire_SendByte(DS18B20_SKIP_ROM); // 发送跳过ROM命令 OneWire_SendByte(DS18B20_CONVERT_T); // 发送开始转换命令 // 注意:发送完转换命令后,DS18B20就开始转换了,此时总线可以释放 // 对于寄生供电模式,这里需要额外加强上拉操作,本例为外部供电,故省略 } /** * @brief 从DS18B20读取温度值 * @param 无 * @retval 温度值,单位为摄氏度,浮点数 */ float DS18B20_ReadT(void) { unsigned char TLSB, TMSB; // 温度低字节,温度高字节 int Temp; // 合并后的16位有符号整数 float T; OneWire_Init(); // 初始化总线 OneWire_SendByte(DS18B20_SKIP_ROM); // 发送跳过ROM命令 OneWire_SendByte(DS18B20_READ_SCRATCHPAD); // 发送读暂存器命令 TLSB = OneWire_ReceiveByte(); // 先读低字节 TMSB = OneWire_ReceiveByte(); // 再读高字节 // 将两个字节合并成一个16位有符号整数 Temp = (TMSB << 8) | TLSB; // DS18B20的温度数据是12位精度,以1/16°C为LSB // 将16位整数除以16.0,得到实际温度值 T = Temp / 16.0; return T; }

最后,我们写一个主函数,把温度显示在LCD1602液晶屏上,这样就完成了一个完整的温度计项目。

// main.c #include <REGX52.H> #include "LCD1602.h" #include "DS18B20.h" #include "Delay.h" // 需要一个毫秒级延时函数 float T; // 全局变量存储温度 void main() { // 上电后先启动一次温度转换并等待完成,避免第一次读取到无效数据 DS18B20_ConvertT(); Delay(1000); // 等待转换完成,12位精度下最多750ms LCD_Init(); // 初始化液晶屏 LCD_ShowString(1, 1, "Temperature:"); // 显示标题 while(1) { DS18B20_ConvertT(); // 启动新一轮温度转换 // 在实际应用中,这里最好加个延时等待转换完成,或者查询状态 // 简单起见,我们直接用一个较长的延时,确保转换完成 Delay(750); // 等待转换完成(最坏情况) T = DS18B20_ReadT(); // 读取温度值 // 处理并显示温度,区分正负 if(T < 0) // 温度为负 { LCD_ShowChar(2, 1, '-'); // 显示负号 T = -T; // 取绝对值方便显示 } else // 温度为正或零 { LCD_ShowChar(2, 1, '+'); // 显示正号 } // 显示整数部分(3位) LCD_ShowNum(2, 2, (unsigned int)T, 3); LCD_ShowChar(2, 5, '.'); // 显示小数点 // 显示小数部分:将浮点数乘以10000取后四位 LCD_ShowNum(2, 6, (unsigned long)(T * 10000) % 10000, 4); Delay(500); // 每隔约0.5秒更新一次温度 } }

5. 温度数据解析与精度处理技巧

从DS18B20读回来的温度数据,并不是一个简单的整数,需要我们按照特定的格式进行解析,才能得到正确的摄氏度值。这是很多新手容易出错的地方。DS18B20默认输出12位精度的温度数据,这12位数据存储在两个字节(共16位)中。

具体存储格式是这样的:读回来的低字节(LSB)在前,高字节(MSB)在后。合并成一个16位有符号整数后,其低12位(bit0-bit11)是有效的温度数据,高4位(bit12-bit15)是符号位。这12位数据,本质上是一个以1/16°C(即0.0625°C)为最小单位的二进制补码数。

如何计算实际温度呢?公式很简单:实际温度 = (读取的16位有符号整数) / 16.0。举个例子,如果读回来的两个字节是0x010x91(即0x9101),合并成16位整数是0x9101。注意,这是一个有符号数,其十进制值是-28415(因为最高位是1,表示负数)。套用公式:-28415 / 16.0 ≈ -1775.9375,这显然不对。等等,我们犯了一个错误:DS18B20的数据是低字节在前!所以正确的合并应该是TMSB<<8 | TLSB,即0x01是高字节?不对,应该是先读的是低字节TLSB=0x01,后读的是高字节TMSB=0x91。所以16位整数是(0x91<<8) | 0x01 = 0x9101。没错,就是这个数。但它的含义是:这是一个负数的补码。我们需要将其转换为原码。

更简单的做法是,直接把这个16位整数当作有符号的short/int类型来处理,然后除以16.0。在C语言中,当我们把0x9101赋值给一个int型变量Temp时,由于int是16位有符号的,0x9101的最高位是1,所以Temp自动被解释为一个负数。Temp / 16.0这个运算,会先对这个负数进行整数除法,再转换为浮点数,得到的就是正确的负温度值。在我的代码float T = Temp / 16.0;中,编译器已经帮我们处理了符号问题。所以,你不需要手动去判断符号位和取反加一,直接使用有符号数运算即可,这是最稳妥的方法。

对于正温度,比如读回0x500x00(即0x0050),计算80 / 16.0 = 5.0°C。对于负温度,比如读回0xFF0xF8(即0xF8FF),这是一个负数补码,其值约为-1793,计算-1793 / 16.0 = -112.0625°C

关于精度选择,DS18B20允许我们通过配置寄存器选择9位到12位的分辨率。分辨率越高,转换时间越长(12位最长达750ms)。在大多数情况下,默认的12位分辨率(0.0625°C)是最佳选择。如果你对刷新速度要求极高(比如每秒要读很多次),可以降低分辨率来换取更快的转换速度。配置方法是通过向传感器写入特定的命令来修改暂存器中的配置字节,这涉及到更复杂的ROM匹配操作,在单传感器且不常更改设置的场景下,我们直接用默认值就好。

6. 调试实战与常见问题排查

代码写完了,烧录进单片机,满怀期待地通电——结果LCD上可能显示乱码、固定的错误温度,或者干脆没反应。别慌,这是嵌入式开发的常态。下面我结合自己踩过的坑,给你梳理一套调试流程和常见问题排查清单。

第一步:检查硬件连接。这是所有问题的根源,务必反复确认。

  1. 电源和地:用万用表测量DS18B20的VDD和GND之间是否有稳定的5V(或3.3V)电压?电压不足会导致传感器工作异常。
  2. 上拉电阻:4.7kΩ的上拉电阻焊上了吗?电阻值是否准确?可以临时用个10kΩ的试试,但4.7kΩ是最佳值。
  3. 接线错误:DQ线是否确实接到了你代码中定义的I/O口(比如P3.7)?有没有虚焊或者接触不良?用万用表通断档仔细检查。
  4. 传感器方向:如果是TO-92封装的直插芯片,注意引脚顺序,圆弧面通常朝自己,从左到右是GND、DQ、VDD。接反了可能会烧坏传感器。

第二步:用示波器或逻辑分析仪抓取时序波形(如果有条件)。这是最直接的调试手段。把探头接到DQ线上,观察单片机和DS18B20的通信波形。

  • 复位脉冲:主机拉低的时间是否够长(>480us)?释放后,有没有看到一个短暂的、由从机产生的低电平脉冲(60-240us)?如果没有从机应答脉冲,说明初始化失败。
  • 读写位时序:主机拉低的时间是否符合协议规定?发送“1”的时间是否足够短(1-15us)?发送“0”的时间是否足够长(60-120us)?读时隙中,主机采样点是否在拉低后15us左右?
  • 波形畸变、上升沿缓慢,都可能是上拉电阻过大或总线电容过大导致的。

第三步:软件调试与代码排查。

  1. 延时精度:这是最常见的问题。你的单片机晶振是12MHz吗?我的示例代码中的循环延时while (--i);是基于12MHz、12T模式计算的。如果你的晶振是11.0592MHz或者工作在6T模式,延时时间会完全不同!你必须根据自己单片机的实际机器周期重新计算延时循环次数。一个简单的验证方法是:写一个让I/O口翻转的程序,用示波器测量翻转周期,来校准你的延时函数。
  2. 初始化返回值:在OneWire_Init()函数中,检查返回值AckBit。如果是1,说明DS18B20没有响应。可以在初始化失败时,让一个LED闪烁,便于诊断。
  3. 命令顺序:确保严格按照“初始化->跳过ROM(0xCC)->启动转换(0x44)”的顺序发送命令。读取时是“初始化->跳过ROM(0xCC)->读暂存器(0xBE)->连续读两个字节”。
  4. 等待转换完成:发送启动转换命令0x44后,DS18B20需要时间进行模数转换。在12位分辨率下,这个过程最长可能需要750ms。如果你发送完0x44后立即去读温度,读到的将是上一次的转换结果,或者无效数据。务必在发送0x44后等待足够长的时间,或者使用更高级的“读忙”状态功能(通过发送读时隙来检测转换是否完成)。
  5. 数据类型处理:在DS18B20_ReadT()函数中,确保Temp变量是int(16位有符号)类型,而不是unsigned int。否则负温度会被错误地解释为一个很大的正数。

第四步:环境与传感器本身。

  • 尝试更换一个DS18B20传感器。虽然DS18B20很耐用,但也有损坏的可能。
  • 检查总线长度。单总线协议不适合长距离通信,一般建议在1-2米以内。线太长会导致信号衰减和畸变。
  • 注意电源噪声。如果系统中存在电机、继电器等大电流设备,可能会在电源上产生噪声,干扰DS18B20。可以在传感器电源引脚就近加一个0.1uF的瓷片电容进行滤波。

我印象最深的一次调试,是温度读数偶尔会跳变到一个极大的值。最后发现是主循环中,启动转换和读取温度之间没有加延时,在CPU运行很快时,绝大部分时间转换都能及时完成,但偶尔在临界点就会读到错误数据。加上一个750ms的延时后,问题彻底解决。所以,在嵌入式开发中,对时序保持敬畏之心总是没错的

7. 项目进阶:从单点到多点测温系统

当你成功驱动了一个DS18B20后,就可以玩点更高级的了:在一根总线上挂载多个DS18B20,实现多点测温。这正是单总线协议强大之处,每个DS18B20在出厂时都拥有一个全球唯一的64位ROM序列号,就像身份证号一样。主机可以通过这个序列号,在总线上精准地“点名”某一个传感器进行操作。

要实现多点测温,你的代码需要增加以下几个关键功能:

  1. ROM搜索算法:这是一个经典的递归算法,用于发现总线上所有DS18B20的64位ROM码,并将它们存储到一个数组中。Dallas官方有标准的搜索算法代码,稍微复杂但逻辑清晰。你需要实现OneWire_SearchRom之类的函数。
  2. 匹配ROM命令:在对特定传感器操作时,不再使用0xCC(跳过ROM),而是使用0x55(匹配ROM),然后紧接着发送你要操作的那个传感器的64位ROM码。这样,只有ROM码匹配的那个传感器才会响应后续的命令。
  3. 读取ROM命令:如果你总线上只有一个传感器,但又不知道它的ROM码,可以使用0x33(读取ROM)命令来获取。但注意,这个命令只能在总线上有且仅有一个传感器时使用,否则会发生数据冲突。

流程会变成这样:上电后,先执行一次ROM搜索,把所有传感器的ID存起来。然后,你可以遍历这个列表,对每一个传感器,依次发送:初始化 -> 匹配ROM命令(0x55) -> 发送该传感器的64位ID -> 启动转换命令(0x44)。等所有传感器都转换完成后,再依次用“匹配ROM”命令去读取每个传感器的温度值。

这带来了新的挑战:转换等待时间。如果你有10个传感器,每个都串行地启动转换并等待750ms,那读完一轮需要7.5秒,刷新率太低了。这里有一个高级技巧:DS18B20支持“寄生供电下的并行转换”。你可以使用0xCC(跳过ROM)命令同时向总线上的所有传感器发送0x44(开始转换)命令,这样所有传感器会同时开始转换。然后,你可以通过“读时隙”来查询任何一个传感器的转换状态(读到一个0表示还在转换,1表示完成)。由于所有传感器同时工作,你只需要等待最慢的那个完成(还是750ms),然后就可以依次去读取每个传感器的结果了,大大提高了系统效率。

当然,多点测温的代码复杂度和对时序的要求更高。我建议你先在单点测温上做到滚瓜烂熟,理解了每一个时序细节后,再去找一些成熟的多点测温库代码来研究和移植。当你成功实现多点测温时,你会发现之前所有的努力都是值得的,因为你可以用极简的布线,构建一个分布式的温度监控网络,这正是很多实际应用(如仓库温控、农业大棚)的雏形。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/10 13:28:53

数据驱动的无人机飞行优化:Flight Review实战指南

数据驱动的无人机飞行优化&#xff1a;Flight Review实战指南 【免费下载链接】flight_review web application for flight log analysis & review 项目地址: https://gitcode.com/gh_mirrors/fl/flight_review 如何从杂乱日志中定位飞行隐患&#xff1f; 每一次无…

作者头像 李华
网站建设 2026/4/26 8:40:04

LiteDB Studio 嵌入式NoSQL数据库可视化管理工具完全指南

LiteDB Studio 嵌入式NoSQL数据库可视化管理工具完全指南 【免费下载链接】LiteDB.Studio A GUI tool for viewing and editing documents for LiteDB v5 项目地址: https://gitcode.com/gh_mirrors/li/LiteDB.Studio 功能特性解析 LiteDB Studio作为一款专为LiteDB v5…

作者头像 李华
网站建设 2026/4/27 7:05:44

如何突破Windows输入模拟限制?Interceptor驱动级解决方案全解析

如何突破Windows输入模拟限制&#xff1f;Interceptor驱动级解决方案全解析 【免费下载链接】Interceptor C# wrapper for a Windows keyboard driver. Can simulate keystrokes and mouse clicks in protected areas like the Windows logon screen (and yes, even in games).…

作者头像 李华
网站建设 2026/4/26 20:03:21

AMD显卡驱动高效精简方案:Radeon Software Slimmer全方位优化指南

AMD显卡驱动高效精简方案&#xff1a;Radeon Software Slimmer全方位优化指南 【免费下载链接】RadeonSoftwareSlimmer Radeon Software Slimmer is a utility to trim down the bloat with Radeon Software for AMD GPUs on Microsoft Windows. 项目地址: https://gitcode.c…

作者头像 李华
网站建设 2026/4/27 16:11:33

PT站点内容分发的自动化解决方案:auto-feed脚本深度解析

PT站点内容分发的自动化解决方案&#xff1a;auto-feed脚本深度解析 【免费下载链接】auto_feed_js PT站一键转载脚本 项目地址: https://gitcode.com/gh_mirrors/au/auto_feed_js 在PT&#xff08;Private Tracker&#xff09;社区的日常运营中&#xff0c;内容分发是维…

作者头像 李华
网站建设 2026/4/26 16:46:32

如何用开源工具OpenBoardView提升电路板分析效率

如何用开源工具OpenBoardView提升电路板分析效率 【免费下载链接】OpenBoardView View .brd files 项目地址: https://gitcode.com/gh_mirrors/op/OpenBoardView 在电子工程师的日常工作中&#xff0c;电路板文件&#xff08;.brd&#xff09;的查看与分析是必不可少的环…

作者头像 李华