news 2026/9/27 11:43:52

欧姆龙PLC通信协议精讲:FINS、Host Link与MODBUS-RTU踩坑实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
欧姆龙PLC通信协议精讲:FINS、Host Link与MODBUS-RTU踩坑实战

搞工控的兄弟应该都有同感:欧姆龙PLC本身不难,梯形图逻辑也直白,真正让人血压飙升的,永远是通信。我刚接触欧姆龙那会儿,光“通信协议”这四个字就折磨了我好几个通宵。FINS、Host Link、MODBUS-RTU,还有一堆端口号、节点号、网号、帧格式,单看每个词都认识,组合在一起就完全不知道从哪下手。更气人的是,很多坑不是手册没写,而是手册写了,但我没看懂它在说什么。

这篇东西就是我这些年跟欧姆龙PLC通信协议死磕之后沉淀下来的东西。把我踩过的坑、绕过的弯、最后搞明白的细节全部摊开讲。不管你是做上位机开发、触摸屏通讯、还是接仪表变频器,只要你手里有欧姆龙PLC,这篇文章应该能帮你省下不少白头发。

1. 先分清欧姆龙的三种通信协议,别一上来就抓瞎

1.1 FINS、Host Link、MODBUS-RTU分别是什么

欧姆龙PLC有三种我体感“日常使用率最高”的通信协议,容易搞混,但本质完全不同。

第一个是FINS,全称Factory Interface Network Service,这是欧姆龙的“亲儿子”协议。它既可以跑在串口上,也可以跑在以太网上,专门用于欧姆龙设备之间、或者上位机与欧姆龙PLC之间的通信。只要你的画面里有“FINS UDP”、“FINS TCP”这类字眼,就是在说这个协议。它的特点是报文结构统一,从C系列到NJ/NX系列基本一脉相承,学会一次,换型号也不至于推翻重来。

第二个是Host Link,也叫上位机链接协议。这是欧姆龙老牌串口协议,走RS-232C或RS-422/485,用ASCII码文本帧通信。上位机发一条类似“@00RD0000010002”的命令,PLC返回一串ASCII文本,人眼直接能看,甚至可以用串口调试助手手搓命令来调试。缺点是功能相对基础,速度也一般,但在工业现场稳定性极佳,早期很多触摸屏、组态软件跟欧姆龙PLC通信都是走它。

第三个是MODBUS-RTU,这不是欧姆龙发明的,但欧姆龙PLC提供了非常成熟的支持。尤其当你需要跟第三方设备通信——温控表、变频器、电量表、流量计——MODBUS-RTU基本是绕不开的。欧姆龙PLC可以当主站主动去轮询从站,也可以当从站被别人读。很多人不知道欧姆龙CP1H、CJ系列都内置了MODBUS-RTU简易主站功能,不用自己一条条拼报文,做工程非常省事。

一段话总结我的理解:FINS是欧姆龙设备之间的“官方语言”,Host Link是串口时代的“通用语言”,MODBUS-RTU是连接第三方设备的“国际普通话”。搞清楚自己当前场景该用哪个,比急着看报文格式重要得多。

1.2 选哪种协议,先看你的硬件接口

协议不是你想用哪个就用哪个,硬件接口先要把你限制死。

我用的比较多的是CP1H和CJ2M系列。CP1H自带USB编程口和一个内置RS-232C口,还能扩展串口选件板;CJ系列则是CPU单元上带外设口,要想走以太网,要么选带以太网口的CPU型号,要么加一块以太网单元。你手里的物理接口,直接决定了你能走什么协议。

做个最简单的分类:

  • 只有编程口,想连电脑调试,那默认走的是外设口通信,一般选Host Link或者直接USB。
  • 有RS-232C/RS-485口,要接仪表、变频器,那就用MODBUS-RTU,或者用Host Link做上位机连接。
  • 有以太网口,要走上位机、触摸屏、多台PLC联网,首选FINS UDP/TCP,稳定性和效率都没得说。

