• PLC48路采集数据监听


    一网友需要采集接收从PLC发过来的48路采集数据,特为其编写了一个小程序,其通信协议如下所示:

    返回固定地址adr	00 43 40 41(固定指令)		返回: adr 43 CRC
    
    读未校准电压读数寄存器	adr 03 0100 0030 CRC	返回: adr 03 60 xxxx xxxx ..... xxxx CRC,  xxxx是48个电压,放大100倍.
    读校准后电压寄存器		adr 03 0130 0030 CRC	返回: adr 03 60 xxxx xxxx ..... xxxx CRC,  xxxx是48个电压,放大100倍.
    
    
    读5V时,ADC电压采样值		adr 03 0300 0030 CRC	返回: adr 03 60 xxxx xxxx ..... xxxx CRC,  xxxx是48个输入电压为5V时各点未校准值,放大100倍.
    读48V时,ADC电压采样值		adr 03 0330 0030 CRC	返回: adr 03 60 xxxx xxxx ..... xxxx CRC,  xxxx是48个输入电压为48V时各点未校准值, 放大100倍.
    读基准电压			adr 03 0460 0002 CRC	返回: adr 03 04 xxxx xxxx CRC,  第一个值为5V基准,第二个值为48V基准,放大100倍.
    
    设定老化机种电压参数寄存器      adr 10 0380 0001 02  XXXX CRC	  
    
    第一通道校准
    写某路 X1点校准寄存器	adr 10 0200 0001 02 XXXX CRC	(第1个点 5V)		返回: adr 10 0200 0002 CRC  //0200  表示第一通道,0201 表示第二通道...022f表示48通道
    写某路 X2点校准寄存器	adr 10 0240 0001 02 XXXX  CRC	(第2个点 48V)		返回: adr 10 0240 0002 CRC////0240  表示第一通道,0241 表示第二通道...026f表示48通道
    
    
    
    校准方法:
    1: 将标准电压X1(比如5V)接入。等待几秒钟以便读数稳定。
    2: 发命令"adr 10 0200 0001 02 uuuu CRC",记录X1点校准, uuuu是标准电压X1(放大100倍,即两位小数)。
    1: 将标准电压X2(比如48V)接入。等待几秒钟以便读数稳定。
    2: 发命令"adr 10 0200 0001 02 uuuu CRC",记录X2点校准, uuuu是标准电压X2(放大100倍,即两位小数)。
    校准结束。
    

     代码如下所示:

    Dim bRev() As Byte
    Dim bSnd() As Byte '发送数据
    Private Sub cmdCom_Click()
     On Error GoTo err1
     If cmdCom.Caption = "打开端口" Then
      '设置串口
    On errr GoTo defaults
      If Cobcom.Text = "" Then
          MsgBox "请选择串口号!"
          Exit Sub
      End If
      If cobspeed.Text = "" Then
          MsgBox "请选择波特率!"
          Exit Sub
      End If
      MSComm.CommPort = Cobcom.Text  '选用com串行口"9600,N,8,1"
      MSComm.Settings = cobspeed.Text & ",N,8,1"
      MSComm.PortOpen = True '打开通信
      cmdCom.Caption = "关闭端口"
      Exit Sub
    defaults:
      MSComm1.CommPort = 1
      MSComm1.Settings = "9600,N,8,1"
      MsgBox "设置错误!" & Chr(13) & "自动设置默认值:9600,N,8,1"
     Else
      cmdCom.Caption = "打开端口"
      MSComm.PortOpen = False '关闭通信
     End If
     Exit Sub
    err1:  MsgBox "端口已打开!"
    End Sub
    
    Private Sub cmdExit_Click()
    If MSComm.PortOpen Then
        MsgBox "串口还在接收处理数据,请关闭后在退出!"
    Else
        End
    End If
    
    End Sub
    
    Private Sub cmdgetaddr_Click()
    ReDim bSnd(0 To 3) As Byte
    bSnd(0) = &H0
    bSnd(1) = &H43
    bSnd(0) = &H40
    bSnd(1) = &H41
    If (MSComm.PortOpen) Then
        MSComm.Output = bSnd
    Else
        MsgBox "串口没有打开,请打开串口!"
    End If
    
    End Sub
    
    Private Sub cmdJiao_Click()
    
    ReDim bSnd(0 To 10) As Byte
    
    If txtV.Text = "" Then
        MsgBox "请填写校准电压值!"
        Exit Sub
    End If
    If cobVol.Text = "" Then
        MsgBox "请选择基准电压!"
        Exit Sub
    End If
    
    If txtChl.Text = "" Then
        MsgBox "请填写通道号!"
        Exit Sub
    End If
    
    bSnd(0) = Int(Val(addr.Text))
    bSnd(1) = &H10 '10
    bSnd(2) = &H2   '02
    If cobVol.Text = "5V" Then
        bSnd(3) = &H0 + Int(Val(txtChl.Text))
    Else
        bSnd(3) = &H40 + Int(Val(txtChl.Text))
    End If
    bSnd(4) = &H0  '00
    bSnd(5) = &H1  '01
    bSnd(6) = &H2  '02
    '处理电压
    Dim v As Integer
    v = Int(Val(txtV.Text) * 100) '获取电压值
    bSnd(7) = (v / 256)
    bSnd(8) = v Mod 256
    
    Dim sTmp(0 To 8) As Byte
    For i = 0 To 8
        sTmp(i) = bSnd(i)
    Next
    bSnd(6) = CRC16INT(sTmp, "L")
    bSnd(7) = CRC16INT(sTmp, "H")
    If MSComm.PortOpen Then
        MSComm.Output = bSnd
    End If
    
    End Sub
    
    Private Sub cmdRun_Click()
    If cmdRun.Caption = "运行" Then
        If zhouqi.Text = "" Then
            MsgBox "请设置好周期在运行程序!"
            Exit Sub
        End If
        If Val(zhouqi.Text) > 0 Then
            timRead.Interval = Int(Val(zhouqi.Text))
            timRead.Enabled = True
            cmdRun.Caption = "停止"
        Else
            MsgBox "周期设置错误!"
        End If
    Else
        timRead.Enabled = False
        cmdRun.Caption = "运行"
    End If
    
    End Sub
    
    Private Sub Form_Load()
    For i = 1 To 16
        On Error Resume Next
       '当运行发生错误时,控件转到紧接着发生错误的语句之后的语句,并在此继续运行
        MSComm.CommPort = i
        MSComm.PortOpen = True
        Select Case Err.Number
           Case 0                       '错误号为0(也就是没出错),
             Cobcom.AddItem i
             MSComm.PortOpen = False
           Case 8005                '错误号为8005,也就是端口被占用
             Cobcom.AddItem i
             MSComm.PortOpen = False
        End Select
        Err = 0     '将错误号置0. 注:Err.Number可以简写为Err ,2者等效
    Next
    If Cobcom.ListCount > 0 Then
       Cobcom.ListIndex = 0
    End If
    cobVol.ListIndex = 0
    
    cobspeed.AddItem ("4800")
    cobspeed.AddItem ("9600")
    cobspeed.AddItem ("115200")
    Call InitCom
    cobspeed.Text = "9600"
    End Sub
    Private Sub InitCom()
        MSComm.Settings = "9600,N,8,1" '波特率9600,无奇偶校验位,8位数据位1位停止位
        
        MSComm.InputLen = 0 'input将读取接收缓冲区的全部内容
        MSComm.InBufferSize = 1024 '设置接收缓冲区的字节长度
        'MSComm1.PortOpen = True '打开通信口
        MSComm.InBufferCount = 0 '清除接收缓冲区数据
        MSComm.OutBufferCount = 0 '清除发送缓冲区数据
        MSComm.InputMode = comInputModeBinary 'comInputModeText
        'MSComm.InputMode = comInputModeBinary
        'periodic.inteval = 100 '设置ls定时间隔,使遥测命令每隔ls发送1次
        MSComm.RThreshold = 1
    
    End Sub
    
    Private Sub MSComm_OnComm()
      Dim i As Integer, SBUF() As Byte
      If MSComm.CommEvent = comEvReceive Then
        SBUF = MSComm.Input
        For i = 0 To UBound(SBUF)
          Text1.Text = Text1.Text & " " & Hex(SBUF(i))
        Next
        Call DealData(SBUF)
      End If
    End Sub
    
    Private Sub DealData(sRev() As Byte)
    Dim sTmp() As Byte
    Dim i As Integer
    Dim crcvalue, crcGet
    Select Case UBound(sRev)
        Case 3 '返回的是地址,或者是错误数据,验证一下
            If (sRev(1) = &H43) Then
                ReDim sTmp(0 To 1) As Byte
                sTmp(0) = sRev(0)
                sTmp(1) = sRev(1)
                crcvalue = GETCRC16(sTmp, "L") '协议中固定为低位在前,高位在后
                crcGet = Hex(sRev(2)) + " " + Hex(sRev(3))
                If crcvalue = crcGet Then
                    addr.Text = sTmp(0)
                    txtShowMsg.Text = txtShowMsg.Text + "获取设备地址成功!" + vbCrLf
                End If
            End If
        Case 100 '正常数据电压采样值
            '判断地址是否相等,不相等就认为不是所要的数据
            If (sRev(0) + "" = addr.Text) And sRev(1) = &H3 Then
                ReDim sTmp(0 To 98) As Byte
                For i = 0 To 98
                    sTmp(i) = sRev(i)
                Next
                crcvalue = GETCRC16(sTmp, "L") '
                crcGet = Hex(sRev(99)) + " " + Hex(sRev(100))
                If crcvalue = crcGet Then
                    For i = 0 To 47
                        txtChannel(i).Text = (sRev(3 + i) * 256 + sRev(4 + i)) / 100 '把值显示在文本框
                    Next
                End If
            End If
        Case 7 '读基准电压,写校准等
            If (sRev(0) + "" = addr.Text) And sRev(1) = &H10 Then
                ReDim sTmp(0 To 6) As Byte
                For i = 0 To 6
                    sTmp(i) = sRev(i)
                Next
                crcvalue = GETCRC16(sTmp, "L") '
                crcGet = Hex(sRev(7)) + " " + Hex(sRev(8))
                If crcvalue = crcGet Then
                    If (sRev(3) - &H40 > 0) Then
                        txtShowMsg.Text = txtShowMsg.Text + "X2点" + (sRev(3) - &H40 + 1) + "通道校准成功!" + vbCrLf
                    Else
                        txtShowMsg.Text = txtShowMsg.Text + "X1点" + (sRev(3) + 1) + "通道校准成功!" + vbCrLf
                    End If
                End If
            End If
            
            If (sRev(0) + "" = addr.Text) And sRev(1) = &H3 Then
                ReDim sTmp(0 To 6) As Byte
                For i = 0 To 6
                    sTmp(i) = sRev(i)
                Next
                crcvalue = GETCRC16(sTmp, "L") '
                crcGet = Hex(sRev(7)) + " " + Hex(sRev(8))
                If crcvalue = crcGet Then
                    txtShowMsg.Text = txtShowMsg.Text + "5V基准值:" + sRev(3) + "." + sRev(4) + vbCrLf
                    txtShowMsg.Text = txtShowMsg.Text + "48V基准值:" + sRev(5) + "." + sRev(6) + vbCrLf
                End If
            End If
    End Select
    
    
    End Sub
    
    Private Sub timRead_Timer()
    '发送数据示例
    '发送 读校准后电压寄存器 指令
    ReDim bSnd(0 To 7) As Byte
    bSnd(0) = Int(Val(addr.Text))
    bSnd(1) = &H3 '03
    bSnd(2) = &H1  '01
    bSnd(3) = &H0  '00
    bSnd(4) = &H0  '00
    bSnd(5) = &H30 '30
    Dim sTmp(0 To 5) As Byte
    For i = 0 To 5
        sTmp(i) = bSnd(i)
    Next
    bSnd(6) = CRC16INT(sTmp, "L")
    bSnd(7) = CRC16INT(sTmp, "H")
    If MSComm.PortOpen Then
        MSComm.Output = bSnd
    End If
    
    End Sub

    公共的CRC代码如下所示:

    Public Function CRC(str1 As String, HL As String)
    
    
    Dim CRCREG As Long
    
    Dim MVAL As Long
    Dim R As Integer
    Dim T As Integer
    CRCREG = 65535
    
    For R = 1 To Len(str1) Step 2
    MVAL = Val("&H" + Mid(str1, R, 2))
    
    CRCREG = CRCREG Xor MVAL
    CRCREG = CRCREG And 65535
    
    For T = 1 To 8 Step 1
    If (CRCREG And &H1) Then
    CRCREG = (CRCREG  2) Xor &HA001
     CRCREG = CRCREG And 65535
     Else
         
       CRCREG = CRCREG  2
    CRCREG = CRCREG And 65535
    End If
    Next
    
    Next
    Dim a As Long
    Dim b As Long
    a = CRCREG And 255
    b = CRCREG And 65280
    a = a * 256
    b = b / 256
    
    If (a + b) < 16 Then
    CRC = "000" + Hex(a + b)
    ElseIf (a + b) < 256 Then
    
    CRC = "00" + Hex(a + b)
    ElseIf (a + b) < 4096 Then
    CRC = "0" + Hex(a + b)
    Else
    CRC = Hex(a + b)
    End If
    
    If HL = "H" Then
    CRC = Left(CRC, 2)
    ElseIf HL = "L" Then
    CRC = Right(CRC, 2)
    End If
    End Function
    
    Function CRC16(data() As Byte, HL As String) As String
    Dim CRC16Lo As Byte
    Dim CRC16Hi As Byte
    Dim CL As Byte
    Dim SaveHi As Byte
    Dim SaveLo As Byte
    Dim ii As Integer
    Dim flag As Integer
    CRC16Lo = &HFF
    CRC16Hi = &HFF
    CL = &H1
    CH = &HA0
    For ii = 0 To UBound(data)
    CRC16Lo = CRC16Lo Xor data(ii)
    For flag = 0 To 7
    SaveHi = CRC16Hi
    SaveLo = CRC16Lo
    CRC16Hi = CRC16Hi  2
    CRC16Lo = CRC16Lo  2
    If ((SaveHi And &H1) = &H1) Then
    CRC16Lo = CRC16Lo Or &H80
    End If
    If ((SaveLo And &H1) = &H1) Then
    CRC16Hi = CRC16Hi Xor CH
    CRC16Lo = CRC16Lo Xor CL
    End If
    Next flag
    Next ii
    Dim ReturnData(1) As Byte
    ReturnData(0) = CRC16Hi
    ReturnData(1) = CRC16Lo
    If HL = "H" Then
    CRC16 = Hex(ReturnData(0))
    End If
    If HL = "L" Then
    CRC16 = Hex(ReturnData(1))
    End If
    End Function
    
    ' 用途:将十六进制转化为十进制
    ' 输入:Hex(十六进制数)
    ' 输入数据类型:String
    ' 输出:H2D(十进制数)
    ' 输出数据类型:Long
    ' 输入的最大数为7FFFFFFF,输出的最大数为2147483647
    Public Function H2D(ByVal Hex As String) As Long
         Dim i As Long
         Dim b As Long
        
        Hex = UCase(Hex)
         For i = 1 To Len(Hex)
             Select Case Mid(Hex, Len(Hex) - i + 1, 1)
                 Case "0": b = b + 16 ^ (i - 1) * 0
                 Case "1": b = b + 16 ^ (i - 1) * 1
                 Case "2": b = b + 16 ^ (i - 1) * 2
                 Case "3": b = b + 16 ^ (i - 1) * 3
                 Case "4": b = b + 16 ^ (i - 1) * 4
                 Case "5": b = b + 16 ^ (i - 1) * 5
                 Case "6": b = b + 16 ^ (i - 1) * 6
                 Case "7": b = b + 16 ^ (i - 1) * 7
                 Case "8": b = b + 16 ^ (i - 1) * 8
                 Case "9": b = b + 16 ^ (i - 1) * 9
                 Case "A": b = b + 16 ^ (i - 1) * 10
                 Case "B": b = b + 16 ^ (i - 1) * 11
                 Case "C": b = b + 16 ^ (i - 1) * 12
                 Case "D": b = b + 16 ^ (i - 1) * 13
                 Case "E": b = b + 16 ^ (i - 1) * 14
                 Case "F": b = b + 16 ^ (i - 1) * 15
             End Select
         Next i
         H2D = b
    End Function
    
    Function GETCRC16(data() As Byte, HLBegin As String) As String
    Dim CRC16Lo As Byte
    Dim CRC16Hi As Byte
    Dim CL As Byte
    Dim SaveHi As Byte
    Dim SaveLo As Byte
    Dim ii As Integer
    Dim flag As Integer
    CRC16Lo = &HFF
    CRC16Hi = &HFF
    CL = &H1
    CH = &HA0
    For ii = 0 To UBound(data)
    CRC16Lo = CRC16Lo Xor data(ii)
    For flag = 0 To 7
    SaveHi = CRC16Hi
    SaveLo = CRC16Lo
    CRC16Hi = CRC16Hi  2
    CRC16Lo = CRC16Lo  2
    If ((SaveHi And &H1) = &H1) Then
    CRC16Lo = CRC16Lo Or &H80
    End If
    If ((SaveLo And &H1) = &H1) Then
    CRC16Hi = CRC16Hi Xor CH
    CRC16Lo = CRC16Lo Xor CL
    End If
    Next flag
    Next ii
    Dim ReturnData(1) As Byte
    ReturnData(0) = CRC16Hi
    ReturnData(1) = CRC16Lo
    'H表示高位在前,L表示低位在前
    If HLBegin = "H" Then
    GETCRC16 = Hex(ReturnData(0)) + " " + Hex(ReturnData(1))
    End If
    If HLBegin = "L" Then
    GETCRC16 = Hex(ReturnData(1)) + " " + Hex(ReturnData(0))
    End If
    End Function
    
    Function CRC16INT(data() As Byte, HL As String) As Integer
    Dim CRC16Lo As Byte
    Dim CRC16Hi As Byte
    Dim CL As Byte
    Dim SaveHi As Byte
    Dim SaveLo As Byte
    Dim ii As Integer
    Dim flag As Integer
    CRC16Lo = &HFF
    CRC16Hi = &HFF
    CL = &H1
    CH = &HA0
    For ii = 0 To UBound(data)
    CRC16Lo = CRC16Lo Xor data(ii)
    For flag = 0 To 7
    SaveHi = CRC16Hi
    SaveLo = CRC16Lo
    CRC16Hi = CRC16Hi  2
    CRC16Lo = CRC16Lo  2
    If ((SaveHi And &H1) = &H1) Then
    CRC16Lo = CRC16Lo Or &H80
    End If
    If ((SaveLo And &H1) = &H1) Then
    CRC16Hi = CRC16Hi Xor CH
    CRC16Lo = CRC16Lo Xor CL
    End If
    Next flag
    Next ii
    Dim ReturnData(1) As Byte
    ReturnData(0) = CRC16Hi
    ReturnData(1) = CRC16Lo
    If HL = "H" Then
    CRC16INT = ReturnData(0)
    End If
    If HL = "L" Then
    CRC16INT = ReturnData(1)
    End If
    End Function

    这样完整的代码就弄好了。

    运行如下所示:

     

  • 相关阅读:
    行业动态 | Instagram: 从Redis到Cassandra成功节省75%的成本
    技术基础 | 有关K8ssandra的那些事儿
    技术基础 | 重要指标和告警
    Cassandra与职业发展 | 阿里云栾小凡 × 蔚来汽车张旭东 × 网龙阙乃祯
    Cassandra与Kubernetes
    为何选择云原生?
    区分NoSQL数据库
    什么是NoSQL
    JMeter学习(一)JMeter的安装和目录解析
    CentOS 7 nfs客户端挂载问题
  • 原文地址:https://www.cnblogs.com/kingkie/p/4789167.html
Copyright © 2020-2023  润新知