前言

本文以Panasonic松下PLC位列,介绍C#通过上位机串口访问松下PLC,可以使用串口助手协助调试。

一、松下PLC简介?

PLC (Programmable Logic Controller),可编程逻辑控制器,一种数字运算操作的电子系统,专为在工业环境应用而设计的。它采用一类可编程存储器,用于其内部 存储程序,执行 逻辑运算,顺序控制,定时,计数与算术操作等面向用户的指令,并通过数字或模拟式输入/输出控制各种类型的机械或生产过程,是工业控制的核心部分。松下公司的 PLC产品中,FPO为微型机,FP1为整体式 小型机,FP3为中型机,FP5/FP10、FP10S(FP10的改进型)、FP20为 大型机,其中FP20是最新产品。松下公司近几年PLC产品的主要特点是:指令系统功能强。

二、使用步骤

1.引入库

using System.IO.Ports;

2.设置串口参数

/// <summary>
        /// 设置串口参数
        /// </summary>
        /// <param name="SP_Panasonic">Serialport类的名字</param>
        /// <param name="portname">串口号</param>
        /// <param name="baudrate">波特率</param>
        /// <param name="stopbits">停止位</param>
        /// <param name="parity">校验位</param>
        /// <param name="databits">数据位</param>
        public static void SetSerialPort(SerialPort SP_Panasonic, string portname, int baudrate, StopBits stopbits, Parity parity, int databits)
        {
            SP_Panasonic.PortName = portname;
            SP_Panasonic.BaudRate = baudrate;
            SP_Panasonic.StopBits = stopbits;
            SP_Panasonic.Parity = parity;
            SP_Panasonic.DataBits = databits;
            if (SP_Panasonic.IsOpen)
            {
                SP_Panasonic.Close();
                SP_Panasonic.Open();
            }
            else
            {
                SP_Panasonic.Open();
            }
        }

3. 读取松下PLC寄存器数据,支持X、Y、R、D

/// <summary>
        /// 3. 3
        /// </summary>
        /// <param name="SP_Panasonic">Serialport类的名字</param>
        /// <param name="Station">站号</param>
        /// <param name="Memory">寄存器类型</param>
        /// <param name="Adress">起始地址</param>
        /// <param name="Count">地址位数</param>
        /// <returns></returns>
        public static string Read_Pan(SerialPort SP_Panasonic, int Station, string Memory, int Adress, int Count)
        {
            string ReadData = "";
            switch (Memory)
            {
                case "X":
                    ReadData = ReadXYR(SP_Panasonic, Station, Memory, Adress, Count);//暂不支持读取多位
                    break;
                case "Y":
                    ReadData = ReadXYR(SP_Panasonic, Station, Memory, Adress, Count);
                    break;
                case "R":
                    ReadData = ReadXYR(SP_Panasonic, Station, Memory, Adress, Count);
                    break;
                case "D":
                    ReadData = ReadD(SP_Panasonic, Station, Memory, Adress, Count);//支持读取多位
                    break;
            }
            if (ReadData == "读取失败")
            {
                String LogMSg = "串口通讯超时";
                cPublic.saveDataLog(LogMSg);
                cPublic.UpdateGUI((int)cPublic.UpdateItem.label_error, "label_error", "串口通讯超时,请检查!");
            }
            else
            {
                cPublic.UpdateGUI((int)cPublic.UpdateItem.label_error, "label_error", "");
            }
            return ReadData;
        }

4. 写入松下PLC寄存器数据,支持X、Y、R、D

/// <summary>
        /// 写入松下PLC寄存器数据,支持X、Y、R、D
        /// </summary>
        /// <param name="SP_Panasonic">Serialport类的名字</param>
        /// <param name="Station">站号</param>
        /// <param name="Memory">寄存器类型</param>
        /// <param name="Adress">起始地址</param>
        /// <param name="Count">地址位数</param>
        /// <param name="Data">写入的字符串</param>
        /// <returns></returns>
        public static bool Write_Pan(SerialPort SP_Panasonic, int Station, string Memory, int Adress, int Count, string Data)
        {
            bool WriteData = false;
            switch (Memory)
            {
                case "Y":
                    WriteData = WriteXYR(SP_Panasonic, Station, Memory, Adress, Count, Data);//暂不支持读取多位
                    break;
                case "R":
                    WriteData = WriteXYR(SP_Panasonic, Station, Memory, Adress, Count, Data);
                    break;
                case "D":
                    WriteData = WriteD(SP_Panasonic, Station, Memory, Adress, Count, Data);//支持读取多位
                    break;
            }
            return WriteData;
        }

 5. 读取DT寄存器数据