有个我早期犯的错误:拿着只有串口的PLC,非要跟组态软件走FINS TCP,结果折腾半天发现硬件根本不支持,最后老老实实加了一块以太网模块。所以做方案设计的时候,第一个问题不是“用什么协议”,而是“我有什么口”。

1.3 通信的本质就是读写内存区,别被各种名词吓住

把协议都理一遍之后会发现,不管FINS、Host Link还是MODBUS,落到最底层,干的事情其实就两件:读PLC的内存区数据,写PLC的内存区数据。

PLC的逻辑程序跑起来之后,温度、压力、产量、设备状态,全部存在内存区里。通信协议做的事情,无非就是按照约定好的格式,把“我要读哪个地址、读多少个字”这条请求发过去,对方再把“这些地址里存着什么”返回来。

所以,每个做通信的人,第一件事就是要拿到一张地址映射表。这张表上写着:CIO区对应哪些I/O点,DM区对应哪些数据字,WR区、HR区又是什么用途。FINS协议里管它们叫“内存区代码”,MODBUS协议里管它们叫“寄存器地址”,Host Link又称呼为“字地址”,名字五花八门,但本质都是同一个东西。

我踩过的第一个大坑就是没把地址映射搞清楚。当时用MODBUS读一台温控表,PLC里设从站地址设成了仪表说明书上的“通道号”,结果怎么读都是0。后来翻遍手册才明白,MODBUS从站地址和仪表内部的参数地址完全是两套编码体系,中间差了一个“左移一位”的转换关系。从那以后我养成了一个习惯:任何协议调试,先把双方的地址映射表打印出来贴在屏幕上,对照着填,不要凭记忆。

2. FINS协议拆解:那些年让我懵圈的报文格式

2.1 一个最简单的FINS读命令,逐字节拆给你看

FINS协议用起来不算难,难点在于理解报文的每一段到底在表达什么。我拿一个最经典的例子来说,上位机读PLC的D100开始的2个字。

一条不含传输层头的FINS命令大概是这样的:

80 00 02 00 01 00 10 00 00 01 01 02 00 64 00 02

别被这一串十六进制吓到,拆开看其实很清楚:

  • 80:ICF,控制标志位,表示这是一个命令帧,应答帧是C0开头。
  • 00:RSV,保留字节,固定为0。
  • 02:GCT,网关计数,一般直连就是2。
  • 00 01 00:DNA是目标网络号,DA1是目标节点号,DA2是目标单元号。这块非常容易错,后面细说。
  • 10 00 00:SA1是源节点号,SA2是源单元号,SID是服务ID,随意设一个就行,用来匹配应答。

后面这段才是正文:

  • 01 01:FINS命令码,0101表示“内存区读取”,0102表示“内存区写入”。
  • 02:内存区代码,02表示DM区,82表示写DM区。
  • 00 64:起始地址,十六进制的0064就是十进制的100,也就是D100。这里注意,地址是用十六进制表示的,不是BCD,也不是ASCII文本。
  • 00 02:读取的字数,这里读2个字,也就是D100和D101。

如果正常,PLC会回一条FINS应答,大概长这样:

C0 00 02 00 01 00 10 00 00 01 01 00 00

从第3个字节开始是返回的数据内容。看明白了这个最简单的读命令,后面什么批量读、写数据、读时钟、控制运行停止,套路都是一样的。

2.2 节点号、网号、端口号:连不上的头号元凶

如果用FINS连接失败,十有八九问题出在节点号、网络号、端口号这三兄弟身上。

我早年用CX-Programmer连CJ2M,填IP地址填的是192.168.0.5,端口号默认9600,理论上没问题,可就是连不上。检查了一圈才发现,以太网单元的“FINS节点号”不是1,而是被拨码开关设成了5。上位机这边设置的“目标节点号”和PLC里实际的节点号对不上,FINS协议直接就丢弃了这条报文。

