一.串口通讯方式
首先,串口、UART口、COM口、U口是指物理接口形式(硬件)。TTL、RS-232和RS-485是指电平标准(电信号)。
串口:串口是一个统称。UART、TTL、RS232、RS485都遵循类似的通信时序协议,所以都称为串口。
工业控制中常用的协议:RS232 RS485
RS232:是电子工业协会(EIA)制定的异步传输标准接口,同时对应电平标准和通信协议(时序)。它的电平标准是0为+3v ~+15v,1为-3v ~-15v。rs232的逻辑电平与TTL不同,但协议相同。
RS485:RS485是一种串行接口标准。长距离传输采用差分传输,传输的是差分信号。抗干扰能力比RS232强很多。两条线的压差为-(2~6)V为0,两条线的压差为+(2~6)V为1。
常见的D型9针串口(通俗说法)。你可以在台式电脑后面看到它。这个接口只有两种协议:RS-232和RS-485。
二.网口通讯方式
TCP/IP是各种互联网相关协议的总称,如TCP、UDP、IP、FTP、HTTP、ICMP、TP等。像这样组装与互联网相关的协议总是被称为TCP/IP。也有人说TCP/IP指的是TCP和IP。还有一种说法是TCP/IP是IP协议在通信过程中使用的协议族的总称。
工业控制中常用的通信是TCP和UDP。
两者的区别是:需要在传输层实现可靠传输时使用TCP。因为它是面向链路的,具有顺序控制和重传控制等机制,所以可以为应用程序提供可靠的传输。一方面,UDP主要用于需要高速传输和实时性的通信或广播通信。让我们举一个通过IP电话打电话的例子。如果使用TCP,数据在传输过程中如果丢失会重新传输,但是呼叫者的语音无法顺利传输,会导致正常通信失败。有了UDP,他就不转发了。所以不会有声音大幅度延迟的问题。即使丢失了一些数据,也支持影响一小部分的呼叫。此外,在多播和广播通信中使用UDP代替TCP。
三.确定PLC硬件
首先要确定PLC支持网口还是串口通信。大部分都支持RS232,TCP或者UDP,有的可能需要买扩展模块才能通讯。
四.确定PLC通信协议
通信协议:指两个实体完成通信或服务必须遵循的规则和协议。通过通信信道和设备互连的位于不同地理位置的多个数据通信系统如果要一起工作以实现信息交换和资源共享就必须有一种共同的语言。交流什么,怎么交流,什么时候交流,都要遵循一些双方都能接受的规则。这个规则就是通信协议。
以欧姆龙FINS通讯协议为例。
1.握手命令
1。客户端向服务器发送命令00000000。该命令长20字节,分为5组,每组4字节。分别是:
header(FINS)+length(Hex0C)+command(00000000)+error code(0000000)+客户端节点地址。
46494E53是FINS的ASCII码值,是命令头。
0000000C是命令长度20。
00000000是命令代码。
00000000是一个错误代码。
000000005是客户端节点地址,即计算机IP地址的最后一位。
输入:
46494 e53 00000000 c 00000000000000000005
点击发送,PLC立即响应:
46494 e53 00000000000001。接下来需要的是之前介绍的HostLink协议中FINS的知识。
图3网络调试助手握手成功
2。这是服务器(PLC)发送给客户端(计算机)的命令00000001。该命令长24字节,分为6组,每组4字节。分别是:
header(FINS)+length(hex 10)+command(00000001)+错误码+客户端节点地址+服务器地址。
以上命令错误代码为0,客户端ip地址05已被服务器32成功记录(hex20)。
如果出现错误,服务器响应的命令将包含错误代码。如果连接断开,端口将立即关闭。连接建立后,不要再发送这个命令,否则服务器会返回03错误代码,这是一个不支持的命令。所有错误代码如下:
十六进制错误代码含义
00000000正常[/br
00000001头不是’ ‘ FINS’ (ASCII码)。
00000002数据太长。
00000003不支持的命令。所有连接都被占用。
00000021指定的节点已经连接。
00000022未指定的IP地址试图访问受保护的节点。
00000023客户端FINS节点地址超出范围。
00000024同一FINS节点地址已被使用。
00000025所有可用的节点地址已被使用。
二、FINS帧发送命令
如果要向服务器发送FINS帧,就需要这个命令。由于FINS帧的长度是12-2012,所以命令长度是可变的,
header(FINS)+length+command(00000002)+error code+FINS帧。
关于FINS命令帧的内容,请参考欧姆龙PLC的HostLink通信协议——FINS命令的字/位操作,其中包含存储区代码和操作代码的内容。
例2-1。从读取DM0开始的两个通道:
发送:
46494 e53 0000000000001 a 00000000000800000000000500ff 0101 820000000000002[/br][/br
00ff 0101:0101是读取操作;
82000000: 82是DM存储区的代码,000000是起始地址;
0002:是量。
Return:
46494 e53 0000000001 a 0000000000000 05000020 000 ff 0101 000001234 5678
00001234:0000代表操作成功,1234。
5678:D1 = hex 5678
例2-2,W210寄存器写入hex 0388:
Send:
46494 e53 0000000001 c 0000000002。
00ff0102: 0102是写操作代码;
b100D200: B1是W字代码,00D2是起始地址,十六进制00d2 = 212,;
00010388:写入的次数,0388是第一次写入的内容;
回复:
46494 e53 000000016 00000000200 050000020 00ff 0102 0000
0102 0000代表写成功。
例2-3,W210寄存器读取:
发送:
46494 e53 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001
b100D200: B1是W字代码,00D2是起始地址,十六进制00d2 = 212,;
0001:是读取数量。
回复:
46494 e53 0000000018 00000000200 050000020 00ff 0101 00000388
0102 0000,代表阅读成功。W210=Hex0388
示例2-4,强制W212.01 = On:
发送:
46494 e530000000000000000000000000000000000000000000000000000000000000000000000000
00010001:前0001为数量,后0001代表强制设置操作;
3100D401: 31是w位码,00D401是起始地址,Hex00D4.01=212.01。
响应:
46494 e53 000000016 00000002 0000000000200 050000200 ff 2301 0000
2301后面跟0000表示操作成功。
注意CX编程器查看窗口中W212.01的值1后的单词(强制)。
图4网络调试助手的强制设置
图5 CX-编程器强制设置成功
示例2-5,强制w 212.01 = off:
Send:
46494 e53 000000000000000000000000000000000000000000000000000000000000000000
00010000: 0001为数量,0000代表强制复位操作;
3100D401: 31是w位码,00D401是起始地址,Hex00D4.01=212.01。
响应:
46494 e53 000000016 00000002 0000000000200 050000200 ff 2301 0000
2301后面跟0000表示操作成功。
例2-6。取消W212.01的强制:
发送:
46494 e53 00000000000000000000000000000000000005 00ff 2301 000001 ffff 3100d 401[
00ff 2301:2301
0001FFFF: 0001是数量,FFFF代表取消强制操作;
3100D401: 31是w位码,00D401是起始地址,Hex00D4.01=212.01。
响应:
46494 e53 000000016 00000002 0000000000200 050000200 ff 2301 0000
2301后面跟0000表示操作成功。
注意CX-编程器查看窗口中W212.01的值0后缺少(forced)字样,表示强制已成功取消。
图6网络调试助手取消强制
图7 CX程序员成功地取消了强制执行。
与CIO不同的是,对于W、A、H、D等寄存器的位操作,实际上没有强制操作,直接写更简洁,可以减少操作步骤。下面是W位操作的一个例子。
例2-7,W212.01按位置:
发送:
46494 e 53 000000001 b 000000000000800000200 2000000000005 00ff 0102 3100d 401 00000101
20000000005:20为目的地址,05为源地址;
00ff0102: 0102是寄存器写操作代码;
3100D401: 31是W位代码,00D401是地址,Hex00D4.01 = 212.01;
000101: 0001是数量,01代表写值1;
响应:
46494 e53 00000016 000000020000020000000500ff 010200000
0000后跟0102表示操作成功。
例2-8,W212.01逐位复位:
发送:
46494 e53 00000000000000000000000002000000000000000000000000000005 00102 31000d 401 00000000100。
2000000005:20是目的地址,05是源地址;
00ff0102: 0102是寄存器写操作代码;
3100D401: 31是W位代码,00D401是地址,Hex00D4.01 = 212.01;
000100: 0001是数量,00代表写值0;
回应:
46494 e53 00000016 0000000200000020000000000500 ff 010200000
0000后跟0102表示操作成功。
五.使用C#进行通信
1.串行端口
SerialPort使用C#中的Serialport类
注意通信协议中的报文内容是十六进制、ASCII码还是普通字符串。如果有格式的话,注意收发信息的转换。
使用系统;使用系统。集合。泛型;使用系统。Linq使用系统。文本;使用系统。线程。任务;使用系统。IO .端口;使用系统。穿线;使用系统。Windows . Formspublic类serial op:IDisposable { private serial port Comport = new serial port();private AutoResetEvent _ manual event = new AutoResetEvent(false);private object _ lock read = new object();private object _ lock write = new object();私有字节[]_ reivedData;私有字符串reive = null私有对象on recive = new object();公共委托void UpdateByteDelegate(byte[]reive);公共事件UpdateByteDelegate UpdateByte///& lt;总结& gt///打开串口//
网络端口分为客户端和服务器端,协议有TCP和UDP。
以TCP为例。
TCP客户端
public class TCP client { public bool connected = false;Socket socketClient//客户端接口任务threadAcceptClient,//客户端接收线程threadClient//客户端线程私有字符串IP address = string.emptyprivate int Port = 0;public TCP client(string IP address,int Port) { this。IpAddress = IpAddress这个。Port =端口;ClientConnectSever();}////& lt;总结& gt///客户端连接到服务器//
public class TcpServer { public delegate void UpdateObjectDelegate(object sender); public event UpdateObjectDelegate UpdataDataString; public event UpdateObjectDelegate UpdateMessage; private string IpAddress = string.Empty; private int Port = 0; public TcpServer(string IpAddress, int Port) { this.IpAddress = IpAddress; this.Port = Port; } static Socket serverSocket; List<Socket> ClientList = new List<Socket>(); //客户端列表 public bool connected = false; public void StartListen() { Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); serverSocket = socket; //ip port socket.Bind(new IPEndPoint( IPAddress.Parse(IpAddress), Port)); //listen socket.Listen(10);//连接等待队列 ThreadPool.QueueUserWorkItem(new WaitCallback(this.AcceptClientConnect), socket); } private void AcceptClientConnect(object socket) { var serverSocket = socket as Socket; UpdateMessage.Invoke("服务器开始监听"); while (true) { try { var proxSocket = serverSocket.Accept(); ClientList.Add(proxSocket); UpdateMessage.Invoke((object)("客户端" + proxSocket.RemoteEndPoint.ToString() + "连接上了")); //接受消息 ThreadPool.QueueUserWorkItem(new WaitCallback(this.ReceiveData), proxSocket); connected = true; } catch (Exception e) { UpdateMessage.Invoke("服务器监听错误:" + e.ToString()); connected = false; } } } private void ReceiveData(object obj) { Socket proxSocket = obj as Socket; byte[] data = new byte[1024 * 1024]; while (true) { int readLen = 0; try { readLen = proxSocket.Receive(data, data.Length,0); //if (readLen <= 0) //{ // //客户端正常退出 // UpdateMessage.Invoke(string.Format("客户端{0}正常退出", proxSocket.RemoteEndPoint.ToString())); // ClientList.Remove(proxSocket); // CloseListen(proxSocket); // connected = false; // return;//方法结束->终结当前接受客户端数据的异步线程 //} string txt = Encoding.Default.GetString(data, 0, readLen).TrimEnd('\0'); if(txt.Length>1) UpdataDataString.Invoke(txt); } catch (Exception ex) { //异常退出时 UpdateMessage.Invoke(string.Format("客户端{0}非正常退出,原因{1}", proxSocket.RemoteEndPoint.ToString(), ex.ToString())); ClientList.Remove(proxSocket); CloseListen(proxSocket); connected = false; return; } } } private void CloseListen(Socket proxSocket) { try { if (proxSocket.Connected) { proxSocket.Shutdown(SocketShutdown.Both); proxSocket.Close(100); connected = false; } } catch (Exception) { UpdateMessage("服务器关闭发生异常"); connected = false; } } public bool ServerSendData(string msg) { try { foreach (Socket s in this.ClientList) { //服务端广播式发送给客户端 (s as Socket).Send(Encoding.Default.GetBytes(msg)); } return true; } catch { return false; } } }
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。