ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

三菱Q系列PLC串行通讯模块QJ71C24N/R2配置与收发避坑

三菱Q系列PLC串行通讯模块QJ71C24N/R2配置与收发避坑

简介:这份文档聚焦三菱Q系列PLC串行通讯模块QJ71C24N与QJ71C24N-R2的RS232通讯方法,面向工业自动化现场调试人员、PLC编程初学者及需要完成设备间数据交换的工程师,重点解决两类模块在无顺序协议下的参数配置与收发程序设计问题。压缩包内仅1个docx文件,约197KB,以图文结合方式呈现模块通道差异、通讯线缆示意、Works2开关设置界面以及发送与接收程序示例,便于对照实际工程查阅。该文档已有3907人学习,在三菱串行通讯调试场景中具备较高参考价值。读者可了解QJ71C24N双通道(RS-232与RS-422/485)与QJ71C24N-R2双RS-232通道的选型差异,掌握波特率、校验方式、数据位、停止位等参数的一致性原则,并从接收缓冲区定义、发送帧打包、CRC校验、超时处理等环节获得可复用的编程与排错思路。

1. 产线上两块串行通讯模块,接错一个口就要查半天

上个月帮人看一条包装线,主控柜里 Q系列PLC 机架上插着两块串行通讯模块,一块 QJ71C24N、一块 QJ71C24N-R2,对面挂着三台只有 9 针口的称重仪和一把老条码枪。现场的人说“通讯时好时坏”,我到现场先做的事不是看程序,而是核对这两个模块的通道到底接到了哪里——QJ71C24N 的两个通道里,只有 CH1 是 RS-232,CH2 是 RS-422/485;而 QJ71C24N-R2 是两个通道全 RS-232。型号差一个后缀,可用的口就少一个,程序里写的通道号也跟着全错。

这套东西干的事其实很朴素:让 Q 系列 PLC 用串口跟第三方设备换数据,走无顺序协议时基本就是“我发一串字节、你回一串字节”,没有主站从站那一套握手。适合谁?做产线改造、设备联网、老设备数据采集的电气和自动化开发,尤其是手上已经有 Q 系列机架、又不想为了几个串口再塞一块通讯网关的人。

2. 硬件与协议选型:通道拓扑、9 针线序和三种协议的分界

动手写程序之前,选型和接线必须先落定,这两件事一旦定错,后面在软件里怎么调都是白费功夫。这一章把模块能力、物理链路、协议三件事按现场顺序捋一遍,都是能直接照抄进图纸和配置表的内容。

2.1 通道拓扑:QJ71C24N 与 QJ71C24N-R2 的差别落在哪

两个模块外观几乎一样,都占用一个智能功能模块槽位,都能开两个通道,但通道的电气规格不同。QJ71C24N 是“一混一单”:CH1 走 RS-232,CH2 走 RS-422/485;QJ71C24N-R2 是“双 232”:CH1、CH2 都是 RS-232。这个差别决定了两件事。

第一件是接什么设备。RS-232 是点对点、距离短,几米到十几米,适合一台仪表、一台扫码器、一台工控机这种一对一场景。RS-422/485 是差分,能拉几百米、能挂多台从站,适合一条线串好几台设备。所以如果现场是“三台仪表分别接三个串口”,选 R2 最省事,两个通道各自独立组帧、独立收发;如果是“一台设备近距离对接 + 一条 485 总线挂若干从站”,那就是 QJ71C24N 的活。

第二件是程序里的通道号。无顺序协议下,每个通道有自己独立的开关设置、独立的缓冲区、独立的发送请求和接收完成信号。CH1 配好了不代表 CH2 能用,两个通道的开关设置必须分别进界面设一遍。我见过最典型的翻车是:现场把线接在 QJ71C24N 的 CH2 上,程序里通篇按 CH1 的缓冲区地址收发,模块本身没有任何报错,就是收不到数。

