PICAXE单片机驱动DS18B20温度传感器:从硬件连接到数据处理
1. 项目概述用PICAXE玩转DS18B20数字温度传感器如果你手头有一块PICAXE单片机想快速实现一个温度监测项目那么DS18B20这颗数字温度传感器绝对是你的绝佳拍档。它只需要一根数据线就能和MCU通信抗干扰能力强还能通过长导线进行远程测温非常适合DIY温控系统、环境监测或者简单的气象站。我在最近的一个小项目中就用PICAXE-08M2芯片配合一块OLED显示屏搭建了一个双路温度显示装置过程中踩了一些坑也总结了不少实用技巧。这篇文章我就来详细拆解如何让PICAXE与DS18B20协同工作从硬件连接到软件编程再到数据处理和显示把整个流程掰开揉碎了讲清楚特别是如何正确处理那有点“绕”的12位二进制补码温度数据。2. 核心硬件连接与电路设计要点2.1 DS18B20传感器简介与选型DS18B20是Dallas现Maxim Integrated推出的一款单总线数字温度传感器。它的核心优势在于“单总线”协议仅需一根数据线外加电源和地线即可完成双向通信。这意味着你可以在同一根总线上挂载多个传感器通过唯一的64位序列号来区分它们非常适合需要多点测温的场景。传感器本身提供9位到12位的可编程分辨率最高精度可达±0.5°C在-10°C至85°C范围内。市面上常见的封装有TO-92像一个小三极管、SO-8贴片以及不锈钢探头封装后者适合恶劣环境或需要防水防腐蚀的场合。对于大多数业余项目TO-92封装就足够了价格也便宜。2.2 绝对不能忽略的上拉电阻这是我踩的第一个也是最重要的一个坑。DS18B20的数据线DQ是开漏输出。简单来说芯片内部只能把这条线拉低到地GND而不能主动把它拉高到电源电压VCC。为了在芯片不主动拉低时让数据线能保持一个确定的高电平状态必须在DQ线和VCC之间连接一个上拉电阻。这个电阻的典型值是4.7kΩ在PICAXE官方手册的READTEMP命令示例图中明确标出。注意忘记接这个4.7kΩ电阻是导致通信失败、读回数据为0或85默认值的最常见原因。我一开始就犯了懒没仔细看手册结果传感器毫无反应折腾了好久。正确的连接电路非常简单VDD引脚接PICAXE系统的正电源通常是5V或3.3VDS18B20的工作范围是3.0V至5.5V与PICAXE兼容。GND引脚接系统地。DQ引脚接PICAXE的任意一个数字I/O引脚例如pinC.1。同时通过一个4.7kΩ的电阻连接到VDD。如果传感器离MCU较远超过1-2米建议在传感器端的VDD和GND之间并联一个0.1μF的陶瓷去耦电容以提高抗干扰能力。我的实际连接示意图如下以PICAXE-08M2和两个DS18B20为例PICAXE-08M2 V (5V) ---------------- VDD (DS18B20 #1 #2) | | 4.7kΩ 4.7kΩ (可选但推荐每个传感器独立上拉) | | pinC.1 ------------------ DQ (DS18B20 #1 #2) [单总线] pinC.2 -------------------- DQ (备用或接第二个独立总线) VSS (GND) ------------------ GND (DS18B20 #1 #2)在实际操作中我将两个DS18B20的DQ引脚都接到了pinC.1构成了一个单总线上挂两个设备的网络。这种方式节省I/O口但编程上需要处理寻址。为了简化初期调试你也可以每个传感器单独接一个I/O口。2.3 电源模式选择寄生供电与外部供电DS18B20支持两种供电模式外部供电如上图所示老老实实接好VDD、GND和DQ。这是最可靠、最推荐的方式尤其在长导线或多设备网络中。寄生供电不连接独立的VDD引脚只接GND和DQ。此时DQ线需要通过一个较强的上拉电阻如4.7kΩ在特定时序下“偷电”为芯片供电。这种方式可以节省一根线但时序要求更严格在驱动多个传感器或总线负载较重时可能不稳定。对于PICAXE初学者和大多数应用强烈建议使用外部供电模式可以避免很多不必要的麻烦。PICAXE的READTEMP命令也是针对外部供电模式优化设计的。3. PICAXE BASIC 编程与温度读取3.1 基础命令READTEMP 与 READTEMP12PICAXE BASIC 语言为DS18B20提供了极其简化的命令这是其易用性的精髓所在。READTEMP pin, variable这是最常用的命令。它执行一次完整的温度转换并从传感器读取结果然后将**四舍五入后的整数值单位为摄氏度**存入指定的字节变量variable中。例如READTEMP C.1, b0会将温度值如24°C存入变量b0。它的内部操作其实包含了单总线协议中的初始化、发送温度转换命令、等待转换完成、读取暂存器等复杂步骤但都被封装成了一行简单的代码。READTEMP12 pin, variable这个命令与READTEMP类似但它读取的是完整的12位分辨率原始数据并将其存储在一个**字变量word variable**中。这个原始数据是理解DS18B20精度和进行高分辨率处理的关键。3.2 我的初始代码与遇到的坑我最开始的代码简单到令人发指symbol tempPin C.1 symbol temperature b0 main: readtemp tempPin, temperature debug temperature pause 1000 goto main结果debug窗口始终显示0。排查过程如下检查接线VCC、GND、DQ都通了没问题啊怀疑传感器换了一个新的DS18B20还是0。检查代码引脚定义、命令拼写都对。最终翻手册才看到那个小小的4.7kΩ上拉电阻示意图。焊上电阻后立刻读出了室温24。教训就是永远不要忽视数据手册或官方手册中的原理图细节哪怕它再小。3.3 实现双路温度读取与显示加上电阻一个传感器工作后添加第二个就很简单了。我采用了单总线多设备方案这就需要用到OWIN和OWOUT命令来手动操作单总线协议因为READTEMP命令默认会向总线上所有设备发送命令。为了简化我初期给每个传感器单独用了I/O口C.1和C.2代码如下symbol tempPin1 C.1 symbol tempPin2 C.2 symbol temp1 b0 symbol temp2 b1 main: readtemp tempPin1, temp1 ; 读取传感器1 readtemp tempPin2, temp2 ; 读取传感器2 ; 此处添加OLED显示代码显示temp1和temp2 pause 5000 ; 每5秒更新一次 goto main这样我就得到了两个独立的温度读数。将传感器一个放在室内一个伸出窗外一个简单的室内外温差计就成型了。4. 深入解析12位温度数据与二进制补码处理4.1 DS18B20的数据格式当使用READTEMP12命令后获得的数据存储在如w0这样的字变量中格式如下表所示位位置从高到低位域含义说明Bit 15S (Sign)符号位0 正温度1 负温度Bit 14 ~ Bit 4T (Temperature)温度整数部分以二进制表示的温度整数值。对于正温度这就是实际值。Bit 3 ~ Bit 0F (Fraction)温度小数部分表示1/16°C的倍数。00000.000010.0625 ...11110.9375例如w0中的二进制值0000 0001 0011 0000表示符号位0 正温度整数部分0000 0001 0011 十进制19小数部分0000 0.0所以温度是19.0°C。4.2 负温度与二进制补码的“陷阱”问题出在负温度。DS18B20输出的是二进制补码形式。对于不熟悉补码的朋友这里简单解释在补码表示中负数的最高位符号位是1其数值部分需要经过转换才能得到绝对值。例如假设READTEMP12读回的w0值是1111 1111 1111 00000xFFF0。如果直接把这个值当成无符号整数来看是65296这显然不是温度。补码转换规则对于一个负数符号位为1其数值部分低15位所代表的实际值等于这个数值部分按位取反再加1即求补然后加上负号。PICAXE BASIC 提供了处理有符号数的能力。最直接的方法是使用** 符号。在PICAXE中变量名前加 **表示将其视为有符号数。但要注意READTEMP12读取的原始数据本身是补码格式PICAXE的数学运算指令如加减乘除在操作字节/字变量时通常将其当作无符号数处理。因此我们需要手动处理。我的处理方法如下判断符号位。如果是正数直接提取整数和小数部分。如果是负数先对低15位数据温度值部分进行补码转换再提取。symbol rawTemp w0 ; READTEMP12读取的原始数据 symbol isNegative bit0 ; 符号位标志 symbol tempInteger b2 ; 整数部分 symbol tempFraction b3 ; 小数部分0-15的数值 readtemp12 C.1, rawTemp ; 读取12位原始数据到w0 ; 1. 判断符号位 (w0的最高位即bit15) isNegative rawTemp / 32768 ; 除以32768(2^15)若结果为1则为负 if isNegative 0 then ; 2. 正温度处理 tempInteger rawTemp / 16 ; 右移4位得到整数部分 tempFraction rawTemp $0F ; 取低4位得到小数部分(0-15) else ; 3. 负温度处理先求补码取反加一但只对温度值部分操作 ; rawTemp是补码形式的负数。计算其绝对值 ; 方法对低15位取反加1。等效于 (rawTemp ^ $7FFF) 1但要注意rawTemp本身是16位带符号数在内存中的表示。 ; 更稳妥的方法利用PICAXE的 **** 运算符但需要先将数据放到有符号运算的上下文中。 ; 一个实用的技巧将 rawTemp 与 $FFFF 进行有符号比较或运算。这里采用数值计算 ; 负温度的绝对值 (rawTemp ^ $FFFF) 1 // 对整个16位取反加1得到的是绝对值的二进制形式但符号位也变了 ; 实际上对于温度值我们关心的是 (rawTemp ^ $7FFF) 1 的低12位即温度部分。 ; 简化操作先提取出整数和小数部分的二进制值再判断。 ; 更简单清晰的步骤 ; a. 将 rawTemp 视为有符号数得到其负值。在PICAXE中可以用 0 - rawTemp但要注意溢出。 ; b. 我们直接手动计算绝对值部分 let w1 rawTemp ^ $7FFF ; 对低15位取反最高位符号位强制为0 let w1 w1 1 ; 加1得到绝对值部分的二进制表示此时w1的高4位是0低12位是温度绝对值 tempInteger w1 / 16 ; 得到整数部分绝对值 tempFraction w1 $0F ; 得到小数部分绝对值 ; 此时 tempInteger 和 tempFraction 是正数代表温度的绝对值。我们需要知道它是负数。 endif ; 现在isNegative 标志指示正负tempInteger是温度的整数绝对值tempFraction是0-15的小数部分值。这段代码看起来有点复杂核心思想就是遇到符号位为1时对表示温度值的那些位bit14-bit0进行补码转换。在实际项目中如果温度范围在0°C以上可以暂时不处理负数逻辑以简化代码。4.3 小数部分转换为可显示的格式tempFraction是一个0到15的值代表0到0.9375°C。为了在例如OLED上显示为“0.1°C”这样的格式需要转换。我采用了一个近似但快速的转换方法适用于精度要求不极端高的场合小数部分值 (tempFraction) 范围是0-15对应0/16 到 15/16。如果想显示一位小数0.0, 0.1, ..., 0.9可以将0-15映射到0-9。映射关系不是线性的因为15/160.9375。一个简单近似是显示的小数位 (tempFraction * 10) / 16。在整数运算中这等于(tempFraction * 10) / 16。更简单粗暴的近似也是我最初用的显示的小数位 tempFraction / 2因为16/28这样0-15映射到0-7覆盖0.0-0.7丢失了0.8和0.9。或者(tempFraction * 16) / 25这是我文中提到的方法它试图将0-15的输入映射到0-9的输出但存在一些舍入误差。更精确的方法是使用查表法或者直接计算(tempFraction * 100) / 16得到百分度值然后根据需要格式化显示。; 假设 tempFraction 在 b3 中 symbol fracDecimal b4 ; 用于存储换算后的一位小数值(0-9) ; 方法1较精确的计算 (tempFraction * 10) / 16 fracDecimal tempFraction * 10 / 16 ; 方法2我的快速近似法 (tempFraction * 16) / 25 fracDecimal tempFraction * 16 / 25 ; 然后显示时可以这样组织伪代码 if isNegative 1 then sertxd (“-“) ; 发送负号 endif sertxd (#tempInteger, “.”, #fracDecimal, “C”, 13, 10) ; 发送如“-5.3C”的格式5. 系统集成、显示与实战心得5.1 与OLED显示屏的配合我使用了一款128x64像素的I2C接口OLED显示屏SSD1306驱动来显示两个温度值。PICAXE通过HI2C命令可以很方便地驱动它。你需要先初始化I2C总线然后向显示屏发送命令和数据来设置地址、清屏、写入字符串等。网上有很多PICAXE驱动SSD1306的代码片段通常需要包含一个字体数据表。我将两个DS18B20的温度值经过整数和小数部分格式化后的字符串轮流或同屏显示在OLED上。5.2 整体代码结构梳理一个完整的双路温度监测程序结构如下; —— 定义符号与变量 —— symbol DS18B20_1 C.1 symbol DS18B20_2 C.2 symbol rawTemp1 w0 symbol rawTemp2 w1 symbol temp1_int b4 symbol temp1_frac b5 symbol temp2_int b6 symbol temp2_frac b7 symbol sign1 bit0 symbol sign2 bit1 symbol fracDecimal1 b8 symbol fracDecimal2 b9 ; —— 初始化 —— ; 初始化I2C for OLED hi2csetup i2cmaster, $3C, i2cfast, i2cbyte ; SSD1306地址通常是0x3C gosub init_oled pause 100 ; —— 主循环 —— main: gosub read_and_convert_temp1 gosub read_and_convert_temp2 gosub update_oled_display pause 5000 ; 5秒更新一次 goto main ; —— 子程序读取并转换传感器1 —— read_and_convert_temp1: readtemp12 DS18B20_1, rawTemp1 sign1 rawTemp1 / 32768 if sign1 0 then temp1_int rawTemp1 / 16 temp1_frac rawTemp1 $0F else ; 负温度处理简化版假设温度不低于-128°C ; 这里使用一个技巧将补码视为有符号数PICAXE的 ** 运算符在某些情况下可用但更通用的是 w2 rawTemp1 ^ $7FFF 1 temp1_int w2 / 16 temp1_frac w2 $0F endif ; 转换小数部分为一位十进制数 fracDecimal1 temp1_frac * 10 / 16 ; 相对精确的方法 return ; read_and_convert_temp2 子程序类似... ; update_oled_display 子程序负责将temp1_int, fracDecimal1等变量格式化成字符串并显示5.3 实测中的注意事项与心得响应速度DS18B20在12位分辨率下完成一次温度转换最慢需要750ms。READTEMP命令会等待这个转换完成。如果你的程序循环很快需要注意这个延迟或者考虑使用READTEMP命令的“启动转换”加“延迟”加“读取结果”的分步操作来优化程序结构在等待转换时让MCU做其他事。总线负载与线长单总线对时序要求严格。线上设备越多分布电容越大可能影响通信稳定性。使用质量好、线径粗的导线并确保上拉电阻连接可靠。如果通信不稳定可以尝试减小上拉电阻值如用2.2kΩ但不要小于1kΩ以免电流过大。传感器自热DS18B20在工作时会有轻微自热。在静止空气中这可能导致读数比环境温度高0.5°C左右。在要求精确测量流体或表面温度时需要考虑这一点。关于精度官方标称±0.5°C的精度是足够的。我对比了两个传感器的读数在室温下差异通常在0.1°C以内。对于家庭气象站、鱼缸温控、发酵监控等应用完全胜任。不必过分纠结于小数部分转换的那一点点舍入误差那已经超出了传感器本身的绝对精度范围。防静电与防水TO-92封装的DS18B20比较脆弱焊接时要防止过热操作时注意防静电。如果用于潮湿环境可以使用热缩管或灌胶进行防护。不锈钢探头封装则直接适用于恶劣环境。6. 常见问题排查与进阶思路6.1 问题速查表现象可能原因排查步骤始终读回0°C或85°C默认值1.缺少4.7kΩ上拉电阻最常见2. 接线错误VCC/GND接反或DQ虚焊3. 电源电压不足或不稳4. 传感器损坏1. 检查DQ线是否有4.7kΩ上拉到VCC。2. 用万用表检查VCC、GND、DQ电压和连通性。3. 确保电源能提供足够电流DS18B20工作电流约1mA。4. 更换传感器测试。读回的数据随机跳动或为2551. 总线受到干扰长线无屏蔽2. 上拉电阻阻值过大或虚焊3. 电源噪声大4. 程序读取时序过快1. 缩短导线或使用双绞线、屏蔽线。2. 确保上拉电阻焊接牢固可尝试换用2.2kΩ电阻。3. 在VCC和GND间加0.1μF陶瓷电容。4. 在READTEMP命令后增加短暂延时pause 10。只能识别一个传感器多设备时1. 单总线协议冲突未正确进行ROM匹配操作。2. 传感器序列号读取错误。1. 使用READOWSN命令先读取总线上所有设备的64位ROM码确认它们都被识别。2. 在发送温度转换命令OWOUT发送0x44时使用匹配ROM命令0x55后跟特定ROM码而非跳过ROM命令0xCC。负温度显示错误1. 未正确处理二进制补码。2. 显示程序将变量错误地解释为无符号数。1. 参考本文第4.2节的补码处理逻辑。2. 在发送到串口或显示屏前确保对负数添加了负号“-”并正确计算其绝对值。READTEMP12返回值异常大如65535变量类型错误。READTEMP12的结果应存入字变量word如w0而非字节变量byte。检查变量定义确保使用symbol varName w0这样的字变量。6.2 进阶应用单总线上挂载多个DS18B20这是DS18B20真正强大的地方。每个DS18B20都有一个全球唯一的64位激光ROM码。操作流程如下搜索ROM使用OWOUT发送搜索ROM命令0xF0然后通过OWIN读取响应可以枚举出总线上所有设备的ROM码。PICAXE的READOWSN命令可以简化此过程直接读取单个设备的序列号。匹配ROM要对特定设备操作如启动温度转换、读取暂存器先发送匹配ROM命令0x55紧接着发送该设备的64位ROM码。跳过ROM如果总线上只有一个设备或者你想向所有设备广播命令例如同时启动所有传感器进行温度转换可以使用跳过ROM命令0xCC。示例思路; 假设已知两个传感器的ROM码存储在字节数组中 ; 启动所有传感器转换 owout DS18B20_PIN, %1, ($CC, $44) ; 跳过ROM发送转换命令(0x44) pause 750 ; 等待12位转换完成 ; 读取传感器1 owout DS18B20_PIN, %1, ($55) ; 匹配ROM owout DS18B20_PIN, %1, (romCode1_byte0, romCode1_byte1, ... , romCode1_byte7) ; 发送64位ROM owout DS18B20_PIN, %1, ($BE) ; 发送读取暂存器命令 owin DS18B20_PIN, %1, (tempLSB, tempMSB) ; 读取温度值低字节、高字节 ; 将tempLSB和tempMSB组合成字变量再进行数据解析6.3 项目扩展思路温度报警与自动控制设置温度阈值高温、低温当DS18B20读数超过阈值时控制PICAXE的另一个I/O口驱动继电器打开风扇、加热器或报警器。数据记录将温度数据通过PICAXE的串口sertxd发送到电脑用串口助手软件记录或连接一个SD卡模块进行本地存储。无线传输为PICAXE增加一个433MHz或2.4GHz无线模块将温度数据无线发送到远处的接收端实现远程监控。多点网络监测利用单总线特性在一条线上挂接4、8甚至更多个DS18B20分布在不同房间或设备的不同部位用一块PICAXE进行集中监测和显示。这个项目让我再次体会到硬件项目成功的关键往往在于最基础的细节一份清晰的数据手册、一个不可或缺的上拉电阻、对通信协议底层原理的理解。PICAXE的READTEMP命令虽然封装了复杂性但当你需要更高级的功能或遇到问题时深入理解DS18B20的单总线协议和数据结构就变得至关重要。希望我的这些经验和代码片段能帮你绕过我踩过的那些坑更顺畅地把你手中的PICAXE和DS18B20组合玩转起来。