ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

Modbus功能码与PLC通讯实战:从数据模型到现场排障

Modbus功能码与PLC通讯实战:从数据模型到现场排障 做工控这些年Modbus可能是和PLC打交道最多的协议。不管是西门子、三菱还是国产PLC也不管对面接的是仪表、变频器、电表还是触摸屏Modbus RTU/TCP几乎是无处不在。很多新入行的朋友一上来就被功能码搞懵01、02、03、04、05、06、0F、10这一堆数字到底是干什么的为什么有的设备用03能读有的用04才能读今天我就把这些年在现场摸爬滚打总结出来的经验配合具体的报文例子和PLC实操逻辑一次性讲透。这篇文章不是什么教科书式的协议解读就是纯粹从工程应用角度告诉你Modbus功能码怎么选、报文怎么认、地址怎么偏移、字节序怎么处理、现场又该怎么排障。适合刚接触PLC通讯的电气工程师、设备维护人员也适合那些已经在用Modbus但一直“知其然不知其所以然”的朋友。看完之后你至少能独立完成一个设备从接线、调试到并入PLC程序的全过程。1. 先把Modbus的数据模型理清楚1.1 四个数据区就是四张抽屉Modbus协议把人机交互的数据分成四个区域我习惯把它们理解成四张抽屉。你不能把一张纸条塞进锁着的抽屉也不能从空的抽屉里取东西所以先搞懂哪张抽屉能放什么、能取什么后面选功能码才不会乱。第一张抽屉是线圈Coil它只有0和1两种状态而且可以读也可以写。对应到PLC上就是Q输出或者M中间继电器这种可读可写的位元件。第二张抽屉是离散输入Discrete Input同样只有0和1两种状态但只能读不能写。对应到PLC上就是I输入点外部按钮、接近开关的信号都在这张抽屉里。第三张抽屉是保持寄存器Holding Register一个寄存器16位可以读也可以写这是现场最常用的区域仪表参数、设定值、变频器频率基本都放在这里。第四张抽屉是输入寄存器Input Register同样是16位一个字但只能读不能写一般存放实时采集值比如电压、电流、温度这些测量数据。这里有个实操经验很多设备的“只读数据”并不一定放在输入寄存器里而是放在保持寄存器里。所以遇到新设备时不要死守着“输入寄存器只能放输入信号”这个想法先看它的MODBUS寄存器表怎么定义。有的厂商习惯把所有数据一股脑放在保持寄存器区读起来反而方便。1.2 四个区对应的功能码规律把数据模型和功能码放在一起看你会发现规律极其简单每个区有两类操作一类读、一类写对角线一对应就全记住了。数据区位/字读写属性读功能码写功能码PLC常见对应线圈位可读可写0105单点/0F多点Q、M、Y离散输入位只读02无I、X保持寄存器16位字可读可写0306单点/10多点VW、D、DBW输入寄存器16位字只读04无AIW、ADC数据这个表格可以直接收藏现场翻出来对照着用。只要你确定要读的数据属于哪个区功能码就锁定了一大半。比如接一个压力变送器它说“压力值在保持寄存器0地址”那就用03如果它说“压力值在输入寄存器0地址”那就换成04。剩下要判断的无非是读几个字、一个字还是两个字的问题这个下文会细说。1.3 为什么现场实际用的功能码就那么几个打开Modbus协议规范功能码从1到127看着一大堆但是你去翻仪表手册、变频器通讯手册翻来翻去常用的就是8个01、02、03、04、05、06、0F、10。为什么因为这8个功能码已经覆盖了数据模型里所有读写需求。像07读异常状态、11读事件计数这类功能码属于早期串行总线的遗留物现在几乎没有设备支持更不会接到PLC里用。还有一些厂商自定义功能码比如65、66这些属于私有用法只有在特定品牌设备上才见得到。还有一个原因PLC主站指令和组态软件对功能码的支持也是有限度的。像信捷、汇川、三菱这些PLC的Modbus指令往往直接让你填“功能码”三个字下拉列表里都是固定选项。你用一个私有功能码可能整个通讯模块都不认识。所以我的建议是把上面8个功能码吃透你就能应付95%以上的Modbus现场。2. 八个常用功能码逐个拆解2.1 读寄存器03和04是绝对主力03读保持寄存器、04读输入寄存器这两个功能码是我在现场用得最多的而且它们的报文结构一模一样只有功能码那一格不同。先看报文格式请求帧是“从站地址 功能码 起始地址高字节 起始地址低字节 寄存器数量高字节 寄存器数量低字节 CRC校验”共8个字节。举个例子读取地址为1的从站协议地址从0开始连续2个保持寄存器请求帧就是01 03 00 00 00 02 C4 0B。这里面01是从站地址03是功能码00 00是协议起始地址00 02是读取数量后面两个C4 0B是CRC校验。注意报文里出现的地址是协议地址不是你在仪表参数表里看到的那种40001之类的显示地址这里有个偏移关系后面我会单独讲。正常响应帧结构是从站地址 功能码 字节数 数据区 CRC。比如从站返回01 03 04 43 21 56 78表示读到了4个字节内容分别是0x4321和0x5678两个字。关于CRC校验现场看波形时不用自己手算串口调试工具和调试软件都会自动处理。但如果你想写自己的通讯库算法原理很简单初始值0xFFFF每字节异或后右移8位低位为1时异或0xA001最后低字节在前发送。uint16_t modbus_crc(uint8_t *buf, uint16_t len) { uint16_t crc 0xFFFF; for (uint16_t i 0; i len; i) { crc ^ buf[i]; for (uint8_t j 0; j 8; j) { if (crc 0x0001) crc (crc 1) ^ 0xA001; else crc 1; } } return crc; // 发送时低字节在前 }03和04在PLC里的区别更直观。比如S7-200 SMART做Modbus从站时03读的就是V区保持寄存器04读的是AIW区。而读电表参数这种场景厂商手册会明确说电压数据用04功能码读输入寄存器。遇到一个新设备如果没有特别说明优先试03因为很多厂商把参数和测量值都塞进保持寄存器里了。2.2 读位状态01和02处理开关量01读线圈、02读离散输入这两个功能码用于读开关量状态。报文格式同样是“从站地址 功能码 起始地址 数量 CRC”和03完全一样唯一的区别是功能码不同、目标数据区不同、返回的数据是位。响应帧会特别一点它把位数据按八个一组打包成一个字节比如读回来一个字节0x05二进制是00000101那么在这个组里的第1位和第3位就是1其余是0。如果读的数量不是8的整数倍最后一个字节剩余的高位补0。这几个功能码的应用场景很典型监控配电柜里断路器的分合闸状态读变频器的运行/停止/故障状态或者读阀门开到位/关到位的限位信号。特别是很多变频器和软启动器你不需要一直发指令问它“你运行了吗”直接把它的状态线圈映射到PLC输入区程序里就能当普通常开常闭点用。这里有个容易踩的坑01和02虽然都是位读但有的设备会把“运行状态”放在线圈区有的放在离散输入区。你要在对应区域才能读得到。最好的办法是先看设备通讯手册里的“Modbus地址映射表”上面会明确写出“线圈地址0运行”“离散输入地址1故障”这类信息。2.3 写单点05和06用于启停和控制字05写单个线圈06写单个寄存器这两个功能码是“单点写”报文结构也极其简单。写线圈的请求帧是从站地址 05 线圈地址 数据 CRC。数据区有讲究0xFF00表示置ON0x0000表示置OFF不能直接发01表示ON这是Modbus协议的规定。比如让1号从站的0号线圈置ON01 05 00 00 FF 00 8C 3A。写单个寄存器的请求帧是从站地址 06 寄存器地址 要写入的值 CRC。比如把1号从站的0号保持寄存器写入101 06 00 00 00 01 48 0A。在现场06最常见的用途是写变频器的给定频率、写PID设定值、写仪表的控制字。05最常见的用途是启停电机、复位故障。这里要特别提醒一下很多设备把“启停”做成了寄存器而不是线圈控制字可能是固定的几个字节组合。比如某品牌变频器你需要往“命令寄存器”写入0x0001表示正转写入0x0002表示反转写入0x0005表示停止而不是用05功能码去置位。所以执行写操作前一定先做两件事查手册确认目标区再用调试软件单独测试一下。2.4 批量写0F和10一次搞定一片0F十进制的15写多个线圈10十进制的16写多个保持寄存器这两个是批量写功能码。0F的请求帧比05复杂从站地址 0F 起始地址 线圈数量 字节数 数据区。10的请求帧类似从站地址 10 起始地址 寄存器数量 字节数 数据区。这就是为什么很多PLC通讯指令会让你填一个“长度”或者“数量”参数因为报文的字节数会随数据量变化。批量写在什么场景用最常见的就是触摸屏和PLC之间的数据交换。比如一次性把10台变频器的给定频率同时下发或者把配方数据一次性写入PLC。还有一个很实用的场景是批量复位把几十个故障线圈一次性复位比一个个发05快得多通讯压力也小。批量写有个注意事项两个连续寄存器最好保持写入的原子性。Modbus RTU本身没有事务机制如果PLC正在读你这20个字你这边也在写这20个字可能出现读到一半是旧值、另一半是新值的现象。解决方法是把这些数据集中在PLC的一块缓冲区内先整体计算好再一次性通过10号功能码下发别让从站那边拼凑数据。3. 地址映射和字节序这两个坑3.1 协议地址和PLC地址之间的偏移换算这是现场最容易晕的地方。Modbus的协议地址是从0开始的但绝大多数设备手册里写的是“40001”这种显示地址或者“4x0001”这种PLC风格地址。它们之间差一个1。具体来说保持寄存器区的显示地址40001对应的协议地址是000040002对应0001依此类推。所以当你看到仪表参数表写“地址40003读取量程上限”你填到PLC指令里的起始地址应该是0002。反过来你从调试软件读到寄存器0的数据对应到设备手册里是40001不是没有地址。很多新手在这里摔跤就是因为把手册里的40001直接填到程序里当协议地址结果读回来的数据全对不上。我的习惯是涉及Modbus地址永远先问一句“这是协议地址还是显示地址”如果没有说明默认按两者相差1处理。3.2 IEEE 754浮点的字节序问题比地址偏移更头疼的是字节序尤其是读32位浮点数的时候。Modbus的单个寄存器16位内部字节顺序是大端模式也就是高字节在前、低字节在后。这在单个字的数据下没问题。一旦涉及32位数据比如一个16位精度的浮点数占用两个寄存器问题就来了这两个寄存器谁在前谁在后每个寄存器内部高低字节要不要交换不同厂商的处理方式千奇百怪。以一个压力值234.56为例它的IEEE 754单精度浮点十六进制表示是0x436AD1EC。这时可能出现四种存储顺序寄存器N0x436A、寄存器N10xD1EC正常顺序也可能是寄存器N0xD1EC、寄存器N10x436A字序反了还有可能每个寄存器内部字节颠倒变成0xA643和0xECD1。如果PLC程序里没有做对应的重新组合读出来的数值就是个天文数字。还有更隐蔽的情况有的仪表让你在参数里选“ABCD”“CDAB”“BADC”“DCBA”四种字节序。这四种对应了上述排列的不同组合。处理这类问题的标准套路是先用调试软件看寄存器原始值写成十六进制再根据正确数值反推排列方式最后在PLC程序里用“字交换”或“字节交换”指令把顺序调对。3.3 现场快速判断字节序的方法给你一个可以直接抄作业的判断流程。第一步用MODBUS POLL这类调试软件读回目标寄存器的原始十六进制值比如寄存器0读到0x436A寄存器1读到0xD1EC。第二步用浮点数在线转换工具把这个原始值组合成正常的浮点格式看看如果组合出234.56说明字序是对的。第三步如果数值不对把两个字反着组合试试或者把每个字的字节序对调试试直到得到合理量程范围内的数值。在PLC程序里如果字序反了用“MOV”和“SWAP”指令做字交换就行如果字节序反了用字节交换指令。很多PLC厂商的通讯库函数直接提供了“四字节浮点”读取功能你只需要在配置里选对字节序模式。用汇川或信捷PLC的朋友可以直接在Modbus数据块配置界面里看到“双字大小端”的选项先看懂默认值再根据调试结果调整比自己在梯形图里拼数据省心得多。4. 实战PLC通过Modbus读取压力变送器4.1 硬件准备与接线我以一个水泵房项目为例现场有一台带RS485通讯的压力变送器量程0到1.6MPa支持Modbus RTU协议从站地址设成1波特率96008数据位、1停止位、无校验。PLC选用支持Modbus RTU主站的国产PLC通过通讯板上的RS485口与变送器相连。接线是第一步坑最多的环节。RS485是半双工差分信号A和B两根线不能接反。我见过太多“通讯时好时坏”的故障最后查下来都是A、B接反或者屏蔽层没接地。变送器上一般标有A和B也有的标D和D-对应关系是A对D、B对D-接好后如果通讯失败先不要改程序直接把A、B对调再试。尽量用屏蔽双绞线屏蔽层单端接地线缆长度超过50米时在末端并接一个120欧终端电阻这个在很多现场能有效减少信号反射带来的偶发通讯错误。4.2 先用调试软件验证从站正式写PLC程序之前先在电脑上用USB转RS485转换器连接变送器打开调试软件新建一条RTU连接设置好串口号、波特率、数据格式然后发送一条读取请求。用03功能码读取从站地址1、起始地址0、数量2个寄存器。如果变送器正常调试软件会返回两个字比如0x436A 0xD1EC。这时就可以判断这是一个32位浮点数实际压力值234.56kPa。如果返回的是0x0000 0x0000说明设备可能还没上电或者地址不对如果返回异常码说明功能码或地址不对。这一步的意义是把设备期望的通讯参数和PLC程序要做的数据处理先验证清楚避免PLC写完了再回头排障。调试通过后记录下几个关键参数从站地址1、功能码03、起始地址0、数量2、字序模式ABCD。这些参数直接决定PLC程序里怎么填。4.3 PLC程序实现以某国产PLC的Modbus主站指令为例思路是通用的。先把串口初始化参数配置好波特率9600、无校验、1停止位。然后执行一条读保持寄存器指令从站号填1功能码填03起始地址填0数据个数填2把读回的数据放到指定寄存器区D0和D1。// 伪代码示意具体指令名以所用PLC手册为准 // 从站号 1 // 功能码 03读保持寄存器 // 协议起始地址 0 // 读取数量 2 // 数据存放区 D0, D1 // 完成标志 M0错误标志 M1 IF M0 THEN // 通讯完成后处理32位浮点数 // 默认读回来的D00x436A, D10xD1EC // 组合成浮点数存入浮点寄存器比如 R10 // 如果PLC不支持直接组合则先做字交换再MOV END_IF不同品牌的指令细节有差异。西门子S7-1200用MB_MASTER指令需要定义数据指针信捷XC系列用MODBUS/COLM指令三菱FX3U用ADPRW指令。但核心参数都是一样的从站号、功能码、地址、数据长度、存放地址。这部分只要把调试软件里验证过的参数一对一填进去成功率就很高。最后把组合出来的浮点值做一个量程转换显示到触摸屏。比如变送器输出的是kPa触摸屏上直接显示MPa数值只需除以1000。这个转换最好放到HMI侧变量里做不要在PLC里反复做浮点运算占扫描周期。4.4 触摸屏变量绑定如果用的是触摸屏直接连接变送器不经过PLC那更好办。以常见组态软件为例新建设备时选“Modbus RTU”填从站地址和通讯参数然后在变量表里新建两个16位变量指向寄存器0和1再新建一个32位浮点变量数据源选择这两个16位变量的组合选择正确的字序。这样就能在画面上直接显示压力值。我建议即使有PLC也可以让触摸屏分担一部分数据采集工作比如直接从触摸屏读电表数据用画面脚本做计算减少PLC通讯指令占用的扫描时间。不过要注意主从冲突问题同一个RS485总线上同一时刻只能有一个主站发起请求不能让PLC和触摸屏同时去当主站轮询同一批从站否则总线冲突通讯会乱套。5. 现场排障实录遇到的几个典型问题5.1 数据能通但数值不对这是排在第一位的经典问题。现象是通讯正常读回来的值也是变化的但数值明显不符合物理意义要么是天文数字要么是负数要么是乱跳。排查思路按顺序来第一确认寄存器地址对不对很多设备手册会把数据地址和编号搞混差一个1就会读错位置第二确认数量对不对如果设备是两个寄存器存一个32位数据而你只读了一个寄存器后半段数据丢失数值自然不对第三确认字节序对不对用调试软件看原始十六进制值然后按前文说的四种排列方式试验。我至少遇到三成以上的“数值不对”问题最后都指向字节序所以先怀疑字节序再怀疑地址。5.2 主机与从机单独测都正常连起来就不行这几乎是搜索热词里的一个标准场景单独用调试软件测试从站通讯正常单独用PLC测试从站也正常但把PLC和从站接到同一条485总线上通讯马上异常。这个现象在工程里非常典型。原因主要有三个。第一是收发冲突调试软件和PLC主站同时挂在总线上尝试发请求虽然平时不会同时发但两边时序不对时就会出现碰撞。规范做法是总线上只保留一个主站。第二是终端电阻和偏置问题从站单独测试时短线路没感觉接上PLC后线缆变长反射信号干扰变大需要加终端电阻必要时在A、B间接偏置电阻让空闲态电平稳定。第三是地址冲突如果总线上挂了多个从站地址没设独立就会出现有时正常、有时报错的假象。遇到这个情况我的做法是先把PLC和电脑上的调试软件断开只留PLC和从站看是否正常然后在总线两端加120欧终端电阻最后逐个排查从站地址。多试几轮基本能定位。5.3 超时和通讯中断现场最常见的表现是运行一段时间后通讯中断重新上电又好然后过一阵又断。这类问题集中在三个方向布线干扰、波特率不匹配、从站看门狗超时。布线干扰主要查屏蔽层是否单端接地、动力线是否与通讯线同槽走线、通讯线长度是否超标。波特率不匹配会在系统上电初期就报错但有时是设备端设置被人误改查起来要细心。从站看门狗超时隐蔽一些某些仪表为了保证通讯安全会设置“通讯超时自动进入安全状态”如果主站轮询周期太长从站认为通讯丢了就自动断开。解决方法是缩短轮询间隔或者在PLC程序里判断通讯失败时主动重新建立通讯。5.4 写操作失败或写错位置写寄存器失败比读寄存器失败更容易排查重点是看返回的异常码。比如正常响应返回功能码本身异常响应返回功能码加0x80后面跟一个异常码。0x01是非法功能码0x02是非法数据地址0x03是非法数据值。看到异常码再翻协议说明能少走很多弯路。写错位置则完全是人的问题多半是地址偏移没搞清楚。把手册上的4xxxx地址当协议地址填结果写到了别的寄存器里。另外写寄存器的时候还要注意数据格式有的指令需要先做BCD转换有的需要高低字交换先确认再写入。5.5 问题速查表现象可能原因排查顺序完全不通接线A/B接反、从站地址不对、波特率/校验位不一致1. 量电压 2. 换A/B 3. 核对参数能通但数值不对地址偏移、数量错误、字节序不对、量纲换算错误1. 看原始值 2. 换字节序 3. 检查地址单独测正常组网不行多主站冲突、终端电阻缺失、地址冲突1. 只留一个主站 2. 加终端电阻 3. 查地址运行一段时间中断屏蔽接地不良、动力线干扰、从站看门狗1. 查布线 2. 加滤波 3. 缩短轮询周期写失败功能码选错、地址越界、写入值非法1. 看异常码 2. 查手册 3. 核对数值范围6. 最后说说我的几个习惯文章写到最后分享几个自己这几年沉淀下来的小习惯。第一个习惯是无论多简单的通讯先在上位机用调试软件跑通再写PLC程序。虽然多一道工序但能把通讯问题和程序问题分开排障时间直接砍半。第二个习惯是每次项目都把设备寄存器表截图存档整理成一份Excel“点表”标注好协议地址、显示地址、数据格式、缩放系数调试时随手翻不用反复问厂家。第三个习惯是在PLC程序里留通讯状态位和错误码寄存器哪怕现场只是接一个压力表也把通讯成功标志做到触摸屏报警画面里这样设备出问题时操作工能第一时间告诉你“通讯断了”而不是让你漫无目的地查程序。Modbus这个协议上手容易但真正做到稳定、可维护、可排查需要积累不少现场经验。希望这篇经验帖能帮你少踩一些坑。如果你在实际项目中遇到其他奇怪的通讯故障也欢迎按文章里的排查思路试着走一遍多数时候问题的答案就在报文里。
返回列表