一个务实的习惯是先在图纸上给每个物理口编号,写成“模块位号-通道号-对接设备-协议”四列,程序里的变量命名直接沿用这个编号。后面调试时不用回翻图纸,看变量名就知道在说哪个口。

2.2 交叉线缆与信号地:9 针接头最容易忽略的两根线

QJ71C24N 系列用的都是模块自带的 9 针 D-sub 母座,RS-232 三线制(TXD、RXD、GND)就能通。要点只有一个:交叉。PLC 侧的发(TXD)要接到对方侧的收(RXD),PLC 侧的收(RXD)接对方侧的发(TXD),GND 对 GND。

现场遇到最多的是两种线:一种是设备手册写“直连线”,其实对方已经内部交叉过了,你再交叉一次就变成收发对接;另一种是只接了两根信号线、GND 没接,短距离、同电源时勉强能通,一旦两台设备取自不同配电回路,共模电压差就会让通讯随机丢字节,表现为“白天正常、晚上电机一启动就乱码”。这种问题换程序、调波特率都没用。

我的做法是:所有 RS-232 线一律三线接全,屏蔽层在 PLC 侧单端接地,线长超过 10 米就改 485。另外 9 针口上的 RTS/CTS 在无顺序协议的三线模式下不参与流控,不要为了让“看起来更完整”把它们短接成自环,某些设备会把 RTS 当成硬件流控使能,反而卡住发送。

提示:线做好之后先在断电状态下用万用表量通断,别指望上电后用程序去验证线序,那会把“线接错”和“参数设错”两个故障混在一起。

2.3 三种协议怎么选:MC、无顺序、双向协议的分界

这三块串行通讯模块支持的协议里,MC 协议是给自家生态用的,PLC 当从站,上位机按固定报文格式读写软元件,报文结构由协议规范定死,你不需要自己拼帧,但也意味着对方必须说这套“方言”。双向协议更轻,模块把收到的字符直接映射到软元件区,发也是从软元件区直接往外吐,中间不做帧解析,适合对方设备报文格式已经固定死、你只想搬字节的场景。

无顺序协议是这三者里最通用的:你自己决定发什么字节、什么时候发、收到多少字节算一帧,模块只负责按开关设置里的帧格式(是否加 STX/ETX、是否加和校验码、结束符用 CR 还是 CR+LF)帮你拼装和拆解。跟第三方仪表、扫码枪、称重设备打交道,绝大多数时候走这条。

选型上的判断顺序我一般是这样:对方设备手册里明确写“支持 MC 协议”或者对方是自家触摸屏/上位机组态,优先 MC;对方报文格式固定、只需要单向搬运,用双向协议最省代码;对方要求“发一条查询指令、回一条应答”,那必然是无顺序协议,因为你必须控制发送时机和判帧逻辑。

无顺序协议里还有个容易被忽略的点:它是“无顺序”的,意思是协议本身不保证多帧之间的先后和完整性,模块不会帮你做序列号排序,也不会自动重发。可靠性得靠应用层自己补——加和校验、加结束符、加超时重发,这三样在后面的程序章节里会具体落。

3. 参数配置落地:Works2 里从添加模块到通道 1 的每一格

配置这一章按实际操作顺序写:先把模块加进工程,再把型号对准,最后逐项设开关。三菱体系的智能功能模块配置有个特点——所有参数都存在工程里,下载时必须连模块参数一起下载,只下程序不下参数,模块仍然按出厂默认跑,这是新手最容易卡住的地方。

3.1 添加模块:型号选错后面全白搭

在 Works2 里对着机架上的智能功能模块位置右键,选“添加新模块”,模块类型挑“串行通信”,型号从列表里选实际插的那一块。这里必须和物理模块严格一致:插的是 QJ71C24N-R2,就不能选 QJ71C24N。原因很实际——两种模块的通道能力不同,选错之后打开开关设置界面,你会发现通道 2 那一页的选项跟物理模块对不上,或者在 R2 上按 485 的参数去配,最后跑起来通道 2 完全没反应。

