forever 发表于 2017-9-6 10:49:57

基于RS485 Modbus RTU的上位机通讯软件实现,附VB源码,C#部分源码

本帖最后由 forever 于 2017-9-6 10:55 编辑

       最近碰到个项目需要配合仪表厂家对S7 200 PLC做上位机通讯软件的开发,由不是搞代码专业的,在网上搜了一堆的资料,总算也是可以把东西做出来了,抽空做了一个小的Demo,在这里也跟大家分享一点点经验,作为自己的总结,也以期能给后来人带来稍许启发。
      首先,既然是基于RS485 Modbus RTU通讯协议的软件,我们肯定需要对它有一定的了解,很多人可能会去搜Modbus协议的标准文件,当然我也是,说实话这个文件实在是太长了,看完之后说实话对我的帮助也有限,在这里我推荐大家去百度文库里看一篇文章:《MODBUS规约与报文解析详细说明》,因为我没有下载券了,所以没法放在附件里了,里面很简洁、明了的介绍Modbus的内容以及相应的报文格式,这个报文格式对编写上位机软件是对我帮助最大的,大家把这个看一下应该比我做大篇幅的介绍要好的多,这里我就不说了,只把上位机读取PLC里V区变量(即保持型寄存器)的流程简单说一下。
      我们通常的做法是把仪表测量的信号通过4-20mA传入PLC,再把相应的AIW传给我们分配的VW,上位机发送读取对应VW区的报文,下位机(从站,也就是我们的PLC)接收指令之后会给出响应,反回一条信息给上位机,然后上位机对相应的报文做解析。其他读取I区,Q区,写入V区的原理大概相同。
      好了,了解Modbus报文格式以及相应的流程之后,我们需要做的就是与S7 200 PLC通讯了,以CPU224xp为例,它有两个RS485通讯口,分别为port0和port1,其中做主站port0,port1都行,做从站只能用port0,这里因为是通过上位机控制PLC,所以我们选择plc为从站就可以了,开发的时候用RS232/RS485的s7 200编程电缆把电脑与PLC连上就行了,硬件的连接很简单,接下来就是s7 200的编程了。不过在开始之后我还是建议大家去看一下S7 200关于Modbus通讯的官方文档介绍,百度搜 s7 200 Modbus关键字,第一条就是,可能大家看完之后我下面关于s7 200的编程就可以略过了。但是这里我还是做个简单的介绍。
   第一步我们需要去下载一个S7 200的Modbus通讯库,因为Step7默认安装是没有这个库的,直接百度搜索吧,有很多链接可以下载,装完之后重新打开step7,在库里会出现相应的图标,展开Modbus Slave Port0,将MBUS-INIT和MBUS-SLAVE分别添加到程序中去,如下面三个图所示:

这里面对应每一个属性的意思大家在Step7中点击相应的栏目,按F1调出帮助文档,里面有详细的介绍,我也不啰嗦了。其他PLC的编程就只是把你模拟量AIW的值传到你想要放置的VW区了,最后记得做库存储区分配,在step7软件的 文件/库存储区菜单里。到这里为止,PLC上需要做的工作就结束了。(其他你们需要控制程序就由自己发挥吧。。。)
       -------------------------------------------------------------------------------------------------------------------------------------------
       接下来就是我们的重点,关于上位机的部分了, 我还是以VB6.0读取V区为例来说明,关于C#的实现方法,如果与VB不一样的地方,我也会做相应的说明,其他的大家可以下载附件VB6.0的源代码之后去看一下,形式基本上都是一样的。
       工作模式我在这里再说一下:上位机发送相应的请求,下位机接收请求之后做出相应的反馈
       在VB6.0里,我们与串口的通讯需要用到MSComm控件,在工程/部件菜单里找到Microsoft Comm Control 6.0添加一下就行了。
       为了尽可能简单的让大家明白,我直接上代码了,首先我们要进行通讯,肯定要进行相应的连接,如下
MSComm1.CommPort = 1   ‘自己电脑的com口
MSComm1.Settings = "9600,n,8,1"    ‘这个不多说了,一般人都能看得懂
MSComm1.InputMode = comInputModeBinary '二进制收发      
MSComm1.InBufferSize = 1024   ’设置相应的缓冲区
MSComm1.OutBufferSize = 1024
If (Not MSComm1.PortOpen) Then MSComm1.PortOpen = True   ‘打开串口


       到这里,我们就可以与S7 200进行信息的传递了。接下来我们就发送读取V区的报文,(报文的格式在前面我已经提到了,请看《MODBUS规约与报文解析详细说明》里的介绍)如下:
   Dim btSend(7) As Byte '定义一个用于存储发送报文的数组
   btSend(0) = &H1 '目标站号,也就是从站PLC的站地址
   btSend(1) = &H3 '功能码
   btSend(2) = &H0 '&VW1000地址(0000)高字节
   btSend(3) = &H0 '&VW1000地址(0000)低字节
   btSend(4) = &H0 '读取个数高字节
   btSend(5) = &H2 '读取个数低字节,2表示读取2个字,即&VW1000(&VB1000,&VB1001),&VW1002(&VB1002,&VB1003),返回4个字节,我在S7 200中的库存储区是从VW1000开始的。

   Dim crc   ‘关于CRC校验部分可以直接看我的源码,或者百度搜索一下,有很多
   Dim btCRCHi As Byte, btCRCLo As Byte

   crc = CalCRC16Fast(btSend, 6, btCRCLo, btCRCHi)   ‘调用CRC校验
   btSend(6) = btCRCHi 'CRC高字节
   btSend(7) = btCRCLo   'CRC低字节

   MSComm1.InBufferCount = 0
   MSComm1.Output = btSend‘发送报文
   MSComm1.RThreshold = 9       ‘当接收缓冲区的数据字节数达到9时,会触发MSComm的OnComm()事件

