网关下面挂一串几块钱的传感器和身份芯片,I2C 拉不了几十米、RS-485 要管地址还要两根线:1-Wire 一条数据线加地线、器件自带 64 位 ROM 免地址、还能总线取电。本文讲清 DS2480B 串口桥接的上电握手与 E1/E3 数据包、搜索加速器把 64 次位操作压成 16 字节、强上拉与 500m 长线驱动,附 STM32 网关代码与 MQTT 上云通路。
做 物联网 网关,最头疼的往往不是主控和无线,而是"最后一米"的传感器接入:一台设备要挂十几片温度、身份、存储芯片,每片只值几块钱,可接线、地址、供电三件事加起来成本就上去了。结论先给三条:第一,1-Wire 用一条数据线加一条地线就能挂几十片器件,每片自带 64 位全球唯一 ROM,天生免地址、免拨码;第二,主控不要用 GPIO 去"打" 1-Wire 波形,交给 DS2480B 这类串口桥接芯片,微秒级时序由它自己产生,MCU 只管收发字节;第三,组网效率的关键在搜索加速器——它把"读 2 位写 1 位"的 64 轮位操作压缩成 16 字节一次收发,搜索从上百毫秒降到几毫秒。下面按子总线选型、桥接芯片、上电握手、数据包、搜索加速、供电长线、网关落地七步讲透。
先看网关的实际约束。现场设备要采的温度点、身份识别、料仓标定数据,往往分散在一台机柜或一条产线上,点位多、单个数据量小、器件成本敏感。这时候主控那几条 I2C/SPI 就不够用了:I2C 是板级总线,走线一长、电容一上来波形就塌;RS-485 能拉很远,但每个节点都要分配地址、要 MCU、要收发器,成本下不来;CAN 更贵。而 1-Wire 恰好补上这块空白:单线加地、总线取电、器件自带 ROM。把四种子总线放在一起比,选择就清楚了。
| 对比项 | I2C | RS-485 | 1-Wire | LIN |
|---|---|---|---|---|
| 线数(不含地) | 2(SCL/SDA) | 2(A/B) | 1(DQ) | 1(LIN) |
| 典型通信距离 | 板级,<1m | 1200m(低速) | 100m 级,加驱动器可达 500m | 约 40m |
| 速率 | 100k~1Mbps | 10k~10Mbps | 标准 16.3kbps / 高速 142kbps | 最高 20kbps |
| 地址方式 | 7 位地址,需规划 | 需上层协议分配 | 器件自带 64 位 ROM,免地址 | 标识符+帧调度 |
| 节点成本 | 低 | 中(收发器+MCU) | 最低(可无 MCU) | 中 |
| 供电 | 独立供电 | 独立供电 | 可总线寄生取电 | 12V 单线 |
| 适用场景 | 板内器件互联 | 长距离多机通信 | 多点测温/身份/存储 | 车身小节点 |
关键差异在"地址"和"供电"。RS-485 的从机必须有自己的 MCU 和地址规划,多挂一个点就多一份软件工作量;1-Wire 器件出厂就烧好了 64 位 ROM(前 8 位是家族码、中间 48 位序列号、末 8 位是 CRC8 校验),主机搜索一遍就知道线上有谁,点名时直接用 ROM 匹配。这正好契合网关"接一堆小器件、不写一堆固件"的诉求。
1-Wire 的麻烦全在时序:复位脉冲、读写时隙都是微秒级,让 STM32 用 GPIO 加延时去"打"波形,一旦被中断打断、或者编译优化改了延时,通信就时通时断。AN192 里就点出了这个现象——用 GPIO 软件模拟 1-Wire,最容易出的问题就是"时通时断"。解决办法是加一颗桥接芯片:主机给串口字节,芯片负责把它翻译成 1-Wire 波形。
DS2480B 就是干这个的:它是通用共地的串口到 1-Wire 线驱动器,接 8 位、无校验的 UART,把每个串口字符翻译成 8 个 1-Wire 时隙,主机负担一下就没了。它内部没有晶振,靠采样主机的串口时序来校准自己的时基,容差 ±5%,所以对主控晶振精度不敏感。
| 参数项 | 指标 | 工程含义 |
|---|---|---|
| 封装 / 引脚 | 8 脚 SO | GND、1-W、NC、VDD、VPP、POL、TXD、RXD |
| 供电电压 | 4.5V ~ 5.5V | 直接与 5V 系统共地,无需电平转换 |
| 串口速率 | 9600(上电默认)/ 19200 / 57600 / 115200 bps | 初始化固定 9600,之后可提速 |
| 1-Wire 速度 | 标准速度 / 高速(Overdrive) | 高速可达标准 10 倍以上吞吐 |
| 时基 | 自校准,容差 ±5%,无晶振 | 主控晶振精度要求低 |
| 1-Wire 驱动 | 压摆率受控的下拉 + 主动上拉 | 适应长线、降低辐射 |
| 供电能力 | 12V EPROM 编程脉冲 + 5V 强上拉 | 支持加密器件、EEPROM、温度转换 |
| 工作温度 | −40℃ ~ +85℃ | 工业现场可用 |
硬件上最省心的是"共地"两个字:DS2480B 可以直接接 5V UART,省掉一颗电平转换芯片;POL 引脚用来选择 RXD/TXD 极性,兼容不同接法,进一步减少外围器件。只有接到真正的 RS232(±12V)时才需要一级电平转换加被动钳位电路。
DS2480B 上电后默认 9600 波特,但此时它和主机的时基还没对齐,必须先走一遍"检测+自校准"流程,也就是 AN192 里的 DS2480B_Detect。这一步做不对,后面所有 1-Wire 操作都会拿到格式不对的应答字节。流程分四拍:
复位会把所有 1-Wire 配置参数恢复默认,所以每次复位后都要重装一遍。标准速度下推荐用 flex 模式:PDSRC = 1.37V/µs、写 1 低电平时间 = 10µs、采样偏移 = 8µs。这三项决定了 1-Wire 波形在中小型网络里的整形质量。AN192 给出的握手数据包是 5 个字节,应答也是 5 个字节,正常应答形如 0x16 0x44 0x5A ...,只要有一项对不上就判为握手失败并重试。
/* DS2480B 上电握手:复位 + 自校准 + 读回校验
* UART 先配 9600 8N1,DS2480B 上电默认也是 9600 */
uint8_t ds2480b_detect(void)
{
uint8_t tx[5] = {0x17, 0x45, 0x5B, 0x0F, 0x91}; /* PDSRC / W1LT / DSO / 读RBR / 读1位 */
uint8_t rx[5] = {0};
uart_set_baud(9600);
uart_send_break(2); /* 串口发 break >= 2ms,DS2480B 复位 */
delay_ms(2);
uart_flush();
uart_write_byte(0xC1); /* 定时字节,供 DS2480B 自校准时基 */
delay_ms(2);
uart_flush();
uart_write(tx, 5); /* 下发 5 字节配置包 */
uart_read(rx, 5); /* 读回 5 字节应答 */
/* 应答为命令码回显(bit0=0):0x16/0x44/0x5A 表示三项设置已生效 */
if (rx[0] != 0x16 || rx[1] != 0x44 || rx[2] != 0x5A) {
return 0; /* 握手失败,调用方重试 */
}
return 1;
}
握手通过后就可以提速:把波特率从 9600 切到 115200。但要注意 DS2480B 的输入缓冲区只有 1 个字节——下一条指令必须在上一条指令到达前发完,所以"复位操作"不能塞进连续字节流里,必须单独发、单独收结果。
DS2480B 把数据和配置揉在一条串口链路上,靠两个特殊字节切换"模式":0xE1 切到数据模式(后面的字节直接当 1-Wire 数据收发)、0xE3 切回命令模式(后面是芯片配置命令)。配置命令的格式很规整:8 位里 bit7 固定为 0、bit0 固定为 1,中间 6 位分成"3 位参数码 + 3 位值码"。这套编码让"设置某个参数为某个值"变成一个字节就能表达。
| 配置参数 | 参数码 | 可选值(值码 000→111) | 典型设置 |
|---|---|---|---|
| PDSRC(下拉压摆率) | 001 | 15 / 2.2 / 1.65 / 1.37 / 1.1 / 0.83 / 0.7 / 0.55 V/µs | 1.37V/µs |
| W1LT(写 1 低电平时间) | 100 | 8 ~ 15 µs | 10µs |
| DSO/W0RT(采样偏移/写 0 恢复) | 101 | 3 ~ 10 µs | 8µs |
| SPUD(强上拉时长) | 011 | 16.4 / 65.5 / 131 / 262 / 524 / 1048 / 动态 ms | 按器件取 65.5ms |
| RBR(串口波特率) | 111 | 9.6 / 19.2 / 57.6 / 115.2 kbps(含极性反转位) | 115.2kbps |
举个换算例子:把 PDSRC 设为 1.37V/µs,参数码 001、值码 011,拼成 0 001 011 1 就是 0x17;把写 1 低电平时间设为 10µs,参数码 100、值码 010,拼成 0 100 010 1 就是 0x45。这就是握手数据包里前两个字节的来历。常用操作则包括:OWReset(发复位脉冲并读在线应答)、OWWriteByte / OWReadByte(读写单字节)、OWBlock(多字节块)、OWWriteBytePower(写完字节立刻强上拉供电)、OWProgramPulse(发 12V 编程脉冲)。
1-Wire 找器件靠的是二进制树搜索:每个器件把自己的 64 位 ROM 一位位往外送,主机读一位、再读该位的补码,两次结果不同说明这一位所有器件一致,两次都是 0 就是"冲突点",主机在冲突点写一个方向位,让一半器件退出、另一半继续,走完 64 位就锁定一片器件的 ROM,再沿其他分支继续找下一片。
问题是:按最原始的做法,每个 ROM 位要"读 2 位、写 1 位"三次总线动作,64 位就是 192 次位操作,主机和从机之间的串口交互量极大。AN192 给出的答案是搜索加速器模式:把位通信捆绑成字节组甚至字节块,一次交互处理 16 个字节——也就是 128 位数据,正好对应 64 对"位+补码"。搜索所需的串行通信量因此大幅下降。
| 搜索方式 | 每片器件的位操作次数 | 主机与桥的交互量 | 适用速率 |
|---|---|---|---|
| 逐位搜索(无加速) | 64 位 × 3 次 = 192 次 | 大,逐位收发 | 9600 bps 也可 |
| 搜索加速器 | 一次打包 64 对 2 位数据 | 16 字节 / 片,显著降低 | 标准速度用 57600 bps |
| 高速模式搜索 | 同上 | 同上,但时隙更短 | 建议降速到 57600 bps |
这里有个很实在的坑:搜索操作不要跑在 115200 波特上。AN192 明确提醒,115200 波特下执行加速模式搜索会让 DS2480B 的输入缓冲区溢出,正确做法是把波特率降到 57600 再做搜索。搜索完成后,主机拿到的 16 字节数据代表 64 对 2 位信息,按"每对第一位是数据位、第二位是补码位"的规则解析出冲突标志和搜索方向,就能拼出当前器件的 ROM 并决定下一条分支。
/* 1-Wire 二进制树搜索:每轮"读 2 位、写 1 位",走完 64 位得到一片 ROM
* ROM_NO[0] 为家族码,ROM_NO[7] 为前 7 字节的 CRC8 */
static uint8_t ROM_NO[8];
static uint8_t LastDiscrepancy;
static uint8_t LastDeviceFlag;
uint8_t ow_search_next(void)
{
uint8_t id_bit, cmp_bit, dir;
int bit_index, byte_index;
if (LastDeviceFlag) return 0; /* 已是最后一个器件 */
if (!ow_reset()) { /* 复位失败则重新开始 */
LastDiscrepancy = 0; LastDeviceFlag = 0; return 0;
}
ow_write_byte(0xF0); /* Search ROM 指令 */
for (bit_index = 1; bit_index <= 64; bit_index++) {
id_bit = ow_read_bit(); /* 器件发 ROM 位(逻辑与) */
cmp_bit = ow_read_bit(); /* 器件发该位的补码 */
if (id_bit && cmp_bit) break; /* 线上已无器件 */
byte_index = (bit_index - 1) / 8;
if (id_bit != cmp_bit) {
dir = id_bit; /* 无冲突,方向即该位 */
} else if (bit_index < LastDiscrepancy) {
dir = (ROM_NO[byte_index] >> ((bit_index - 1) % 8)) & 0x01; /* 沿旧分支 */
} else {
dir = 0; /* 新冲突点,先取 0 分支 */
LastDiscrepancy = bit_index;
}
if (dir) ROM_NO[byte_index] |= (1 << ((bit_index - 1) % 8));
else ROM_NO[byte_index] &= ~(1 << ((bit_index - 1) % 8));
ow_write_bit(dir);
}
if (bit_index > 64 && crc8(ROM_NO, 7) == ROM_NO[7]) {
LastDeviceFlag = (LastDiscrepancy == 0);
return 1; /* 找到一片器件,ROM_NO 有效 */
}
LastDiscrepancy = 0; LastDeviceFlag = 0; /* CRC 不过,重置搜索 */
return 0;
}
搜索出来的 ROM 要先用 CRC8 校验末字节,通过才算找到器件;找到后用 Match ROM(匹配 ROM)点名,线上只有 ROM 相同的那片器件会响应,其余进入等待——这就是 1-Wire "免地址"的实现方式。
1-Wire 器件可以总线取电(寄生电源),但有些操作耗电大:温度转换、SHA-1 散列计算、EEPROM 写入,光靠 4.7kΩ 上拉那点电流撑不住。这时要用强上拉:用 OWWriteBytePower 写完启动命令后立刻把总线拉到 5V 强供电,或者用 OWProgramPulse 发 12V 脉冲给 EPROM 器件编程。DS2480B 内置负载传感器,会在加密器件的电流需求回落时自动结束强上拉,避免长时间大电流。
| 场景 | 做法 | 关键参数 |
|---|---|---|
| 普通读写 | 4.7kΩ 上拉,总线空闲拉高 | 寄生取电,电流小 |
| 温度转换 / SHA-1 | OWWriteBytePower 强上拉 5V | 强上拉时长可选 16.4~1048ms |
| EPROM 编程 | OWProgramPulse 发 12V 脉冲 | 需 VPP 提供 12V |
| 长线(100m 以上) | 网络驱动器 + 主动上拉 | 5 类双绞线约 100Ω,可达 500m |
长线是另一个工程难点。AN244《性能优异的 1-Wire 网络驱动器》给的方案是主机端做阻抗匹配 + 智能软件控制强上拉:下拉部分和上拉部分分别用 RC 控制压摆率,时间常数约 0.5µs、压摆率约 4V/µs,配合 5 类非屏蔽双绞线(特性阻抗约 100Ω)做终端匹配,通信距离可达 500m,并且在轻载到重载范围内都能可靠工作。DS2480B 本身也带压摆率受控的下拉和主动上拉,中等长度的网络直接用它就够,再长就要按 AN244 加外部驱动器。
把上面几步拼成一个可用的网关,软件栈分三层:底层是 STM32 的 UART 收发,负责和 DS2480B 交换字节;中间层是 1-Wire 操作封装(复位、读写字节、搜索、匹配 ROM、强上拉);上层是按 ROM 组织的设备表,把每个 ROM 映射成"几号温度点/哪台设备",再打包成 JSON 经 4G 或以太网走 MQTT 上云。STC/STM32 这类 MCU 跑这一套完全够用,因为微秒级时序都交给 DS2480B 了,MCU 只需按毫秒级节奏轮询。
上云数据打包建议:每条记录带 rom(16 位十六进制字符串)、family(家族码)、value、ts 四个字段。ROM 作为设备唯一标识直接当云端主键,换器件、加器件都不需要改固件里的地址表——这正是 1-Wire "免地址"在物联网侧的最大红利。
安全上要留个心眼:1-Wire 器件里有带 SHA-1 质询响应认证的加密型号,用于防伪和防克隆。如果网关下挂的是身份识别或计量类器件,优先选带 SHA-1 认证的型号,主机发随机质询、器件回摘要,比单纯读 ROM 可靠得多——ROM 是公开的,谁都能读出来照着仿。
最后三个工程建议:其一,每次 1-Wire 操作前先确认电平上拉正常,异常时走重试而不是直接报错;其二,搜索流程加超时,线上短路或器件异常时不能死等;其三,ROM 与业务编号的映射表存在 Flash 里并做校验,避免掉电后设备身份丢失。
网关接 1-Wire 子总线的落地逻辑,可以压成三步:先看器件——点位多、数据量小、成本敏感,就用一条线加地线的 1-Wire;再上桥接——把微秒级时序交给 DS2480B,MCU 只管收发字节,上电先走一遍 Detect 握手;最后提效率——用搜索加速器把 64 次位操作压成 16 字节,搜索时波特率降到 57600,大电流操作走强上拉。
工程上再记三件事:1-Wire 上拉电阻别省、搜索前后都要 CRC8 校验、ROM 当云端主键。把这三件事做扎实,一颗 DS2480B 加一条双绞线,就能让 STM32 网关稳定挂起几十片几块钱的传感器,接线、地址、供电三件麻烦事一次解决。
1-Wire 组网的价值不在"线少",而在器件自带 ROM、免地址、可总线取电——DS2480B 把时序从 MCU 手里接过去,搜索加速器把组网开销压下来,网关侧剩下的功课就是 ROM 表管理、强上拉供电和长线驱动这三件事。
本文基于《DS2480B 串行接口 1-Wire 线驱动器的使用(AN192)》《DS2480B 数据手册》《1-Wire 搜索算法》《1-Wire SHA-1 概述》《性能优异的 1-Wire 网络驱动器(AN244)》等技术资料整理优化,并结合物联网网关子总线组网与嵌入式开发的工程实践进行了系统化梳理。
沧州艾诺威电子 — 国家高新技术企业,20+项国家专利
嵌入式系统开发 · 物联网方案 · AI智能硬件 · 一站式交付
电话:13930711029 | 邮箱:tech@czinv.com | 24小时内响应