ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

1-Wire单总线协议详解:从时序原理到DS18B20工程实践

1-Wire单总线协议详解:从时序原理到DS18B20工程实践 聊到1-Wire协议也就是常说的单总线协议很多做嵌入式的朋友第一反应往往是这老古董还值得专门拿出来折腾确实1-Wire从Dallas Semiconductor时代一路走来到今天手里攥着的还是“一根数据线搞定低速传感器”这张牌。别看它名字里带个Wire实际通信速率只是几十kbps的量级但这并不妨碍它在温度采集、电池管理、EEPROM读写、设备身份识别这些场景里稳坐江山。这篇文章我会从协议原理、电气时序、完整代码到现场踩坑把1-Wire Protocol从纸面拉进实际工程里。无论你是想用DS18B20做温度采集还是想挂一串传感器到单总线上又或者只是被“一根线也能双向通信”这个思路勾起了好奇心这篇内容都能给你一份能直接照做的参考资料。1. 为什么还在用1-Wire总线选型背后的门道很多人在选型时第一反应是上I2C或者SPI毕竟这俩协议资料多、参考代码好找、速度快。但真到了现场1-Wire的价值才会显示出来。它的核心优势不是性能而是极致的简洁和一根总线上的设备身份识别能力。1.1 1-Wire到底解决什么问题1-Wire总线只需要一根数据线加一根公共地线就能完成双向通信。对比一下I2C至少需要SCL和SDA两根线SPI常规情况下需要SCK、MOSI、MISO再加片选线如果挂多个设备片选线数量也跟着涨。而1-Wire总线上的每一个从设备出厂时就带有一个全球唯一的64位ROM编号只要挂在同一条总线上主机就能通过ROM搜索命令把它们一个一个找出来并单独访问。实际项目里“省引脚”这个特性非常实用。我之前做过一个冷链运输记录仪主控只有8个引脚又要接按键又要接LCD留给温度采集的引脚只剩一个。当时如果走I2C总线上的每个传感器还需要额外配置地址引脚或者用I2C switch扩展一块板子下来成本直接翻倍。最后方案就是一根1-Wire总线挂了5个DS18B20引脚只占一个地址问题靠ROM编号天然解决整个传感器网络干干净净。1-Wire还有一个隐蔽优势从设备可以工作在寄生供电模式。也就是说传感器不需要单独的VCC引脚接电源完全依靠数据线上的寄生电容充电工作。这在电池供电设备、旋转机构内部的温度检测、以及需要穿过滑环走线的场景里能省掉一根实打实的电源线。1.2 什么时候选1-Wire什么时候果断绕开任何协议都有适用边界1-Wire不是万能钥匙。它的传输速率很低标准模式下读一个DS18B20的温度最快要750ms左右12位分辨率下。如果应用场景是高频采样、实时控制反馈那1-Wire会让人很痛苦。我曾经在某个振动监测项目里试过用1-Wire挂加速度传感器结果采样率完全跟不上最后还是老老实实换回SPI。选不选1-Wire我的判断标准很简单设备数量少但布线距离可能较长数据量小传感器采样周期在秒级主控引脚紧张或者需要穿过旋转机构/狭窄管道走线需要每个从设备带唯一ID便于资产识别和管理反过来如果需要高速连续传输或者总线节点非常多而且每个设备都需要频繁通信1-Wire就不是好选择。节点一多总线电容增大时序余量被压缩通信稳定性急剧下降。另外注意1-Wire所有通信都由主机发起从设备无法主动上报需要主机定时轮询。1.3 一条总线上能挂多少设备ROM机制的底层逻辑1-Wire从设备内部都有一块64位ROM结构是8位家族码 48位唯一序列号 8位CRC校验。家族码用来标识设备类型比如DS18B20是0x28DS2401是0x01DS2413是0x0A。序列号是出厂激光刻录的理论上全球唯一。最后的CRC8可以校验前面56位数据有没有读错。主机要用到几条基础ROM命令0x33 读ROM总线上只有一个从设备时直接读取它的64位ROM码0x55 匹配ROM随后发送某个设备的64位ROM码只有匹配的设备响应后续功能命令0xF0 搜索ROM通过多次位操作逐层筛出总线上所有设备的ROM码0xCC 跳过ROM不关心地址所有设备同时响应适合单设备场景0xEC 条件搜索只有满足告警条件的设备参与搜索0xB4 直接读用于某些特殊器件搜索ROM的算法本质上是一个二叉树遍历。主机先发出搜索命令然后对每一位发送“读0”和“读1”两个读时隙根据返回的两组电平组合来判断当前节点的类型。组合可能是00说明总线上有设备这一位是0、也有设备这一位是1存在分支可能是01或10说明所有设备这一位都相同。如果出现00就需要按位来决定下一步的方向等搜索完一轮就能得到一个完整ROM码。这个过程用代码写起来并不复杂只要理解了位决策逻辑基本不会出错。关于挂载数量官方文档里写的理论值是“一条总线上最多可以挂几十个”但实际工程中线缆长度、节点间距、上拉电阻选择都会影响极限值。一般建议常规短距离场景控制在8~16个以内超过这个数量就要评估总线电容和上拉方案。我试过在30米双绞线上挂5个DS18B20稳定运行没问题但同样线长挂到15个偶尔就出现搜索ROM漏设备的情况。这个不是协议本身不行是物理层的RC参数被推到了极限。2. 单总线不是“一根线那么简单”核心细节拆解很多人第一次调1-Wire以为只要把数据脚拉高拉低就行结果死活读不到正确的温度值。原因很简单1-Wire是严格的时许协议时序窗口都是微秒级的而且所有时间参数都有上下限做产品时必须确保参数落在中间值不能卡着极限去设计。2.1 电气基础上拉电阻、寄生供电与总线电容1-Wire总线采用开漏结构也就是说主机和从设备都只能把总线拉低不能主动拉高。总线空闲时的高电平是由一个上拉电阻提供的。这个上拉电阻的取值是整个1-Wire物理层设计里最关键的参数。常规做法是4.7kΩ上拉电阻。这个值适用于大多数短距离、节点数适中的场景。如果总线上设备多或线缆长总线电容增加4.7k可能拉高速度跟不上导致时序错误。这时候需要减小上拉电阻到2.2k甚至1k让总线快速回到高电平。不过电阻也不是越小越好太小会导致低电平时灌入电流太大可能损坏从设备端口或导致逻辑判断异常。评估上拉电阻是否合适可以简单估算RC时间常数。总线等效电容等于线缆分布电容加上所有从设备输入电容之和。比如一条10米双绞线分布电容大约100pF再加5个DS18B20每个约25pF输入电容总电容大约225pF。上拉4.7k时时间常数τ 4.7k × 225pF ≈ 1.06μs。这个数值会让上升沿偏慢但只要能在一个时隙内完成翻转且从设备采样点没落在模糊区就还能工作。如果电容到1nF以上τ会超过4.7μs此时就真的要换上拉更小的电阻了。寄生供电模式也是1-Wire的特色。以DS18B20为例当工作在寄生供电模式时VDD引脚直接接地设备从数据线“偷电”。数据线为高时内部电容充电数据线为低时内部电容放电维持工作。但有一个坑温度转换时电流消耗会突然增大到1.5mA左右如果数据线上没有足够能量转换结果会完全错乱。官方方案是在转换命令发出后主机快速切换为强上拉比如用MOSFET把数据线直接接到3.3V或5V持续至少480ms然后再恢复普通上拉。如果不做强上拉就会出现“读到的温度永远是85℃”这种经典故障因为85℃是DS18B20上电复位后的默认值表示转换根本没完成。2.2 时序协议复位、存在脉冲、读时隙、写时隙1-Wire的通信基础是四类时隙复位时序、写0/写1时隙、读时隙。所有通信都从主机发起每个时隙至少60μs时隙之间至少1μs恢复时间。复位时序是这个协议里最好理解也最容易出问题的一步。主机先把总线拉低480μs以上官方建议480~960μs然后释放总线等待15~60μs再读取总线电平。如果总线上有从设备它会在一段时间后主动把总线拉低60~240μs产生一个存在脉冲。主机需要在这个窗口内采样到低电平才算确认有设备在线。如果这个脉冲没读到后续一切免谈。写时隙的规则是主机拉低总线开始一个时隙然后在拉低后的15μs内决定是写1还是写0。写0时主机继续把总线拉满整个60μs写1时主机在拉低6~15μs后释放总线让上拉电阻把电平拉高持续到60μs结束。关键点在于写0时总线必须在整个时隙内保持低写1时总线必须在采样窗口前回到高而且这个切换要在15μs内完成。读时隙和写时隙类似主机先拉低总线6~15μs然后释放总线在释放后的15μs内采样总线电平。如果从设备想返回0它会把总线拉低想返回1就让总线保持高。所以主机的采样点必须卡在15μs之前通常在释放后7~12μs时采样最稳。这里有一个我最初栽过跟头的点主机在写时隙和读时隙的拉低时间并不完全相同但很多参考代码共用一个delay函数导致某些设备工作正常、某些设备不行。我的做法是把读时隙和写时隙的延时分开定义并且每个延时留出20%以上的余量保证批量产品里的时序一致性。2.3 指令分发ROM命令与功能命令1-Wire通信分两个阶段ROM命令阶段和功能命令阶段。ROM命令用来选择具体访问哪个设备功能命令则是针对选定设备执行的具体操作。以DS18B20读温度为例完整的通信流程是主机发复位时序检测存在脉冲主机发送0xCC跳过ROM表示不关心地址所有设备同时响应主机发送0x44启动温度转换如果使用寄生供电紧接着需要强上拉等待转换完成主机再次发复位时序主机再次发送0xCC主机发送0xBE读暂存器然后连续读取9个字节温度低字节、温度高字节、上限TH、下限TL、配置寄存器、保留、保留、保留、CRC校验CRC拼装温度值多设备场景下把0xCC换成0x55匹配ROM后面跟64位ROM码就能单独访问某个设备。这个“先选人再办事”的模式和CAN总线的仲裁机制有异曲同工之处虽然方式不同但都是在一个共享物理介质上解决设备寻址问题。功能命令因设备而异不限于0x44和0xBE。比如DS2413双通道IO设备功能命令是0x5A写通道、0x96读通道DS2431 EEPROM则用0x0F写暂存器、0xAA读暂存器、0x55复制暂存器。做驱动开发时一定要仔细看目标芯片的数据手册不能照搬DS18B20的流程。3. 实战用DS18B20读完整个1-Wire协议栈理论说了这么多接下来直接上实操。我用GPIO模拟1-Wire时序主控是STM32F103代码用标准外设库。这套代码的逻辑可以直接移植到任何主控上只要把底层GPIO读写函数换成对应平台API即可。3.1 硬件准备选型与接线硬件清单1个DS18B20温度传感器TO-92封装或防水探头都可以1个4.7kΩ电阻总线上拉到3.3V或5V1个10μF和0.1μF去耦电容靠近主控电源引脚若干杜邦线或者直接焊板接线方式很简单DS18B20的GND接主控GNDDS18B20的DQ接主控一个GPIO引脚比如PA0同时通过4.7kΩ电阻上拉到VCCDS18B20的VDD接主控VCC如果使用寄生供电则接地注意DS18B20供电电压范围是3.0V到5.5V用3.3V供电时总线信号幅度就是3.3V设计时需要确认主控GPIO容忍度。如果用5V供电GPIO需要设置为开漏输出并且允许5V输入或者加电平转换避免直接烧坏3.3V GPIO。我用的是3.3V供电、开漏输出模式。开漏输出的好处是主机释放总线时正好让上拉电阻决定电平这和1-Wire协议天然契合。如果用推挽输出直接拉低再拉高理论也行但时序上容易有毛刺而且遇到多从设备同时拉总线时会存在打架风险所以不建议。3.2 核心代码实现从时序到读取温度先写最底层的时序函数。以下是经过多次调参后的稳定版本#include stm32f10x.h #include delay.h #define OW_GPIO_PORT GPIOA #define OW_GPIO_PIN GPIO_Pin_0 #define OW_RCC RCC_APB2Periph_GPIOA // 配置为开漏输出模式 void ow_init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(OW_RCC, ENABLE); GPIO_InitStructure.GPIO_Pin OW_GPIO_PIN; GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_OD; // 开漏输出 GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(OW_GPIO_PORT, GPIO_InitStructure); GPIO_SetBits(OW_GPIO_PORT, OW_GPIO_PIN); // 空闲时总线拉高 } // 拉低总线 void ow_low(void) { GPIO_ResetBits(OW_GPIO_PORT, OW_GPIO_PIN); } // 释放总线由外部上拉拉高 void ow_high(void) { GPIO_SetBits(OW_GPIO_PORT, OW_GPIO_PIN); } // 读取总线电平 uint8_t ow_read_pin(void) { return GPIO_ReadInputDataBit(OW_GPIO_PORT, OW_GPIO_PIN); }然后是核心时隙函数。这里的delay_us是基于STM32的SysTick做的微秒延时必须保证精度尤其是短延时的部分。// 1-Wire复位时序返回0表示检测到存在脉冲 uint8_t ow_reset(void) { uint8_t presence 1; ow_low(); delay_us(500); // 拉低500us ow_high(); // 释放总线 delay_us(30); // 等待从设备响应窗口 presence ow_read_pin(); // 读到低电平表示有设备在线 delay_us(450); // 等待时隙结束 return presence; } // 写一个bit void ow_write_bit(uint8_t bit) { if (bit) { ow_low(); delay_us(6); // 写1拉低6us后释放 ow_high(); delay_us(60); // 保持到时隙结束 } else { ow_low(); delay_us(60); // 写0拉满60us ow_high(); delay_us(6); // 恢复时间 } } // 读一个bit uint8_t ow_read_bit(void) { uint8_t bit 0; ow_low(); delay_us(6); // 拉低6us ow_high(); // 释放总线 delay_us(8); // 等从设备驱动总线 bit ow_read_pin(); delay_us(55); // 等待时隙结束 return bit; } // 写一个字节LSB先行 void ow_write_byte(uint8_t data) { for (uint8_t i 0; i 8; i) { ow_write_bit(data 0x01); data 1; } } // 读一个字节LSB先行 uint8_t ow_read_byte(void) { uint8_t data 0; for (uint8_t i 0; i 8; i) { data 1; if (ow_read_bit()) { data | 0x80; } } return data; }需要注意ow_read_bit内部data 1和 data | 0x80的组合配合LSB先行的约定才能组装出正确字节。很多新手把移位方向搞反结果读出来的数据完全对不上。3.3 完整读取流程温度转换与CRC校验底层时序函数准备好后读温度就是业务流程了。以下是完整读取函数// 读取DS18B20温度返回0表示成功非0表示失败 uint8_t ds18b20_read_temperature(float *temperature) { uint8_t data[9]; uint8_t crc; int16_t raw_temp; // 1. 复位总线 if (ow_reset() ! 0) { return 1; // 未检测到设备 } // 2. 跳过ROM启动温度转换 ow_write_byte(0xCC); ow_write_byte(0x44); // 3. 如果是寄生供电这里需要强上拉外部供电可直接延时 delay_ms(750); // 12位分辨率需要750ms // 4. 再次复位 if (ow_reset() ! 0) { return 2; } // 5. 跳过ROM读取暂存器 ow_write_byte(0xCC); ow_write_byte(0xBE); // 6. 读取9字节 for (uint8_t i 0; i 9; i) { data[i] ow_read_byte(); } // 7. CRC校验确保数据完整 crc ow_crc8(data, 8); if (crc ! data[8]) { return 3; // CRC错误 } // 8. 解析温度值 raw_temp (data[1] 8) | data[0]; if (raw_temp 0x8000) { raw_temp ~raw_temp 1; // 负温度时转成正数再标负号 *temperature -raw_temp * 0.0625; } else { *temperature raw_temp * 0.0625; } return 0; }关于0.0625这个系数DS18B20在12位分辨率下的步进是0.0625℃也就是每个LSB代表1/16℃。这个系数是固定的不需要根据配置寄存器去换算直接乘就行。如果用的是DS18S20温度分辨率变成0.5℃换算公式不同不要混用。CRC校验是很多人容易忽略的环节。1-Wire从设备会在暂存器第9字节返回CRC8校验码校验前面8个字节。如果CRC不对最稳妥的做法是丢弃本次数据重新读而不是直接采用。特别是在工业现场线缆受到干扰导致偶尔读错字节是正常现象CRC就是用来拦住这种错误数据的。我见过不少人省掉这步结果温度偶尔跳一个离谱的值排查半天发现是数据错位。// 1-Wire CRC8校验多项式为 x^8 x^5 x^4 10x8C uint8_t ow_crc8(const uint8_t *data, uint8_t len) { uint8_t crc 0; for (uint8_t i 0; i len; i) { uint8_t byte data[i]; for (uint8_t j 0; j 8; j) { uint8_t mix (crc ^ byte) 0x01; crc 1; if (mix) { crc ^ 0x8C; } byte 1; } } return crc; }3.4 多设备读取ROM搜索与匹配当总线上挂了多个DS18B20时0xCC跳过ROM这条路走不通了因为发温度转换命令后所有设备都会一起转换虽然这也不算错但读暂存器时所有设备同时往总线上发数据数据就打架了。正确的做法是先用搜索ROM把所有设备的ROM码找出来然后用匹配ROM命令逐一对单个设备下发指令。ROM搜索代码比较长这里只拆解关键逻辑uint8_t ow_search_rom(uint64_t *rom_codes, uint8_t max_devices) { uint8_t num_devices 0; uint8_t last_discrepancy 0; uint8_t done 0; uint8_t rom[8]; while (!done num_devices max_devices) { uint8_t discrepancy_marker 0; uint8_t bit_index 0; uint8_t byte_index 0; uint8_t current_bit 0; memset(rom, 0, sizeof(rom)); if (ow_reset() ! 0) { return num_devices; } ow_write_byte(0xF0); for (uint8_t i 0; i 64; i) { uint8_t bit_a ow_read_bit(); // 读本位的“0”响应 uint8_t bit_b ow_read_bit(); // 读本位的“1”响应 if (bit_a bit_b) { // 两个都是1说明总线上没有设备或者通信故障 return num_devices; } if (bit_a 0 bit_b 0) { // 出现分歧这一位上既有0又有1 if (i last_discrepancy) { current_bit (rom[byte_index] bit_index) 0x01; } else if (i last_discrepancy) { current_bit 1; // 向一个方向搜索 last_discrepancy i 1; } else { current_bit 0; } } else { current_bit bit_a ? 1 : 0; } if (current_bit) { rom[byte_index] | (1 bit_index); } ow_write_bit(current_bit); bit_index; if (bit_index 8) { byte_index; bit_index 0; } } uint8_t crc ow_crc8(rom, 7); if (crc ! rom[7]) { return num_devices; } memcpy(rom_codes[num_devices], rom, 8); num_devices; done (last_discrepancy 0); } return num_devices; }这个算法用到了一个叫“最后分歧位”的状态变量。简单理解就是每一轮搜索都把一个分支走完记录下上一次遇到分歧的位置下一轮从那个位置改走另一条路这样可以像深度优先遍历一样把总线上所有ROM码都挖出来。初次上手时可以先在2~3个设备上实验确认能枚举出所有设备再挂更多节点。找到所有ROM码后读单个设备温度就是标准的“匹配ROM”流程uint8_t ds18b20_read_temp_by_rom(uint64_t rom, float *temperature) { if (ow_reset() ! 0) return 1; ow_write_byte(0x55); // 匹配ROM for (uint8_t i 0; i 8; i) { uint8_t byte (rom (i * 8)) 0xFF; ow_write_byte(byte); } ow_write_byte(0x44); // 启动转换 delay_ms(750); if (ow_reset() ! 0) return 2; ow_write_byte(0x55); for (uint8_t i 0; i 8; i) { uint8_t byte (rom (i * 8)) 0xFF; ow_write_byte(byte); } ow_write_byte(0xBE); // 读暂存器 // 后续读取和解析同上 }注意匹配ROM时发送ROM码的字节顺序低位字节先发也就是先发序列号最低的那个字节。这个顺序搞反匹配命令就无法命中任何设备。4. 踩坑记录与问题排查速查表4.1 经典故障读不到存在脉冲这是我见过最多的故障也是新人最容易卡住的地方。表现是主机执行复位时序后在采样窗口读到的是高电平说明总线上根本没有存在脉冲。排查方向按优先级排列第一查接线。确认数据线接的是DS18B20的DQ引脚不是VDD或者GND。TO-92封装下引脚朝下、平面朝自己从左到右是GND、DQ、VDD这个顺序我经常记混建议直接看手册。第二查上拉电阻。如果总线悬空没有上拉主机释放总线后电平是浮动的读到的数据毫无意义。第三查电源。如果用外部供电确认VDD有3.3V或5V电压如果用寄生供电确认VDD接地同时数据线上有足够强的上拉。第四查延时参数。复位低电平时间至少要480μs很多人的延时函数实际不到这个值比如用简单的空循环在优化等级高的情况下被编译器优化掉导致时序完全不对。4.2 时序余量与中断影响另一个高频坑是时序被中断打断。1-Wire是微秒级协议一个读时隙从拉低到采样只有十几微秒任何中断服务程序如果在这个窗口内抢占了CPU采样点就会错过数据位直接读错。在RTOS环境下如果任务调度在时序中间发生同样会出问题。我的建议是所有1-Wire时序函数执行期间必须关中断或者放在临界区里执行。最稳妥的做法是定义宏#define OW_ENTER_CRITICAL() __disable_irq() #define OW_EXIT_CRITICAL() __enable_irq()然后在一个时隙操作的开头结尾包住。对于整个读温度函数我一般选择只包住时序敏感的部分也就是ow_read_bit和ow_write_bit的循环体中间的大延时保持中断开启避免系统响应卡顿。还有一种情况是时隙参数没问题但设备偶尔超时。这往往和主频不准确有关。比如用内部RC振荡器频率误差可能达到±5%换算到微秒延时就不精确了。我的做法是在延时函数里先做一次示波器校准把delay_us(50)实际量出来然后反向修正参数。4.3 线缆长度与拓扑物理层的隐性限制1-Wire总线虽然号称可以传几十米但那指的是“标准测试条件下”。实际工程里线缆长度、线缆电容、拓扑结构都会影响稳定性。我做过一个项目温湿度传感器分布在工厂车间各个角落总线走线超过40米用的是普通网线的一对双绞线。测试发现单独挂一个传感器时一切正常挂两个之后就偶发读不到数据。最后排查出来是总线电容太大上升沿太慢导致主机和从设备对高低电平的判断出现偏差。解决办法有两个一是把上拉电阻从4.7k降到2.2k二是给总线末端加一个“强上拉驱动”类似DS2480B做的主动上拉。我后来在靠近主控端加了一颗PMOS管做主动上拉效果明显改善。拓扑结构上1-Wire最理想是“菊花链”式接线也就是从主控出去一根线沿线依次接入每个传感器最后末端终结。尽量避免“星型”拓扑也就是从主控一个点引出多根分支线到不同传感器因为分支线会形成反射和电容叠加严重时会看到多个“影子脉冲”干扰正常工作。4.4 常见问题速查表现象可能原因解决办法复位读不到存在脉冲接线错误、无上拉、电源问题、延时过短对照引脚定义重新接线检查4.7k上拉检查供电把复位低电平延时至500μs以上温度固定为85℃寄生供电模式下没做强上拉转换未完成外部供电或转换期间开启强上拉持续至少480ms温度值剧烈跳变没做CRC校验、时序被中断干扰、线缆受干扰增加CRC校验关中断保护时序总线加屏蔽/双绞线多设备搜索ROM时漏设备总线电容过大、上拉不足、分支线过多减小上拉电阻缩短线缆调整拓扑为菊花链单设备正常多设备不稳定电源驱动能力不足、总线上电平下降沿过慢增加电源退耦电容检查每个传感器的VDD必要时加总线驱动器读到的字节全是0xFF总线一直为高从设备未响应或写时序失败检查写时序拉低时间确认从设备供电用逻辑分析仪抓时序提示排查1-Wire问题时逻辑分析仪比示波器好用得多。把采样率调到50MHz以上一组复位读暂存器流程能直观看到每个时隙的电平宽度一眼就能定位是哪个阶段出了问题。不要凭猜测去改代码先把波形抓出来再动手。最后说点这几年的体会吧。1-Wire这套东西工程量不大但坑全藏在细节里上拉电阻取值、时序余量、中断屏蔽、线缆电容……每一个单独看都不致命凑在一起就够你调一晚上。我的习惯是先把时序用逻辑分析仪抓出来确认复位脉冲和存在脉冲都正常再谈读温度这能省掉至少一半的排查时间。如果你手头正好要挂一排在室外的温度传感器或者想给设备加个身份ID直接把文中的代码拿来改改大概率一天之内就能跑通。等跑通了你会发现自己对“一条线能完成双向通信”这件事的理解和光看协议手册时完全不同。
返回列表