添加时还要填起始 I/O 号。这个号不是随便填的,它由模块插在机架第几槽决定,前面每占一个槽(含 CPU、电源占用的逻辑位)就往后推。填错的表现很有迷惑性:程序能下载、模块 READY 灯也亮,但所有U0\G缓冲区读写全部打到别的模块或者空地址上,读回来的永远是 0。

添加完成后,在工程树的智能功能模块下面会多出这个模块的节点,展开后能看到“开关设置”“中断设置”等项。通道是分开的两页,CH1 一页、CH2 一页,两边都要设。

工程树结构(添加完成后) PLC 参数 + 机架/模块配置 + 智能功能模块 + QJ71C24N <- 位号 0,起始 I/O 按实际槽位 - 开关设置 <- CH1 / CH2 分开两页 - 中断设置 + QJ71C24N-R2 <- 位号 1 - 开关设置

参数说明上,起始 I/O 号决定程序里U0\Gxxx前面的那个U0;位号只影响工程里的显示顺序,不影响寻址。所以核对时看的是 I/O 号,不是位号。

3.2 开关设置逐项:速率、数据位、校验、停止位

打开通道 1 的开关设置,是一张逐项下拉的表。这些参数必须和对方设备完全一致——不是“兼容就行”,波特率差一档、校验方式差一位,收到的就是成片乱码或者干脆一个字节都没有。

设置项示例值说明
通信速率19200 bps与对方设备一致;长线或多设备总线建议降到 9600
数据位8 位传 ASCII 指令用 8 位;对方只支持 7 位时校验位会占一位
奇偶校验偶校验无校验、奇校验、偶校验三选一,必须和对方一致
停止位1 位少数老仪表固定 2 位
通信方式全双工RS-232 点对点用全双工;485 半双工时选半双工
和校验码有决定模块是否自动计算并附加/校验累加和
帧格式格式 1决定 STX/ETX、结束符的组合方式

配的时候有个隐性约束:数据位和校验位是捆在一起的。对方写“7 位数据、偶校验”,你就不能设 8 位数据加偶校验,因为 7E1 和 8E1 在一帧里的位数不同,接收端会持续判帧错误。同理,停止位 1 和 2 也是逐位对齐的,差一位就整帧错位。

速率不是越高越好。现场 19200 足够跑几十毫秒一条的查询应答,如果线缆质量一般或者跟变频器同槽走线,38400 以上丢字节的概率明显上升。我的默认值是 9600 起步,跑通之后再往上加,加到出现偶发校验错就退回上一档——这比一开始就设很高、然后花两天怀疑程序要省事得多。

3.3 无顺序协议的专属设置:帧格式、结束符与缓冲区分配

无顺序协议下还有几项专门服务于“自定义报文”的设置,它们决定模块帮你做多少事。

帧格式决定模块收发时是否自动加 STX(帧头)、ETX(帧尾)、是否附加和校验码。如果对方设备的协议是“STX + 数据 + ETX + 校验”,那就可以让模块自动组帧,程序里只写数据段;如果对方协议是“STX + 命令字 + 数据 + 和校验 + CR”,模块的自动格式包不住,就得选不自动附加,全部在 PLC 里用指令拼好再写进发送缓冲区。

接收结束条件有两种常见做法:按字节数判(收到固定长度就算一帧)和按结束符判(收到 CR/LF 就算一帧)。变长报文必须用结束符方式,否则你永远不知道一帧什么时候结束;定长报文用字节数方式更稳,因为对方如果漏发结束符,按结束符判会一直等下去。

缓冲区是这一节最需要记住的东西。无顺序协议下,收发数据不是走指令参数,而是走模块内部的缓冲区,用U0\G直接读写。常见分配是(以模块手册的缓冲区分配表为准,这里的地址按首槽、CH1 起始给出):

地址内容
U0\G256CH1 发送数据字节数(注意是字节,不是字数)
U0\G257~U0\G511CH1 发送数据区
U0\G512CH1 接收数据字节数
U0\G513~U0\G767CH1 接收数据区
U0\G768/U0\G769~CH2 发送字节数 / 发送数据区
U0\G1024/U0\G1025~CH2 接收字节数 / 接收数据区

