C#实现Modbus协议与PLC通信

项目需要用C#写一个上位机,用Modbus/TCP协议与PLC通信,控制伺服电机的启停、转速等。D:\Code\C#\ConsoleApp1

1. 获取PLC的IP地址

待续。。。

2.

“启动”的代码如下,btn_stop_Click 是“启动”这个按钮对应的响应函数,启动一个新线程,实际的代码在StopEnginer函数中实现

        private void btn_stop_Click(object sender, EventArgs e) //控制电机停止
        {
            r_timer.Stop();
            t_timer.Stop();
            Thread t = new Thread(StopEnginer) { IsBackground = true };
            t.Start();
        }
private void StopEnginer()
        {
            string cmd_str = "0105081E0000";
            //01从站地址salve address
            //05功能码 写入单个线圈函数WriteSingleCoil(开源通讯库Nmodbus)
            //Nmodbus 地址https://github.com/NModbus/NModbus
            //081E是写入单个线圈地址,参考台达PLC应用手册161页DVP装置通讯地址和x工给的excel表
            //081E = M30是控制启停的地址
            //0000后四位是写入的值
            // string cmd_str = (tbx_cmdString.Text).ToUpper();

            ushort[] returnValues = new ushort[30];
            //将字符串cmd_str切割成地址、功能码、写入地址、写入值
            byte address = Convert.ToByte(cmd_str.Substring(0, 2), 16);
            byte Function = Convert.ToByte(cmd_str.Substring(2, 2), 16);
            ushort start = Convert.ToUInt16(cmd_str.Substring(4, 4), 16);
            ushort values = Convert.ToUInt16(cmd_str.Substring(8, 4), 16);
            Console.WriteLine("这是故障台停止程调用");
            try
            {
                bool value_TrueOrFalse = Convert.ToUInt16(values) == 0xFF00;
           
                //调用WriteSingleCoil函数
                master.WriteSingleCoil(address, start, value_TrueOrFalse);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }

代码解释如下,需要Modbus协议、PLC通信协议的相关知识,以及PLC地址表。项目用的PLC型号为台达DELTA DVP系列

1. PLC地址表

名称 对应的台达DVP系列PLC地址
转速设定 D520
扭矩设定 D502
实时转速 D46
实时扭矩 D26
启停 M30
扭力计量程 D410
转速量程 D414
加载器量程 D422
PLC数字量 D418
转速系数 D426
伺服加速时间 D504
伺服减速时间 D506
加减速系数 D508

其中,控制电机启停的PLC地址是M30。台达DVP系列PLC中,S, X, D, M等表示PLC装置(应该与PLC内部结构有关??分别代表不同的线圈区域?不是很确定),总之知道地址M30就OK了。
现在去查M30对应的十六进制编码地址是多少,因为用C给PLC发送信号时,需要以十六进制数形式发送。下表来自台达PLC应用手册(厚达743页)中的第161页,可以看出,M30000~255这一区域,该区域的编码以十六进制08开头,进一步,30的十六进制为1e,所以,PLC地址M30的十六进制地址是0x081e,其中0x表示十六进制。类似的,256~511这一区域的地址,其编码以十六进制09开头,256~511分别对应0x00 ~ 0xFF。例如,M267,它的十六进制地址计算方式为 :267-256=11,11的十六进制为0b,因此M267的十六进制地址为0x090b
C#实现Modbus协议与PLC通信_第1张图片
本项目PLC与上位机的通讯用到了一个开源通讯库NModbus ,下载地址https://github.com/NModbus/NModbus。该通信库官方提供的手册NModbus
在VS2017中,可以通过以下方式下载安装:
C#实现Modbus协议与PLC通信_第2张图片
C#实现Modbus协议与PLC通信_第3张图片

下载后,在相应的处理业务的类中定义成员变量

private NModbus.IModbusMaster master;

建立连接,用NModbus.ModbusFactory.CreateMaster创建主站,返回IModbusMaster类对象,对PLC地址的读写主要通过该类提供的方法实现

    public bool connect(string ip,int port)
    {
        if (hasConnected) return true;
       
        var tcp=new TcpClient();
        var result=tcp.BeginConnect(ip,port,null,null);
        result.AsyncWaitHandle.WaitOne(5000);
        if (result.IsCompleted)
        {
            var factory = new NModbus.ModbusFactory();
            master = factory.CreateMaster(tcp);
            hasConnected = true;
        }
        else
        {
            throw new TimeoutException("连接超时!");
        }
        return hasConnected;
    }

之后,调用IModbusMaster的成员函数就可以通信了,可以看到IModbusMaster 的部分常用API函数如下。
C#实现Modbus协议与PLC通信_第4张图片
本例中,将使用WriteSingleCoil函数往M30地址中写入数据,控制电机的启动和停止。WriteSingleCoil的函数原型为void WriteSingleCoil(byte slaveAddress, ushort coilAddress, bool value);,三个参数,slaveAddress,coilAddress分别表示从站地址和线圈地址,类型分别为byteushortvlaue为布尔型,其值为trueorfalse。给PLC的M30地址发送信号启动电机的代码如下。

public void start() //给PLC的M30地址发送信号启动电机
    {
        check();
        byte slaveAddress = 1; //单元标识符
        ushort coilAddress = 0x81e; //起始地址
        bool value = true;
        master.WriteSingleCoil(slaveAddress, coilAddress, value);
        // void WriteSingleCoil(byte slaveAddress, ushort coilAddress, bool value);函数原型
    }

2. DELTA DVP系列PLC通信协议详解

翻译自 DELTA DVP系列PLC官方文档
通信数据框架(Communication Data Frame)如下:
C#实现Modbus协议与PLC通信_第5张图片
解释如下:

  • STX:起始字符,为 “:”
  • ADR1 和 ADR0: 通信地址,由2个ASCII码组成,有效的通信地址范围为0…31。
  • CMD1和CMD0:命令码,由2个ASCII码组成,用来告诉PLC该执行什么任务。该系列PLC的命令码取了Modbus协议的部分功能码,Modbus协议如下表所示。完整的Modbus协议见xxxx。表中的Description 是PLC对应的部件
Code Name Translation Function Description
01 Read Coil Status 读取线圈状态 取得一组逻辑线圈的当前状态(on/off) S, Y, M, T, C
02 Read Input Status 读取输入状态 取得一组开关输入的当前状态(on/off) S,X,Y,M,T,C
03 Read Holding Registers 读取保持寄存器 在一个或多个保持寄存器中取得当前的二进制值 T,C,D
05 Force Single Coil 强制单线圈 强制一个逻辑线圈的通断状态 S,Y,M,T,C
06 Preset Signle Register 预置单寄存器 把具体二进制装入一个保持器 T,C,D
15 Force Multiple Coils 强置多线圈 强置一串连续逻辑线圈的通断 S,Y,M,T,C
16 Preset Multiple Register 预置多寄存器 把具体的二进制值装入一串连续的保持 T,C,D
17 Report Slave ID None
  • DATA(0)~DATA(n-1): 所要发送的数据内容,DATA字符的格式取决于功能码。数据的内容包括n个8位数据,由2n个ASCII码组成(一个8-bit的数据由2个ASCII码表示),n<=37,因此DATA最大可包含74个ASCII码

例子:Reading Coils T20~T27 from slave device 01,从从站设备地址01开始读取线圈T20-T27中的内容,一共8个字节(8 words)
PC发给PLC的代码
PC→PLC “:01 03 06 14 00 08 DA CR LF”
代码解析如下:

Field Name Example(Hex)
Heading 3A
Slave Address 01
Command code 03
Starting Address Hi 06
Starting Address Lo 14
Number of Points Hi 00
Number of points Lo 08
Error Check (LRC) DA
End character 1 CR
End character 2 LF

PLC发送给PC的代码:
:01 03 10 00 01 00 02 00 03 00 04 00 05 00 06 00 07 00 08 B8 CR LF
代码解析如下:

Field Name Example(Hex)
Slave Address 01
Command code 03
Bytes Count 10
Data Hi(T20) 00
Data Lo (T20) 01
Data Hi (T21) 00
Data Lo (T21) 02
Data Hi (T22) 00
Data Lo (T22) 03
Data Hi (T23) 00
Data Lo (T23) 04
Data Hi (T24) 00
Data Lo (T24) 05
Data Hi (T25) 00
Data Lo (T25) 06
Data Hi (T26) 00
Data Lo (T26) 07
Data Hi (T27) 00
Data Lo (T27) 08
Error Check (LRC) C8

LRC(Longitudinal Redundancy Check)纵向冗余度检查,

DELTA DVP系列的PLC的设备地址

你可能感兴趣的:(c#)