2. DNP3协议栈结构:物理层、数据链路层、传输层、应用层详解

好,咱们直接切入正题。DNP3这个协议,说白了就是一套“规规矩矩”的通信语言。它不像Modbus那么“野”,上来就干。DNP3是分层设计的,每一层各司其职。我经常跟学员讲,你把它想象成一个快递公司,从寄件到收件,每个环节都有专人负责。

DNP3协议栈一共四层:物理层、数据链路层、传输层、应用层。嗯,这里要注意,它没有网络层。为什么?因为DNP3最初设计就是跑在串口上的,串口不需要路由。后来扩展到了TCP/IP,但那是把整个DNP3报文当成了TCP的负载,这是后话了。

2.1 物理层:最底层的“电线”

物理层,说白了就是信号怎么在线上跑。DNP3对物理层没有强制规定,但实际项目中,我见过最多的就是RS-232和RS-485。

  • RS-232:点对点,距离短(15米左右)。老式RTU常用。我个人习惯,调试时首选RS-232,简单直接。
  • RS-485:多点通信,距离远(1200米)。变电站里几乎全是它。我记得有一次在西北的风电场,光纤还没铺好,全靠RS-485手拉手把几十台风机串起来,跑了两年都没出过大问题。
  • TCP/IP:现在的新站,基本都是以太网了。DNP3 over TCP,端口号20000。你想想看,一个站控层交换机,下面挂几十台IED,全走网络,维护起来多方便。
我的小经验: 串口通信时,一定要把地线接好。我曾经遇到过一个问题,两台设备距离200米,数据总是偶尔错一两个字节。查了半天,发现是地电位差导致的共模干扰。把屏蔽层单端接地后,问题立刻消失。

2.2 数据链路层:帧的“搬运工”

数据链路层,是DNP3最核心的一层。它负责把原始比特流组装成“帧”。一个DNP3帧长什么样?我直接给你看结构:

| 起始字 (0x0564) | 长度 | 控制字 | 目的地址 | 源地址 | CRC | 用户数据 | CRC | ... | CRC |

这里有几个关键点,我一个个说:

  • 起始字:固定0x0564。接收端看到这个,就知道“哦,DNP3报文来了”。
  • 长度:从控制字开始到最后一个CRC的字节数。注意,不包含起始字和长度本身。
  • 控制字:这是灵魂。它决定了这个帧是主站发的还是从站发的,是请求还是响应,要不要确认。
  • 地址:DNP3的地址范围是0到65535。我建议你给每个设备分配一个唯一的地址,别偷懒用默认值。我曾经见过一个站,两台保护装置都设成了地址1,结果一召唤数据,两台同时响应,报文全乱了。
  • CRC:每16个字节就有一个CRC校验。DNP3对数据完整性要求极高,这一点比Modbus强太多。
避坑指南: 我曾经调试一个项目,主站总是收不到从站的响应。用抓包软件一看,从站确实发了数据,但主站就是不认。最后发现,从站配置的“目的地址”填错了,填成了自己的地址。DNP3的地址是“源地址”和“目的地址”都要对的,缺一不可。

2.3 传输层:大数据的“拆分器”

你想想看,数据链路层一帧最多能传多少数据?答案是250字节左右(包含开销)。但有时候,一个应用层报文可能很大,比如历史事件记录,可能有几千字节。怎么办?

这时候传输层就上场了。它的工作很简单:把大的应用层报文拆成多个小段,每个小段塞进一个数据链路层帧里。接收端再把这些小段拼回去。

传输层的头部只有两个字节:

| 位7-6: 保留 | 位5: FIN | 位4: FIR | 位3-0: 序号 |
  • FIR:第一个分片。为1时,表示这是整个报文的第一段。
  • FIN:最后一个分片。为1时,表示这是整个报文的最后一段。
  • 序号:从0到15循环。接收端靠这个来排序和检测丢包。

举个例子:一个应用层报文被拆成了3段。第一段的FIR=1,FIN=0,序号=0;第二段的FIR=0,FIN=0,序号=1;第三段的FIR=0,FIN=1,序号=2。接收端收到后,按序号拼起来,就是一个完整的报文。

注意: 序号只有4位,最大15。如果分片超过16段,序号会回绕。但实际项目中,一个应用层报文很少会超过16段,因为数据链路层一帧能传200多字节,16段就是3KB以上了。如果真遇到超大报文,我建议你检查一下应用层设计是不是有问题。

2.4 应用层:真正的“业务逻辑”

应用层,才是我们真正关心的。它定义了“读什么数据”、“写什么数据”、“怎么控制”。

应用层报文的结构是这样的:

| 应用层头部 | 对象头 | 数据 |

应用层头部包含:

  • 应用层控制字:包含FIR、FIN、CON(是否需要确认)、序列号等。
  • 功能码:这是核心。比如0x01是“读”,0x02是“写”,0x05是“直接操作”,0x14是“响应”。
  • 内部指示字(IIN):从站返回的状态信息。比如“设备重启了”、“缓冲区满了”、“时间不同步”等。

对象头则定义了数据的“类型”和“格式”。DNP3把数据分成了很多“对象组”和“对象变体”。比如:

对象组 对象变体 描述
01 01 二进制输入(单点)
01 02 二进制输入(带状态)
10 01 二进制输出(控制继电器)
30 01 模拟输入(32位浮点)
40 01 模拟输出(16位整数)

你想想看,有了这个对象模型,主站和从站之间就能精确地知道“我要读第1组第1变体的数据”,而不是像Modbus那样只知道“读寄存器地址100”。这就是DNP3灵活的地方。

我的建议: 刚开始接触DNP3时,别急着背对象组编号。你先理解“对象组=数据类型”、“对象变体=数据格式”这个逻辑。比如“二进制输入”就是开关量,“模拟输入”就是模拟量。等用熟了,自然就记住了。

2.5 四层之间的协作

最后,我画个简单的流程,帮你理解这四层是怎么配合的:

  1. 应用层:主站想读从站的遥测数据。它构造一个“读请求”报文,功能码=0x01,对象=30变体01。
  2. 传输层:如果这个请求报文太大,传输层把它拆成多个分片,每个分片加上FIR/FIN和序号。
  3. 数据链路层:每个分片被封装成一个帧,加上起始字、地址、CRC。然后交给物理层。
  4. 物理层:把帧变成电信号(RS-485差分电平)或者光信号(光纤),发到线路上。

从站收到后,反过来:物理层收信号,数据链路层校验CRC、检查地址,传输层拼分片,应用层解析请求并执行。

嗯,这就是DNP3协议栈的全貌。每一层各干各的,互不干扰。这种分层设计的好处是,你换物理层(比如从串口换成以太网),上三层完全不用改。我在项目中就干过这事,把老站点的RS-485升级成光纤环网,只改了物理层配置,应用层代码一行没动。

下一章,我会带你手把手配置一个DNP3从站,把今天讲的这些层,一个一个在软件里实现出来。