最后一个动作是把参数和程序一起下载。Works2 里“下载”对话框要勾上模块参数,只勾程序的话,模块跑的还是出厂默认值,表现就是“程序没错、参数看着也对、就是通讯不上”。这一条我在现场至少遇到过三次。

4. 收发程序拆解:发送帧拼装、接收缓冲轮询与判帧

程序部分按“发送侧在 QJ71C24N、接收侧在 QJ71C24N-R2”这个方向写,这是原项目里给的组合,实际项目里反过来也一样,因为两侧用的都是同一套缓冲区机制。核心思路就三句话:发送方把字节数写进去、把数据搬进去、置发送请求;接收方盯着接收字节数、有数就搬走、搬完复位。

4.1 发送侧:把 ASCII 指令打包成字节写进缓冲区

假设对方设备要求发一条 8 字节的查询指令,比如STX + 命令 + 参数 + ETX + CR,其中 STX、ETX、CR 由程序拼好(模块不开自动帧格式),那发送逻辑就是先组帧再搬运。

(* QJ71C24N CH1 无顺序协议发送:D200 起存放已拼好的 8 字节 ASCII 帧 *) (* ① 写发送字节数,单位是字节,不是字 *) MOV K8 U0\G256; (* ② 8 字节 = 4 个字,按字块搬进发送缓冲区 *) BMOV D200 U0\G257 K4; (* ③ 置通道 1 发送请求,模块按开关设置把缓冲区内容打到串口上 *) SET Y1;

逐段说明。第一行写的是字节数,这是最常见的错:有人按字数写,8 字节的帧写成 4,模块就只发前 4 个字节,对方永远判不出完整帧,回的报文自然也不对。第二行用BMOV按字搬,一个字两个字节,所以搬 4 个字;如果用MOV一条条搬,注意高低字节顺序,Q 系列是低字节在前,MOV H4241发出的顺序是41 42(即AB),拼 ASCII 帧时按这个顺序排。第三行置位发送请求,Y1的具体点数由模块在机架上的起始 I/O 决定,示例按首槽给出,实际项目里以模块的 I/O 信号表为准。

发送完成之后要复位发送请求,否则下一次发送不会触发。稳妥的写法是等“发送完成”输入信号,再RST Y1,同时把发送成功计数加一。如果对方设备处理慢,两次发送之间要留间隔——模块发完最后一个字节到对方开始回包之间通常有几十毫秒的空档,连续快速重发会把对方的接收缓冲冲掉。

(* 发送完成回执:清请求、计数、置下一次可发标志 *) LD X1 (* 通道 1 发送完成,点数按起始 I/O 核对 *) RST Y1; INC D500; (* 发送成功计数,便于和接收计数对比查丢包 *) SET M10; (* 允许进入下一次查询 *)

这里有个小细节值得说:D500这个发送计数后面会派上大用场。接收侧也有一个接收计数,两个数一对,立刻就能判断是“发不出去”还是“发出去但没回来”,比盯着串口灯猜要快得多。

4.2 接收侧:轮询接收字节数、搬数、复位

接收侧不做中断,用轮询最省心:每个扫描周期看一眼接收字节数寄存器,非零就说明模块收到东西了。

(* QJ71C24N-R2 CH1 无顺序协议接收:轮询接收字节数 *) LD SM400; CMP U0\G512 K0 M100; (* M100=1 表示接收区有数据 *) LD M100; MOV U0\G512 D300; (* D300 = 本次收到的字节数 *) BMOV U0\G513 D301 K10; (* 最多取 20 字节 = 10 个字,按实际帧长调整 *) (* 取完之后必须告知模块可以收下一帧,否则接收区不再更新 *) MOV K0 U0\G512;

