南瑞101规约C代码解析:从RTU101.rar到报文调试实战

发布时间:2026/10/26 16:58:51
南瑞101规约C代码解析:从RTU101.rar到报文调试实战 简介这份资源围绕IEC 101规约与南瑞101实现展开面向电力自动化方向的开发者、RTU调试人员及电力系统通信协议学习者帮助理解远动通信中RTU与主站之间的数据交互机制。压缩包为rar格式共2个文件包含1个cpp源码与1个txt说明文档整体约13KB体量轻便便于快速查阅与二次开发。内容涉及101报文结构、控制域与地址域含义、遥信变位20报文上报、遥测数据实时传输以及南瑞设备在具体厂站中的厂家标志配置等实践细节可帮助读者建立从协议标准到工程实现的完整认知。目前已有789人学习下载适合需要结合源码理解101规约报文解析、排查通信问题或开展电力远动调试的技术人员参考。1. 南瑞101规约报文解析从RTU101.rar里翻出一套能跑的C代码手里拿到一个叫rtu101.rar的压缩包解压后是一堆.c和.h文件名里带着101、南瑞、报文这些字眼这是很多做电力自动化的工程师都会遇到的场景。IEC 101 规约也就是常说的南瑞101是国内变电站、配电终端和主站之间最常用的串口通信协议之一RTU 侧要按这个规约组帧、应答、上送遥测遥信。问题在于网上流传的这套 C 代码往往缺文档、缺注释变量名还是拼音缩写直接编译能过但报文对不上就对不上串口一抓全是乱码。这篇笔记就围绕这套rtu101.rar里的 C 101 规约实现把报文结构、代码怎么读、参数怎么调、调试时哪里最容易翻车讲清楚。适合手里已经有这套代码、或者正准备用 C 语言对接南瑞101主站的嵌入式工程师读完能自己把一条完整报文从组帧到解析跑通。2. 南瑞101规约的报文骨架为什么你的C代码总是对不上帧2.1 固定帧长与可变帧长两种格式别混用IEC 101 规约的报文分两种基本格式这是读任何一份 C 101 代码前必须先在脑子里立住的东西。固定帧长格式只有 5 个字节结构是启动字符0x10、控制域 C、链路地址 A、校验和 CS、结束字符0x16。它用来做链路层的确认、请求链路状态、复位链路这些不需要传数据的场合。可变帧长格式则是启动字符0x68、长度域 L、再一个0x68、控制域 C、链路地址 A、应用层数据、校验和 CS、结束字符0x16。长度域 L 表示的是从控制域到应用层数据结束的字节数不含启动字符、长度域本身、校验和和结束符。很多南瑞101的 C 代码翻车第一个原因就是把这两种格式的判断条件写错。常见做法是在接收状态机里先判断首字节收到0x10走固定帧分支收到0x68走可变帧分支。但有些代码为了省事统一按可变帧处理结果固定帧的链路确认报文被当成可变帧解析长度域读到了控制域的值整个缓冲区错位。我一般会在接收函数入口就做一次显式分支并且把两种帧的最小长度校验写死固定帧必须正好 5 字节可变帧至少 8 字节不满足直接丢弃并重新同步。2.2 控制域I格式、S格式、U格式的判定逻辑控制域 C 是南瑞101规约里信息量最大的一个字节它决定了这条报文是干什么的。按最低两位有些实现按最高两位取决于位序约定南瑞101通常按低位在前区分I 格式帧最低位为 0携带应用层数据发送序号 N(S) 和接收序号 N(R) 各占 7 位S 格式帧最低两位是01只做确认带接收序号 N(R)U 格式帧最低两位是11用于链路控制比如启动链路、停止链路、测试链路。在 C 代码里这个判定通常写成宏或者内联函数。下面这段是我从类似rtu101.rar的代码里整理出来的典型写法逻辑清晰可以直接对照你手里的版本/* 控制域格式判定南瑞101按低位在前约定 */ #define CTRL_IS_I(c) (((c) 0x01) 0x00) #define CTRL_IS_S(c) (((c) 0x03) 0x01) #define CTRL_IS_U(c) (((c) 0x03) 0x03) /* 从I格式控制域提取发送序号和接收序号 */ static void parse_i_ctrl(unsigned char c1, unsigned char c2, unsigned short *ns, unsigned short *nr) { /* 第一个控制字节低7位是N(S)第二个控制字节低7位是N(R) */ *ns (unsigned short)((c1 1) 0x7F); *nr (unsigned short)((c2 1) 0x7F); }这段代码的关键在于位运算的移位方向。南瑞101的序号是 15 位循环实际只用低 7 位发送时 N(S) 放在第一个控制字节的 bit1 到 bit7N(R) 放在第二个控制字节的 bit1 到 bit7。参数上要注意0x7F这个掩码不能写成0xFF否则会把控制位带进来序号直接错乱。如果你手里的代码用的是 1之后没做掩码短时间跑不出问题一旦序号超过 127 回绕就会对不上这是血泪经验。2.3 链路地址与校验和南瑞101的隐形门槛链路地址 A 在南瑞101里通常是一个字节但实际项目里可能扩展成两个字节取决于主站配置。校验和 CS 是算术和校验把控制域、链路地址、应用层数据所有字节相加取低 8 位再取反加一或者直接取反具体看主站约定。南瑞的很多主站用的是「和校验取反」也就是所有字节累加后按位取反。这里有个容易被忽略的点校验和的计算范围。固定帧和可变帧的计算范围不一样。固定帧是从控制域到链路地址可变帧是从控制域到应用层数据最后一个字节都不包含启动字符、长度域、校验和自身和结束符。我见过有人把长度域也算进去结果主站一直回否定确认。下面是一个通用的校验和计算函数参数说明写在注释里/* 计算南瑞101校验和buf指向控制域起始len为参与计算的字节数 */ static unsigned char calc_cs(const unsigned char *buf, int len) { unsigned char sum 0; int i; for (i 0; i len; i) { sum buf[i]; } return (unsigned char)(~sum); /* 按位取反南瑞主站常用约定 */ }调用的时候固定帧传len 2控制域加链路地址可变帧传len 2 应用层数据长度。这个len怎么来的就是从长度域 L 减去 2控制域和链路地址各占一字节得到应用层数据长度再加 2。参数改错校验和必错主站直接丢帧连否定确认都不给这是调试时最让人抓狂的黑匣子。3. 把RTU101.rar里的C代码跑起来从工程结构到最小可运行3.1 先理清代码目录哪些文件是规约核心拿到rtu101.rar解压后的目录通常会有这么几类文件规约核心文件名字里带101、proc、frame的、串口驱动文件带uart、serial的、应用层映射文件带yc、yx、yk的对应遥测遥信遥控、以及主循环和配置文件。不要一上来就通读先找规约核心里的帧解析函数和组帧函数这两个是命脉。我一般会先看头文件里的结构体定义尤其是帧缓冲区结构体和链路状态结构体。帧缓冲区通常是一个unsigned char数组加一个长度变量链路状态里会有发送序号、接收序号、链路状态标志。把这些结构体成员和前面讲的报文格式一一对应上代码就理解了一半。如果代码里用了全局变量来存这些状态注意多任务环境下要加锁裸机跑就没这个问题。3.2 串口参数配置南瑞101的默认档位南瑞101规约跑在串口上物理层参数通常是波特率 9600、数据位 8、停止位 1、校验位偶校验EVEN。有些现场用 1200 或 2400 波特率校验位也可能改成无校验这个必须和主站侧完全一致差一个参数就是全乱码。在 Linux 下用 termios 配置在 Windows 下用 DCB在裸机上进寄存器。下面给一个 Linux 下的配置片段参数含义逐行说明#include termios.h #include fcntl.h #include unistd.h int open_101_port(const char *dev) { int fd open(dev, O_RDWR | O_NOCTTY); struct termios opt; tcgetattr(fd, opt); cfsetispeed(opt, B9600); /* 输入波特率9600 */ cfsetospeed(opt, B9600); /* 输出波特率9600 */ opt.c_cflag | (CLOCAL | CREAD); /* 使能接收忽略调制解调器控制线 */ opt.c_cflag ~CSIZE; opt.c_cflag | CS8; /* 8位数据位 */ opt.c_cflag | PARENB; /* 使能校验 */ opt.c_cflag ~PARODD; /* 偶校验 */ opt.c_cflag ~CSTOPB; /* 1位停止位 */ opt.c_lflag ~(ICANON | ECHO | ECHOE | ISIG); /* 原始模式 */ opt.c_cc[VMIN] 1; /* 至少读1字节 */ opt.c_cc[VTIME] 10; /* 超时1秒 */ tcsetattr(fd, TCSANOW, opt); return fd; }这段代码里VMIN和VTIME的组合很关键。VMIN1, VTIME10表示至少读到 1 个字节且字节间超时 1 秒。南瑞101的报文帧内字节间隔通常要求小于 1 秒帧间可以长一些。如果VTIME设成 0读操作会一直阻塞设成太大帧内超时判断会失效。现场调试时如果发现报文被拆成两段读上来先查这两个参数。3.3 最小可运行手动构造一条固定帧并解析在把整个工程跑起来之前我习惯先写一个最小测试手动构造一条固定帧喂给解析函数看输出对不对。固定帧「请求链路状态」的典型内容是10 49 01 4A 16。其中10是启动49是 U 格式控制域测试链路功能01是链路地址4A是校验和16是结束。校验和计算0x49 0x01 0x4A取反是0xB5但这里4A是直接和说明这套代码用的是「和校验不取反」的约定。这就是为什么前面强调校验和约定必须和主站一致同一个规约不同厂家实现可能不同。构造好之后调用解析函数打印出帧类型、控制域格式、链路地址。如果解析出来是 U 格式、地址 1说明接收状态机和格式判定没问题。这一步过了再接入真实串口用主站模拟器发报文观察应答。很多新手跳过这一步直接上现场结果分不清是代码问题还是接线问题浪费大量时间。4. 报文解析与组帧的C实现细节遥测遥信怎么塞进应用层4.1 应用层数据单元类型标识与传送原因可变帧的应用层数据部分第一个字节是类型标识 TI第二个字节是可变结构限定词 VSQ然后是传送原因 COT 和公共地址之后才是信息对象。类型标识决定了这条报文是遥测、遥信还是总召唤。南瑞101常用的类型标识单点遥信是 1双点遥信是 3归一化遥测是 9标度化遥测是 11总召唤是 100时钟同步是 103。在 C 代码里这些通常定义成枚举或宏。解析时先读 TI然后根据 TI 决定后续信息对象的解析方式。比如遥测是每个对象 2 字节值加 1 字节品质遥信是每个对象 1 字节状态加 1 字节品质。VSQ 的低 7 位是信息对象数量最高位表示是否带时标。如果带时标每个对象后面还要跟 7 个字节的时标。这个逻辑如果写错解析出来的遥测值会整体偏移看起来像随机数。4.2 组帧函数从结构体到字节流组帧是解析的逆过程。给定类型标识、传送原因、信息对象数组拼出一条完整的可变帧。下面是一个简化的组帧函数重点看长度域和校验和的处理/* 组一条可变帧ti类型标识cot传送原因data应用层数据dlen数据长度 */ int build_var_frame(unsigned char *out, unsigned char ti, unsigned char cot, const unsigned char *data, int dlen) { int idx 0; int i; unsigned char cs; out[idx] 0x68; /* 启动字符 */ out[idx] (unsigned char)(dlen 4); /* 长度域控制域1地址1ASDU头2数据 */ out[idx] 0x68; /* 第二个启动字符 */ out[idx] 0x00; /* 控制域I格式序号由调用者填充 */ out[idx] 0x01; /* 链路地址按现场配置改 */ out[idx] ti; /* 类型标识 */ out[idx] 0x01; /* VSQ假设1个信息对象不带时标 */ out[idx] cot; /* 传送原因 */ out[idx] 0x01; /* 公共地址 */ for (i 0; i dlen; i) { out[idx] data[i]; } cs calc_cs(out[3], idx - 3); /* 从控制域到数据末尾 */ out[idx] cs; out[idx] 0x16; /* 结束字符 */ return idx; }长度域的计算是dlen 4这 4 个字节是控制域、链路地址、类型标识、VSQ、传送原因、公共地址里的前四个这里要仔细长度域 L 统计的是控制域、链路地址、应用层数据的总字节数。应用层数据包括 TI、VSQ、COT、公共地址、信息对象。所以 L 1控制域 1链路地址 1TI 1VSQ 1COT 1公共地址 dlen dlen 6。上面代码里写dlen 4是错的这是一个典型的翻车点。正确的应该是dlen 6。校验和从控制域开始到数据末尾长度是idx - 3减去启动、长度、启动三个字节这个是对的。4.3 序号管理与超时重传I 格式帧的发送序号 N(S) 和接收序号 N(R) 是 15 位循环每发一帧 N(S) 加一每收一帧 N(R) 更新为对方期望的下一个序号。如果发送后在一定时间内没收到对方的 S 格式确认需要重传。南瑞101的超时时间通常设 10 到 30 秒具体看主站。重传次数一般 3 次超过就报链路中断。在 C 代码里序号管理通常用一个结构体存ns、nr、ack_pending标志和重传计数。每次组 I 帧时把ns填进控制域发送后ns (ns 1) 0x7F。收到 S 帧时用其中的 N(R) 确认自己已发送的帧。这里有个坑N(R) 是累积确认表示该序号之前的所有帧都收到了不是只确认这一帧。如果代码里按单帧确认处理重传逻辑会乱。5. 调试南瑞101报文的避坑清单串口抓包与常见故障5.1 现象主站不回确认串口无任何输出原因通常有三个一是串口参数不匹配尤其是校验位和停止位二是 RS-485 收发方向控制没做对发送时没使能发送或者发送完没及时切回接收三是链路地址配错主站直接忽略。解决方法是先用示波器或串口监听工具看物理线上有没有波形有波形但主站不回查地址和校验和没波形查驱动和方向控制。RS-485 方向控制脚一般在发送前拉高发送完最后一位后延时一小段时间再拉低这个延时不够会导致最后一字节发不出去。5.2 现象报文能收到但解析出来遥测值是乱码原因多半是字节序或数据类型搞错。南瑞101的遥测值如果是归一化值是 2 字节有符号数范围 -1 到 1 对应 0x8000 到 0x7FFF需要除以 32768 再乘量程。如果是标度化值是 2 字节整数直接乘系数。如果代码里把归一化值当标度化值处理数值会差几个数量级。另外信息对象的顺序必须和主站的点表一致顺序错了遥测值会串位看起来像乱码。5.3 现象运行一段时间后序号对不上通信中断这是序号回绕处理不当的典型表现。15 位序号从 0 到 32767 循环但实际只用低 7 位也就是 0 到 127。如果代码里用unsigned short存序号但没做 0x7F序号会一直涨超过 127 后控制域里的值就错了。解决方法是每次更新序号后都做掩码比较序号时也要考虑回绕不能用简单的或。常见做法是计算差值(a - b) 0x7F根据差值判断前后。5.4 现象总召唤时遥信全为 0 或全为 1总召唤是主站问 RTU 要全部数据RTU 需要按点表逐个上送。如果遥信全 0 或全 1先查点表映射看遥信状态有没有从硬件采集更新到规约层的数据结构。再查品质位如果品质位是无效主站可能显示为 0。还有一种可能是总召唤的激活确认和激活终止没处理好主站以为召唤没完成一直等。总召唤的流程是主站发总召唤激活RTU 回激活确认然后 RTU 逐帧上送数据最后发激活终止。少任何一步主站都会超时。5.5 现象校验和偶尔错重发后正常偶发校验错通常是串口干扰或波特率偏差。先检查通信线是否屏蔽接地波特率晶振是否匹配。如果硬件没问题看代码里的校验和计算是否用了有符号char累加时溢出导致结果不对。所有参与校验和的变量都应该用unsigned char累加变量用unsigned int或unsigned short最后取低 8 位再取反。这个细节在移植代码时经常被忽略。6. 用Python脚本辅助验证南瑞101报文抓包、解析、对比一条龙调试现场不可能总带着主站模拟器我习惯用 Python 写一个小脚本从串口读原始字节按南瑞101格式解析出来打印成可读的表格。这样即使没有主站也能验证 RTU 侧发出的报文对不对。下面这个脚本用pyserial读串口核心解析逻辑和 C 代码一一对应import serial def calc_cs(data): 南瑞101校验和按位取反 return (~sum(data)) 0xFF def parse_frame(buf): 解析一条完整帧返回字典 if buf[0] 0x10 and len(buf) 5: c, a, cs buf[1], buf[2], buf[3] return {type: fixed, ctrl: c, addr: a, cs_ok: calc_cs(buf[1:3]) cs} if buf[0] 0x68 and buf[2] 0x68: l buf[1] c, a buf[3], buf[4] asdu buf[5:3l] cs buf[3l] return {type: variable, len: l, ctrl: c, addr: a, asdu: asdu.hex(), cs_ok: calc_cs(buf[3:3l]) cs} return {type: unknown} ser serial.Serial(/dev/ttyS0, 9600, parityserial.PARITY_EVEN, bytesize8, stopbits1, timeout1) while True: raw ser.read(256) if raw: print(parse_frame(raw))脚本里calc_cs的取反约定要和 C 代码一致如果现场主站用的是不取反这里也要改。parse_frame里可变帧的asdu切片是buf[5:3l]因为长度域 L 从控制域算起控制域在 buf[3]所以应用层数据从 buf[5] 开始到3l结束。这个索引关系在 C 代码里也经常写错用 Python 先验证一遍再回去改 C效率高很多。进阶一点可以把解析出来的 ASDU 进一步拆成类型标识、传送原因、信息对象和主站点表对比。我一般会在脚本里加一个点表字典把信息对象地址映射成中文名这样打印出来就是「遥测A相电压220.5V」这种可读信息现场排查时一眼就能看出哪个点不对。这个习惯帮我省下了大量和主站扯皮的时间。最后说一个我自己的教训不要相信任何没有经过校验和验证的报文。早期调试时看到串口有数据就以为通了结果主站一直报通信中断查了两天才发现是校验和约定搞反了。从那以后我拿到任何一套南瑞101的 C 代码第一件事就是写个脚本把校验和函数单独测一遍确认和主站约定一致再往下走。希望帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询