资讯动态

S7-1200 Modbus RTU主站轮询实战:从组态到多从站调度

发布时间:2026/10/16 17:19:16 来源:尧图企业网站定制
简介面向西门子S7-1200 PLC开发者的一份Modbus RTU主从通讯例程覆盖主站轮询多从站的完整交互流程重点解决从站地址表维护、超时时间设定、掉线自动跳过等工程落地痛点。压缩包共8个文件以TIA Portal V13工程文件.ap13为主体辅以XML配置、PEDataplf/idx等工程数据整体仅2.58MB便于自动化工程师快速载入项目查看轮询任务组态、通信参数与程序逻辑。已有16006人学习下载适合正在实现多台Modbus RTU从站集中管理的工控开发与现场调试人员。例程按主站轮询框架展开展示了从站未响应时跳过该站并继续下一轮询的处理方式同时对通信参数设置、数据同步与异常状态记录提供了可参考代码结构读者可复用其中的轮询调度思路与故障容错机制减少重复开发时间。1. S7-1200 做主站轮询一条 RS485 总线上把几十台设备按序盘活一个现场的第三方设备超过二十台每台只有 RS485 口PLC 却要按顺序把它们的数据一批批接回来这就是 S7-1200 做 Modbus RTU 主站轮询最典型的场景。标题里的“主站轮询”四个字本质是 PLC 作为总线上的唯一发起方按一张访问计划表依次对多个从站设备发起读写请求。这个方案解决的是设备接入的标准化问题只要对方支持 Modbus RTU不管它内部是什么样的黑匣子主站统一用功能码、寄存器地址、CRC 校验打交道。很多工程师第一次接触这个标题以为只是把两个设备连起来、指令发出去就行。实际落地后会发现从站数量一上来轮询调度、超时处理、报文间隔、地址映射才是工作量的大头。本文的读者是两类人一类要从零开始在 S7-1200 上搭第一版主站轮询需要把指令块怎么配、参数怎么填、轮询怎么写才合理都搞清楚另一类已经在跑但经常翻车需要一篇能对号入座的排查手册。只要手里有 S7-1200 和任意一台支持 Modbus RTU 的设备就能按下面步骤完整复现这个例程。2. Modbus RTU 主站通讯的三个根基报文格式、硬件路径与指令体系2.1 一条 Modbus RTU 报文里必须搞懂的五个字段Modbus RTU 主站通讯的本质是“一问一答”主站发出带从站地址的请求帧从站校验地址和 CRC 后回一帧响应。整个链路上不存在主站主动断开或重连的概念也不需要注册握手状态全靠帧里的字段表达。报文的固定组成是从站地址1 字节1~247、功能码1 字节、数据区N 字节内容由功能码决定和 CRC162 字节低字节在前。这里第一个容易踩的坑是 CRC 的计算顺序发送时先发 CRC 低字节再发高字节很多自制上位机在这个细节上吃过亏。S7-1200 的指令块里 CRC 由系统自动算但你在 PC 端用调试工具对比时必须清楚这个字节序。功能码决定了数据区的解释方式03 读保持寄存器、04 读输入寄存器、06 写单个保持寄存器、16 写多个保持寄存器。主站轮询绝大多数工作在 03 和 04 上少数设备需要 06/16 去写设定值或启停位。另一个必须记住的规则是Modbus 数据模型里的寄存器编号从 1 开始比如 40001而总线上的协议地址从 0 开始S7-1200 的 MB_MASTER 指令里填的 ADDR 是协议地址还是编号直接决定你会不会读出一串 32767。这里还有一个常被忽略的细节Modbus RTU 的报文间隔不是由帧头帧尾标记的而是靠总线静默时间。主站发出请求后必须等待至少 3.5 个字符时间的总线静默从站才能认为一帧结束。S7-1200 在指令块内部自动处理了这个静默窗口但如果你在主站侧做了任何“先发一帧再延时再发下一帧”的操作延时反而会干扰总线的帧间空闲判断。所以轮询状态机里不要引入额外的帧间隙延时交给指令块处理。2.2 三种硬件路径信号板、通讯模块与网关怎么选S7-1200 本身不带标准的 RS485 接口主站轮询常用的是三种路径。最常见的是加一块 CB1241 信号板它占用 CPU 的信号板插槽提供一路隔离的 RS485支持 Modbus RTU 主站和从站成本低适合固定接一条总线。第二种是 CM1241 RS485 通讯模块挂在 CPU 左侧支持扩展多路适合总线数量超过一路、或者现场要把 RTU 和后续其他串口协议分开的场景。第三种是用第三方协议网关把 Modbus RTU 转成其他总线再进 PLC这种路径能解协议转换问题但不是让 S7-1200 直接当主站不在本文的例程范围内。选型的边界条件我一般看两点。第一是总线数量只有一条 RS485 总线、从站不超过 30 台CB1241 足够有多条总线、且每条总线协议或波特率不同就按路数配 CM1241。第二是隔离与供电现场变频器多、地电位乱优先选带隔离的版本否则雷击或接地环流打到通讯芯片上模块损坏是迟早的事。这一点在后期排查时经常被忽视因为通讯异常时大家先怀疑程序很少有人拿万用表量 A/B 线对地电压。从成本角度看一条总线上挂 10 台设备用 CB1241 和用 CM1241 的接线成本几乎一样但 CM1241 支持扩展串口的数量适合未来要加第二条总线的场合。另一种情况是 CPU 本身没有空余的信号板插槽比如已经装了其他信号板这时只能用 CM1241。选型时先数插槽再谈协议需求。2.3 指令体系与固件要求MB_COMM_LOAD、MB_MASTER、MB_SLAVE 的分工S7-1200 的 Modbus RTU 支持从固件 4.0 开始以“指令库”形式提供主站轮询这条链路上只用到两个块MB_COMM_LOAD 负责初始化串口参数MB_MASTER 负责发起每一条读写请求。MB_SLAVE 是你把 1200 当从站时才用的主站轮询场景不需要它。MB_COMM_LOAD 和 MB_MASTER 有一个容易混淆的地方MB_COMM_LOAD 里的 MB_DB 参数要填它自己的背景数据块而 MB_MASTER 的 MB_DB 参数填的是 MB_MASTER 自己的背景块。很多人两块共用一个 DB编译不报错运行报 16#809B 之类的错就是因为内部资源被冲突占用了。正确写法是两个背景 DB 分别建立名字可以叫 CommLoad_DB 和 Master_DB。我在工程里还习惯把这两个块放进同一个 FC 里用先调用 LOAD、再调用 MASTER 的顺序组织代码这样整个通讯上下文在一个程序块内闭环。轮询调度则放在这个 FC 的上级 FB 里用状态机控制后面第三章就按这个结构展开。还有一部分工程师习惯把 MB_MASTER 放在 OB35 定时中断里调用理由是定时唤醒更精准。这里要提醒一下OB35 的调用频率如果高于从站响应时间同一个请求还没完成又触发下一次调用MB_MASTER 的 DONE 和 BUSY 状态会互相踩。我的建议是把轮询状态机和 MB_MASTER 的调用都放在 OB1 里扫描周期本身已经足够快用定时器控制轮询节奏即可别在 OB35 里和中断嵌套较劲。3. 从组态到第一条报文主站轮询的最小可运行工程3.1 硬件组态与端口标识符获取第一步是把 CB1241 或 CM1241 添加到设备组态里。在 TIA Portal 的设备视图中从硬件目录里把信号板拖到 CPU 下方的插槽组态里会出现一个 RS485 接口子模块。这里要记录的是子模块属性里的“硬件标识符”通常是一个整数可能是 269 或 270 附近的值。这个值就是后面 MB_COMM_LOAD 的 PORT 参数抄错一位整个通讯就完全没反应而且编译不一定会报错属于静默失效的一类问题。常见做法是在组态里把 RS485 接口重命名成容易识别的名字比如 RS485_MAIN然后在程序里直接用这个符号名替代数字端口。TIA Portal 会把符号名解析成硬件标识符可读性好一个量级。对于只有一个串口的情况用数字 269 问题不大但工程里建议养成用符号名的习惯因为后期加第二个通讯模块时你不会想从一堆魔法数字里辨认哪个端口对应哪条总线。3.2 MB_COMM_LOAD 端口初始化参数逐一说明MB_COMM_LOAD 要在 OB100 启动组织块里调用一次或者在主程序里用首次扫描标志调用它的效果是把串口初始化成 Modbus RTU 主站模式。下面是常见的赋值写法// 启动时调用一次完成RS485端口初始化 CommLoad_DB( PORT : RS485_MAIN, // 硬件标识符来自硬件组态 BAUD : 9600, // 波特率须与所有从站一致 PARITY : 2, // 2偶校验 0无校验 1奇校验 DATABITS : 8, // 数据位Modbus RTU固定8 STOPBITS : 1, // 停止位RTU通常1位 SIGNAL : 1, // 1RS485模式 TIMEOUT : 1000, // 报文超时单位毫秒 MODE : 0, // 0RTU模式 MB_DB : CommLoad_DB // 必须填背景DB勿与MASTER共用 );PARITY 参数不同版本有细微差别早期版本里枚举值 0、1、2 分别对应无校验、奇校验、偶校验后期版本改成符号常量 NONE、ODD、EVEN。写程序时用符号常量可移植性更好但梯形图里拖动指令块时填数字也常见。注意这里没有流控和停止位的高级选项Modbus RTU 的标准配置就是 8 数据位、1 停止位校验位由从站设备决定多数老仪表默认是偶校验。TIMEOUT 参数是主站等待从站响应的最长时限单位毫秒。这个值不要设得太小因为从站设备千奇百怪有些老设备从收到请求到发出响应之间的处理时间有 500 毫秒以上。我一般把默认值设成 1000 毫秒遇到响应特别慢的设备再单独调大。3.3 MB_MASTER 单条读写REQ、RW、ADDR 与 DATA_PTR初始化完成后每发一条读写请求就是一次 MB_MASTER 调用。MB_MASTER 是功能块必须在每个扫描周期都调用才能让块内部的状态机正常推进。下面是读取 1 号从站保持寄存器从 40001 开始的 10 个字的写法// 每次扫描周期调用REQ 上升沿触发一次请求 Master_DB( REQ : ReadReq, // 上升沿触发请求 RW : 0, // 0读 1写 ADDR : 40001, // Modbus地址40001协议地址0 MODE : 0, // 0标准Modbus功能码映射 DATA_LEN : 10, // 读10个保持寄存器 DATA_PTR : Buffer.Data[0], // 数据落地位置 DONE ReadDone, ERROR ReadError, STATUS ReadStatus );ADDR 参数是个大坑。MB_MASTER 的 ADDR 填的是“Modbus 地址编号”40001 对应的协议地址是 040010 对应协议地址 9。有些设备手册写寄存器地址时直接给十六进制协议地址比如 0x0000这时你需要自己转换成 40001 再填进去。转换错了的典型现象是读回来的数据全是对的但偏移了一位——比如温度出现在第二第三个寄存器里而不是第一个。RW0 是读读的落地位置由 DATA_PTR 指定TIA 里可以用符号访问指向数组元素也可以用 ANY 指针。DATA_LEN 的单位是“字”不是字节。读取输入寄存器时 ADDR 用 30001 起始读取线圈用 00001 起始保持寄存器用 40001 起始功能码的映射由 MODE 参数配合完成。默认 MODE0 时系统按标准映射处理基本不用动。3.4 多台从站的轮询调度一个状态机把请求排队单台设备通讯通了接下来就是把请求按顺序排起来。最可靠的做法是用一个整数变量 step 做状态机每个 step 对应一条请求请求完成后切换到下一条。下面是一个两从站、四条请求的轮询骨架CASE step OF 0: // 从站1读10个保持寄存器 Master_DB(REQ : req0, RW : 0, ADDR : 40001, DATA_LEN : 10, DATA_PTR : Buffer.S1[0], DONE done0, ERROR err0, STATUS st0); 1: // 从站1写1个保持寄存器写阀门设定值 Master_DB(REQ : req1, RW : 1, ADDR : 40100, DATA_LEN : 1, DATA_PTR : Buffer.ValveSet); 2: // 从站2读4个输入寄存器 Master_DB(REQ : req2, RW : 0, ADDR : 30001, DATA_LEN : 4, DATA_PTR : Buffer.S2[0]); 3: // 从站2读20个保持寄存器 Master_DB(REQ : req3, RW : 0, ADDR : 40120, DATA_LEN : 20, DATA_PTR : Buffer.S2[10]); END_CASE;这里最核心的逻辑是每个 step 的 REQ 边沿要错开不能在同一个扫描周期里给两个 step 同时触发上升沿。常见做法是在状态机入口判断“上一条 DONE 或 ERROR”然后置位下一条的 REQ并在切换 step 后复位当前 REQ。我的写法是用一个 FC 统一处理IF done OR err THEN step : step 1; // 请求完成切下一条 req0 : FALSE; // 复位旧请求边沿 END_IF; CASE step OF // 每个分支把对应 req 置 TRUE END_CASE;轮询一圈完成后 step 回到 0这就形成了周期。周期的长短等于所有请求的响应时间之和加上间隙。注意千万不要在某个 step 里写死延时或者用定时器卡住切换那样会把轮询周期拉得很长也没法应对设备响应速度的波动。4. 地址映射与轮询表设计主站代码里真正花时间的部分4.1 三个从站设备的寄存器换算实例常见的从站设备分几类。温控器类仪表一般用 03 功能码读保持寄存器地址范围 40001~40100 左右数据格式多为有符号整数或 IEEE754 浮点。智能电表绝大多数用 04 功能码读输入寄存器地址从 30001 开始数据格式常是两字组合的 32 位浮点。变频器则混合使用运行频率和电流用 03 读设定频率和启停控制用 06/16 写。实际换算时我按三步走。先把设备手册里的寄存器表抄到一张表里列清楚协议地址十六进制、功能码、数据类型、单位。然后把协议地址换算成 MB_MASTER 的 ADDR保持寄存器 0x0000 对应 40001输入寄存器 0x0000 对应 30001线圈 0x0000 对应 00001。最后确认数据格式两个字组一个浮点数时要确定高低字顺序多数设备默认高字在前但国内不少仪表是高字在后这个差异会让读出来的浮点数出现明显的离谱数值。拿一台变频器举例手册写 0x2000 是运行频率偏移量 0x2001 是设定频率数据类型都是两字浮点。换算时 0x2000 对应保持寄存器协议地址 8192加 40001 后 ADDR49153长度 4读两个字。如果是某国产电表用 04 功能码地址表写的是 0x0000~0x003F对应 ADDR30001~30064数据格式大多是低高字对调。动手前先开调试工具读一次比对着手册猜省一个晚上。4.2 把轮询计划做成一页纸DB 里的轮询表结构从站多了以后在 CASE 里手写每一条请求会变得没法维护。我习惯把轮询计划做成一张数据表放在全局 DB 里每条记录包含从站地址、功能码类型、寄存器起始地址、长度、数据落点、超时值。运行时代码只是遍历这张表TYPE PollItem STRUCT SlaveID : INT; -- 从站地址1~247 RW : INT; -- 0读 1写 StartAddr : INT; -- 转换后的Modbus地址如40001 Length : INT; -- 字长度 Timeout : INT; -- 该条请求专用超时ms0则用默认 DestOff : INT; -- 在Buffer里的起始偏移 END_STRUCT END_TYPE表定义好之后在运行代码里用一个 FOR 循环或者按索引访问数组元素把 MB_MASTER 的输入参数全部从表中取出。这样新增一台从站只需要在 DB 里加一行改一条请求只需要改一行数据不用碰逻辑代码。多从站轮询表更新时还有一个容易出错的点把某台从站的请求从表里删除后后面的索引会前移DB 数组的引用地址如果写的是硬下标数据就会错位。我的做法是用一个枚举或者符号常量命名每行的用途比如 FIRST_READ、SEC_READ通过符号常量访问行号避免硬编码 0、1、2。这样增删轮询行时只需要调整表内容和符号映射逻辑里的索引不动。4.3 响应超时、重试与错误记账主站不能只会发请求轮询状态机切换的条件是 DONE 或 ERROR。DONE 表示成功ERROR 表示通讯失败但 ERROR 只是代表这条请求没成功不代表总线废了。所以主站逻辑里必须有重试和记账机制。我的做法是每条请求连续失败 3 次后跳过这台从站继续轮询后面的设备同时把失败次数和 STATUS 错误码记录到另一个 DB 里供上位机或 HMI 查询。重试要注意不能无限重试。如果一台从站断电或者总线被断开MB_MASTER 会一直超时如果卡在这台设备上反复重试后面所有设备都会被拖死。常见做法是设置“失败跳过”标志这台设备连续失败 N 次后标记离线本轮不再访问它直到下一轮轮询时再给它一次机会。错误码记账里我还会加一个时间戳字段记录最近一次错误发生的时间用系统时间或运行分钟数。这样在现场看不到实时抓包的情况下也能知道错误是持续发生还是偶发。如果 STATUS 码以 0x80 开头是 Modbus 异常码对应从站设备返回的异常如果以 0x81 开头且是 8xxx 格式常见于指令库自身的错误比如超时和 CRC。两类错误码的处理策略完全不同异常码说明从站收到了请求但业务上拒绝需要查设备侧配置超时则多半是物理链路或设备重启要看通信质量。5. 主站轮询的五个典型翻车点与排查路径5.1 现象MB_COMM_LOAD 一调用就报错STATUS16#809B原因PORT 参数填的不是硬件标识符而是填了 IO 地址或者随便抄了个数字。另一个常见原因是两块指令块共用了同一个背景 DB。解决打开设备视图选中 RS485 接口在属性里查硬件标识符把那个整数填进 PORT。再确认 CommLoad_DB 和 Master_DB 是两个不同的背景 DB。检查时先看编译信息再看运行时的 STATUS809B 基本就是背景块或硬件标识符问题。我在现场见过最典型的误用是把组态里的“IO 起始地址”当 PORT 填那个地址是给用户程序访问输入输出映像区用的和通讯端口的硬件标识符完全是两回事。区分方法也简单硬件标识符在属性里叫 Hardware identifierIO 地址叫 I/O address翻译不同。多看几遍组态界面抄错一次之后就再也不会弄混了。5.2 现象第一台从站通讯正常第二台开始频繁超时原因两条请求之间的总线空闲间隔不够。Modbus 标准没有规定帧与帧的最小间隔但很多设备要求请求之间至少有 3.5 个字符时间的静默有些老设备实际要求更高。轮询状态机里如果上一个 DONE 刚置位就立刻触发下一条 REQ下一台设备可能还没从上一条响应中恢复过来。解决在状态机切换那里加两三个扫描周期的稳定延时不用定时器用一个计数器扫描几次即可或者把 MB_COMM_LOAD 的 TIMEOUT 稍微调大。另外检查是不是波特率不一致第二台设备如果被拨码开关设置了不同波特率现象也是超时。这个坑最隐蔽的地方在于第一台设备永远正常于是你会怀疑第二台设备本身坏了。实际上拿 PC 调试工具单独连第二台通讯完全正常连上总线就出问题。根因就在帧间隙。我现在的习惯是所有从站统一波特率、统一校验位拨码开关状态在接线前逐一核对能省掉一大半这类玄学问题。5.3 现象读回来的数据要么是 32767 要么是 0偶尔还对一次原因地址换算错误或功能码用错。32767 是保持寄存器读到一个未使用地址的典型值0 则常见于读输入寄存器时用了保持寄存器的地址段。还有可能是 DATA_LEN 和实际数据类型不匹配比如设备要按字读你读的字数少了数据整体错位。解决先用 PC 端的 Modbus 调试工具直接连从站把地址、功能码、数据格式都确认好再回 PLC 里对照。这一步能省掉后面大量“PLC 是不是坏了”的猜疑。字节序问题也在这里验证PLC 端在把 DATA_PTR 指向的 Buffer 转成浮点数时注意高字低字顺序。拿 32767 这个数举例很多工程师看到这个值第一反应是“PLC 读错了”其实 32767 是寄存器空置时的上电默认值恰恰说明通讯链路是通的只是地址落在了设备没有实际数据的区域。正确做法是查设备手册里这个地址是否有效而不是去改波特率或者换通讯模块。我在调试阶段遇到这种情况会先用调试工具把所有寄存器批量读一遍找到实际有数据变化的那几个地址再回 PLC 对表。5.4 现象写操作偶发失败读操作一直正常原因06 功能码写单个保持寄存器不是所有设备都支持有些设备只支持 16 写多个保持寄存器也可能是设备有写保护比如某些仪表需要先解锁或者处于远程模式才允许写。解决查设备手册确认写入功能码和写地址范围。如果是写保护先用设备面板或上位机解锁再在 PLC 里发写请求。还有一种隐蔽情况写入值超出设备允许范围PLC 端发请求正常设备端返回异常码STATUS 显示类似 16#80D4 的非法数据值错误要把写值的类型和范围核对清楚。我在项目里遇到过一台温控器的写设定值偶发失败查到最后是设备有“本地/远程”切换开关拨到本地模式时所有写请求都会被拒绝。这种问题在程序层面几乎看不出来因为请求和响应都正常只是响应内容是一个异常码。所以排查写操作问题时第一个动作不是看程序而是查设备当前的操作模式和写权限。5.5 现象一上电主站就发请求设备还没初始化完成通讯全乱原因MB_COMM_LOAD 在启动块里调用了但 RS485 接口的初始化需要时间而 OB1 里的 MB_MASTER 首个扫描周期就已经开始发请求。现场设备也要时间上电自检。解决把 MB_COMM_LOAD 放在 OB100 启动组织块里同时用一个“通讯就绪”标志位只有标志位置位后才允许进入轮询状态机。这个标志位可以在 OB100 执行完后再等 2~3 个周期置位或者等首个请求成功后保持。我的习惯是初始化完再延时几百毫秒再开轮询对现场一堆慢启动的设备特别有效。6. 把轮询周期从 2 秒压到 300 毫秒响应计时、批量读写与断线重连的实操技巧轮询周期这个指标直接决定数据刷新频率温度要 1 秒刷新一次而阀门状态可能 500 毫秒就要看一次。很多工程师一开始把轮询周期做成了“每条请求串行 每条间隔 200ms”从站数量一到五台周期立刻破 2 秒。压周期的第一步是量化每条请求的时间一个读 10 个字的请求请求帧约 8 字节响应帧约 320 字节按 9600 波特率算每字节 10 位传输时间就是 (823)*10/9600 约 32 毫秒。如果设备处理时间 20 毫秒一条请求总耗时约 50 多毫秒这是硬下限间隔延时再压也压不过这个数。常见做法里有三个优化点。第一是减少请求条数把相邻的读请求合并成一条比如从站地址连续的寄存器段用一条 DATA_LEN 更长的请求一次读完替代两条短请求。第二是按设备的响应速度给每条请求单独设超时快的设备 300 毫秒超时足够慢的设备给 2000 毫秒不要让慢设备拖累整轮的节奏。第三是调整轮询顺序把响应慢的设备排到轮询表的最后让响应快的设备先完成这样关键数据比如当前运行频率和报警状态能更早更新。断线重连这块我的经验是不要用“反复重试”去治“离线”一台设备离线本轮跳过下一轮再试探一次比连续重试 10 次合理。因为重试只会把后面的从站全部阻塞而探测间隔可以很长设备恢复后最多一个轮询周期就能被重新纳入。我在现场有个习惯在 HMI 或调试面板里放三个数——本轮状态、当前请求的从站地址、最近一次 STATUS 错误码。轮询卡死时第一时间看的是当前停在哪条请求上而不是翻程序断点。这三个数配合一个简单的计数器记录轮询完成的圈数就能判断是单台设备掉线还是总线整体断线圈数还在涨说明只是这台设备有问题圈数停了总线才是真断了。调试阶段这个面板帮我省了不少时间。说到最后主站轮询这个例程我前后改过很多版最深的教训是“先调通单台设备再上多台轮询”。单台设备通讯稳定只是串口通了多台轮询不出乱才是协议处理真正过关。如果你正在做这个方向我建议把这篇文章里的轮询表结构、状态机切换和 STATUS 记账机制先搭起来再根据手里的设备逐台接入。按照上面的步骤走一遍你就能得到一套稳定的主站轮询例程后面再遇到什么样的第三方设备无非是往轮询表里加一行的事。希望这些细节能帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