这里我解释一下RThreshold为什么是9,因为btSend到btSend各占一个字节,即5个字节,btSend返回4个字节
另外,MSComm的OnComm()事件是用来处理接收返回的报文的,双击添加到窗口的MSComm控件,进行相应的编程即可:
Dim btReceive() As Byte
Dim Buf As String
Dim crc
Dim btCRCLo As Byte, btCRCHi As Byte
Dim Data As Long


If btReceive(1) = &H3 Then '判断是否为读取V区功能
      crc = CalCRC16Fast(btReceive, 9, btCRCLo, btCRCHi)
      If btReceive(UBound(btReceive) - 1) = btCRCLo & btReceive(UBound(btReceive)) = btCRCHi Then    ’判断CRC校验是否正确
         For i = 3 To UBound(btReceive) - 2 Step 2   ‘报文处理
               Data = btReceive(i) * 256 + btReceive(i + 1)
               Buf = Buf + Str(Data) + Chr(32) + Chr(10)
         Next i
      txtReceiveV.Text = Trim(Buf)‘用text文本控件显示获取的值
      End If
    End If

    MSComm1.InBufferCount = 0 ’清空接收缓冲区

这一段好像没有什么能解释的了。
   到这里差不多就结束了,更多的功能就由你自己去添加吧。
    --------------------------------------------------------------------------------------------------------------
   现在估计VB用得很少了,我再简单的说下C#的实现方法吧,其实掌握了原理,用什么语言来写都差不多。
    C#的串口通讯需要用到serialPort控件,其用法跟VB的MSComm类似,设置完相应的参数之后直接调用open()方法就可以了,对应onCom事件是serialPort_DataReceived,也是当serialPort.ReceivedBytesThreshold达到设置值时触发,在其中调用serialPort.Read(btReceive, 0, btReceive.Length)方法就可以了,如果需要在界面做数据显示,则需要做到委托或者多线程,这个一下子就说不明白了,自己可以百度一下C#的serialPort控件,都有很详细的说明。
贴点示例代码,
数据的发送:
定义一个发送数据的方法,用一个timer控件或者一个循环调用这个方法,然后write(btSend, 0, 8);就行了。
private static byte[] SendMsg(int node, byte stat, int addr, int len)
      {
            byte[] btSend = new byte;
            byte[] CRC = new byte;

            btSend = Convert.ToByte(node);   //功能码
            btSend = stat;                   //目标站号
            btSend = (byte)(addr >> 8);    //I0.0地址(0000)高字节,即高8位
            btSend = (byte)(addr & 0xFF);    //I0.0地址(0000)低字节,即低8位
            btSend = (byte)(len >> 8);       //读取个数高字节
            btSend = (byte)(len & 0xFF);   //读取个数低字节
            CRC = BitConverter.GetBytes(CRC16.crc16(btSend, 6));                     //计算CRC校验码
            btSend = CRC;
            btSend = CRC;
            return btSend;
      }

//CRC校验类
class CRC16
    {
      public static uint crc16(byte[] modbusdata, uint Length)//Length为modbusdata的长度
      {
            uint i, j;
            uint crc16 = 0xFFFF;
            for (i = 0; i < Length; i++)
            {
                crc16 ^= modbusdata; // CRC = BYTE xor CRC
                for (j = 0; j < 8; j++)
                {
                  if ((crc16 & 0x01) == 1) //如果CRC最后一位为1&#1048651;右移一位后carry=1&#1048651;则将CRC右移一位后&#1048651;再与POLY16=0xA001进行xor运算
                        crc16 = (crc16 >> 1) ^ 0xA001;
                  else //如果CRC最后一位为0&#1048651;则只将CRC右移一位
                        crc16 = crc16 >> 1;
                }
            }
            return crc16;
      }


怎么样,是不是跟VB的差不多:lol,上面都有注释了,我也不多费话了。

另外,版主大大,看在我码了这么多字的情况下,请允许我打个小广告,哈。。我是M&C技术工程师,在这里已经有5年了,对CEMS或者工业过程采样跟预处理了解还算可以,各位如果有采样及预处理方面的咨询,可以跟我们联系,我们会为您提供最合适的解决方案。
我的个人邮箱是290094363@qq.com

到这里,本贴就结束了,手工码字,希望各位多多支持。:handshake:)

   

red 发表于 2017-9-7 09:55:41

精彩继续!没写完吧?http://www.ai-a.cn//mobcent//app/data/phiz/default/03.png

一片云 发表于 2017-9-7 10:27:22

red 发表于 2017-9-7 09:55 static/image/common/back.gif
精彩继续!没写完吧?

是的,未完待续,么么哒。

forever 发表于 2017-9-7 14:49:11

red 发表于 2017-9-7 09:55
精彩继续!没写完吧?

电脑版的我看都是全的,不知道手机上看怎么就只有一半了,难道手机版的有字数限制。。。不懂。。

xh021 发表于 2017-12-2 11:16:39

虽然我不是搞通讯的,但为楼主的钻研精神点几个大大的赞。:victory::victory:顶起!

northwester 发表于 2023-9-6 08:30:44

楼主很专业,文章值得借鉴。谢谢分享。
页: [1]
查看完整版本: 基于RS485 Modbus RTU的上位机通讯软件实现,附VB源码,C#部分源码