/// <summary>
        /// 读取DT寄存器数据
        /// </summary>
        /// <param name="SP_Panasonic">Serialport类的名字</param>
        /// <param name="station">站号</param>
        /// <param name="memory">寄存器类型</param>
        /// <param name="address">起始地址</param>
        /// <param name="addressCount">地址位数</param>
        /// <returns></returns>
        private static string ReadD(SerialPort SP_Panasonic, int station, string memory, int address, int addressCount)
        {
            lock(Lock_Serial)
            {
                send_count = 0;
                string strSendData;
                string strReceiveData = "";
                string[] arrstrReceiveData = new string[addressCount * 2];
                byte[] arrbyReceiveData = new byte[addressCount * 2];
                string strStartAddress = address.ToString("d5");
                string strEndAddress = (address + addressCount - 1).ToString("d5");
                // 生成发送数据字符串
                strSendData = "%0" + station.ToString() + "#RD" + memory.ToUpper() + strStartAddress + strEndAddress;
                // 字符添加校验码和回车结束符
                strSendData += GetCheckCode(strSendData) + "\r";
                send_count += strSendData.Length;
                if (SendData(SP_Panasonic, strSendData))
                    strReceiveData = ReceiveData(SP_Panasonic, 300);
                receive_count += strReceiveData.Length;
                if (strReceiveData != "" && strReceiveData[3] == '$')
                {
                    // 解析串口接收的数据
                    for (int i = 0; i < addressCount * 2; i++)
                    {
                        arrstrReceiveData[i] = strReceiveData.Substring(6 + 2 * i, 2);
                        arrbyReceiveData[i] = Convert.ToByte(arrstrReceiveData[i], 16);
                    }
                    String s = BitConverter.ToString(arrbyReceiveData).Replace("-", "");
                    return s;
                }
                else
                {
                    return "读取失败";
                }
            }
        }

6. 读取X、Y、R寄存器数据

/// <summary>
        /// 读取X、Y、R寄存器数据
        /// </summary>
        /// <param name="SP_Panasonic">Serialport类的名字</param>
        /// <param name="station">站号</param>
        /// <param name="memory">寄存器类型</param>
        /// <param name="address">起始地址</param>
        /// <param name="addressCount">地址位数</param>
        /// <returns></returns>
        private static string ReadXYR(SerialPort SP_Panasonic, int station, string memory, int address, int addressCount)
        {
            lock (Lock_Serial)
            {
                send_count = 0;
                string strSendData;
                string strReceiveData = "";
                string[] arrstrReceiveData = new string[addressCount];
                byte[] arrbyReceiveData = new byte[addressCount];
                string strStartAddress = address.ToString("d4");
                string strEndAddress = (address + addressCount - 1).ToString("d4");
                // 生成发送数据字符串
                strSendData = "%0" + station.ToString() + "#RCS" + memory.ToUpper() + strStartAddress;
                // 字符添加校验码和回车结束符
                strSendData += GetCheckCode(strSendData) + "\r";
                send_count += strSendData.Length;
                if (SendData(SP_Panasonic, strSendData))
                    strReceiveData = ReceiveData(SP_Panasonic, 100);
                cPublic.strReceiveData_public = strReceiveData;
                receive_count += strReceiveData.Length;
                if (strReceiveData != "" && strReceiveData[3] == '$')
                {
                    // 解析串口接收的数据
                    for (int i = 0; i < addressCount; i++)
                    {
                        arrstrReceiveData[i] = strReceiveData.Substring(6, 1);
                        arrbyReceiveData[i] = Convert.ToByte(arrstrReceiveData[i], 16);
                    }
                    String s = BitConverter.ToString(arrbyReceiveData).Replace("-", "");
                    return s;
                }
                else
                {
                    return "读取失败";
                }
            }
        }

7. 写入DT寄存器

/// <summary>
        /// 写入DT寄存器
        /// </summary>
        /// <param name="SP_Panasonic">Serialport类的名字</param>
        /// <param name="station">站号</param>
        /// <param name="memory">寄存器类型</param>
        /// <param name="address">起始地址</param>
        /// <param name="addressCount">地址位数</param>
        /// <param name="data">写入的字符串</param>
        /// <returns></returns>
        public static bool WriteD(SerialPort SP_Panasonic, int station, string memory, int address, int addressCount, string data)
        {
            lock (Lock_Serial)
            {
                send_count = 0;
                string strSendData;
                string strReceiveData = "";
                string[] arrstrReceiveData = new string[addressCount * 2];
                byte[] arrbyReceiveData = new byte[addressCount * 2];
                string strStartAddress = address.ToString("d5");
                string strEndAddress = (address + addressCount - 1).ToString("d5");
                // 生成发送数据字符串
                strSendData = "%0" + station.ToString() + "#WD" + memory.ToUpper() + strStartAddress + strEndAddress + data;
                // 字符添加校验码和回车结束符
                strSendData += GetCheckCode(strSendData) + "\r";
                send_count += strSendData.Length;
                if (SendData(SP_Panasonic, strSendData))
                {
                    strReceiveData = ReceiveData(SP_Panasonic, 300);
                    receive_count += strReceiveData.Length;
                    if (strReceiveData != "" && strReceiveData[3] == '$')
                    {
                        return true;
                    }
                    else
                    {
                        return false;
                    }
                }
                else
                {
                    return false;
                }
            }
        }