这里有个经验供参考:欧姆龙PLC的FINS节点号,一般有几种设置方式。有的型号在硬件上有旋转拨码开关,直接拨到几就是几;有的型号在PLC设置里可以手动指定;还有的型号默认和IP地址的最后一位有关联。调试前千万别偷懒,去PLC的以太网设置界面里看一眼实际值,比在组态软件里瞎试快得多。

端口号也有讲究。FINS标准端口是9600,UDP和TCP都用这个。但有些上位机软件默认填的是其它值,两边对不上,数据包发过去了也石沉大海。另外一个常见坑是TCP和UDP的选择:TCP有握手保证,传输稳定;UDP无连接,速度快。如果走FINS TCP,上位机要主动建立连接;如果走UDP,上位机不用握手,发命令直接等应答就行,但前提是两边的节点号、网络号已经对上。

2.3 字节序和ASCII的恩怨情仇

这个坑我栽了不止一次,而且每次都栽得不冤,因为只要没转过弯,数据读回来就是错乱的。

先说字节序。FINS报文里,16位数据的表示方式是高字节在前、低字节在后。比如你要写一个十六进制的值0x1234到D100,报文里的数据部分就是12 34,不是34 12。大部分上位机开发都遵循这个大端序规则,但总有些第三方设备、有些自己写脚本的兄弟,习惯性地按照小端序处理,结果读回来的数据完全对不上。

再说ASCII问题。用串口助手调试FINS时,千万别把十六进制数当成ASCII码去发。我就干过这种事:在串口助手的发送框里输了个“0101”,然后选了“按ASCII发送”,实际上发送的是字符‘0’、‘1’、‘0’、‘1’对应的ASCII码0x30 0x31 0x30 0x31,PLC收到之后直接判定非法命令。正确的做法是选“按Hex发送”,才代表真正的十六进制报文。

还有一个特别迷惑的地方:Host Link协议因为本身是ASCII文本协议,里面带的数据又是用ASCII表示的十六进制,比如D100在Host Link里写成“0100”,来回来去全是文本,反而没有字节序的烦恼。这也是为什么老工程师调试串口时特别喜欢Host Link,因为肉眼可读,出错了拿串口助手一看就知道问题在哪。

3. 实操:用一台CP1H通过485读取温控表,从零讲到通

3.1 硬件连接与PLC通信参数设置

纸上谈兵到此为止,说一个我实际做过的一整套方案:用CP1H的RS-232C口,通过一个RS-232转RS-485的转换器,接一台温控表,PLC做MODBUS-RTU主站,轮询读取温度值。这套配置在工业现场非常常见,而且很有代表性。

接线方面,RS-485是两线制的,A、B两根线不能接反,接反了最直观的现象就是完全无响应。转换器这边要供电,有些无源的转RS-485转换器不稳定,尤其在环境干扰大的车间里,数据帧经常损坏。我后来一律买带隔离的、有源供电的转换器,虽然贵个几十块钱,但省下排查的时间远超这个差价。

PLC侧参数设置,重点说几个位置:

  • 在CX-P(CX-Programmer)的PLC设置里,找到“串口1”,通信模式选择“MODBUS-RTU主站”。
  • 波特率建议先从9600开始调,确认通了再往上提。现场很多旧仪表最高只支持9600。
  • 数据格式一般选8位数据位、无校验或偶校验、1位停止位。第三方仪表手册会写清楚,照抄就是。
  • 发送等待时间可以适当调大一些,有些仪表响应慢,PLC发完命令等200~300ms是常有的事。

设置改完,一定要“下载到PLC”,并且让PLC重新上电,设置才会真正生效。这个细节我吃了大亏。有一次改完设置直接在软件里监视,以为生效了,结果PLC里跑的通信参数还是旧的,浪费了一个多小时。

3.2 报文从哪里来:地址映射、功能码与CRC校验

在MODBUS-RTU这条链路里,最需要花心思的就是“怎么把仪表的参数地址翻译成MODBUS报文”。

拿温控表举例。假设表上有一组寄存器,02地址是当前温度PV(假设的例子里这样),功能码是03(读保持寄存器),从站地址是1。要读这个参数,主站发送的报文大概是:

01 03 00 02 00 01 25 CA

拆开看:01是从站地址,03是功能码读保持寄存器,00 02是寄存器起始地址,00 01是读1个寄存器,25 CA是CRC校验。这串报文最终要以十六进制的字节形式发送,不是ASCII文本。

这里最容易翻车的,就是仪表手册上标注的地址和MODBUS报文里的寄存器地址之间的关系。我碰到的国产仪表,有的手册直接写“MODBUS地址=0201H”,但实际往报文里填的时候需要左移一位;有的则是“寄存器号3200”,需要先转成十六进制再减1。每个厂家都不太一样,拿到手册第一件事,不是看功能说明,而是先把通讯协议那章找到,确认地址换算规则。

CRC校验是MODBUS-RTU的另一个阴间细节。它的计算是对报文中从站地址到数据结束所有字节做CRC16,多项式标准是0xA001,结果低字节在前。举个例子,上面那条报文的CRC部分25 CA,计算出来的原始值是CA 25,但发送顺序是低字节在前,所以要写成25 CA。如果自己写上位机程序,拼报文时稍微手一抖,顺序写反了,仪表那边直接不做任何响应。

3.3 调试顺序怎么排,才能不被坑

调试MODBUS通信,一定不要上来就写一堆PLC逻辑。我推荐一套效率极高的顺序:

第一步,用串口助手先验证仪表的地址和CRC。把USB转RS-485接到仪表上,手动发送上面那条报文,如果能收到仪表回复,说明从站侧没问题。这一步能帮你把协议问题从接线问题中剥离出来。

第二步,PLC不写程序,先在PLC设置里确认模式,然后用“PLC监视”看串口通信监视区的状态。欧姆龙PLC自带有通信状态字,能看到本次通信是否成功,比如D0里会有错误代码,方便定位是不是参数设置问题。

第三步,再写程序。用欧姆龙官方提供的MODBUS功能块,或者直接用TXD/RXD指令拼接报文,把串口助手验证过的报文原封不动发送出去。只要能收到应答,回读的数据再经过高低字节组合,就能用了。

这套顺序看起来啰嗦,但能帮你把问题范围一刀切成小的。直接跳过第一步去调PLC,遇到问题就分不清是仪表没回、还是PLC发出的报文有问题,只能在原地打转。

4. 常见问题与排查:一条一条对着查

4.1 高频问题速查表

我把这些年遇到最多的通信问题整理成一张表,遇到问题先对着查,比上网搜半天强。

现象可能原因排查思路
PLC和上位机完全没通信IP地址不在同一网段先ping一下,能通再查协议设置
FINS TCP可以连上,但UDP不通防火墙拦截UDP,或节点号不对检查防火墙规则,核对节点号映射
MODBUS仪表偶尔正常偶尔超时485线太长、干扰大、接地不良换带屏蔽双绞线,终端电阻匹配,降低波特率
MODBUS能收到仪表返回,但数据明显不对字节序高低位对调,或寄存器地址换算错了用串口助手抓原始报文,对照手册逐字节分析
Host Link发命令,PLC不响应FCS校验算错,或者命令字符没有转为ASCII用官方手册的FCS计算例子核对
PLC从站模式别人读不了单元号(从站地址)设置错误在PLC设置里把单元号改成从站地址一致
通信偶发失败,复位通信又恢复节点号冲突,两台设备用了同一个节点号把所有设备的FINS节点号改成唯一值
CP1H串口设置改了没反应没有下载到PLC并断电重启把PLC设置下载到PLC,重新上电

这张表覆盖了我遇到的大部分问题场景。其中最有迷惑性的就是“偶发失败”,因为不少工程师会怀疑自己的报文写得不对,反复改程序,实际却是节点号冲突这种低级问题。

4.2 两个印象最深的坑:节点号冲突和字节序错乱

先讲节点号冲突。我第一次给设备加触摸屏时,继电器柜里一台CP1H既要和触摸屏通信,又要和上位机通信。触摸屏走的是FINS TCP,上位机走的也是FINS TCP,两台设备一开机都没问题,运行一会儿就开始卡顿,偶尔还掉线。

