一、通信帧类型
很多人在学习三菱PLC通信协议时,很头疼的是三菱PLC有很多种通信帧类型,每种通信帧又有ASCII和二进制两种编码格式。
二、PLC配置
三菱PLC与西门子PLC有所不同。
西门子PLC是固定端口102,一个端口支持多个连接。
三菱PLC需要手动添加端口,一个端口只支持一个连接。
因此三菱PLC需要手动配置,这里以三菱R系列为例:
1、在导航中,通过参数>>R08ENCPU>> 模块参数,双击在打开的界面中设置好PLC的IP地址信息,将通信数据代码改成二进制,然后找到【对象设备连接配置设置】:

2、点击设置,可以拖入多个SLMP连接设备,端口号根据自己需求设置,然后反应设置并关闭设置结束,设置完成后,重新下载PLC程序,断电重启PLC。

三、读取协议帧
接下来我们来分析一下三菱的通信报文,以QnA兼容3E帧为例,其他通信帧大同小异。
协议帧一般分为请求帧、响应帧及异常帧。
请求帧:表示发送请求的报文。
响应帧:如果请求正确,PLC会以响应帧进行返回。
异常帧:如果请求错误,CPU会以异常帧返回。
读取请求帧报文格式:

读取响应帧报文格式:

读取异常帧报文格式:

说明:以上三种协议帧是参考三菱官方文档总结而成,其中头部指的是TCP头部,我们可以不用管。
四、写入协议帧
写入请求帧报文格式:
写入响应帧报文格式:

写入异常帧报文格式:

说明:如果我们学过Modbus,可以看到,协议都是相通的,MC只是比Modbus报文结构更复杂而已。
五、通信测试
我们以读取D0开始的5个寄存器为例,结合协议文档,来进行报文拼接。
发送报文如下:
副头部:0x50 0x00
网络编号:0x00
PLC编号:0xFF
请求目标模块I/O编号:0xFF 0x03
请求目标模块站号:0x00
请求数据长度:0x0C 0x00
CPU监视定时器:0x0A 0x00
指令:0x01 0x04
子指令:0x00 0x00
起始软元件:0x00 0x00 0x00
软元件代码:0xA8
软元件点数:0x05 0x00
我们通过网络调试助手发送这个报文,观察一下返回的报文:

响应报文如下:
副头部:0xD0 0x00
网络编号:0x00
PLC编号:0xFF
请求目标模块I/O编号:0xFF 0x03
请求目标模块站号:0x00
响应数据长度:0x0C 0x00
结束代码:0x00 0x00
软元件数据:0x0A 0x00 0x14 0x00 0x1E 0x00 0x28 0x00 0x32 0x00
其中0x0A 0x00 0x14 0x00 0x1E 0x00 0x28 0x00 0x32 0x00即表示D0-D4的值,进行数据解析处理后的值分别为10、20、30、40、50,与PLC数据一致。

六、代码实现
我们也可以编写C#程序来实现整个过程。
这里测试为主,代码相对简单,实际应用时可进一步封装,代码如下:
static void Main(string[] args){ // 连接 Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); socket.Connect("192.168.2.144", 4096); byte[] bytes = new byte[] { 0x50,0x00,//副头部,固定50 00 0x00,// 网络编号 0xFF,//PLC编号 0xFF,0x03,//目标模块IO编号,固定FF 03 0x00,// 目标模块站号 0x0C,0x00, // 字节长度,当前字节往后 0x0A,0x00,//PLC响应超时时间,以250ms为单位计算 0x01,0x04,// 成批读出,主命令 0x00,0x00,// 字操作,子命令 0x00,0x00,0x00,// 起始地址 0xA8,// 区域代码 0x05,0x00 //读取长度 }; socket.Send(bytes); byte[] respBytes = new byte[1024]; int count = socket.Receive(respBytes); if (count == 21) { for (int i = 11; i < count; i+=2) { // 小端处理,每2个字节作为一个数据 byte[] dataBytes = new byte[2]; dataBytes[0] = respBytes[i]; dataBytes[1] = respBytes[i+1]; Console.WriteLine(BitConverter.ToInt16(dataBytes, 0)); } }}
输出结果如下:
