串口:串口是一个统称。UART、TTL、RS232、RS485都遵循类似的通信时序协议,所以都称为串口。
:RS232RS485,工业控制中的通用协议
RS232:是电子工业协会(EIA)制定的异步传输标准接口,同时对应着电平标准和通信协议(时序)。它的电平标准:3V~ 15V对应0,-3v ~-15v对应1。rs232的逻辑电平与TTL不同,但协议相同。
RS485:RS485是一种串行端口接口标准。对于远距离传输,采用差分传输,传输的是差分信号,抗干扰能力比RS232强很多。两条线的压差为-(2~6)V为0,两条线的压差为(2~6)V为1。
常见的D型9针串口(通俗说法)。在台式电脑后边都可以看到。这种接口的协议只有两种:RS-232和RS-485
工业控制中常用的通信是TCP和UDP.
当需要在传输层实现可靠传输时,使用这两个区别就是:TCP。因为它是面向链路的,具有顺序控制和重传控制等机制,所以可以为应用程序提供可靠的传输。一方面,UDP主要用于需要高速传输和实时性的通信或广播通信。让我们举一个通过IP电话打电话的例子。如果使用TCP,数据在传输过程中如果丢失会重新传输,但是呼叫者的语音无法顺利传输,会导致正常通信失败。有了UDP,他就不转发了。所以不会有声音大幅度延迟的问题。即使丢失了一些数据,也支持影响一小部分的呼叫。此外,在多播和广播通信中使用UDP代替TCP。
以欧姆龙FINS通讯协议为例。
首先,握手命令
1.客户端向服务器发送命令00000000。该命令长20字节,分为5组,每组4字节。分别是:FINS长度(Hex0C)命令(00000000)错误码(00000000)客户端节点地址。
46494E53是FINS的ASCII码值,是命令头。
0000000C是命令长度20。
000000000是命令代码。
000000000是一个错误代码。
00000005是客户端节点地址,它是计算机IP地址的最后一位。
在发送区输入:46494 e 5300000000000000000000000000000000000005。
点击发送,PLC会立即响应:46494 e 5300000001000000000000000000005000000000020。
至此我们已经成功完成了第一步!接下来需要的是之前介绍的HostLink协议中FINS的知识。
图3。网络调试助手握手成功。2、,这是服务器端(PLC)向客户端(电脑)发送's订单00000001。该命令长24字节,分为6组,每组4字节。分别是:header(FINS)length(hex 10)command(00000001)错误码客户端节点地址服务器地址。
上述命令错误代码为0,客户端ip地址05已被服务器32成功记录(hex20)。
如果出现错误,服务器响应的命令将包含一个错误代码,连接将被断开,端口将立即关闭。连接建立后,不要再发送这个命令,否则服务器会返回03错误代码,这是一个不支持的命令。所有错误代码如下:
十六进制错误代码含义
000000000正常
000000001标题不是“fins”(ascii码)。
数据太长。
00000003不支持的命令。
00000020所有连接都被占用。
。图4网络调试助手强制置位
图5CX-Programmer强制置位成功
例2-5、强制W212.01=Off:
发送:
46494E530000001C0000000200000000800002002000000500FF2301000100003100D401
20000005:20是目标地址,05是源地址;
00FF2301:2301是强制操作代码;
00010000:0001是数量,0000代表强制复位操作;
3100D401:31是W位代码,00D401是起始地址,Hex00D4.01=212.01。
回应:
46494E53000000160000000200000000C00002000500002000FF23010000
2301后面紧跟的0000表示操作成功。
例2-6、取消W212.01强制:
发送:
46494E530000001C0000000200000000800002002000000500FF23010001FFFF3100D401
20000005:20是目标地址,05是源地址;
00FF2301:2301是强制操作代码;
0001FFFF:0001是数量,FFFF代表取消强制操作;
3100D401:31是W位代码,00D401是起始地址,Hex00D4.01=212.01。
回应:
46494E53000000160000000200000000C00002000500002000FF23010000
2301后面紧跟的0000表示操作成功。
注意在CX-Programmer查看窗口中W212.01的值0后面的(强制)字样不见了,表示已经成功地取消了强制。
图6网络调试助手取消强制
图7CX-Programmer取消强制成功
与CIO不同,对于W、A、H及D这样的寄存器进行位操作,其实不用强制操作,直接写入更简洁,可以减少操作步骤,下面以W位操作为例介绍。
例2-7、W212.01按位置位:
发送:
46494E530000001B0000000200000000800002002000000500FF01023100D401000101
20000005:20是目标地址,05是源地址;
00FF0102:0102是寄存器写操作代码;
3100D401:31是W位代码,00D401是地址,Hex00D4.01=212.01;
000101:0001是数量,01代表写入值1;
回应:
46494E53000000160000000200000000C00002002000000500FF01020000
0102后面紧跟的0000表示操作成功。
例2-8、W212.01按位复位:
发送:
46494E530000001B0000000200000000800002002000000500FF01023100D401000100
20000005:20是目标地址,05是源地址;
00FF0102:0102是寄存器写操作代码;
3100D401:31是W位代码,00D401是地址,Hex00D4.01=212.01;
000100:0001是数量,00代表写入值0;
回应:
46494E53000000160000000200000000C00002002000000500FF01020000
0102后面紧跟的0000表示操作成功。
1.串口
串口在C#中使用的是SerialPort这个类
要注意通讯协议中报文内容是16进制,还是ASCII码,还是就是普通的字符串,有格式的话注意发送和接收的报文需要转换
usingSystem;usingSystem.Collections.Generic;usingSystem.Linq;usingSystem.Text;usingSystem.Threading.Tasks;usingSystem.IO.Ports;usingSystem.Threading;usingSystem.Windows.Forms;publicclassSerialOP:IDisposable{privateSerialPortComport=newSerialPort();privateAutoResetEvent_manualEvent=newAutoResetEvent(false);privateobject_lockRead=newobject();privateobject_lockWrite=newobject();privatebyte[]_reivedData;privatestringreive=null;privateobjectonRecive=newobject();publicdelegatevoidUpdateByteDelegate(byte[]reive);publiceventUpdateByteDelegateUpdateByte;//////打开串口/// ///////////////publicboolOpen(stringcom,stringbps,stringdatabit,stringstopbit,stringcheck){try{if(Comport.IsOpen)Comport.Close();Thread.Sleep(100);//串口端口号Comport.PortName=com;//串口波特率Comport.BaudRate=int.Parse(bps);//串口数据位Comport.DataBits=int.Parse(databit);//串口停止位switch(stopbit){case"0":Comport.StopBits=StopBits.None;break;case"1":Comport.StopBits=StopBits.One;break;case"1.5":Comport.StopBits=StopBits.OnePointFive;break;case"2":Comport.StopBits=StopBits.Two;break;default:Comport.StopBits=StopBits.None;break;}//串口奇偶校验switch(check){case"无":Comport.Parity=Parity.None;break;case"奇校验":Comport.Parity=Parity.Odd;break;case"偶校验":Comport.Parity=Parity.Even;break;default:Comport.Parity=Parity.None;break;}//Comport.ReceivedBytesThreshold=1;//1字节触发数据获取//Comport.DataReceived+=newSerialDataReceivedEventHandler(OnReceived);Comport.Open();Comport.DtrEnable=true;Comport.RtsEnable=true;returntrue;}catch(Exceptionex){LogHelper.error(ex.ToString());MessageBox.Show("连续打开关闭串口出现错误:"+ex.Message,"SerialPort---Message",MessageBoxButtons.OK,MessageBoxIcon.Error,MessageBoxDefaultButton.Button1,MessageBoxOptions.DefaultDesktopOnly);returnfalse;}}//////检测串口是否打开/// /// privateboolIsOpen(){try{if(Comport==null)returnfalse;if(Comport.IsOpen)returntrue;elsereturnfalse;}catch(Exceptionex){LogHelper.error(ex.ToString());MessageBox.Show("检测串口是否打开出现错误:"+ex.Message,"SerialPort---Message",MessageBoxButtons.OK,MessageBoxIcon.Error,MessageBoxDefaultButton.Button1,MessageBoxOptions.DefaultDesktopOnly);returnfalse;}}privatevoidOnReceived(objectsender,SerialDataReceivedEventArgse){try{lock(onRecive){byte[]reive=newbyte[Comport.BytesToRead];Comport.Read(reive,0,Comport.BytesToRead);UpdateByte.Invoke(reive);}}catch(Exceptionex){LogHelper.error(ex.ToString());MessageBox.Show("串口接收数据出现错误:"+ex.Message,"SerialPort---Message",MessageBoxButtons.OK,MessageBoxIcon.Error,MessageBoxDefaultButton.Button1,MessageBoxOptions.DefaultDesktopOnly);}finally{_manualEvent.Set();}}//////字符串转ASII码发送/// /// publicvoidSend_ASCII(stringstr){try{lock(_lockWrite){varByteSendW=Encoding.ASCII.GetBytes(str);//把发送数据转换为ASCII数组Comport.Write(ByteSendW,0,ByteSendW.Length);}}catch(Exceptionex){LogHelper.error(ex.ToString());MessageBox.Show("串口发送数据出现错误:"+ex.Message,"SerialPort---Message",MessageBoxButtons.OK,MessageBoxIcon.Error,MessageBoxDefaultButton.Button1,MessageBoxOptions.DefaultDesktopOnly);}}//////发送16进制数组,需要用,隔开/// //////publicvoidSend_HEX(stringHex){try{lock(_lockWrite){//16进制字符串转换成16进制数组varHEX=Hex.Split(',').Select(temp=>"0x"+temp).Select(temp=>(byte)Convert.ToInt32(temp,16)).ToArray();Comport.Write(HEX,0,HEX.Length);}}catch(Exceptionex){LogHelper.error(ex.ToString());MessageBox.Show("串口发送数据出现错误:"+ex.Message,"SerialPort---Message",MessageBoxButtons.OK,MessageBoxIcon.Error,MessageBoxDefaultButton.Button1,MessageBoxOptions.DefaultDesktopOnly);}}//////发送字符串不转换/// ///publicvoidSend_String(stringstr){try{lock(_lockWrite){Comport.Write(str);}}catch(Exceptionex){LogHelper.error(ex.ToString());MessageBox.Show("串口发送数据出现错误:"+ex.Message,"SerialPort---Message",MessageBoxButtons.OK,MessageBoxIcon.Error,MessageBoxDefaultButton.Button1,MessageBoxOptions.DefaultDesktopOnly);}}//////接收ASCII码,并转换成字符串/// ///publicvoidRead_ASCII(outstringstr){str="error";try{lock(_lockRead){if(Comport.BytesToRead==0){LogHelper.error("缓存区没有数据");return;}for(inti=0;i<100;i++)//读取100个字节{if(Comport.BytesToRead==0){str=reive;reive=null;break;}stringreivestring="";_reivedData=newbyte[1];Comport.Read(_reivedData,0,_reivedData.Length);reivestring=Encoding.ASCII.GetString(_reivedData);reive+=reivestring;//Comport.DiscardInBuffer();}}}catch(Exceptionex){str=ex.ToString();LogHelper.error(ex.ToString());MessageBox.Show("串口读取数据出现错误:"+ex.Message,"SerialPort---Message",MessageBoxButtons.OK,MessageBoxIcon.Error,MessageBoxDefaultButton.Button1,MessageBoxOptions.DefaultDesktopOnly);}}//////接收字符串,不进行任何转换/// ///publicvoidRead_String(outstringstr){str="error";try{lock(_lockRead){if(Comport.BytesToRead==0){LogHelper.error("缓存区没有数据");return;}for(inti=0;i<100;i++)//读取100个字节{if(Comport.BytesToRead==0){str=reive;reive=null;break;}_reivedData=newbyte[1];Comport.Read(_reivedData,0,_reivedData.Length);reive+=_reivedData[0].ToString()+"";}}}catch(Exceptionex){str=ex.ToString();LogHelper.error(ex.ToString());MessageBox.Show("串口读取数据出现错误:"+ex.Message,"SerialPort---Message",MessageBoxButtons.OK,MessageBoxIcon.Error,MessageBoxDefaultButton.Button1,MessageBoxOptions.DefaultDesktopOnly);}}//////接收到16进制,转成字符串/// ///publicvoidRead_HEX_String(outstringstr){str="error";try{lock(_lockRead){if(Comport.BytesToRead==0){LogHelper.error("缓存区没有数据");return;}for(inti=0;i<100;i++)//读取100个字节{if(Comport.BytesToRead==0){reive=reive.Substring(0,reive.Length-1);//移除最后一位逗号str=reive;reive=null;break;}_reivedData=newbyte[1];Comport.Read(_reivedData,0,_reivedData.Length);reive+=_reivedData[0].ToString("x2").ToUpper()+",";}}}catch(Exceptionex){str=ex.ToString();LogHelper.error(ex.ToString());MessageBox.Show("串口读取数据出现错误:"+ex.Message,"SerialPort---Message",MessageBoxButtons.OK,MessageBoxIcon.Error,MessageBoxDefaultButton.Button1,MessageBoxOptions.DefaultDesktopOnly);}}//////关闭串口/// publicvoidClose(){try{if(Comport.IsOpen)Comport.Close();}catch(Exceptionex){LogHelper.error(ex.ToString());MessageBox.Show("关闭串口出现错误:"+ex.Message,"SerialPort---Message",MessageBoxButtons.OK,MessageBoxIcon.Error,MessageBoxDefaultButton.Button1,MessageBoxOptions.DefaultDesktopOnly);}}//////释放串口/// publicvoidDispose(){try{if(Comport.IsOpen)Comport.Close();Comport.Dispose();}catch(Exceptionex){LogHelper.error(ex.ToString());MessageBox.Show("释放串口出现错误:"+ex.Message,"SerialPort---Message",MessageBoxButtons.OK,MessageBoxIcon.Error,MessageBoxDefaultButton.Button1,MessageBoxOptions.DefaultDesktopOnly);}//Comport.DataReceived-=newSerialDataReceivedEventHandler(Comport_DataReceived);}//////CRC16校验/// ////// publicstringCRCCalc(stringdata){string[]datas=data.Split('');Listbytedata=newList();foreach(stringstrindatas){bytedata.Add(byte.Parse(str,System.Globalization.NumberStyles.AllowHexSpecifier));}byte[]crcbuf=bytedata.ToArray();//计算并填写CRC校验码intcrc=0xffff;intlen=crcbuf.Length;for(intn=0;n>1;crc=crc&0x7fff;if(TT==1){crc=crc^0xa001;}crc=crc&0xffff;}}string[]redata=newstring[2];redata[1]=Convert.ToString((byte)((crc>>8)&0xff),16);redata[0]=Convert.ToString((byte)((crc&0xff)),16);returnredata[0].ToUpper()+""+redata[1].ToUpper();}}
2.网口
网口分客户端和服务器,协议有TCP和UDP
以下以TCP为例
TCP客户端
publicclassTcpClient{publicboolconnected=false;SocketsocketClient;//客户端接口TaskthreadAcceptClient,//客户端接收线程threadClient;//客户端线程privatestringIpAddress=string.Empty;privateintPort=0;publicTcpClient(stringIpAddress,intPort){this.IpAddress=IpAddress;this.Port=Port;ClientConnectSever();}//////客户端连接服务器/// publicboolClientConnectSever(){IPAddressip=IPAddress.Parse(IpAddress);IPEndPointport=newIPEndPoint(ip,Port);//服务器port//创建TCP类型端口socketClient=newSocket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);try{socketClient.Connect(port);connected=true;returntrue;}catch(Exceptionex){LogHelper.error(IpAddress+"连接服务器失败:"+ex.Message);returnfalse;}}publicvoidColseConnect(){socketClient.Close();}publicboolClientSendData(stringdata){if(!connected){LogHelper.error(IpAddress+"未连接到服务器");returnfalse;}byte[]msg=Encoding.Default.GetBytes(data);try{NetworkStreamnetStream=newNetworkStream(socketClient);netStream.Write(msg,0,msg.Length);netStream.Flush();returntrue;}catch(Exceptionex){LogHelper.error(IpAddress+"发送失败:"+ex.Message);returnfalse;}}publicstringClientReadData(){try{if(!connected){LogHelper.error(IpAddress+"未连接到服务器");return"error";}NetworkStreamnetStream=newNetworkStream(socketClient);byte[]dataSize=newbyte[1024];netStream.Read(dataSize,0,dataSize.Length);varResult=Encoding.Default.GetString(dataSize).TrimEnd('\0');if(Result.Length>1)returnResult;elsereturnstring.Empty;//this.rtb_accept1.Rtf=Encoding.Unicode.GetString(message);}catch(Exceptionex){LogHelper.error(IpAddress+"读取数据失败:"+ex.Message);return"error";}}}
TCP服务器
publicclassTcpServer{publicdelegatevoidUpdateObjectDelegate(objectsender);publiceventUpdateObjectDelegateUpdataDataString;publiceventUpdateObjectDelegateUpdateMessage;privatestringIpAddress=string.Empty;privateintPort=0;publicTcpServer(stringIpAddress,intPort){this.IpAddress=IpAddress;this.Port=Port;}staticSocketserverSocket;ListClientList=newList();//客户端列表publicboolconnected=false;publicvoidStartListen(){Socketsocket=newSocket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);serverSocket=socket;//ipportsocket.Bind(newIPEndPoint(IPAddress.Parse(IpAddress),Port));//listensocket.Listen(10);//连接等待队列ThreadPool.QueueUserWorkItem(newWaitCallback(this.AcceptClientConnect),socket);}privatevoidAcceptClientConnect(objectsocket){varserverSocket=socketasSocket;UpdateMessage.Invoke("服务器开始监听");while(true){try{varproxSocket=serverSocket.Accept();ClientList.Add(proxSocket);UpdateMessage.Invoke((object)("客户端"+proxSocket.RemoteEndPoint.ToString()+"连接上了"));//接受消息ThreadPool.QueueUserWorkItem(newWaitCallback(this.ReceiveData),proxSocket);connected=true;}catch(Exceptione){UpdateMessage.Invoke("服务器监听错误:"+e.ToString());connected=false;}}}privatevoidReceiveData(objectobj){SocketproxSocket=objasSocket;byte[]data=newbyte[1024*1024];while(true){intreadLen=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;//方法结束->终结当前接受客户端数据的异步线程//}stringtxt=Encoding.Default.GetString(data,0,readLen).TrimEnd('\0');if(txt.Length>1)UpdataDataString.Invoke(txt);}catch(Exceptionex){//异常退出时UpdateMessage.Invoke(string.Format("客户端{0}非正常退出,原因{1}",proxSocket.RemoteEndPoint.ToString(),ex.ToString()));ClientList.Remove(proxSocket);CloseListen(proxSocket);connected=false;return;}}}privatevoidCloseListen(SocketproxSocket){try{if(proxSocket.Connected){proxSocket.Shutdown(SocketShutdown.Both);proxSocket.Close(100);connected=false;}}catch(Exception){UpdateMessage("服务器关闭发生异常");connected=false;}}publicboolServerSendData(stringmsg){try{foreach(Socketsinthis.ClientList){//服务端广播式发送给客户端(sasSocket).Send(Encoding.Default.GetBytes(msg));}returntrue;}catch{returnfalse;}}}
上一篇:花甲里面都能吃吗?