请选择 进入手机版 | 继续访问电脑版
分析部落 - AIA,赶快注册吧!
立即注册

合作站点账号登陆

扫一扫,访问微社区

快捷导航
查看: 316|回复: 4

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

[复制链接]
  • TA的每日心情
    无聊
    2017-9-7 14:47
  • 签到天数: 14 天

    连续签到: 2 天

    [LV.3]偶尔看看II

    2

    主题

    10

    帖子

    384

    积分

    中级会员

    Rank: 3Rank: 3

    积分
    384
    发表于 2017-9-6 10:49:57 | 显示全部楼层 |阅读模式

    马上注册,精彩即将继续...

    您需要 登录 才可以下载或查看,没有帐号?立即注册

    x
    本帖最后由 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分别添加到程序中去,如下面三个图所示:
    库.jpg 1.jpg 2.jpg
    这里面对应每一个属性的意思大家在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[0]到btSend[4]各占一个字节,即5个字节,btSend[5]返回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[8];
                byte[] CRC = new byte[2];

                btSend[0] = Convert.ToByte(node);   //功能码
                btSend[1] = stat;                   //目标站号
                btSend[2] = (byte)(addr >> 8);      //I0.0地址(0000)高字节,即高8位
                btSend[3] = (byte)(addr & 0xFF);    //I0.0地址(0000)低字节,即低8位
                btSend[4] = (byte)(len >> 8);       //读取个数高字节
                btSend[5] = (byte)(len & 0xFF);     //读取个数低字节
                CRC = BitConverter.GetBytes(CRC16.crc16(btSend, 6));                     //计算CRC校验码
                btSend[6] = CRC[0];
                btSend[7] = CRC[1];
                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的差不多,上面都有注释了,我也不多费话了。

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

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

          vb modbus test 2(CRC校验-计算法).rar (4.38 KB, 下载次数: 1)
  • TA的每日心情

    2017-12-3 18:04
  • 签到天数: 208 天

    连续签到: 2 天

    [LV.7]常住居民III

    1264

    主题

    2084

    帖子

    3万

    积分

    超级版主

    Rank: 8Rank: 8

    积分
    30043
    发表于 2017-9-7 09:55:41 | 显示全部楼层
    精彩继续!没写完吧?

                                   
    登录/注册后可看大图

    来自苹果客户端来自苹果客户端
    努力打造AIA成为在线分析行业第一科技媒体!
  • TA的每日心情
    开心
    2017-9-18 09:10
  • 签到天数: 35 天

    连续签到: 1 天

    [LV.5]常住居民I

    2

    主题

    37

    帖子

    614

    积分

    高级会员

    Rank: 4

    积分
    614
    发表于 2017-9-7 10:27:22 | 显示全部楼层
    red 发表于 2017-9-7 09:55
    精彩继续!没写完吧?

    是的,未完待续,么么哒。
    来自微站
  • TA的每日心情
    无聊
    2017-9-7 14:47
  • 签到天数: 14 天

    连续签到: 2 天

    [LV.3]偶尔看看II

    2

    主题

    10

    帖子

    384

    积分

    中级会员

    Rank: 3Rank: 3

    积分
    384
     楼主| 发表于 2017-9-7 14:49:11 | 显示全部楼层
    red 发表于 2017-9-7 09:55
    精彩继续!没写完吧?

    电脑版的我看都是全的,不知道手机上看怎么就只有一半了,难道手机版的有字数限制。。。不懂。。
  • TA的每日心情
    开心
    昨天 16:47
  • 签到天数: 46 天

    连续签到: 9 天

    [LV.5]常住居民I

    1

    主题

    27

    帖子

    3599

    积分

    荣誉顾问

    Rank: 8Rank: 8

    积分
    3599
    发表于 2017-12-2 11:16:39 | 显示全部楼层
    虽然我不是搞通讯的,但为楼主的钻研精神点几个大大的赞。顶起!
    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    在线分析QQ群164930606|Archiver|手机版|小黑屋|分析部落 - AIA ( 冀ICP备16007748号

    GMT+8, 2017-12-14 10:26 , Processed in 0.166094 second(s), 58 queries .

    Copyright © 2016 分析部落 - AIA | www.ai-a.cn

    Powered by www.ai-a.cn X3.2

    快速回复 返回顶部 返回列表