解析三菱 PLC 的MC通信协议报文

511 阅读6分钟

前言

在工业自动化领域,可编程逻辑控制器(PLC)是实现自动化控制的核心设备之一。三菱PLC凭借其卓越的性能和可靠性,在全球范围内得到了广泛应用。而MC(Memory Card)通信协议作为三菱PLC与外部设备进行数据交换的重要手段,其重要性不言而喻。

正文

西门子、三菱、欧姆龙是我们自动化行业使用最多的三个PLC品牌。大家在学习MC通信协议时,建议先熟练掌握Modbus通信协议。

三菱PLC的MC协议是一种数据通信协议,它用于在计算机和三菱PLC之间传输数据。MC协议是三菱公司独有的一种协议,主要用于控制三菱PLC。

MC协议是Melsec协议的简称。

通信帧类型

很多人在学习三菱PLC通信协议时,很头疼的是三菱PLC有很多种通信帧类型,每种通信帧又有ASCII和二进制两种编码格式。

在我们实际开发中,以太网通信主要使用的是QnA兼容3E帧(FX5U系列/Q系列/Qna系列/L系列/R系列),如果是FX3U通过加装Fx3U-ENET-ADP,会使用到A兼容1E帧。对于串口设备,一般会使用QnA兼容2C帧或QnA兼容4C帧。

通信编码格式有ASCII和二进制两种方式,通过二进制编码数据进行的通信与通过ASCII编码数据进行的通信相比,前者的通信数据量约为后者的二分之一,因此二进制编码的方式通信效率更高。

PLC配置

三菱PLC与西门子PLC有所不同。西门子PLC是固定端口102,一个端口支持多个连接。三菱PLC需要手动添加端口,一个端口只支持一个连接。因此三菱PLC需要手动配置,这里以三菱R系列为例:

1、在导航中,通过参数>>R08ENCPU>> 模块参数,双击在打开的界面中设置好PLC的IP地址信息,将通信数据代码改成二进制,然后找到【对象设备连接配置设置】:

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

SLMP(Seamless Message Protocol)是在以太网中使用的协议。

MC协议则包含了串口以及以太网的通信协议,范围更广。

读取协议帧

接下来我们来分析一下三菱的通信报文,以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));
        }
    }
}

输出结果如下:

总结

希望通过本文的学习,你能够更加自信地应对三菱PLC的通信需求,提升你的技术能力,并在实际项目中游刃有余地运用这些知识。

最后

如果你觉得这篇文章对你有帮助,不妨点个赞支持一下!你的支持是我继续分享知识的动力。如果有任何疑问或需要进一步的帮助,欢迎随时留言。

也可以加入微信公众号 [DotNet技术匠] 社区,与其他热爱技术的同行一起交流心得,共同成长!

优秀是一种习惯,欢迎大家留言学习!

作者:上位机付工 

出处:mp.weixin.qq.com/s/e5vTj0rE4We_y3LYs1MeCQ

声明:网络内容,仅供学习,尊重版权,侵权速删,歉意致谢!