这段的关键在于两个“单位”和两个“复位”。U0\G512里是字节数,D300存下来之后做长度判断,比如对方协议固定 12 字节,那就先比D300=12,不等就丢掉。BMOV的 K10 是字数,要按最大帧长往上取整,宁可多搬几个字也别少搬——少搬的后果是判帧时拿到截断的报文,校验必然不过。

最后一行MOV K0 U0\G512是很多人漏掉的:模块的接收区在数据被取走之后不会自己清零,字节数不清零,下一帧的到达判断就永远成立,程序会把同一帧反复处理几百遍,表现是“数据刷屏、全是重复”。更规范的做法是用模块的“接收数据读出请求”信号配合清零,逻辑等价。

4.3 判帧与超时:把“能通”变成“可靠”

收发都通了只算及格。产线上的通讯要的是“错了能发现、卡了能恢复”,这两件事得在应用层补。

判帧放在取数之后:先看长度对不对,再看帧头帧尾对不对,最后算和校验。和校验如果让模块自动加了(开关设置里“和校验码=有”),那就直接比对模块给的校验字节;如果模块不开自动校验,就在 PLC 里自己累加。三段判断任何一段不过,直接丢弃这一帧并计数,不去解析数据,避免把乱码当有效值写进控制逻辑。

(* 简易判帧:长度 + 帧头帧尾 + 累加和 *) LD SM400; AND= D300 K12; (* 长度必须是 12 字节 *) AND= D301 H0230; (* 首字低字节 30H = '0',即 STX 之后的起始字符 *) AND= D306 H0D0A; (* 末尾 CR/LF *) OUT M20; (* M20 = 帧结构合法 *) LD M20; MOV D302 D400; (* 只把合法帧的数据段搬进工艺变量区 *) INC D501; (* 接收有效帧计数 *)

超时是另一半。发送之后启动一个定时器,比如 500 ms,等接收计数变化;超时就重发,连续三次超时就置通讯故障位、报警、把相关工艺变量标成“无效”,不要让上层逻辑拿着上一次的旧值继续算。这条在配方、称重这类场景里特别重要,一个陈旧的重量值可能导致整批料废掉。

(* 发送后 500ms 无回应则重发,三次判故障 *) LD M10; OUT T10 K500; LD T10; AND< D501 D501_OLD; (* 接收计数没变化 = 没收到新帧 *) INC D510; (* 超时次数 *) LD= D510 K3; SET M30; (* 通讯故障位,上位机报警用 *) RST M10;

参数说明:T10的 500 是 100 ms 基准下的值,也就是 50 s?不对——Q 系列默认定时器是 100 ms 基准,K500就是 50 秒,这里要写K5(0.5 秒)或者把基准改成 10 ms。这类基础参数的坑我吃过不止一次,调试时盯着“怎么五秒还不重发”,结果是自己把基准搞混了。保险做法是在程序注释里把基准写清楚。

5. 避坑排查:通讯不上和数据错乱,各查一半

串口调试最耗时间的不是写代码,是分不清故障出在哪一层。我的排查原则是先把“物理层和参数层”和“程序层”切开:前者表现为一个字节都收不到、或者整片乱码;后者表现为能收到但内容不对、帧错位、重复处理。下面五条都是现场反复出现的。

5.1 链路层:收不到、乱码、时通时断

现象一:模块 READY 灯亮,程序照跑,接收字节数永远是 0。原因通常是参数没随程序一起下载,模块还在跑出厂默认(默认速率和校验跟对方对不上),或者起始 I/O 号填错,U0\G512读的根本不是这块模块。解决:重新下载时勾上模块参数;用 Works2 的模块监视确认起始 I/O,再读一次U0\G512,如果读出来是随机值说明地址错了。

现象二:收到的全是乱码,但长度对得上。基本是速率或校验、数据位不一致。解决:把两侧参数列成表逐项比,重点看“7 位数据 + 偶校验”有没有被写成“8 位数据 + 偶校验”、停止位是 1 还是 2。改完记得重新下载参数,改完不下载等于没改。

现象三:白天正常,晚上或电机启动就断。共模干扰。RS-232 只接了 TXD/RXD,GND 没接,或者屏蔽层两端都接了地形成地环路。解决:补上 GND,屏蔽层 PLC 侧单端接地,信号线远离动力线槽;距离超过 10 米直接换 485。

5.2 应用层:半包、重复、帧错位

现象四:同一帧数据被反复处理几百遍,上位机曲线出现平台。接收字节数没复位。模块收到一帧后,U0\G512保持非零,轮询逻辑每个扫描周期都认为“有新数据”。解决:取完数据立刻把字节数寄存器清零,或者用“接收数据读出请求”信号做边沿触发。这条我建议写进模板里,别靠每个项目现场记得。

现象五:偶发校验失败,重发一次就好。多半是帧边界没判干净。上一帧的尾巴残留在接收区,或者对方一次发了两帧、你按一帧的长度取走,剩下半帧留在缓冲区里,下一轮被当成新帧的开头。解决:接收区按最大帧长一次性清干净再取;判帧时严格校验帧头,帧头不对就整段丢弃并重新同步,不要“凑合着从中间解析”。

另外提三个容易忽略的配置项:中断设置里如果开了接收中断,轮询逻辑会和中断抢同一块缓冲区,数据会出现“一半在中断里处理、一半在轮询里处理”的怪象,非必要不开;通道 2 在 QJ71C24N 上是 485,半双工时收发切换需要时间,程序里发完要留至少几个字符时间的间隔;还有模块的看门狗和 CPU 的扫描周期,如果扫描周期被别的逻辑拖到几百毫秒,轮询接收就会漏帧,这种情况要么缩短扫描、要么改成中断接收。

6. 二次开发:把这条链路做成可复用的通讯模板

真正让这套东西变得好用的,不是把某一次通讯调通,而是把它封成模板。我的做法是抽三个功能块:FB_SerialSend、FB_SerialRecv、FB_SerialLink。前两个只管单通道的收发和缓冲区地址,后一个管状态机——空闲、发送中、等待应答、超时重发、故障锁定。

FB_SerialSend的输入只留四个:通道号(决定用U0\G256还是U0\G768)、数据区首地址、字节数、触发位;输出是完成位和错误码。通道号到缓冲区地址的映射用一个常量数组做,CH1映射到 256/257,CH2映射到 768/769,换模块型号时只改这张表,不动逻辑。这一步做完,“接错通道”这类问题在编译期就能暴露一半。

FB_SerialRecv里把“取数—复位—判帧”三步焊死在一个循环里,不再给调用者“忘记复位”的机会。判帧规则做成参数:最小长度、最大长度、帧头字节、是否校验和。现场遇到新设备,只改参数不改代码。

复用点做法收益
通道映射常量数组 + 通道号入参换型号只改表,程序不动
缓冲区复位封在接收块内部杜绝重复处理
判帧规则长度/帧头/校验参数化新设备只改参数
超时重发状态机统一处理各设备行为一致
诊断计数发送数、接收数、校验错数现场十分钟定位

验证方法上,我习惯在模板里留一组诊断寄存器,把发送计数、接收计数、校验错计数、超时计数都放进去,映射到触摸屏的一页。现场一出问题,看这四个数的关系就能定性:发送在涨、接收不涨,查线和对端;接收在涨、校验错也在涨,查参数和干扰;两边都在涨但工艺值不动,查判帧和数据搬移的地址。这比拿笔记本连 PLC 在线监视快得多。

还可以再加一层“回路自测”:把模块的一个通道设成自发自收(外部把 TXD 和 RXD 短接),程序定时发一条固定帧,看能不能原样收回来。这段代码在出厂前跑一遍,能把参数配置、缓冲区地址、复位逻辑全部验证掉,剩下的问题就只可能出在对端设备上。

后来我把这套模板固化成了一个检查清单:上电先跑自测回路,再核对接线表和通道映射表,最后才打开对端设备。从那以后我每次上新项目,都强制走一遍这个顺序,再没出现过“调了两天发现是参数没下载”这种事。希望帮到你。

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进