8.  写入X、Y、R寄存器数据

/// <summary>
        /// 写入X、Y、R寄存器数据
        /// </summary>
        /// <param name="SP_Panasonic">Serialport类的名字</param>
        /// <param name="station">站号</param>
        /// <param name="memory">寄存器类型</param>
        /// <param name="address">起始地址</param>
        /// <param name="addressCount">地址位数</param>
        /// <param name="data">写入的字符串</param>
        /// <returns></returns>
        public static bool WriteXYR(SerialPort SP_Panasonic, int station, string memory, int address, int addressCount, string data)
        {
            lock (Lock_Serial)
            {
                send_count = 0;
                string strSendData;
                string strReceiveData = "";
                string[] arrstrReceiveData = new string[addressCount * 2];
                byte[] arrbyReceiveData = new byte[addressCount * 2];
                string strStartAddress = address.ToString("d4");
                string strEndAddress = (address + addressCount - 1).ToString("d4");
                // 生成发送数据字符串
                strSendData = "%0" + station.ToString() + "#WCS" + memory.ToUpper() + strStartAddress + data;
                //strSendData = "%0" + station.ToString() + "#WCS" + memory.ToUpper() + strStartAddress + strEndAddress + data;
                // 字符添加校验码和回车结束符
                strSendData += GetCheckCode(strSendData) + "\r";
                send_count += strSendData.Length;
                if (SendData(SP_Panasonic, strSendData))
                {
                    strReceiveData = ReceiveData(SP_Panasonic, 300);
                    receive_count += strReceiveData.Length;
                    //if (strReceiveData != "" && strReceiveData[3] == '$')
                    if (strReceiveData != "" && strReceiveData[3] == '$')
                    {
                        return true;
                    }
                    else
                    {
                        return false;
                    }
                }
                else
                {
                    return false;
                }
            }
        }

9. 校验码计算

/// <summary>
        /// 
        /// </summary>
        /// <param name="sendData"></param>
        /// <returns></returns>
        private static string GetCheckCode(string sendData)
        {
            //将字符串转换成字节数组
            byte[] arrSendData = new byte[sendData.Length];
            arrSendData = Encoding.Default.GetBytes(sendData);
            byte sum = 0;
            // 进行校验码计算,异或求和后生成
            for (int i = 0; i < arrSendData.Length; i++)
            { sum = (byte)(sum ^ arrSendData[i]); }
            // 将计算结果转换成字符串
            string strCheck = Convert.ToString(sum, 16);
            return strCheck.ToUpper();
        }

10. 串口发送字符串

/// <summary>
        /// 往串口发送字符串
        /// </summary>
        /// <param name="SP_Panasonic">Serialport类的名字</param>
        /// <param name="sendData">发送的字符串</param>
        /// <returns></returns>
        private static bool SendData(SerialPort SP_Panasonic, string sendData)
        {
            // 将字符串转换成字节数组
            byte[] arrbySendBuffer = new byte[sendData.Length];
            arrbySendBuffer = Encoding.Default.GetBytes(sendData);
            // 通过串口发送数据
            try
            {
                //发送数据
                SP_Panasonic.Write(arrbySendBuffer, 0, arrbySendBuffer.Length);
                return true;
            }
            catch (Exception ex)
            {
                //MessageBox.Show(ex.Message);
                return false;
            }
        }

11. 串口接收字符串

/// <summary>
        /// 从串口接收字符串
        /// </summary>
        /// <param name="SP_Panasonic">Serialport类的名字</param>
        /// <param name="time">延时时间</param>
        /// <returns>返回接收的字符串</returns>
        private static string ReceiveData(SerialPort SP_Panasonic, int time)
        {
            int i = 0;
            Thread.Sleep(time);
            while (SP_Panasonic.BytesToRead == 0)
            {
                i++;
                Thread.Sleep(20);
                if (i == 50) return "";
            }
            byte[] arrReceiveData = new byte[SP_Panasonic.BytesToRead];
            SP_Panasonic.Read(arrReceiveData, 0, SP_Panasonic.BytesToRead);
            return Encoding.ASCII.GetString(arrReceiveData);
        }

总结

例如:以上就是今天要讲的内容,本文仅仅简单介绍以松下PLC为列,使用C#上位机通过串口访问PLC所有寄存器,提供了大量能使我们快速便捷地处理数据的函数和方法。