C#上位机和松下PLC通讯
C#上位机和松下PLC通讯
1、前言
也好久没回来看博客了,看大家对于其他的PLC的需求都是有的,说明搞我们工业控制这一行的人也是很多的。刚好最近基于项目需要,一些新的东西,需要和松下PLC的FP系列的CPU进行通讯然后使用松下的NewTocol协议进行通讯,对对应的寄存器/线圈进行读写操作。于是,就随笔记录下相应的程序代码。
松下FP系列PLC与工控机之间的通信方式可以采用串口通信,与工控机连接的RS232电缆(长度有限485的长度会比较长很多)必须按照松下的产品手册所给的连线图进行制作,否则通信将无法实现。电缆连线图如下:
至于怎么接线,需要的可以去了解下,不过一般在项目过程中,PLC的编程人员会把线接好,直接接入到工控机就可以使用了。
既然是串口通讯,我们就需要了解下串口通讯的一些概念。我们往下走。
2、概念
2.1 波特率
在电子通信领域,波特(Baud)即调制速率,指的是有效数据讯号调制载波的速率,即单位时间内载波调制状态变化的次数。通俗一点,就是一个单位时间内数据传递的符号的个数
2.2 奇偶校验位
奇偶校验位 (Parity)通常用在数据通信中来保证数据的有效性。分为偶校验、奇校验、或非校验。如果偶校验在使用,校验位将这些位置为偶数;如果奇校验在使用,校验位将这些位置为奇数。
2.3 数据位
数据位一般为8位一个字节的数据(也有6位、7位的情况),低位(LSB)在前,高位(MSB)在后。
2.4 停止位
用于表示单个包的最后一位。典型的值为1,1.5和2位。
2.5 SerialPort类
需要引入System.IO.Port命名空间。
3、NewTocol协议
至于协议文档,大家可以去官方或者百度查询了解,该协议由以下特点:
- 数据传输采用ASCII的形式。
- 应答式协议,首先由工控机发送指令,然后PLC会自动对指令进行响应。也就是说,不需要编写任何PLC程序,只要PLC和工控机连接正常,工控机给PLC发送指令,都能得到PLC的响应回复。
4、上位机发送数据帧格式
指令是以帧为单位进行,工控机向PLC发送命令帧,然后PLC作出响应,向工控机发送响应帧。
格式内容如下:
其中:
- %为起始码,这是固定不变的。
- AD(H)和 AD(L)是目标站号的高位和低位。一般如果只有一个PLC的话,那么就填写01,高位是0,低位是1。
- #也是固定不变的。
- 指令代码。每个指令会有不同的指令代码,下面会讲。
文本代码。指令的内容,不同的指令,内容也不同。 - BCC(H)和BCC(L),是帧的数据校验的高低位,数据校验范围是BCC前面的所有字符;下面我们会展开介绍说明。
- CR,回车键,ASCII为0x0D,不可见字符。
5、PLC响应帧格式
响应帧有两种,一种是正确响应,一种是错误响应。也就是说,如果工控机给PLC发送的指令是正确的,那么PLC就会返回正确的响应帧,否则就返回错误的响应帧。
6、工作站的配置
以下是电气工程师提供的PLC配置图为例,我们可以看到PLC内部的设备预设站号是1,那么对于上位机而言目标站号就是1,此配置可以找电气工程师告知即可。
7、指令代码
工控机可以给PLC发送的指令一共有20多种,不过我们常用的指令一般有9种。如下图所示:
其中常用的:
8、BCC的计算方式
BCC校验码的计算方式是将指令中的各个ASCII字符的16进制(00~FF)进行异或求和后生成的. 该校验码也以两个ASCII码字符表示(高位在前,低位在后)。
例如这条指令:
%01#RCSX00001DCR
注意:CR不是两个字符,是一个字符,回车键,但是是不可显示字符,所以这里用CR来表示。
计算方式:
在此贴上BCC校验的代码:
/// <summary>/// BCC校验码/// 计算方式是将指令中的各个ASCII字符的16进制(00~FF)进行异或求和后生成的. /// 该校验码也以两个ASCII码字符表示(高位在前,低位在后)/// </summary>/// <param name="cmd"></param>/// <returns></returns>public static string Bcc(string cmd){cmd = cmd.Trim();byte bcc = 0;byte[] cmdArr = System.Text.Encoding.ASCII.GetBytes(cmd);for (int i = 0; i < cmdArr.Length; i++){bcc = (byte)(bcc ^ cmdArr[i]);}return bcc.ToString("X2");}
9、常用指令举例
例1:写入单触点状态(指令代码:WCS)
例如我们往触点R12写入1,则命令帧为:
%01#WCSR0012120CR
拆开成各个部分:% 01 # WCS R 0012 1 20 CR
正常通信情况下,PLC会返回正确的响应帧:%01$WC14CR
例2:读取单触点状态(指令代码:RCS)
例如我们读取触点R12的值,则命令帧为:
%01#RCSR001214CR
拆分成各个部分:% 01 # RCS R 0012 14 CR
正常通信情况下,假如R12触点的值为1,那么PLC返回的响应帧为
%01$RC120**CR
例3:写入数据寄存器值(指令代码:WD)
例如我们写入字数值到PLC的 DT1到DT3,其中:DT1=05H,DT2=1507H,DT3=900H,则命令帧为:
这里需要注意的是:
写入的数值是按字写入,也就是说,每个值占2个字节。
低位在前,高位在后。
写入的字符是16进制的。
所以,需要代码进行处理。例如此处的DT2=1507H,1507H的10进制值是5383,则处理步骤为:
先将5383转换为16进制的字符1507
将字符转换顺序,低位在前,高位在后,也就是转换成:0715
我们写一个函数来进行处理:
private string ConvertShortToPlcFormat(short value){string temp = value.ToString("X4");return temp.Substring(2, 2) + temp.Substring(0, 2);}
正常通信情况下,PLC的响应帧为:
10、代码实现
1、先定义松下通讯配置用的数据模型
以下是配置文件内容和模型代码:
public class PanasonicParaModel{/// <summary>/// 名称/// </summary>public string PlcName { get; set; }/// <summary>/// 站号/// </summary>public int PlcStationNo { get; set; }/// <summary>/// COM口号 比如COM1/// </summary>public string PlcComNo { get; set; }/// <summary>/// 波特率/// </summary>public int PlcBoardRate { get; set; }/// <summary>/// 奇偶校验1奇校验 2偶校验 0不校验 /// </summary>public Parity PlcParity { get; set; }/// <summary>/// 数据位 正常是8/// </summary>public int PlcDataLen { get; set; }/// <summary>/// 停止位1 2 1.5 默认是1/// </summary>public StopBits PlcStopBit { get; set; }/// <summary>/// 连接标志位/// </summary>public bool LinkRes { get; set; }}
2、定义一个串口帮助方法类
public class SerialPortHelper{#region 单例实现private static SerialPortHelper instance = null;private Dictionary<int, SerialPort> _dicSerialPort = null; //保存串口对象的集合private SerialPortHelper(){if (this._dicSerialPort == null){this._dicSerialPort = new Dictionary<int, SerialPort>();}}public static SerialPortHelper Instance{get{if (instance == null){instance = new SerialPortHelper();}return instance;}}#endregion#region 获得奇偶校验/// <summary>/// 获得奇偶校验/// </summary>/// <param name="num"></param>/// <returns></returns>public static Parity GetParityByConfigNum(int num){switch(num){case 1:return Parity.Odd;case 2:return Parity.Even;case 0:default:return Parity.None;}}#endregion#region 获得停止位/// <summary>/// 获得停止位/// </summary>/// <param name="num"></param>/// <returns></returns>public static StopBits GetStopBitByConfigNum(double num){switch (num){case 1:return StopBits.One;case 2:return StopBits.Two;case 1.5:return StopBits.OnePointFive;default:return StopBits.One;}}#endregion}
3、定义连接对象类
话不多说了,直接上代码
/// <summary>/// 松下PLC Newtocol协议/// </summary>public class Panasonic_Newtocol : IDisposable{/// <summary>/// 232串口对象/// </summary>public SerialPort serialPort { get; set; }/// <summary>/// 起始码%固定不变/// </summary>private const string headStr = "%";/// <summary>/// 目标站号 高位和低位/// </summary>private static string stationCode { get; set; }/// <summary>/// 分隔符#固定不变/// </summary>public const string fixCode = "#";/// <summary>/// 结束符\r 固定不变/// </summary>public const string endStr = "\r";/// <summary>/// 正确响应时的字符串内容/// </summary>private string successResponseHead = "";/// <summary>/// 失败响应时的字符串内容/// </summary>private string failResponseHead = "";public double[] DTValue = null;public bool[] arrXYMValue = null;/// <summary>/// 保存日志的委托/// </summary>/// <param name="logStr"></param>public delegate void SaveLogForRecord(string logStr);/// <summary>/// 记录日志事件实现/// </summary>public event SaveLogForRecord SaveLog;/// <summary>/// 写入单触点标志位/// </summary>bool writeCoilSingleResult = false;/// <summary>/// 写入寄存器数据结果/// </summary>bool writeDataResult = false;/// <summary>/// 读取寄存器数据结果/// </summary>bool readDataResult = false;/// <summary>/// 读取多个/单个触点结果/// </summary>bool readCoilMany_SingleResult = false;public Panasonic_Newtocol(PanasonicParaModel _serialPort){stationCode = _serialPort.PlcStationNo.ToString("X2");serialPort = new SerialPort(){PortName = _serialPort.PlcComNo.ToString(),BaudRate = _serialPort.PlcBoardRate,Parity = _serialPort.PlcParity,DataBits = _serialPort.PlcDataLen,StopBits = _serialPort.PlcStopBit,ReceivedBytesThreshold = 8};successResponseHead = headStr + stationCode + "$";failResponseHead = headStr + stationCode + "!";SaveLog += Panasonic_Newtocol_SaveLog;}private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e){Thread.Sleep(50);//让缓存区数据接收完成int n = serialPort.BytesToRead;byte[] buf = new byte[n];serialPort.Read(buf, 0, n);string readText = Encoding.ASCII.GetString(buf).Replace("\r", "").Replace("\n", "");if (readText.StartsWith("%") && readText.Length >= serialPort.ReceivedBytesThreshold){#region 字节流数据正确int len = readText.Length;string sHead = readText.Substring(0, 4);string sComm = readText.Substring(4, 2);string sComm1 = readText.Substring(4, 3);string sValues = "";string tValue = "";if (sHead == successResponseHead){switch (sComm){case "RD":{//%01$RD XXXX_XXXX_XXXX_XXXX_XXXX_XXXX 文本代码 BCC高低位 结束符if (!readDataResult){#region 读取D,转换为十进制sValues = readText.Substring(6, len - 8);DTValue = new double[sValues.Length / 4];for (int i = 0; i < (sValues.Length / 4); i++){tValue = sValues.Substring(i * 4, 4);DTValue[i] = (double)Convert.ToInt32(tValue.Substring(2, 2) + tValue.Substring(0, 2), 16);}#endregionreadDataResult = true;}break;}case "RC":{if (!readCoilMany_SingleResult){#region 读多个/单个触点sValues = readText.Substring(6, len - 8);arrXYMValue = new bool[sValues.Length];for (int i = 0; i < arrXYMValue.Length; i++){tValue = sValues.Substring(i, 1);arrXYMValue[i] = tValue == "1";}#endregionreadCoilMany_SingleResult = true;}break;}case "WD":{//写入D成功,没返回值if (!writeDataResult){writeDataResult = true;}break;}case "WC":{//写入触点成功,没返回值if (!writeCoilSingleResult){writeCoilSingleResult = true;}break;}default:break;}}#endregion}}#region 记录日志/// <summary>/// 记录日志/// </summary>/// <param name="logStr"></param>private void Panasonic_Newtocol_SaveLog(string logStr){System.Diagnostics.Debug.WriteLine(logStr);}#endregion#region 打开串口连接/// <summary>/// 打开串口连接/// </summary>/// <returns></returns>public bool OpenLinkSerial(){try{if (serialPort != null){CloseLinkSerial();Thread.Sleep(10);serialPort.DataReceived += SerialPort_DataReceived;serialPort.Open();Panasonic_Newtocol_SaveLog("串口打开成功");return true;}else{return false;}}catch (Exception ex){Panasonic_Newtocol_SaveLog(ex.StackTrace.ToString());return false;}}#endregion#region 关闭串口连接/// <summary>/// 关闭串口连接/// </summary>public void CloseLinkSerial(){if (serialPort != null && serialPort.IsOpen){serialPort.DiscardInBuffer();serialPort.DiscardOutBuffer();serialPort.Close();}}#endregion#region 写入单触点值/// <summary>/// 写入单触点值/// </summary>/// <param name="XYMAddr">例如R12 X触点 Y触点 M状态寄存器</param>/// <param name="value">0=off 1=on</param>/// <returns></returns>public bool WriteCoilSingle(string XYMAddr, bool value){writeCoilSingleResult = false;string writeStr = WriteCoilSingle_CommStr(XYMAddr, value);if (serialPort != null && serialPort.IsOpen){serialPort.Write(writeStr);int numPro = 0;while (!writeCoilSingleResult && numPro < 100){Thread.Sleep(1);numPro++;}if (numPro < 100){return true;}return false;}else{return false;}}#endregion#region 写入单触点值发送的字符串内容 WCS/// <summary>/// 写入单触点值发送的字符串内容 WCS X Y M/// </summary>/// <param name="XYMAddr">例如R12 X触点 Y触点 M状态寄存器</param>/// <param name="value">0=off 1=on</param>public string WriteCoilSingle_CommStr(string XYMAddr, bool value){string commmandCode = "WCS";//发送string outStr = "";string sReg = XYMAddr.Substring(0, 1);string sAddr = XYMAddr.Substring(1).PadLeft(4, '0');outStr = headStr + stationCode + fixCode + commmandCode + sReg + sAddr + string.Format(value ? "1" : "0");outStr = outStr + Bcc(outStr) + endStr;return outStr;}#endregion#region 读单触点状态/// <summary>/// 读单触点状态/// </summary>/// <param name="XYMAddr">X Y M地址 比如M1</param>/// <param name="value">读取的结果</param>/// <returns>TRUE表示读取成功 false表示读取失败</returns>public bool ReadCoilSingle(string XYMAddr, out bool value){readCoilMany_SingleResult = false;string writeStr = ReadCoilSingle_CommStr(XYMAddr);if (serialPort != null && serialPort.IsOpen){serialPort.Write(writeStr);int numPro = 0;while (!readCoilMany_SingleResult && numPro < 100){Thread.Sleep(1);numPro++;}if (numPro < 100){value = arrXYMValue[0];return true;}value = false;return false;}else{value = false;return false;}}#endregion#region 读单触点状态发送的字符串内容 RCS/// <summary>/// 读单触点状态发送的字符串内容 RCS/// </summary>/// <param name="XYMAddr">例如R12 X触点 Y触点 M状态寄存器</param>/// <returns></returns>public string ReadCoilSingle_CommStr(string XYMAddr){string commmandCode = "RCS";//发送string outStr = "";string sReg = XYMAddr.Substring(0, 1);string sAddr = XYMAddr.Substring(1).PadLeft(4, '0');outStr = headStr + stationCode + fixCode + commmandCode + sReg + sAddr;outStr = outStr + Bcc(outStr) + endStr;return outStr;}#endregion#region 读多个单触点状态结果 RCP/// <summary>/// 读多个单触点状态结果 RCP/// </summary>/// <param name="startAddr"></param>/// <param name="endAddr"></param>/// <param name="value"></param>/// <returns></returns>public bool ReadManyCoilData(List<string> xymAddr, out bool[] value){readCoilMany_SingleResult = false;string writeStr = ReadCoilMany_CommStr(xymAddr);value = new bool[xymAddr.Count];for (int i = 0; i < value.Length; i++){value[i] = false;}if (serialPort != null && serialPort.IsOpen){serialPort.Write(writeStr);int numPro = 0;while (!readCoilMany_SingleResult && numPro < 100){Thread.Sleep(1);numPro++;}if (numPro < 100){value = arrXYMValue;Thread.Sleep(1);return true;}return false;}else{return false;}}#endregion#region 读多个单触点状态发送的字符串内容 RCP/// <summary>/// 读多个单触点状态发送的字符串内容 RCP/// </summary>/// <param name="XYMAddr">长度为1-8的触点集合</param>/// <returns></returns>public string ReadCoilMany_CommStr(List<string> XYMAddr){string commmandCode = "RCP";//发送string outStr = "";outStr = headStr + stationCode + fixCode + commmandCode + XYMAddr.Count.ToString();for (int i = 0; i < XYMAddr.Count; i++){string sReg1 = XYMAddr[i].Substring(0, 1);string sAddr1 = XYMAddr[i].Substring(1).PadLeft(4, '0');outStr += sReg1 + sAddr1;}outStr = outStr + Bcc(outStr) + endStr;return outStr;}#endregion#region 读取数据寄存器值/// <summary>/// 读取数据寄存器值/// </summary>/// <param name="startAddr"></param>/// <param name="endAddr"></param>/// <param name="value"></param>/// <returns></returns>public bool ReadDT_Data(string startAddr, string endAddr, out double[] value){readDataResult = false;string writeStr = ReadData_CommStr(startAddr, endAddr);string sAddr1 = startAddr.Substring(1).PadLeft(5, '0');string sAddr2 = endAddr.Substring(1).PadLeft(5, '0');int readLength = int.Parse(sAddr2) - int.Parse(sAddr1) + 1;value = new double[readLength];for (int i = 0; i < value.Length; i++){value[i] = 0;}if (serialPort != null && serialPort.IsOpen){serialPort.Write(writeStr);int numPro = 0;while (!readDataResult && numPro < 100){Thread.Sleep(1);numPro++;}if (numPro < 100){value = DTValue;return true;}return false;}else{return false;}}#endregion#region 读取数据寄存器值发送的字符串内容 RD/// <summary>/// 读取数据寄存器值发送的字符串内容 RD/// </summary>/// <param name="startAddr">起始地址D/L/F XXXX</param>/// <param name="endAddr">结束地址D/L/F XXXX</param>/// <returns></returns>public string ReadData_CommStr(string startAddr, string endAddr){string commmandCode = "RD";//发送string outStr = "";string sReg = startAddr.Substring(0, 1);string sAddr1 = startAddr.Substring(1).PadLeft(5, '0');string sAddr2 = endAddr.Substring(1).PadLeft(5, '0');outStr = headStr + stationCode + fixCode + commmandCode + sReg + sAddr1 + sAddr2;outStr = outStr + Bcc(outStr) + endStr;return outStr;}#endregion#region 写入数据寄存器值/// <summary>/// 写入数据寄存器值/// </summary>/// <param name="startAddr">D/L/F XXXX</param>/// <param name="endAddr">D/L/F XXXX</param>/// <param name="writeContent">写入的数据内容</param>/// <returns></returns>public bool WriteData(string startAddr, string endAddr, int[] writeContent){writeDataResult = false;string writeStr = WriteData_CommStr(startAddr, endAddr, writeContent);if (serialPort != null && serialPort.IsOpen){serialPort.Write(writeStr);int numPro = 0;while (!writeDataResult && numPro < 100){Thread.Sleep(1);numPro++;//等待正确响应 或者超时}if (numPro < 100){return true;}return false;}else{return false;}}#endregion#region 写入数据寄存器值发送的字符串内容 WD/// <summary>/// 写入数据寄存器值发送的字符串内容 WD/// 写入的数值是按字写入,也就是说,每个值占2个字节/// 低位在前,高位在后。/// </summary>/// <param name="startAddr">D/L/F XXXX</param>/// <param name="endAddr">D/L/F XXXX</param>/// <param name="writeContent">写入的数据内容</param>/// <returns></returns>public string WriteData_CommStr(string startAddr, string endAddr, int[] writeContent){string commmandCode = "WD";//发送string outStr = "";string sReg = startAddr.Substring(0, 1);string sAddr1 = startAddr.Substring(1).PadLeft(5, '0');string sAddr2 = endAddr.Substring(1).PadLeft(5, '0');outStr = headStr + stationCode + fixCode + commmandCode + sReg + sAddr1 + sAddr2;int writeLength = int.Parse(sAddr2) - int.Parse(sAddr1) + 1;if (writeLength == 1){string strTemp = ConvertShortToPlcFormat(writeContent[0]);outStr += strTemp + strTemp;}else{for (int i = 0; i < writeLength; i++){string strTemp = ConvertShortToPlcFormat(writeContent[i]);outStr += strTemp;}}outStr = outStr + Bcc(outStr) + endStr;return outStr;}#endregion#region 写入字单位的触点的状态信息发送的字符串内容 WCC/// <summary>/// 写入字单位的触点的状态信息发送的字符串内容 WCC/// </summary>/// <param name="startAddr">起始地址</param>/// <param name="endAddr">结束地址</param>/// <param name="writeValues">写入的值数组 1=on 0=off</param>/// <returns></returns>public string WriteCoilCoils_CommStr(string startAddr, string endAddr, bool[] writeValues){string commmandCode = "WCC";//发送string outStr = "";string sReg = startAddr.Substring(0, 1);//Y R Lstring sAddr1 = startAddr.Substring(1).PadLeft(4, '0');string sAddr2 = endAddr.Substring(1).PadLeft(4, '0');outStr = headStr + stationCode + fixCode + commmandCode + sReg + sAddr1 + sAddr2;int readLength = int.Parse(sAddr2) - int.Parse(sAddr1) + 1;for (int i = 0; i < readLength; i++){string strTemp = ConvertShortToPlcFormat(writeValues[i] ? 1 : 0);outStr += strTemp;}outStr = outStr + Bcc(outStr) + endStr;return outStr;}#endregion#region 读取字单位的触点的状态信息发送的字符串内容 RCC/// <summary>/// 读取字单位的触点的状态信息发送的字符串内容 RCC/// </summary>/// <param name="startAddr">M1</param>/// <param name="endAddr">M4</param>/// <returns></returns>public string ReadCoilCoils_CommStr(string startAddr, string endAddr){string commmandCode = "RCC";//发送string outStr = "";string sReg = startAddr.Substring(0, 1);string sAddr1 = startAddr.Substring(1).PadLeft(4, '0');string sAddr2 = endAddr.Substring(1).PadLeft(4, '0');outStr = headStr + stationCode + fixCode + commmandCode + sReg + sAddr1 + sAddr2;outStr = outStr + Bcc(outStr) + endStr;return outStr;}#endregion#region 读多个触点发送的字符串内容/// <summary>/// 读多个触点发送的字符串内容/// </summary>/// <param name="XYMAddr"></param>/// <returns></returns>public string ReadCoilPlural_CommStr(string XYMAddr){string commmandCode = "RCP";//发送string outStr = "";string sReg = XYMAddr.Substring(0, 1);string sAddr = XYMAddr.Substring(1).PadLeft(4, '0');outStr = headStr + stationCode + fixCode + commmandCode + sReg + sAddr;outStr = outStr + Bcc(outStr) + endStr;return outStr;}#endregion#region 获得BCC校验码/// <summary>/// BCC校验码/// 计算方式是将指令中的各个ASCII字符的16进制(00~FF)进行异或求和后生成的. /// 该校验码也以两个ASCII码字符表示(高位在前,低位在后)/// </summary>/// <param name="cmd"></param>/// <returns></returns>public static string Bcc(string cmd){cmd = cmd.Trim();byte bcc = 0;byte[] cmdArr = System.Text.Encoding.ASCII.GetBytes(cmd);for (int i = 0; i < cmdArr.Length; i++){bcc = (byte)(bcc ^ cmdArr[i]);}return bcc.ToString("X2");}#endregion#region ASC/// <summary>/// ASC/// </summary>/// <param name="cmd"></param>/// <returns></returns>public static int Asc(string cmd){if (cmd.Length == 1){ASCIIEncoding ascii = new ASCIIEncoding();int intAscii = (int)ascii.GetBytes(cmd)[0];return intAscii;}else{return -1;}}#endregion#region 将整形转换为16进制 然后低位在前 高位在后/// <summary>/// 将整形转换为16进制 然后低位在前 高位在后/// </summary>/// <param name="value"></param>/// <returns></returns>private string ConvertShortToPlcFormat(int value){string temp = value.ToString("X4");return temp.Substring(2, 2) + temp.Substring(0, 2);}#endregion#region 释放public void Dispose(){if (serialPort != null){if (serialPort.IsOpen){serialPort.Close();Thread.Sleep(10);serialPort.Dispose();}}}#endregion#region 获得所有串口名数组/// <summary>/// 所有串口名数组/// </summary>/// <returns></returns>public static List<string> GetPortsName(){return SerialPort.GetPortNames().ToList<string>();}#endregion }
11、项目案例
以下是PLC定义的值表内容
来看看我的项目截图
12、总结
只要我们明白NewTocol协议的机制和内容,其他的逻辑部分的处理对于我们程序猿来说就是洒洒水的事的,祝大家看完我的博客可以有所收获,也欢迎大家踊跃来交流交流技术。
ps:部分截图和文字内容来自网上,程序是自己写的,如有侵权,请告知删除,写写。
C#上位机和松下PLC通讯相关推荐
- wincc上位机与1200组态步骤_组态上位机WINCC与PLC通讯连接
WINCC 与 PLC 通讯连接 1 . STEP 7 硬件组态 STEP7 设置 MPI 通讯, 2 .安装 CP5611 通讯板卡 3 .添加驱动程序和系统参数设置 打开 WINCC 工程在 Ta ...
- 上位机与PLC 通讯源码 上位机与三菱PLC,西门子PLC通讯 同时一起通讯,单独控制,三菱采用官方MX 通讯,支持三菱FX系列,
上位机与PLC 通讯源码 上位机与三菱PLC,西门子PLC通讯 同时一起通讯,单独控制,三菱采用官方MX 通讯,支持三菱FX系列,A系列,Q系列,L系列,R系列,全系系列,各种串口和各种网口通讯,, ...
- 上位机与PLC 通讯源码 上位机与三菱PLC,西门子PLC通讯
上位机与PLC 通讯源码 上位机与三菱PLC,西门子PLC通讯 同时一起通讯,单独控制,三菱采用官方MX 通讯,支持三菱FX系列,A系列,Q系列,L系列,R系列,全系系列,各种串口和各种网口通讯,, ...
- 上位机与PLC 通讯源码DEMO 上位机与三菱PLC,西门子PLC通讯 同时一起通讯,单独控制
上位机与PLC 通讯源码DEMO 上位机与三菱PLC,西门子PLC通讯 同时一起通讯,单独控制,三菱采用官方MX 通讯,支持三菱FX系列,A系列,Q系列,L系列,R系列,全系系列,各种串口和各种网口通 ...
- 【无标题】C#上位机与三菱PLC FX2NC通讯方法
*** C#上位机与三菱PLC FX2NC通讯方法*** 第一步从官网中下载MX COMPONENT并安装好; 第二步从安装好的MX COMPONENT路径中找到"Samples" ...
- S7300与G120变频器控制上位机 C#与S7300通讯上位机
S7300与G120变频器控制上位机 C#与S7300通讯上位机 提供给需要学习的同学,C#读取,经过xml配置文件读取进行定义,G120使用说明,写入S7300控制西门子G120源代码,手动控制正反 ...
- VB.NET上位机和西门子PLC通信模块程序
VB.NET上位机和西门子PLC通信模块程序 新项目可直接调用模块! 可创建与PLC对应的变量表! 项目直接访问变量,省去中间转化步骤! 目前支持常用的布尔.字符.整数.浮点数变量! 模块中带有参数设 ...
- 先学习上位机编程还是PLC编程
当我们们谈到学习自动化编程时,可能会面临一个选择:学习上位机编程还是PLC编程?针对这个问题,我想说,这两种编程方式之间没有直接的联系.PLC编程是基于梯形图的编程方式,而上位机编程则是基于代码的编程 ...
- 上位机与fx3u通讯串口_c#上位机与三菱PLC(FX3U)串口通讯
项目中会经常用到上位机与PLC之间的串口通信,本文介绍一下C#如何编写上位机代码 与三菱FX3U进行通讯 1. 第一种方法是自己写代码实现,主要代码如下: //对PLC的Y7进行置1 byte[] Y ...
最新文章
- ScrollView 嵌套 ListView 只显示第一行的源码分析
- Spring Boot 整合 Elasticsearch,实现 function score query 权重分查询
- 利用BeanUtils在对象间复制属性
- OSS 下载延迟或超时
- 数据中心的未来:一体化无人值守
- 为防止员工带薪拉屎,快手公司推出“计时厕所”?官方称只是为了测试!
- 1.2.5 计算机系统的多级层次结构(硬联逻辑级、微程序级、传统机器级、操作系统级、汇编语言级、高级语言级、应用语言级)
- Jira Concept- Issues
- GDOI2020游记
- linux umount 时出现device is busy 的处理方法--fuser
- o oia ospf 路由优先_乾颐堂军哥HCIE课程4-OSPF的3、4、5类LSA以及虚连接和转发地址的理论与实践...
- 通过代码学 Sutton 强化学习:SARSA、Q-Learning 时序差分算法训练 CartPole
- JSR-303 数据校验学习
- svn 把本地的项目,上传到服务器端
- 关于微信隐藏分享按钮的心得
- java文件下载文件损坏_java上传并下载以及解压zip文件有时会报文件被损坏错误分析以及解决...
- vue中如何设置鼠标经过切换样式
- html图片缩放全部显示不全,100% width CSS 在缩小/放大窗口时候内容被截断或显示不全...
- 全国院线总票房破50亿!影院复工后,哪些电影最受欢迎?可视化案例
- ReadFile WriteFile DO_DIRECT_IO 中的IRP操作
热门文章
- Navicat链接数据库奇葩错误
- MySql的数据找回
- Virtualbox虚拟机网络配置(NAT + Host-only - Bridged) 实现主机->虚拟机,虚拟机->虚拟机
- 关于等号数量判断的问题
- 常见的几种最优化方法Matlab原理和深度分析
- 【引用】稳压二极管扩流电路
- 实现微信跳转外部浏览器从而生成微信跳转链接
- 网站架构之缓存应用(3)实现篇
- oracle sys dba 01031,SYS用户登录Oracle报错ORA-01031: insufficient privileges
- 英语语音语调练习-American Accent Training(PDF+CD1-5下载)