C#实现上位机与欧姆龙PLC的通信.docx
《C#实现上位机与欧姆龙PLC的通信.docx》由会员分享,可在线阅读,更多相关《C#实现上位机与欧姆龙PLC的通信.docx(13页珍藏版)》请在冰豆网上搜索。
C#实现上位机与欧姆龙PLC的通信
本人最近做了一个上位机与欧姆龙PLC通信的系统。
与大家分享分享心得吧!
废话不多说,先看看实际的效果图吧!
由于本人能力有限,请大家多多指正吧!
要想做好上位机,就必须搞好通信问题。
欧姆龙PLC与上位机通信采用的是HostLink协议。
下面就先来介绍介绍HostLink协议。
使用HostLink协议要注意以下几点:
(1)通信线的连线,RS232口接线。
一般使用无握手信号连线,无握手信号连线仅需要3根线,1根地线,2根信号线:
发送线TXD,接受线RXD。
(2)通信参数的设置,一般我们VisualStdio中选用控件SerialPort,在其属性中选择波特率9600,数据位7位,偶检验,2个停止位。
在调试的时候可以用串口助手先进行调试,调试明白了再开始写上位机也不迟。
(3)HostLink协议的格式,一般包括起始符,单元号,正文,FCS检验码,结束符。
起始符都是@,一般你的上位机控制的只有一个下位机,那么你的单元号就是00,正文内容是举一个例子,比如说@00WD0100123456*CR,@是起始符,00是单元号,WD0100表示向DM区0100,写入数据,写入的就是1234(写的是字符,传入PLC中自动变成16进制的1234),56是FCS校验,FCS校验就是校验码前面所有的字符按其ASCII码异或,结果是两位的。
在写上位机之前你也可以现写一个FCS校验器,反正后续的代码总是要写的。
*CR是结束符,*比较容易打出来,CR就是回车键,程序中表现时可以用”\r”来表示。
再比如说@00RD0100000156*CR是向DM0100开始的数据区,连续读0001个数据,在这里也就是连续读一个数据。
当然你给PLC发一个数据,如果格式正确,PLC也会响应你一个数据,见下表。
在这里讲一下返回的状态码,根据状态码,我们可以知道发送的数据有没有出现错误。
正常情况下,返回的只能是00,如果不是00,请根据上表,自己修改错误。
下面我先把我写的FCS校验码贴出来吧,
代码如下:
usingSystem;
usingSystem.Collections.Generic;
usingSystem.ComponentModel;
usingSystem.Data;
usingSystem.Drawing;
usingSystem.Linq;
usingSystem.Text;
usingSystem.Threading.Tasks;
usingSystem.Windows.Forms;
namespace异或测试
{
publicpartialclassForm1:
Form
publicForm1()
InitializeComponent();
}
privatevoidbutton1_Click(objectsender,EventArgse)
stringstr=textBox1.Text;
Int16[]a=newInt16[str.Length];//将字符串中的一个个字符,分离出来,并保存
for(inti=0;i{a[i]=Convert.ToInt16(str[i]);}Int16FCS=0;for(inti=0;i{FCS=(Int16)(FCS^a[i]);//异或计算FCS码}stringstr_FCS;//把计算出来的FCS码再转成字符串str_FCS=FCS.ToString("x2");textBox2.Text=str_FCS;}}}写的不太好,就当自己测试的时候用了。特别要注意的是,PLC在运行状态是不能向DM区写数据的。必要的时候我们可以通过向PLC发送不同指令来改变其工作模式。指令如下:模式的改变PROGRAM@00SC0050*MONITOR@00SC0252*RUN@00SC0353*"00"是站號最基本的东西已经说得比较清楚了,下面把代码也贴出来吧,同样代码也写的比较乱,让内行人见笑了。大体上看一共就用了这几个事件。下面是其具体的代码:本人做的是一个恒压供水的项目,这里只是一个产品的测试,故有很多地方还不严谨,思路不是很清楚,我感觉这也我一周弄出来的,还是比较不错的usingSystem;usingSystem.Collections.Generic;usingSystem.ComponentModel;usingSystem.Data;usingSystem.Drawing;usingSystem.Linq;usingSystem.Text;usingSystem.Threading.Tasks;usingSystem.Windows.Forms;usingSystem.IO.Ports;namespaceWindowsFormsApplication2{publicpartialclassForm1:Form{publicForm1(){InitializeComponent();System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls=false;//关闭线程检查}privatevoidForm1_Load(objectsender,EventArgse){//窗口载入时,自动搜索可用的串口stringbuffer;comboBox1.Items.Clear();for(inti=0;i<20;i++){try{buffer="COM"+i.ToString();serialPort1.PortName=buffer;serialPort1.Open();comboBox1.Items.Add(buffer);serialPort1.Close();}catch{}}serialPort1.WriteLine("MONITOR@00SC0252*"+"\r");//让PLC工作在监视模式下//serialPort1.WriteLine("@00WD0201000050*"+"\r");}privatevoidserialPort1_DataReceived(objectsender,SerialDataReceivedEventArgse){//串口接受事件所触发执行if(radioButton1.Checked){//如果处于运行状态,则接受的字符串只显示在接受数据去//高级功能中的接受面板不予显示stringstr=serialPort1.ReadExisting();//接受到的字符stringstr1;//通道1的字符//stringstr2;//通道2的字符//该设备没有外接stringstr3;//通道3的字符//stringstr4;//通道4的字符//该设备没有外接stringstr_Z_S_PLC;//转速的字符PLC模块所采集的try{str1=str.Substring(8,2);}catch{str1="";}S_Y_1.Text=str1;Application.DoEvents();//System.Threading.Thread.Sleep(100);try{str3=str.Substring(12,2);}catch{str3="";}S_Y_3.Text=str3;Application.DoEvents();/******************///System.Threading.Thread.Sleep(100);//让线程在此休眠100MS,以便处理//其他线程的事情。//这对刷屏,text属性用比较大的作用S_Y_2.Text="60";S_Y_4.Text="60";Application.DoEvents();/******************/try{str_Z_S_PLC=str.Substring(15,3);}catch{str_Z_S_PLC="";//str_Z_S_PLC=str.Substring(15,3);}S_B_Z_S.Text=str_Z_S_PLC;Application.DoEvents();/******************/}else{stringstr_gaoji=serialPort1.ReadExisting();//接受到的字符textBox1.AppendText(str_gaoji);}}privatevoidbutton2_Click(objectsender,EventArgse){//高级功能中的发送按钮所执行的功能if(serialPort1.IsOpen){try{//若串口为打开状态则,发送命令serialPort1.WriteLine(textBox2.Text);}catch{MessageBox.Show("数据写入有错","错误");}}}privatevoidbutton1_Click(objectsender,EventArgse){//打开串口按钮执行的功能if(serialPort1.IsOpen){try{//若串口原状态是开,再按一次则关闭穿口serialPort1.Close();button1.Text="打开串口";ovalShape1.FillColor=Color.Gray;}catch{MessageBox.Show("串口关闭错误","错误");}}else{try{//若串口状态原先是关闭,则按一次打开串口serialPort1.PortName=comboBox1.Text;serialPort1.Open();button1.Text="关闭串口";ovalShape1.FillColor=Color.GreenYellow;}catch{MessageBox.Show("串口打开错误","错误");}}}privatevoidtimer1_Tick(objectsender,EventArgse){//每隔1S,上位机给PLC发送一条指令//PLC返回一条指令,用于刷新,各采集的数据if(serialPor
a[i]=Convert.ToInt16(str[i]);
Int16FCS=0;
for(inti=0;i{FCS=(Int16)(FCS^a[i]);//异或计算FCS码}stringstr_FCS;//把计算出来的FCS码再转成字符串str_FCS=FCS.ToString("x2");textBox2.Text=str_FCS;}}}写的不太好,就当自己测试的时候用了。特别要注意的是,PLC在运行状态是不能向DM区写数据的。必要的时候我们可以通过向PLC发送不同指令来改变其工作模式。指令如下:模式的改变PROGRAM@00SC0050*MONITOR@00SC0252*RUN@00SC0353*"00"是站號最基本的东西已经说得比较清楚了,下面把代码也贴出来吧,同样代码也写的比较乱,让内行人见笑了。大体上看一共就用了这几个事件。下面是其具体的代码:本人做的是一个恒压供水的项目,这里只是一个产品的测试,故有很多地方还不严谨,思路不是很清楚,我感觉这也我一周弄出来的,还是比较不错的usingSystem;usingSystem.Collections.Generic;usingSystem.ComponentModel;usingSystem.Data;usingSystem.Drawing;usingSystem.Linq;usingSystem.Text;usingSystem.Threading.Tasks;usingSystem.Windows.Forms;usingSystem.IO.Ports;namespaceWindowsFormsApplication2{publicpartialclassForm1:Form{publicForm1(){InitializeComponent();System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls=false;//关闭线程检查}privatevoidForm1_Load(objectsender,EventArgse){//窗口载入时,自动搜索可用的串口stringbuffer;comboBox1.Items.Clear();for(inti=0;i<20;i++){try{buffer="COM"+i.ToString();serialPort1.PortName=buffer;serialPort1.Open();comboBox1.Items.Add(buffer);serialPort1.Close();}catch{}}serialPort1.WriteLine("MONITOR@00SC0252*"+"\r");//让PLC工作在监视模式下//serialPort1.WriteLine("@00WD0201000050*"+"\r");}privatevoidserialPort1_DataReceived(objectsender,SerialDataReceivedEventArgse){//串口接受事件所触发执行if(radioButton1.Checked){//如果处于运行状态,则接受的字符串只显示在接受数据去//高级功能中的接受面板不予显示stringstr=serialPort1.ReadExisting();//接受到的字符stringstr1;//通道1的字符//stringstr2;//通道2的字符//该设备没有外接stringstr3;//通道3的字符//stringstr4;//通道4的字符//该设备没有外接stringstr_Z_S_PLC;//转速的字符PLC模块所采集的try{str1=str.Substring(8,2);}catch{str1="";}S_Y_1.Text=str1;Application.DoEvents();//System.Threading.Thread.Sleep(100);try{str3=str.Substring(12,2);}catch{str3="";}S_Y_3.Text=str3;Application.DoEvents();/******************///System.Threading.Thread.Sleep(100);//让线程在此休眠100MS,以便处理//其他线程的事情。//这对刷屏,text属性用比较大的作用S_Y_2.Text="60";S_Y_4.Text="60";Application.DoEvents();/******************/try{str_Z_S_PLC=str.Substring(15,3);}catch{str_Z_S_PLC="";//str_Z_S_PLC=str.Substring(15,3);}S_B_Z_S.Text=str_Z_S_PLC;Application.DoEvents();/******************/}else{stringstr_gaoji=serialPort1.ReadExisting();//接受到的字符textBox1.AppendText(str_gaoji);}}privatevoidbutton2_Click(objectsender,EventArgse){//高级功能中的发送按钮所执行的功能if(serialPort1.IsOpen){try{//若串口为打开状态则,发送命令serialPort1.WriteLine(textBox2.Text);}catch{MessageBox.Show("数据写入有错","错误");}}}privatevoidbutton1_Click(objectsender,EventArgse){//打开串口按钮执行的功能if(serialPort1.IsOpen){try{//若串口原状态是开,再按一次则关闭穿口serialPort1.Close();button1.Text="打开串口";ovalShape1.FillColor=Color.Gray;}catch{MessageBox.Show("串口关闭错误","错误");}}else{try{//若串口状态原先是关闭,则按一次打开串口serialPort1.PortName=comboBox1.Text;serialPort1.Open();button1.Text="关闭串口";ovalShape1.FillColor=Color.GreenYellow;}catch{MessageBox.Show("串口打开错误","错误");}}}privatevoidtimer1_Tick(objectsender,EventArgse){//每隔1S,上位机给PLC发送一条指令//PLC返回一条指令,用于刷新,各采集的数据if(serialPor
FCS=(Int16)(FCS^a[i]);//异或计算FCS码
stringstr_FCS;//把计算出来的FCS码再转成字符串
str_FCS=FCS.ToString("x2");
textBox2.Text=str_FCS;
写的不太好,就当自己测试的时候用了。
特别要注意的是,PLC在运行状态是不能向DM区写数据的。
必要的时候我们可以通过向PLC发送不同指令来改变其工作模式。
指令如下:
模式的改变
PROGRAM@00SC0050*
MONITOR@00SC0252*
RUN@00SC0353*
"00"是站號
最基本的东西已经说得比较清楚了,下面把代码也贴出来吧,同样代码也写的比较乱,让内行人见笑了。
大体上看一共就用了这几个事件。
下面是其具体的代码:
本人做的是一个恒压供水的项目,这里只是一个产品的测试,故有很多地方还不严谨,思路不是很清楚,我感觉这也我一周弄出来的,还是比较不错的
usingSystem.IO.Ports;
namespaceWindowsFormsApplication2
System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls=false;
//关闭线程检查
privatevoidForm1_Load(objectsender,EventArgse)
{//窗口载入时,自动搜索可用的串口
stringbuffer;
comboBox1.Items.Clear();
for(inti=0;i<20;i++)
try
buffer="COM"+i.ToString();
serialPort1.PortName=buffer;
serialPort1.Open();
comboBox1.Items.Add(buffer);
serialPort1.Close();
catch{}
serialPort1.WriteLine("MONITOR@00SC0252*"+"\r");//让PLC工作在监视模式下
//serialPort1.WriteLine("@00WD0201000050*"+"\r");
privatevoidserialPort1_DataReceived(objectsender,SerialDataReceivedEventArgse)
//串口接受事件所触发执行
if(radioButton1.Checked)
//如果处于运行状态,则接受的字符串只显示在接受数据去
//高级功能中的接受面板不予显示
stringstr=serialPort1.ReadExisting();//接受到的字符
stringstr1;//通道1的字符
//stringstr2;//通道2的字符//该设备没有外接
stringstr3;//通道3的字符
//stringstr4;//通道4的字符//该设备没有外接
stringstr_Z_S_PLC;//转速的字符PLC模块所采集的
str1=str.Substring(8,2);
catch
str1="";
S_Y_1.Text=str1;
Application.DoEvents();
//System.Threading.Thread.Sleep(100);
str3=str.Substring(12,2);
str3="";
S_Y_3.Text=str3;
Application.DoEvents();/******************/
//System.Threading.Thread.Sleep(100);//让线程在此休眠100MS,以便处理
//其他线程的事情。
//这对刷屏,text属性用比较大的作用
S_Y_2.Text="60";
S_Y_4.Text="60";
str_Z_S_PLC=str.Substring(15,3);
str_Z_S_PLC="";
//str_Z_S_PLC=str.Substring(15,3);
S_B_Z_S.Text=str_Z_S_PLC;
else
stringstr_gaoji=serialPort1.ReadExisting();//接受到的字符
textBox1.AppendText(str_gaoji);
privatevoidbutton2_Click(objectsender,EventArgse)
//高级功能中的发送按钮所执行的功能
if(serialPort1.IsOpen)
//若串口为打开状态则,发送命令
serialPort1.WriteLine(textBox2.Text);
MessageBox.Show("数据写入有错","错误");
//打开串口按钮执行的功能
//若串口原状态是开,再按一次则关闭穿口
button1.Text="打开串口";
ovalShape1.FillColor=Color.Gray;
MessageBox.Show("串口关闭错误","错误");
//若串口状态原先是关闭,则按一次打开串口
serialPort1.PortName=comboBox1.Text;
button1.Text="关闭串口";
ovalShape1.FillColor=Color.GreenYellow;
MessageBox.Show("串口打开错误","错误");
privatevoidtimer1_Tick(objectsender,EventArgse)
//每隔1S,上位机给PLC发送一条指令
//PLC返回一条指令,用于刷新,各采集的数据
if(serialPor
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1