单总线(1-Wire):一根线又供电又通信
接 DS18B20 测温度,你会发现接线少得离谱:除了地(GND),信号就一根线。数据走它,电也能走它。这就是单总线(1-Wire)——一根数据线(加上地)就把活全干了。
一根线怎么又收又发
只有一根线,主机和从机不能同时说话,得轮流,靠严格的时序约定谁在什么时候动作。这里没有独立的时钟线(不像 I2C 有 SCL),时序全靠双方各自掐着表数微秒——线上拉低了多久,就代表不同的意思。
把这根线想成一段绳子,平时被上拉电阻拽到高电平。收发的基本单位叫时隙(time slot),一个时隙固定约 60µs,一个时隙传 1 个比特。说话的方式是"拉低再松手",区别只在拉低的时长:
- 写
1:主机拉低很短一下(约 1~15µs)就松手,剩下的时隙由上拉电阻把线拉回高,从机在拉低后约 15µs 采样,读到高。 - 写
0:主机拉低较长(整个时隙约 60µs 都压着),从机采样时读到的还是低。 - 读数据:主机先拉低一小下(>1µs)开启一个读时隙,然后立刻松手,接下来轮到从机说话——它想回
0就把线继续压住,想回1就撒手让线弹高;主机在开启时隙后约 15µs 内掐表读一眼线上电平。 - 复位:主机先拉低 480µs 以上,喊一声"都醒醒",从机听到后会自己拉低 60~240µs 应答一下,这一下叫存在脉冲(presence pulse),主机靠它确认线上到底挂没挂人。
关键就一句:谁都不能同时收发,全程用拉低时长在一根线上编码 0/1,时序差太多就读错。 好处是线少到极致,代价是速度慢(标准模式典型十几 kbps),不适合传大数据。想更全面地跟其它总线横向比,看通信协议横评。
寄生供电
更省的玩法叫寄生供电:从机连 VCC 都不接,平时数据线是高电平,从机就在这时候从线上"偷电"存进内部小电容,等需要干活(比如 DS18B20 启动一次温度转换)时再放出来用。
这样一颗传感器真就只剩两根线(数据 + 地)。代价是测温转换这种耗电动作期间,主机得把数据线强拉高给它"喂电",时序上要配合。
64 位地址挂多颗
一根线为什么能挂好几颗传感器不打架?因为每颗出厂就烧死了一个全球唯一的 64 位 ROM 地址。这 64 位里,低 8 位是器件家族码(DS18B20 是 0x28)、中间 48 位是唯一序列号、最高 8 位是 CRC 校验,主机读回来能自己核对没读错。
主机先做一遍"搜索 ROM"把线上所有地址都点出来——这是一套按二进制位逐层试探的算法,一位一位问"这一位是 0 的举手/是 1 的举手",遇到分叉就分头再搜,最终把所有挂着的地址枚举干净。点完名之后要跟哪颗说话,就先报它的 64 位地址"匹配 ROM",其它颗听到不是自己就装没听见。典型用法就是 DS18B20 多点测温:一根线串十几颗,每颗贴在不同位置,挨个按地址读回温度,布线成本极低。详见 DS18B20 测温传感器。
补一句:只挂一颗从机时可以跳过点名,用"跳过 ROM"命令直接跟它对话,代码更简单——这也是新手最先跑通的接法。
上拉电阻别忘
1-Wire 这根数据线必须接一个上拉电阻到 VCC,常见 4.7kΩ。它负责在没人拉低时把线拽回高电平,整个时序才有"默认高、谁拉低谁说话"的基础。
两件容易踩的事:一是上拉电阻别漏,裸接 DS18B20 不加 4.7kΩ 经常读出 85(它的上电默认值)或乱码;模块板多半自带,裸传感器要自己加。二是寄生供电虽省一根线,但布线长、挂多颗或转换期供电跟不上时容易不稳,调试阶段先老老实实接上 VCC 三根线,稳了再考虑省。上拉电阻为什么这么接,见上拉电阻。
时序这么严,别手写抖腿
看到上面全是"1µs、15µs、60µs、480µs"这些微秒级卡点,你可能想用 delay 硬凑。别。CPU 一被中断打断、或者编译器优化改了指令时序,那一下拉低就可能从 60µs 变成 70µs,从机直接读错。1-Wire 是出了名的时序敏感,工程上基本不手搓:
- 优先用现成库。ESP-IDF 生态有成熟的 1-Wire / DS18B20 组件(如
esp-idf-lib里的相关驱动),把时序细节都封好了,你只管调"读温度"。 - 让硬件外设来打点更稳。ESP32-S3 上更专业的做法是把时序交给 RMT 外设——它本来是发红外遥控码的,专门用来精确产生/捕获微秒级的电平脉冲,用它驱动 1-Wire 时序不受 CPU 忙不忙的影响,比软件位操作(bit-bang)稳得多。乐鑫官方也提供了基于 RMT 的 1-Wire 组件。
一句话:时序自己算一遍是为了看懂波形和排故障,真跑代码请用库或 RMT,别拿软件死等去卡微秒。
读回来的数据要核 CRC
1-Wire 时序敏感,线一长、干扰一大,偶尔读错在所难免。好在它每一批数据末尾都跟着一个 CRC 校验字节:64 位 ROM 地址的最高字节是 CRC,DS18B20 那 9 字节暂存器(scratchpad)的最后一字节也是 CRC。主机把前面几个字节按同一个多项式(1-Wire 用 CRC-8,多项式 X⁸+X⁵+X⁴+1)自己算一遍,和读回的 CRC 一对,不一样就说明这次读坏了。
工程上的做法很直接:读温度时顺手校一下 CRC,对不上就丢弃这次、重读一遍,别把一个跳变的错误温度当真送去控制继电器。现成的库一般都带 CRC 校验开关,别图省事把它关掉。
什么时候用
| 1-Wire | I2C | |
|---|---|---|
| 信号线 | 1 根 | 2 根 |
| 挂多设备 | 能(按 64 位地址) | 能(按 7 位地址) |
| 速度 | 慢 | 快一些 |
| 典型场景 | 温度、iButton | 传感器、小屏 |
挂多设备这件事两者都行,区别在:1-Wire 只要一根线、最省,但慢,几乎专用于测温(DS18B20、多点温度阵列)和 iButton 这类身份钥匙;要快一点、设备杂一点,就用两根线的 I2C。
一句话口诀
单总线一根数据线又供电又通信,靠拉低时长在线上编码 0/1,收发全按微秒级时隙掐表走;线上必挂 4.7kΩ 上拉做默认高,省线还能玩寄生供电从线上偷电;每颗有全球唯一 64 位 ROM 地址,所以一根线能串一堆、按地址点名;时序敏感,代码请用现成库或 RMT 外设,别手搓死等。记住四个词:一根线、卡时序、加上拉、64 位点名。
下一步
要测温且想省线到极致,直接看 DS18B20 测温传感器;想快一点、设备杂一点的场景,转 I2C;想把各种总线放一起横向比选型,看通信协议横评。