查了很久,最后发现触摸屏和上位机软件都把自己设成了同一个FINS节点号。FINS协议规定,同一网络里节点号在通信时是唯一的。两个设备抢同一个节点号,报文就会互相干扰。解决办法很简单,给触摸屏和上位机各分配一个不同的节点号。这个坑我在其他项目里也遇过,建议在做任何带多个上位设备的系统时,先在方案里画一张“节点号分配表”,从1到254排下去,谁都不能重复。

第二个典型坑是字节序错乱。有一次上位机读出来的温度值是“9640”,实际显示却是12000多,懵了好久才反应过来,FINS报文里读回来的原始数据是25 80,上位机按低字节在前拼成了0x8025,而实际上欧姆龙PLC返回的是高字节在前,应当是0x2580。这个坑其实不难避,关键是新人容易忽视“数据在协议层怎么排”和“数据在应用程序层怎么解释”是两码事。

4.3 用最简单的报文尽快验证通路

很多兄弟在调试通信时,一上来就写几十条指令、读几百个字,结果报错都不知道错在哪。我的习惯是:一切从最简单的单点读写开始。

比如测试FINS通路,就先读一个D区字,比如读D0的1个字。报文短,容易检查,一眼就能看出是否通。通了之后,再扩大到批量读。测试MODBUS,也先读一个寄存器的1个数据,比如仪表PV值,通了之后再去读其它参数。

这个顺序看着简单,但背后是一个排查逻辑:每一次只改变一个变量,问题就永远是可定位的。如果第一次就去读一堆参数,报文长、干扰多、寄存器地址错一个都查不出来,那就成了大海捞针。

另外强烈建议调试的时候打开串口调试助手或者Wireshark看一下到底是什么数据在线路上跑。上位机和PLC的连接,抓一下网口包,能看到FINS帧到底有没有发出来、发的什么内容。串口通信就挂个USB转485监听工具,把PLC和仪表的通讯数据抓下来。很多时候你想象中“我发了这条报文”和实际上“你发了那条报文”差的还挺远。

最后,几个过来人的实在话

说几句掏心窝子的经验。我做欧姆龙通信这几年,最深的一个体会是:不管是FINS、Host Link还是MODBUS,调不通的大多数原因都不是协议高深,而是低级细节没对上。地址映射表错一位、节点号冲突、CRC高低字节颠倒、串口参数不一致——这些坑没有什么技术含量,但每一个都可能让你加班到深夜。

所以我养成了一个习惯,在每个通信项目开工之前,先花半小时做三件事:把双方地址映射表打印出来,把节点号和端口号规划表写清楚,把一条最简单的报文用串口助手提前验证通。这三件事做完,后面全程基本就是按部就班地实现了。

另一个建议是做上位机开发的朋友,手里的抓包工具一定要顺手。Wireshark抓FINS TCP/UDP非常直观,串口调试助手的Hex发送功能更是必备。调试时不要把时间花在“猜”上,直接把报文抓出来对照手册,问题大概率一眼就能揪出来。

最后再送个锦囊:欧姆龙不同系列的协议细节有时会有小差异,比如CP1H和NJ在FINS区域的编码上略有不同。看手册时重点看命令码和内存区代码那几页,用之前先在笔记里写下一个完整的报文例子,后面照着改就行。通信这件事,一旦通了第一条,后面就都顺了。希望这篇踩坑记录,能让你少走几段弯路。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/27 11:40:46

嵌入式开发工具链详解:固件烧录、仿真验证与调试实战

做嵌入式软件开发这行,十有八九的时间其实不是花在“写代码”本身上。你可以在一个项目周期里经历无数轮这样的循环:改一行打印信息,按一下编译,然后连上调试器烧录、按复位、盯着串口终端看输出,有时候还得开着仿真软…

作者头像 李华
网站建设 2026/9/27 11:38:43

深度拆解 HermesAgent(四):多终端后端与 Gateway 网关配置实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华