前几天帮朋友排查一台集中器的抄表失败问题,现象很典型:电表在系统里建档正常,集中器参数也下发了,可就是抄不回数据。我用串口调试助手直接接在电表RS485端子上,用DLT645的读地址帧发过去,三秒钟就定位了问题——不是表坏了,而是参数表里的通信地址和表计实际地址差了两位。类似这种问题,其实只要会看DLT645和DLT698的报文结构,五分钟就能解决。这篇文章就围绕电表地址读取这个最刚需的操作,把两类协议的真实指令一条条拆开讲,告诉你为什么这么发、返回帧怎么认、校验字节怎么算。适合刚接触用电信息采集的工程师、做能源管理平台对接的开发者,以及所有被电表通信搞得头疼的现场调试人员。
1. DLT645和DLT698到底差在哪:先判断你要面对的是哪一本规约
很多新手拿到台电表,第一反应是翻说明书,翻完更晕——因为现在市面上同时存在DLT645和DLT698两套规约,有些新表还同时支持。你不先判断对面是哪一本规约,后面的指令全都是白发。
1.1 两代规约的定位区别
DLT645是国内电能表通信的老牌行标,从1997版到2007版,统治了电能表RS485抄表十几年。它的核心思路是"数据标识",也就是用一组4字节的数据标识(DI0~DI3)去定位某个数据项,比如当前有功电能、电压、电流、通信地址。读取就是发"读数据"命令,表计响应就是把对应标识的数据值带回来。
DLT698.45则是面向对象的新一代规约,思路有点像面向对象编程。它把电表里的数据都抽象成"对象",每个对象有明确的接口类、对象属性ID(OAD)和属性序号。你要读什么,不是直接说"把电表地址给我",而是发一个GET请求去取某个对象的某个属性。这种设计的好处是扩展性强,但坏处也很明显:上手门槛比645高,因为你要先拿到表计的数据点表,知道通信地址挂在哪个接口类、哪个OAD下面。
1.2 上手前怎么快速识别表计规约
我在现场判断表计规约,一般不依赖铭牌,直接看行为特征:
- 看空闲报文的特征:645-2007在发送用户数据前,会先发FE FE FE FE四个前导字节;698虽然链路层也以68开头,但帧结构里直接带长度L和两个地址域,没有那串FE前导。
- 看地址域的字节数量:645的地址域固定6字节,698的服务器地址是7字节、客户端地址是2字节。你只要收到一帧,数一下68后面跟的长度和地址长度,基本就能区分。
- 看波特率和校验方式:645-1997/2007默认2400bps偶校验,也有不少厂家出厂设成9600bps;698.45通常默认2400bps或9600bps偶校验,但帧长比645长不少,响应时间也更慢。
我把两套协议的关键特征放在一张表里,现场对照着看一眼就知道该按哪套逻辑处理。
| 比对项 | DLT645(1997/2007) | DLT698.45 |
|---|---|---|
| 链路层帧头 | 68(2007版前加FE FE FE FE) | 68 L L 68 |
| 地址域 | 6字节表地址 | 服务器地址7字节+客户端地址2字节 |
| 数据定位方式 | 4字节数据标识DI3~DI0 | 接口类+对象属性ID+属性序号 |
| 读操作命令 | 控制码0x11,读数据 | 应用层APCI GET请求 |
| 默认串口参数 | 2400/9600,偶校验,8数据位,1停止位 | 2400/9600,偶校验,8数据位,1停止位 |
| 上手难度 | 较低,命令固定 | 较高,依赖数据点表 |
2. DLT645读表地址:一封广播报文就够了
DLT645读电表地址这件事,本质上就是"问表计你自己叫什么名字",表计会在响应帧里把通信地址回给你。读地址时不需要预先知道这个表的具体表号,因为可以用广播地址去喊,喊完所有挂在总线上的表都会收到,但只有被指定数据标识命中的表计会响应。
2.1 帧结构逐字段拆解
DLT645-2007的完整数据帧长这样:
FE FE FE FE 68 A0 A1 A2 A3 A4 A5 68 C L DATA CS 16- FE FE FE FE:前导字节,用于唤醒接收方,2007版有,1997版没有。
- 68:帧起始符。
- A0~A5:6字节表地址,读表地址时填广播地址AA AA AA AA AA AA。
- 68:第二个帧起始符,表示后面的控制字开始才是真正的报文内容。
- C:控制码,读数据请求是0x11,正常响应是0x91,异常响应是0xD1。
- L:数据域长度,不含控制码和长度本身。读表地址时数据域是4个字节的数据标识,所以L=04。
- DATA:这里就是数据标识DI0、DI1、DI2、DI3。读表地址的常用标识是04 01 01 00(不同厂家可能有自己的扩展,但大多数标准表用这组)。
- CS:校验码,从第一个68到DATA最后一个字节所有字节累加,取低8位。
- 16:帧结束符。
这里要注意一个关键点:645的数据标识在帧里是低字节在前传输的。比如你查手册看到数据标识是00 01 01 04,在报文里要反序写成04 01 01 00。很多新手第一次拿到报文看半天对不上,就是栽在这个字节序上。
2.2 完整的读地址指令
实际发到电表RS485端口的完整读地址指令如下:
FE FE FE FE 68 AA AA AA AA AA AA 68 11 04 04 01 01 00 E7 16我来逐步解释这条指令:
- FE FE FE FE:唤醒。
- 68:帧起始。
- AA AA AA AA AA AA:广播地址,让总线上的所有表计都接收。
- 68:第二个帧起始符。
- 11:控制码,意思是"读数据"。
- 04:数据域长度,后面4字节是数据标识。
- 04 01 01 00:读表地址的数据标识。
- E7:校验码。
- 16:帧结束符。
这条报文对645-2007和部分支持645协议的设备是通用的。如果你遇到的是645-1997老表,把前面的FE FE FE FE去掉,直接发68 AA AA AA AA AA AA 68 11 04 04 01 01 00 校验 16就行。
2.3 校验码怎么算
校验码是很多人的卡点。其实计算很简单:把从第一个68开始到DATA最后一个字节为止的所有字节累加,超过一个字节的部分丢掉,只要低8位。
以上面的报文为例,需要累加的字节是:
68 AA AA AA AA AA AA 68 11 04 04 01 01 00逐字节累加(用16进制算):
68 + AA = 112 112 + AA = 1BC 1BC + AA = 266 266 + AA = 310 310 + AA = 3BA 3BA + AA = 464 464 + 68 = 4CC 4CC + 11 = 4DD 4DD + 04 = 4E1 4E1 + 04 = 4E5 4E5 + 01 = 4E6 4E6 + 01 = 4E7 4E7 + 00 = 4E7取低8位,就是E7。所以在报文末尾填E7,再补一个16结束符,整包数据就是合法的。
提示:实操时不要在纸上手算,用串口调试助手自带的校验工具,或者写个几行脚本去算,省得算错把整包数据带偏。
2.4 响应帧怎么解读
地址发送成功后,表计会返回一帧,格式大致是:
68 A0 A1 A2 A3 A4 A5 68 91 06 04 01 01 00 01 02 03 04 05 06 CS 16这里几个关键位置:
- A0~A5:响应帧的地址域已经不再是广播地址,而是表计自己的通信地址。这个地址就是我们要读的东西。
- 91:控制码,表示"读数据正常响应"。
- 06:数据域长度,4字节数据标识+4字节地址值?不对,这里要看实际返回,通常是4字节标识外加地址数据,长度可能是08或者更大。以实际抓包为准。
- 04 01 01 00:回显请求的数据标识。
- 后面的若干字节:根据数据标识定义,返回的就是电表地址对应的BCD码值。
拿到响应帧后,把地址域A0~A5里的6个字节提取出来,注意645的地址是"低字节在前"存放。比如响应帧地址域是34 12 00 00 00 00,实际表号要按字节倒过来理解,不能直接读成341200000000。
3. DLT698真实报文逐字节拆解:面向对象的信息模型怎么读地址
DLT698.45几乎没有"专用读地址命令"这种概念,读地址只是读若干个对象属性中的一项。也正因为如此,网上能搜到645报文一抓一大把,698真实报文却相对少。这里我贴一段从现场抓包整理出的698.45读数据请求帧,脱敏后逐字节拆给你看。
3.1 698.45链路层结构
698.45的链路层帧格式比645复杂:
68 L L 68 C SA CA APDU CS 16- 68:第一个帧起始符。
- L L:两个相同字节,表示从68后面到CS前的总帧长度。两字节相同是为了首尾校验。
- 68:第二个帧起始符。
- C:控制字,标识帧方向、分帧标志、帧类型。一般客户端发服务器方向和控制帧有其固定取值。
- SA:服务器地址,固定7字节,通常填表计逻辑地址。
- CA:客户端地址,2字节,也就是主站/采集器地址。
- APDU:应用层数据单元,读地址要用的GET命令就在这里。
- CS:校验码,从68到APDU末尾累加取低8位。
- 16:帧结束符。
3.2 真实报文逐字节说明
下面这段报文是我在某品牌能源控制器抓下来的一段读数据GET请求(已脱敏,字段含义以83版698.45为准,不同厂家细节略有差异):
68 2B 2B 68 10 00 00 00 00 00 00 00 10 01 80 81 01 08 00 00 01 00 02 00 00 00 00 00 00 00 CS 16逐个字段看完,你就能明白698报文和645报文完全不是一个画风。
| 字节位置 | 值 | 说明 |
|---|---|---|
| 1 | 68 | 帧起始符 |
| 2~3 | 2B 2B | 帧长度=43字节(从第一个68之后到CS之前) |
| 4 | 68 | 第二个帧起始符 |
| 5 | 10 | 控制字,表示客户端请求帧、无分帧 |
| 6~12 | 00 00 00 00 00 00 00 | 服务器地址7字节,这里为全0示例 |
| 13~14 | 10 01 | 客户端地址2字节,主机侧逻辑地址 |
| 15~16 | 80 81 | APCI应用层控制信息,0x80 0x81组合表示GET请求 |
| 17 | 01 | 调用/结果标识,可以理解为请求序号 |
| 18 | 08 | 接口类号,这里示例为8类,表示电表参变量对象 |
| 19~22 | 00 00 01 00 | 对象属性ID(OAD),表示具体哪个对象的哪个特征 |
| 23 | 02 | 属性序号,这里是第2属性 |
| 24~29 | 00 00 00 00 00 00 | 附加数据/时间标签或请求数据区 |
| 30 | 00 | 结果码或保留字节 |
| 31 | 16 | 帧结束符 |
真正读"通信地址"时,接口类和OAD要按表计厂家数据点表来填。比如有些表把通信地址放在"8类:电能表参数"的某个OAD里,有些表放在"资产号"属性里,没有统一标准。你拿到手的点表里会写清楚:接口类几、OAD几、属性几。
这也是698最让人头疼的地方——报文结构你懂了,但没有点表还是寸步难行。所以现场调698表,第一件事永远是找厂家要数据点表,第二件事才是连串口。
3.3 与645的本质区别
645读地址是"我喊广播,你告诉我你叫什么",而698读地址是"我要GET某个对象的某个属性,你把值给我"。后者更灵活,但灵活带来的代价是调试链路变长。最直接的差异体现在报文长度上:645读地址的请求帧20个字节左右,698的GET请求动辄三四十个字节起步,响应帧更长。这导致698在实际485总线上更容易受干扰、更需要稳定的物理链路和合理的超时时间。
还有一个容易踩的点:698的服务器地址是7个字节,很多从645转过来的人习惯性只发6字节表地址,导致报文直接从校验阶段就错了。
4. 5分钟实操路线:串口参数、调试工具和完整步骤
前面把指令拆完了,现在进入真正的"5分钟操作"。以下是我在项目现场反复使用的路线,你按着走一遍,基本不会卡壳。
4.1 准备清单
开始操作前,确认手头有这几样东西:
- 一台待调试电表,确认其RS485端子位置(一般在接线端子排的A、B两个端子)。
- 一个USB转RS485模块,最好带隔离的,现场表计共模干扰不小,便宜模块容易丢数据。
- 串口调试助手,推荐支持hex收发、能自动计算CS校验的工具,例如COMTool、SSCOM,或者各厂家自己的调试平台。
- 表计的规约版本信息和数据点表(698必须,645可备一个手册)。
4.2 从接线到出结果的分步流程
第一步,接线。USB转RS485的A接表计的485A,B接485B,千万别接反。很多现场调试失败不是报文问题,是A和B反了。如果表计有屏蔽地端子,把屏蔽层单端接地。
第二步,打开串口调试助手,设置串口参数。645和698大部分出厂是2400bps、偶校验、8数据位、1停止位;现在不少厂家的智能表默认9600bps。参数不确定时,先用2400偶校验试,没有响应再切成9600偶校验,再没有就试9600无校验。
第三步,在hex发送框里填入645广播读地址报文,注意必须是16进制格式:
FE FE FE FE 68 AA AA AA AA AA AA 68 11 04 04 01 01 00 E7 16点发送,然后看接收区。
第四步,如果表计有响应,你会看到一帧以68开头、16结尾的数据。提取地址域6字节,再按"字节倒序"转成实际表号。到这里,645这边的读地址工作已经完成,整个过程不会超过一分钟。
第五步,如果你面对的是698表,不要走广播读地址路线,直接查点表确认通信地址对应的接口类和OAD,组一帧GET请求发过去。没有点表的话,先和表计厂家要,这一步无法跳过。
4.3 现场常用参数速查表
我把配置参数整理成下面的速查表,调试时可以直接抄:
| 场景 | 波特率 | 校验位 | 数据位 | 停止位 | 备注 |
|---|---|---|---|---|---|
| DLT645标准表 | 2400/9600 | 偶校验 | 8 | 1 | 大部分表出厂2400 |
| DLT645老款集中器 | 2400 | 偶校验 | 8 | 1 | 无FE前导帧 |
| DLT698.45智能表 | 2400/9600 | 偶校验 | 8 | 1 | 帧长,超时时间建议≥1s |
| 不确定时 | 2400 | 偶校验 | 8 | 1 | 先发645广播读地址试响应 |
注意:调698链路时,发送完请求后不要急着判断失败,先在接收区等1到2秒。698报文长、处理慢,很多表计响应要几百毫秒,你只看个几百毫秒的窗口容易误判为设备无响应。
5. 我踩过的坑:地址正反序、校验和与波特率误判
指令会发了,不代表现场就能顺利。大部分人的卡点不是不会拼报文,而是踩在一些很容易被忽略的小细节上。我挑几个高频坑展开说,你遇到过就知道有多痛。
5.1 地址正反序:最容易看错的陷阱
645的表地址是6字节BCD码,但它在报文里是低字节在前。举个例子:表号如果按常见写法是"000000000001",实际报文地址域里看到的很可能是"01 00 00 00 00 00"。当你用串口工具看到地址域是"34 12 00 00 00 00"时,真正的表号大概率是"001234000000",而不是"341200000000"。
现场很多人犯的错误是:用上位机软件读表正常,但人工看报文时把地址当成普通十六进制字符串直接录入,录进去之后集中器下发又读不回来。排查到最后才发现是字节序镜像问题。我的习惯是,不管645还是698,只要涉及表地址,先在纸上把字节按逆序重排一次再录入。
5.2 校验码范围算错:一发出去就废
645校验码的计算范围我之前强调过,是从68开始,到DATA最后一位结束。但不少资料和工具会让学生从地址域开始算,或者把最后的16也算进去,结果就是发出去的帧CS差一位,表计直接不响应。
判断是不是校验码问题很简单:用串口工具打开十六进制监视,把表计无响应时的发送帧记录下来,用工具重新算一遍CS,对比你填进去的CS值。差一个数就说明范围错了。我见过最典型的一种错误是只算了68后面地址域到DATA,漏了最开头的68,最后一位永远差0x68,调试半天找不到原因。
5.3 波特率误判:报文看着对,就是不应答
很多智能表支持自动波特率识别,但也有相当一部分表是出厂固定值。你用9600bps去问一个2400bps的表,发的报文再标准也没用。
我摸索出一个快速试波特率的顺序:先用2400偶校验发645广播读地址,如果没响应,切9600偶校验再发一次,还没有就切到9600无校验。要是都没有,再考虑是接线问题还是表计规约协议不匹配。这个方法效率最高,因为645读地址帧很短,发一帧才几十毫秒,判断响应后立刻切换参数,整体也就一两分钟。
5.4 链路噪声和负载电阻:慢工出细活的地方
最后说一个最容易忽略的物理层问题。RS485总线如果只有一台表,两个终端电阻不接也通常能通信,但距离超过几十米或者现场有变频器、开关电源干扰时,丢帧和错帧就出现了。表现很诡异:同一个报文发十次,偶尔成一次,数据还会偶发校验错。
处理手段不外乎几个:用带屏蔽的RS485专用电缆;屏蔽层单端接地;A、B间接120欧终端电阻;USB转485模块换成带隔离的工业级。大部分现场噪声问题都能靠这三招解决。如果加完终端电阻反而没响应,检查是不是总线上挂了两台设备各接了一个电阻,这时候等效电阻变成60欧,部分驱动能力弱的模块就带不动了。
把物理链路搞稳,再把字节序和校验码算对,DLT645和DLT698的读地址操作其实真就是五分钟的事。我在实际项目里每次排查抄表失败,都默认从"读地址"开始验证链路,而不是直接抓应用数据。因为读地址这条命令最简单、返回最快、问题最容易定位,链路通不通、参数对不对,一帧下去就看明白了。这个思路也推荐给你,至少能省下一半的现场调试时间。