ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Modbus常用功能码与PLC应用:从报文到现场调试一次讲透

Modbus常用功能码与PLC应用:从报文到现场调试一次讲透 搞工控调试这几年我有个特别深的感受现场一半以上的通讯问题最后查来查去要么是功能码用错了要么是地址没有对齐。而Modbus作为自动化行业里最常见的通讯协议无论你是拿PLC去读仪表、控变频器、接温控器还是和组态软件、上位机做对接最终都要落到那几个功能码上。很多新手一开始就啃协议文档啃完还是不会用反而是那些在现场泡过几年的老师傅不一定能把协议条文背全但翻来覆去就用那么几个功能码却什么问题都能搞定。这篇就把Modbus常用功能码和PLC应用这件事一次说透适合刚入门PLC通讯的新人也适合在现场被通讯问题反复折磨的老兵参考。1. 先把Modbus的底子摸清1.1 为什么工控现场永远绕不开ModbusModbus的江湖地位用一句话就能概括它是工业通讯里的“普通话”。不管是西门子、三菱、欧姆龙这类外资PLC还是信捷、汇川这些国产品牌也不管是变频器、温控表、流量计还是智能电表几乎所有设备出厂时都会标配Modbus-RTU或者Modbus-TCP接口。原因很简单这个协议公开、免费、实现成本低而且报文结构足够简单一个单片机就能跑起来。从现场维护的角度看Modbus最大的价值在于“可预测”。它只有一张数据表主站发什么命令、从站回什么数据格式是固定的。那怕你手上没有任何资料用一个串口助手把报文抓出来也能反推出设备的寄存器分布。这一点在工程调试里特别实用。很多时候设备厂家给的说明书语焉不详最后就是靠功能码扫描加寄存器试读把通讯“盲调”通的。1.2 选RTU还是TCP别拍脑袋Modbus协议从物理层上分两大流派串口走RTU网口走TCP。RTU模式用485总线两线制半双工波特率常见的有9600、19200、38400等特点是走线简单、抗干扰能力强适合几十米到几百米的现场布线。TCP模式直接用以太网全双工速度比RTU快几个数量级适合数据量大、实时性要求高的场合。选型时我的习惯是只要能走以太网优先考虑Modbus-TCP。理由是它省掉了串口参数配置这一堆麻烦事——波特率、校验位、停止位不对就通不上这类坑在RTU里太常见了。但如果是老设备改造、现场只有485线那就老老实实做RTU。需要注意的是RTU的从站地址范围是1到247地址0是广播地址实际工程中很少用。而TCP模式没有从站地址的概念取而代之的是IP加端口号默认端口502。提示如果你在做方案阶段还有选择余地别因为“以前都这么干的”就死守着485不放。用TCP省下来的调试时间够你多测两轮设备。2. Modbus常用功能码逐条拆给你看2.1 功能码01、02读线圈和离散输入别把“位”搞混功能码01是读线圈状态02是读离散输入状态。这两个都是按“位”操作的一次读的是若干个开关量返回的数据要按位拆解。先看功能码01的请求报文结构。假设你有一个Modbus从站地址为1想从线圈起始地址0x0000开始读8个线圈请求帧是这样的01 03 00 00 00 08 CRC等等这里我要纠正一个很多人犯的错。功能码01的请求第二个字节应该是01不是03。写全了是这样01 01 00 00 00 08 CRC对照着解释一下第一个字节01是从站地址第二个字节01是功能码第三第四字节00 00是起始地址第五第六字节00 08表示读8个线圈最后两个字节是CRC16校验。响应帧就有意思了。从站会先回自己的地址和功能码然后是数据字节数接着是数据。比如从站返回8个线圈状态时一个字节就装下了各位的含义是bit0对应起始线圈bit1对应该起始线圈的下一个以此类推。如果线圈个数不是8的整数倍最后多余的位补0。这里最容易踩的坑是“位顺序”。比如你读回来的字节是0x05二进制就是00000101这意味着起始线圈是ON第三个线圈是ON而不是第一个和第六个。很多人在上位机比如组态软件、LabVIEW里解析数据时把顺序搞反最终画面上开关状态全是反的而且这种问题极难发现因为看起来通讯又是正常的。功能码02和01的区别就一个01读的是线圈线圈是可读可写的开关量对应PLC里的Q区、M区、中间继电器等02读的是离散输入是只读的开关量输入对应PLC里的I区、数字量输入模块。在工程语义上02读的是外部按钮、限位开关、传感器反馈这类信号本身只允许读不允许写。2.2 功能码03、04读寄存器绝大多数数据交换都靠它们接下来是Modbus最核心的两个功能码03读保持寄存器04读输入寄存器。PLC和仪表、变频器之间的数据交换比如读温度、读转速、读电流或者下发设定值基本都离不开03和04。功能码03和04的报文结构几乎一致。比如从站地址1读保持寄存器起始地址0x0000读2个寄存器01 03 00 00 00 02 CRC从站正常回包是01 03 04 数据1高字节 数据1低字节 数据2高字节 数据2低字节 CRC第三个字节04表示后面跟着4个字节数据也就是两个16位寄存器。功能码03和04的本质区别在于寄存器类型。保持寄存器03是可读可写的你要给设备下参数、写给定值必须用它输入寄存器04是只读的用来存设备实时测量值比如温度采集模块的通道值、电能表的电压电流通常只能用04去读。但在实际工程里设备厂家对寄存器类型的定义并不完全统一。我见过有的仪表把测量值放到保持寄存器里也见过有的PLC从站把输入寄存器映射到了模拟量输入通道。所以选功能码之前一定要确认设备的手册里是怎么定义的。很多通讯调不通不是因为协议不会写而是你把03当成04用或者反过来设备根本不响应。2.3 功能码05、06、15、16写操作PLC控制输出的关键写完再讲写。功能码05是写单个线圈06是写单个寄存器15是写多个线圈16是写多个寄存器。这四个是PLC作为主站去控制从站设备时最常用的功能码。功能码05写单线圈报文里有个非常经典的细节线圈状态用0xFF00表示ON0x0000表示OFF。注意ON不是0x0001而是0xFF00。如果按习惯写0x0001有些从站设备会直接返回非法数据值错误异常码03。报文长这样01 05 00 00 FF 00 CRC这串的意思是给地址1的从站从线圈0x0000开始写一个线圈状态为ON。功能码06写单寄存器报文类似01 06 00 00 00 01 CRC表示给从站1的保持寄存器0x0000写入值1。这个常用于单点设置比如修改变频器的运行频率、修改仪表的报警值。功能码15和16是批量写。当你要一次下发一组参数或者同时控制多路输出时用批量写能大幅度减少通讯次数。批量写寄存器的报文结构比单点写复杂一点因为要带上字节数01 10 00 00 00 02 04 00 01 00 02 CRC第一个字节01是从站地址第二个字节10是功能码16的十六进制表示00 00是起始地址00 02是寄存器个数04是数据字节数2个寄存器就是4个字节后面四个字节是两个寄存器的值。写操作最需要注意的是“一次写多少”。Modbus RTU的单帧数据通常限制在256字节以内实际项目中我建议一次写寄存器的个数不要超过120个线圈不要超过960个。超过这个量有些从站会直接拒绝响应有些则会悄悄丢数据很难排查。2.4 不常用但能救场的功能码除了上面八个功能码还有几个平时用得少、但关键时刻能救场的。功能码07是读异常状态目前支持它的设备不多可以忽略。功能码08是诊断最常用的是子功能码00回环测试——发什么就回什么用来测试链路是否通畅。排查通讯问题时我经常先发一个08 00 00 00 00 00如果从站能原样返回说明物理链路和从站通讯模块都是好的问题大概率出在功能码或地址上。功能码17是读从站ID可以拿到设备的厂家代码、型号、版本号等信息。有些杂牌设备说明书写得不清不楚设备地址也可能和铭牌不一致这时用17功能码探一下能省不少事。不过同样要面对“设备支持不支持”的问题不支持的话就只能老老实实拨码或看软件配置了。下面这张表建议收藏调试时对照着用功能码中文名作用对象典型使用场景0x01读线圈线圈可读写位读取PLC Q/M区输出状态0x02读离散输入离散输入只读位读取PLC I区输入、传感器反馈0x03读保持寄存器保持寄存器可读写16位读写设备参数、设定值0x04读输入寄存器输入寄存器只读16位读模拟量测量值、电参量0x05写单个线圈线圈控制单路DO输出0x06写单个寄存器保持寄存器设置单个参数0x0F写多个线圈线圈批量控制DO输出0x10写多个寄存器保持寄存器批量下发参数或配方0x08诊断回环通讯链路排查物理链路和从站状态0x11读从站ID从站信息识别设备型号与版本3. PLC地址映射与数据处理才是真正翻车高发区3.1 PLC地址到Modbus地址怎么换算很多人把功能码报文写对了也用调试软件测通了但一接上PLC程序就出问题。出问题的地方十有八九是地址映射。PLC里的寄存器地址和Modbus协议里的寄存器地址不是一回事中间需要换算。拿最常见的保持寄存器举例子。Modbus协议里的寄存器地址从0x0000开始而在设备手册里你看到的往往是40001、40002这种OC地址。这里的4是功能码区标识40001对应的就是协议地址0x000040002对应0x0001以此类推。同理30001对应输入寄存器的0x000000001对应线圈的0x000010001对应离散输入的0x0000。为什么会出现这种差异因为Modbus发明的时候用的是“数据区加偏移量”的编址方式4xxxx表示保持寄存器区后面是OC序号而协议报文里为了节省空间用的是从0开始的十六进制偏移地址。两者在工程上必须做转换。转换公式很简单协议地址 PLC/设备地址 - 数据区起始编号 - 1举几个常用的例子设备手册说“运行频率在地址40005”那么协议地址就是40005 - 40001 4十六进制就是0x0004。设备手册说“当前温度在地址30010”那么协议地址就是30010 - 30001 9十六进制0x0009。设备手册说“启动按钮对应线圈00008”那么协议地址就是00008 - 00001 7十六进制0x0007。这些看似简单的加减真到了现场写程序时特别容易在“到底减不减1”上犯迷糊。我自己的习惯是不管从手册里读到什么地址第一件事先换算成协议地址写进程序注释再对照报文抓包确认宁可多花两分钟验算也别凭感觉发。3.2 字节序、字序与浮点数转换95%的工程师都在这翻过车地址对了数据也不一定对数据对上了数值还有可能读出来是个天文数字。这就涉及Modbus数据在字节层面的排列问题。Modbus协议规定16位寄存器在报文里是高位字节在前、低位字节在后。比如寄存器值是0x1234报文里先发0x12再发0x34。这种叫大端模式绝大多数设备都遵守。但32位数据比如浮点数就麻烦了因为32位数据占两个寄存器两个寄存器之间的先后顺序不同厂家并没有统一。常见的有两种顺序大端字序CDAB先发高16位寄存器再发低16位寄存器。比如浮点数123.45的32位十六进制是0x42F6E666寄存器1放0x42F6寄存器2放0x6666。小端字序BADC先发低16位寄存器再发高16位寄存器。同样一个数寄存器1放0x6666寄存器2放0x42F6。这就是为什么同一块仪表你按说明书地址读出来了1.623e-34这种数据而厂家工程师一口咬定设备没问题。数据本身是正常的只是字序解析反了。解决这个问题有两种路径一是部分PLC的Modbus库指令支持“字节交换字序交换”的配置项直接在配置里调二是上位机组态、LabVIEW、.NET程序里把两个寄存器Swap一下再转浮点数。我自己写数据处理时固定用一套方法先用Modbus调试软件读一段已知的、能确认数值的寄存器比如把变频器频率设定为50.00Hz强行分析它的字节排列确定设备到底是哪种字序然后再写PLC程序。不要看说明书想当然说明书里很多厂家根本不会写清楚字序细节。顺带说一句IEEE 754浮点数转换。如果你拿到两个16位寄存器想合成一个32位浮点数在C语言里可以直接用指针强转在PLC里通常用两个字拼接指令或者字交换指令处理。上面说的字序决定你拼接时谁前谁后只处理字节序不处理字序结果一样不对。3.3 不同品牌PLC的Modbus指令怎么用不同品牌的PLC对Modbus的支持方式差别很大这里挑几个常见的说。西门子S7-200 SMART走Modbus官方库里有MBUS_CTRL、MBUS_MSG这些指令其中MBUS_MSG可以同时支持读写。S7-1200/1500则用MB_COMM_LOAD加载通讯参数再用MB_CLIENT作为主站或MB_SERVER作为从站处理请求。用西门子的库函数时特别是S7-200要注意V区地址的分配和保持寄存器映射关系比如MBUS_MSG指令里的DataPtr参数往往要求指向V区。三菱FX系列比较特殊走内置485口时通常用ADPRW指令直接构造Modbus请求也可以用RS指令自己拼报文然后通过CRC校验程序处理。FX5U和Q系列对Modbus-TCP的支持就更完善可以配置成服务器或客户端。国产PLC里信捷、汇川这些品牌基本都内置了Modbus主从站配置不需要自己拼CRC。以汇川为例调用MODBUS通讯功能块填从站地址、功能码、起始地址、数量、数据缓存区就行功能码选好了基本不用操心报文细节。但这里有个通病越方便的功能块越容易让人忽视地址和寄存器类型的对应建议配置完先用调试软件验证一轮再下载到现场。老规矩不管什么品牌我都要强调PLC程序里的人机界面地址最好设计成“配置表”的形式把从站地址、寄存器地址、数据类型、采样周期全部列在表格里集中管理。否则项目后期甲方往上位机里加一堆点位你光查地址就能查到怀疑人生。4. 调试与故障排查实战记录4.1 先用调试软件把协议跑通再动PLC程序我的调试顺序从来都是电脑装Modbus调试软件先把设备测通再写PLC程序和上位机。用Modbus Poll这类的工具可以稳定地发任意功能码、任意地址的请求并直观看到返回的数据。如果你是做从站比如把PLC配成Modbus从站给上位机读可以用Modbus Slave来模拟设备侧返回数据先验证上位机和你的通讯参数对不对。调试软件还有一个好用的功能持续循环发报文。我在测试一批温控仪表时就是把某个寄存器设成周期1秒地读然后拿万用表测仪表实际输出对比数值是否一致。数据对不上先怀疑字序和系数数据完全读不到先怀疑地址和功能码连请求都发不出去先怀疑串口参数和接口转换器。注意Modbus Poll、Modbus Slave这类调试工具在工控圈很普及但正版授权是收费的。工程上建议买授权个人学习可以用官方提供的试用模式来熟悉协议流程别去网上找破解。调试工具本身不复杂但被带伤工具坑掉一个下午的调试时间太不划算。4.2 现场连不上的排查顺序按下述步骤来现场通讯故障五花八门但核心规律就那么几条。遇到“主站和从站分别测都正常一接在一起就不通”的情况绝大多数不是协议的问题而是物理层和参数层的问题。我的排查顺序如下建议直接抄作业第一步看接线。485通讯是A和B两根线但不同厂家对A/B的标法不统一有的标D、D-有的标485、485-先确认A对A、B对B。两个设备间的A/B接反是“单独测都行连着不通”最常见的元凶。第二步查终端电阻。485总线两端要各接一个120欧姆终端电阻。短距离、两台设备的临时测试可以不管但现场几十米以上、节点多的时候不接终端电阻通讯大概率会出现偶发超时或者乱码。第三步核对通讯参数。波特率、数据位、校验位、停止位四项必须完全一致。这里特别强调校验位很多国产设备默认无校验8N1但仪表出厂可能是偶校验8E1两边不一致时会出现“能收到数据但CRC永远不对”的怪现象。第四步核对从站地址。主机发出的请求里带从站地址如果从站设备实际地址是2你发的却是1从站是不会回应的。现场多台设备组网时别忘了检查重复地址——两台设备都设成1总线会异常混乱。第五步检查地电位。485本质上是差分信号理论上可以不共地但现场强电干扰严重、距离又长时建议在主机侧把485的GND和设备的信号地连接起来避免共模电压过高导致通讯时好时坏。这五步走完绝大多数通讯都Wall能通。如果五步都没问题再用功能码08回环测试去判断是不是从站通讯模块本身的问题。4.3 几个典型踩坑案例都是真金白银换来的案例一读温度变成-1000度。某项目用PLC读一个温度采集模块按说明书功能码04读取数据能读到但数值明显不对显示类似65530这种数据。排查发现模块的测量值实际应该用功能码03读说明书里“只读”说的是寄存器属性但厂家实现时把值放在保持寄存器里了。最后改成03数据正常。教训是功能码的“读”和“写”属性看厂家实现不要光看协议标准定义。案例二批量写参数只成功一半。现场要给一块带128路参数的仪表下发预设值我用功能码16一次写了100个寄存器仪表只更新了前60个。后来发现仪表内部处理单帧数据的上限就是64个寄存器超出后整帧丢弃只是仪表响应帧会带异常码但PLC程序没做异常码判断就一直显示“通讯成功”。从那以后我写批量写逻辑时固定按每帧50个寄存器切分并在PLC程序里做响应异常码判断宁可多拆几帧绝不赌设备处理能力。案例三PLC做从站上位机读M区数据总差一位。信捷PLC里有%MB2000这种M区字节地址上位机工程师用Modbus读保持寄存器时总是发现地址对不上。原因是PLC的M区地址映射到Modbus寄存器时有个起始偏移规则通常PLC的MB/ MW地址会对应到寄存器地址加一个固定偏移。这类问题最有效的办法是在PLC里给一个已知的M区地址写个固定值然后在上位机用调试工具去扫确定对应关系不要靠猜。4.4 排查问题时的通病速查表故障现象优先排查项次要排查项完全无响应通讯参数不一致、从站地址错误A/B接反、接口转换器故障响应时好时坏终端电阻缺失、线路过长共模干扰、地线问题能通讯但数据为0/65535功能码选错、寄存器地址错数据字节序处理错误数据数值异常巨大或极小字序、字节序不匹配浮点数转换方法错误PLC程序发请求但上位机收不到从站配置未生效地址映射表未对齐5. 我的一点个人心得做Modbus调试这些年我自己的习惯是不管现场多着急先把主站、从站的通讯参数表、地址映射表整理好然后用电脑端的调试软件在实验室把协议层跑通再写进PLC程序里。通讯这种东西绝大多数故障都不是协议本身高深没搞懂而是参数、地址、字节序这类细节没有对齐。遇到问题不要慌从头到尾按“物理层、参数层、数据层、应用层”的顺序捋一遍八成的坑都能自己填上。最后再分享一个小技巧无论你用的是哪家PLC都建议在程序里把从站返回的异常码给解析出来状态字里留两个字节专门报通讯状态。这样现场设备一旦通讯异常上位机画面上能直接看到“非法功能码”“非法数据地址”“从站忙”这类具体原因而不是只看到一个干巴巴的“通讯故障”。对甲方来说这能省掉无数来回沟通的时间对你自己来说这是半夜被电话叫起来时最值钱的东西。
返回列表