[西门子] 三菱FX系列PLC编程口通信协议

[复制链接]
loveplc | 2010-5-8 18:24:00 | 显示全部楼层

三菱FX系列PLC编程口通信协议

该协议实际上适用于PLC编程端口以及 FX-232AW 模块的通信。

通讯格式:

命令

命令码

目标设备

DEVICE READ CMD

"0"

X,Y,M,S,T,C,D

DEVICE WRITE CMD

"1"

X,Y,M,S,T,C,D

FORCE ON CMD

" 7"

X,Y,M,S,T,C

FORCE OFF CMD

"8"

X,Y,M,S,T,C

传输格式: RS232C波特率: 9600bps奇偶: even校验: 累加方式(和校验)字符: ASCII 

16进制代码:

ENQ

05H

请求

ACK

06H

PLC正确响应

NAK

15H

PLC错误响应

STX

02H

报文开始

ETX

03H

报文结束


三菱FX系列PLC编程口通信协议举例

1、DEVICE READ(读出软设备状态值)

计算机向PLC发送:


命令

首地址

位数


和校验

STX

CMD

GROUP ADDRESS

BYTES

ETX

SUM

2、DEVICE WRITE(向PLC软设备写入值)


命令

首地址

位数

数据


和校验

STX

CMD

GROUP ADDRESS

BYTES

1ST DATA

2ND DATA

......

LAST DATA

ETX

SUM


 

 

 

 

 

 

 

 

 

 

3、位设备强制置位/复位

FORCE ON置位


命令

地址


和校验

STX

CMD

ADDRESS

ETX

SUM

02h

37h

address

03h

sum

FORCE OFF复位


命令

地址


和校验

STX

CMD

ADDRESS

ETX

SUM

02h

38h

address

03h

sum

 说明:

1.帧中的BYTES表示需要读取或者写入的字节数。

2.地址算法上有说明。

3.累加和是从STX后面一个字节开始累加到ETX的和。

    这个就是网上流传的的比较完整的通讯协议说明了,可能对刚刚入门的朋友有点难度。因为光看这些对一个编程的初学者不知道如何下手,那么我将在下面的文章把不同地址的如何读写详细说明一下。

上次的文章主要讲述了三菱PLC的协议以及一些格式。但是如何来利用这个协议来写自己的程序呢?
我现在就讲述利用VB如何对PLC的地址读写和置位与复位等等。

    首先,在这里我先讲述一下对bit的地址置位和复位操作。我要对bit位进行置位复位我们就要了结一下bit位的地址概念。

    bit位包括S,X,Y,T,M,C这些内部位,那么这些地址在PLC里面是如何存放的呢?学过计算机的人都应该知道计算机里面只有0和1,把这些0和1按不同的顺序排列就能组成要计算机语言,在计算机里面也有地址,PLC也是一台简单的计算机所以和它的原理是一样的这些bit位都是被有序的存储起来的。通过我的试验发现三菱FX系列PLC地址如下:

   S就是系统位,在三菱FX系列里面开始是S0点被存储在地址0008(H),X0是0408(H),Y0是0508,T0是0708,M0是0808,C0是0D08。这样就对PLC的内部地址有了一定的了解,知道的bit位的地址我们如何来置位复位呢?对地址的通讯协议是这样的:

位设备强制置位/复位

FORCE ON置位


命令

地址


和校验

STX

CMD

ADDRESS

ETX

SUM

02h

37h

address

03h

sum

FORCE OFF复位


命令

地址


和校验

STX

CMD

ADDRESS

ETX

SUM

02h

38h

address

03h

sum

 PLC返回

ACK(06H) 接受正确NAK(15H) 接受错误


 

    有了格式我们就很清楚的来实现置位复位功能了。那么我们先做一些准备工作。上面的协议可能读者对和效验不太清楚那么我来解释一下,所谓和效验就是为了保证通讯的正确性,下面就是一个例子:

例子:

STX ,CMD ,ADDRESS, BYTES, ETX, SUM

02H, 30H, 31H,30H,46H,36H, 30H,34H, 03H, 37H,34H

SUM=CMD ...... ETX;

30h 31h 30h 46h 36h 30h 34h 03h=74h;

累加和超过两位取低两位

累加和是从STX后面一个字节开始累加到ETX的和

    那么我们做一个小程序来实现它

**************************
*  和校验
*Check_FCS(mStr)
*mStr 需要检查的字符串
**************************
Public Function Check_FCS(mStr As String) As String
  Dim K As Integer, mTest As Integer
  For K = 1 To Len(mStr)
    mTest = mTest Asc(Mid(mStr, K, 1))
  Next
  Check_FCS = Right(Hex(mTest), 2)
End Function

   现在可以对bit位进行置位了程序如下:

****************************
*强置bit位地址
*SetM(address)
****************************
Public Function SetM(address As String) As Boolean
  Dim Q As Long, myTest As String, myHex As String, QQ As String, mStr As Integer, Num As Integer
  On Error GoTo SetMErr
  select Case Left(address, 1)
    Case "M"
        mStr = 2048
    Case "X"
        mStr = 1024
    Case "Y"
        mStr = 1280
    Case "S"
        mStr = 0
    Case "T"
        mStr = 1536
    Case "C"
        mStr = 3584
  End Select
  If mStr = 1024 Then
            Q = OCT_to_DEC(Mid(address, 2)) mStr
     ElseIf mStr = 1280 Then
            Q = OCT_to_DEC(Mid(address, 2)) mStr
     Else
            Q = Val(Mid(address, 2)) mStr 这个算法只能写到M1741点 address * 256 8只能写到M255点
  End If
  QQ = Hex(Q)
  If Len(QQ) = 3 Then
    QQ = "0" & QQ
  ElseIf Len(QQ) = 2 Then
    QQ = "00" & QQ
  ElseIf Len(QQ) = 1 Then
    QQ = "000" & QQ
  End If
  QQ = Right(QQ, 2) & Left(QQ, 2) 换位操作 地位在前高位在后
  myHex = "7" & QQ & Chr(3)   置位操作7是命令置位命令 则8是复位操作
  myHex = Chr(2) & myHex & Check_FCS(myHex)
  Form1.MSComm.Output = myHex
  Call Sleep(Tim)
  myTest = Form1.MSComm.Input
  If Asc(myTest) = 6 Then SetM = True
  Exit Function
SetMErr:
  SetM = False
End Function
****************************
*复位bit位地址
*ResetM(address)
****************************
Public Function ResetM(address As String) As Boolean
  Dim Q As Long, myTest As String, myHex As String, QQ As String, mStr As Integer
  On Error GoTo ResetMErr
 
  select Case Left(address, 1)
    Case "M"
        mStr = 2048
    Case "X"
        mStr = 1024
    Case "Y"
        mStr = 1280
    Case "S"
        mStr = 0
    Case "T"
        mStr = 1536
    Case "C"
        mStr = 3584
  End Select
 
  If mStr = 1024 Then
            Q = OCT_to_DEC(Mid(address, 2)) mStr
     ElseIf mStr = 1280 Then
            Q = OCT_to_DEC(Mid(address, 2)) mStr
     Else
            Q = Val(Mid(address, 2)) mStr 这个算法只能写到M1535点 address * 256 8只能写到M255点
  End If
  QQ = Hex(Q)
  If Len(QQ) = 3 Then
    QQ = "0" & QQ
    ElseIf Len(QQ) = 2 Then
    QQ = "00" & QQ
    ElseIf Len(QQ) = 1 Then
    QQ = "000" & QQ
  End If
  QQ = Right(QQ, 2) & Left(QQ, 2)
  myHex = "8" & QQ & Chr(3)
  myHex = Chr(2) & myHex & Check_FCS(myHex)
  Form1.MSComm.Output = myHex
  Call Sleep(Tim)
  myTest = Form1.MSComm.Input
  If Asc(myTest) = 6 Then ResetM = True
  Exit Function
ResetMErr:
  ResetM = False
End Function

   这里我写成了一个函数形式,方便以后随时调用下篇将继续介绍对D的地址位的读写操作等。

这次我们来说一下对三菱FX系列PLC的D地址的读写操作,先来看看他的通讯协议如下:

1、DEVICE READ(读出软设备状态值)

计算机向PLC发送:


命令

首地址

位数


和校验

STX

CMD

GROUP ADDRESS

BYTES

ETX

SUM

例子:从D123开始读取4个字节数据

02h

30h

31h,30h,46h,36h

30h,34h

03h

37h,34h

地址算法:address=address*2 1000h

再转换成ASCII

31h,30h,46h,36h

PLC返回

STX

1ST DATA

2ND DATA

.....

LAST DATA

ETX

SUM

注:最多可以读取64个字节的数据

例子:从指定的存储器单元读到3584这个数据

02h

33h

35h

38h

34h

03h

44h,36h


 

2、DEVICE WRITE(向PLC软设备写入值)


命令

首地址

位数

数据


和校验

STX

CMD

GROUP ADDRESS

BYTES

1ST DATA

2ND DATA

......

LAST DATA

ETX

SUM


 

 

 

 

 

 

 

 

 

 

例子:向D123开始的两个存储器中写入1234,ABCD

02h

31h

31h,30h,46h,36h

30h,34h

33h,34h,31h,32h,43h,44h,41h,42h

03h

34h,39h

PLC返回

ACK (06H) 接受正确

NAK (15H) 接受错误


 

    上面有一个例子已经讲述的比较明确了,下面就用vb写一个例子来实现这样的功能。

首先,做一个补0操作的函数为以后作准备:

**************************
*  补0操作
*Check_Hex(mStr)
*mStr 可能需要补0的数据
**************************
Private Function Check_Hex(mIndex As Integer) As String
  If mIndex < 16 Then Check_Hex = "0" & Hex(mIndex) Else Check_Hex = Hex(mIndex)
End Function

    好下面就来写一个对D地址的写操作函数

*******************************
*写入PLC
*Write_PLC(mData(),mStart)
*mData() 写入的数据--以数组形式来表示
*mStart 写入的首地址
*******************************
Public Function Write_PLC(mData() As Integer, mStart As Integer) As Boolean
  Dim myHex As String, Q As Integer, myTest As String, mCounts As Integer
  On Error GoTo WriteErr
  Q = mStart * 2 &H1000 地址转换
  mCounts = (UBound(mData) 1) * 2 写几个D的地址
  myHex = "1" & Hex(Q) & Check_Hex(mCounts) 定义的一个函数来对写入的值进行补0操作
  For Q = 0 To UBound(mData)
     myHex = myHex & Check_Hex(mData(Q) Mod 256) & Check_Hex(mData(Q) \ 256)
  Next
  myHex = myHex & Chr(3)
  myHex = Chr(2) & myHex & Check_FCS(myHex)
  Form1.MSComm.Output = myHex
  Call Sleep(Tim)
  myTest = Form1.MSComm.Input
  Debug.Print myHex, Asc(myTest)
  If Asc(myTest) = 6 Then Write_PLC = True
  Exit Function
WriteErr:
  Write_PLC = False
End Function

    再来写一个读D地址的函数

***************************************
*读取PLC
*Read_PLC(mData(),mStart,mCounts)
*mData() 读出的数据--以数组的形式表示
*mSstar  起始读的地址位
*mCounts 读几位地址数据
***************************************
Public Function Read_PLC(mData() As Integer, mStart As Integer, mCounts As Integer) As Boolean
  Dim myHex As String, Q As Integer, myTest As String
  On Error GoTo ReadErr
  Q = mStart * 2 &H1000 地址转换
  mCounts = mCounts * 2
  ReDim mData(mCounts / 2 - 1) 定义数组的数据个数
  myHex = "0" & Hex(Q) & Check_Hex(mCounts) & Chr(3)
  myHex = Chr(2) & myHex & Check_FCS(myHex)
  Form1.MSComm.Output = myHex
  Call Sleep(Tim)
  myTest = Form1.MSComm.Input
  Debug.Print myHex, myTest
  检查是否传送有错误
  If Len(myTest) < 4 2 * mCounts Then GoTo ReadErr
  If Right(myTest, 2) <> Check_FCS(Mid(myTest, 2, 2 * mCounts 1)) Then GoTo ReadErr
  Read_PLC = True
  For Q = 0 To (mCounts \ 2) - 1
      mData(Q) = "&H" & (Mid(myTest, 4 Q * 4, 2) & Mid(myTest, 2 Q * 4, 2)) 把读到的数据写进数组
  Next
  Exit Function
ReadErr:
  Read_PLC = False
End Function

      这两个函数就可以实现对D地址的读写操作了,当然D的地址分16bit、32bit两种具体编程方法是一样的。

我们已经介绍了对三菱FX系列PLC的bit位置位和D地址的读写,下面我们来介绍一下对bit位的查询状态操作如何实现。

    可能有人会说查询bit位状态很简单,就是通过协议的chr(03)读操作来实现。是的没错,但是我有些地方要详细说明一下。首先,bit位的查询和对它的置位、复位地址是不一样的,之前我们介绍了S,X,Y,M,T,C的地址说明,但是那些地址只适用在置位、复位功能上。查询的地址是如下:

    S0~S7=0000  X0~X7=0080 Y0~Y7=00A0 T0~T7=00C0 M0~M7=0100 C0~C8=01C0

其他循序排列,可能有人会问为什么是0~7,读操作是一个字来读取的也就是说一次就要读8位,如果要判断某一位的状态时,程序就要做一个简单的判断。

    我们了解了PLC内部的地址问题就好来编程了,具体代码如下:

***************************************
*读取PLC的状态(X,Y,M,S,T,C)
*ReadM(mAddr)
*mAddr  读的地址位
***************************************
Public Function ReadM(mAddr As String) As Boolean
  Dim myHex As String, Q1 As String, Q2 As Integer, Q3 As Integer, QQ As String
  Dim myTest As String, Ret As String, S1 As Integer, S2 As Integer
  On Error GoTo ReadErr
 
  select Case Left(mAddr, 1)
        Case "M"
            Q1 = 256  0100  能读到M1535
        Case "S"
            Q1 = 0    0000
        Case "T"
            Q1 = 192  00C0
        Case "C"
            Q1 = 448  01C0
        Case "X"
            Q1 = 128  0080
        Case "Y"
            Q1 = 160  00A0
  End Select
 
  Q2 = Mid(mAddr, 2)
  If Q1 = 128 Then
        Q2 = OCT_to_DEC(Q2) 八进制转换成十进制
    ElseIf Q1 = 160 Then
        Q2 = OCT_to_DEC(Q2) 八进制转换成十进制
  End If
  S1 = Q2 \ 8   取整数
  Q3 = Q1 S1
  QQ = Hex(Q3)
  If Len(QQ) = 3 Then
        QQ = "0" & QQ
    ElseIf Len(QQ) = 2 Then
        QQ = "00" & QQ
    ElseIf Len(QQ) = 1 Then
        QQ = "000" & QQ
  End If
 
  myHex = "0" & QQ & "01" & Chr(3)
  myHex = Chr(2) & myHex & Check_FCS(myHex)
  Form1.MSComm.Output = myHex
 
  Call Sleep(Tim)
  myTest = Form1.MSComm.Input
  If Len(myTest) < 4 1 Then GoTo ReadErr  查错
  If Right(myTest, 2) <> Check_FCS(Mid(myTest, 2, 3)) Then GoTo ReadErr 查错
  Ret = "&H" & (Mid(myTest, 2, 2))
  Ret = H_To_B(Ret)
  S2 = Q2 Mod 8 取余数
  S2 = 8 - S2
  If Mid(Ret, S2, 1) = 1 Then
            ReadM = True
        Else
            ReadM = False
  End If
  Exit Function
ReadErr:
  Debug.Print "ReadM Error"
End Function
    这样就能实现查询操作了,可以看到X,Y的地址有点儿变化,这是因为X、Y是8进制的所以要进行一下转换,我们来写个函数如下:


*************************************************************
* 用途:将八进制转化为十进制
* 输入:Oct(八进制数)
* 输入数据类型:String
* 输出:OCT_to_DEC(十进制数)
* 输出数据类型:Long
* 输入的最大数为17777777777,输出的最大数为2147483647
*************************************************************

Public Function OCT_to_DEC(ByVal Oct As String) As Long
    Dim i As Long
    Dim B As Long
   
    For i = 1 To Len(Oct)
        select Case Mid(Oct, Len(Oct) - i 1, 1)
            Case "0": B = B 8 ^ (i - 1) * 0
            Case "1": B = B 8 ^ (i - 1) * 1
            Case "2": B = B 8 ^ (i - 1) * 2
            Case "3": B = B 8 ^ (i - 1) * 3
            Case "4": B = B 8 ^ (i - 1) * 4
            Case "5": B = B 8 ^ (i - 1) * 5
            Case "6": B = B 8 ^ (i - 1) * 6
            Case "7": B = B 8 ^ (i - 1) * 7
        End Select
    Next i
    OCT_to_DEC = B
End Function
    因为要判断读到的8位中到底那个位置的状态就要写个16进制转2进制的函数如下:

*************************************************************
* 用途:将16进制转化为2进制
* 输入:H_To_B(10进制)
* 输入数据类型:String
* 输出:H_To_B(2进制数)
* 输出数据类型:string
*************************************************************
Public Function H_To_B(ByVal Hex As String) As String
Dim i As Long
Dim B As String

Hex = UCase(Hex)
For i = 1 To Len(Hex)
Select Case Mid(Hex, i, 1)
Case "0": B = B & "0000"
Case "1": B = B & "0001"
Case "2": B = B & "0010"
Case "3": B = B & "0011"
Case "4": B = B & "0100"
Case "5": B = B & "0101"
Case "6": B = B & "0110"
Case "7": B = B & "0111"
Case "8": B = B & "1000"
Case "9": B = B & "1001"
Case "A": B = B & "1010"
Case "B": B = B & "1011"
Case "C": B = B & "1100"
Case "D": B = B & "1101"
Case "E": B = B & "1110"Case "F": B = B & "1111"
End Select
Next i
While Left(B, 1) = "0" 此三句话为去0
B = Right(B, Len(B) - 1)
Wend
H_To_B = B
End Function

    这样我们就完成了对bit位的状态查询。


 


 

1、DEVICE READ(读出软设备状态值)

计算机向PLC发送:


命令

首地址

位数


和校验

STX

CMD

GROUP ADDRESS

BYTES

ETX

SUM

例子:从D123开始读取4个字节数据

02h

30h

31h,30h,46h,36h

30h,34h

03h

37h,34h

地址算法:address=address*2 1000h

再转换成ASCII

31h,30h,46h,36h

PLC返回

STX

1ST DATA

2ND DATA

.....

LAST DATA

ETX

SUM

注:最多可以读取64个字节的数据

例子:从指定的存储器单元读到3584这个数据

02h

33h

35h

38h

34h

03h

44h,36h

 

2、DEVICE WRITE(向PLC软设备写入值)


命令

首地址

位数

数据


和校验

STX

CMD

GROUP ADDRESS

BYTES

1ST DATA

2ND DATA

......

LAST DATA

ETX

SUM


 


 


 


 


 


 


 


 


 


 

例子:向D123开始的两个存储器中写入1234,ABCD

02h

31h

31h,30h,46h,36h

30h,34h

33h,34h,31h,32h,43h,44h,41h,42h

03h

34h,39h

PLC返回

ACK (06H) 接受正确

NAK (15H) 接受错误

 

3、位设备强制置位/复位

FORCE ON置位


命令

地址


和校验

STX

CMD

ADDRESS

ETX

SUM

02h

37h

address

03h

sum

FORCE OFF复位


命令

地址


和校验

STX

CMD

ADDRESS

ETX

SUM

02h

38h

address

03h

sum

 

PLC返回

ACK(06H) 接受正确

NAK(15H) 接受错误

 

设备强制中的地址公式:Address=Address * 100h (*) (必须为4位,不足4位前面补0)

 注:*号所代表值:

   C:14  M:8  T:6  Y:5  X:4  S:0

如对M2置位,则为地址为:2*256(100H) 8=0520 转为十六进制为:0208 再换为ASCII就是: 30 32 30 38

 

说明:

1.帧中的BYTES表示需要读取或者写入的字节数。

2.地址算法上有说明。

3.累加和是从STX后面一个字节开始累加到ETX的和。

VB读写三菱FX系列PLC数据示例

  通过前面两篇文章,我们了解了三菱FX系列PLC的编程口通信协议。为了更方便读者学习这里提供一个用VB编写的示例,其中包含一个通用模块,如果你需要对此类PLC进行读写数据区的操作可以下载看看。

  三菱FX系列PLC的校验采用的是和校验,在写数据和读数据时都会有这个和校验,和校验用于检查数据包是否有错。因此我们必需知道和校验的算法,才能成功地进行通信。在这里我们提供了一个和校验的VB源代码:

 

***************************************************
Private Function Check_FCS(mStr As String) As String
 Dim K As Integer, mTest As Integer
 For K = 1 To Len(mStr)
  mTest = mTest Asc(Mid(mStr, K, 1))
 Next
 Check_FCS = Right(Hex(mTest), 2)
End Function
***************************************************
 
函数中的 mStr 参数为命令、位数、数据、终止符的合集,读取时不包括数据。使用Check_FCS函数时将返回和校验码。

  当向PLC写数据时,数据必须为四位的十六进制数,并且低位在前、高位在后。这里提供一个循环代码,将一个数组里的整数转换为向PLC写入数据的字符串:
 
For Q = 0 To UBound(mData)
 myHex = myHex & Format(Hex(mData(Q) Mod &HFF), "00") & Format(Hex(mData(Q) \ &HFF), "00")
Next
 

  在上面的代码中 mData 为向PLC写入数据的数组,myHex 最终得到的就是写入PLC时所要的数据字符串。同样的从PLC读出来的数据也是四位的十六进制,低位在前、高位在后。所以我们有必要将其转换为对应的整数。其主要的转换代码如下:
 

ReDim mData(mCounts - 1)
For Q = 0 To mCounts - 1
 mData(Q) = "&H" & (Mid(myHex, 4 Q * 4, 2) & Mid(myHex, 2 Q * 4, 2))
Next

 
mCounts 为要读取数据区的个数,myHex 是串口返回来的数据,经过上面计算返回的整数值将排列在 mData 数组里。



Option Explicit
Dim outdata() As Byte         定义发送数组,用来存放转换后的命令数据
Dim Rcvlen As Integer        定义接收到的数据的长度
Dim Rcv() As Byte            定义接收数组,用来存放接收到的数据
Dim inString As String       定义输入命令字符串
Dim RcvFinFlag As Boolean    定义接收完成标志
Dim ReadFlag As Boolean      定义“读命令”标志
Dim ReSendFlag As Boolean
Dim CheckFlag As Boolean
Dim No1 As Integer           定义重发次数设定
Dim No2 As Integer           定义重发次数计数器
Dim FinalDataLen As Integer   定义接收到数据的最终长度变量
Dim SaveString As String      定义输入命令暂存字符串变量

Private Sub Cmdopen_Click()
    If Not MSComm1.PortOpen Then MSComm1.PortOpen = True    如果串口没有打开,打开串口
    inString = "00FFWWAD00100202A11111"    输入开机命令字符串
    Call send(inString)                   调用发送子程序,形成命令帧并发送
    Timer1.Enabled = True               打开“定时读取温度值”定时器
End Sub

Private Sub Cmdset_Click()
    inString = "00FFWWAD0010020251"         输入命令报文的固定部分
    Dim temp As Variant
    Dim t As Variant
    Dim j As Integer
    temp = Trim(Txtset.Text)                取设定文本框输入的数据,以字符串的形式处理
    If Not IsNumeric(temp) Then             如果输入的数据有格式错误
        MsgBox "请按如下范围和格式输入数据:-100.0~300.0", vbExclamation, "输入数据范围错误"
        Exit Sub
    End If
    Dim Temperature As Integer
    If temp > 300 Or temp < -100 Then        如果超出规定范围之内
          MsgBox "请输入-100.0~300.0之间的数", vbExclamation, "输入数据格式错误"
          Exit Sub
    Else
          Temperature = CInt(10 * Val(temp))     将输入的数据转换为以0.1度为单位的整数
          t = Hex(Temperature)                   将输入的温度值转换为十六进制数
          j = Len(t)                             求转换后的十六进制数的位数
          t = String(4 - j, "0") & t             十六进制数高位添0,处理为4位的格式
    End If
    ReadFlag = False                             复位读标志
    inString = inString & t                      转换后的温度设定值附加到instring的末尾
    If Timer1.Enabled = True Then                若在开机状态下
         Timer1.Enabled = False                  发送温度设置命令前,暂时关闭定时器1
         Call send(inString)                     发送设定值到PLC
         Timer1.Enabled = True                   发送温度设置命令后,重新开始定时器1
         Timer1.Enabled = True                   若在关机状态下
    Else
         Call send(inString)                     直接发送设定值到PLC
    End If
End Sub

Private Sub Cmdstop_Click()
     MSComm1.PortOpen = False
     End
End Sub

初始化
Private Sub Form_Load()
   With MSComm1
               .CommPort = 1                       选择串口1
               .Settings = "9600,n,8,1"           设置通讯格式
               .InputMode = comInputModeBinary     以二进制格式读取缓冲区
               .RThreshold = 1                     接收到的字符数大于等于1时产生接收事件
               .InputLen = 0                       读出接收缓冲区所有的内容
               .OutBufferCount = 0                 清空发送缓冲区
               .InBufferCount = 0                  清空接收缓冲区
    End With
    If Not MSComm1.PortOpen = True Then MSComm1.PortOpen = True        打开串口1
    Timer1.Interval = 4000                         设置定时读取温度值的中断时间
    Timer1.Enabled = False                         初始化定时读取温度值定时器
    Timer2.Interval = 1000                         设置超时判定定时器的中断时间
    Timer2.Enabled = False                         初始化超时判断定时器
    No1 = 3                                        初始化重新发送次数3次
    No2 = 0                                        初始化重发次数为0
    RcvFinFlag = True                              初始化接收完成标志
    ReSendFlag = True                              初始化重发标志
    End Sub

Private Sub MSComm1_OnComm()
    Dim RcvTemp() As Byte                          定义存放每次接收到的数据的暂存数组
    Dim i As Integer
    Dim t As Variant
    ReDim Preserve Rcv(100) As Byte                预设接收字符数组Rcv
    If RcvFinFlag Then                             如果报文接收处理完成
        Exit Sub
    Else
    Select Case MSComm1.CommEvent                  MSComm控件产生通讯事件或通讯错误
    Case comEventFrame                             检测到一个因双方的通讯格式不同引发的错误
         MsgBox "双方通讯格式不一致", vbExclamation, "提示"      弹出错误
         Timer1.Enabled = False                   关闭定时发送定时器
         Timer2.Enabled = False                   关闭超时判断定时器
         Exit Sub
    Case comEvReceive                             若接收到字符
         RcvTemp = MSComm1.Input                      将接收缓冲区的内容送入暂存数组
         For i = LBound(RcvTemp) To UBound(RcvTemp)   
               Rcvlen = Rcvlen   1                    接收字符个数加1,Rcvlen的初始值为-1
               If Rcvlen > 100 Then                   如果接收数据超过接收数组上限
                        Rcvlen = -1                   复位接收到的数据的长度变量
                        Call ErrorHandle              进行错误处理
                        Exit Sub
                End If
                Rcv(Rcvlen) = RcvTemp(i)              将接收到的各字节送入接收字节数组
                
        Next
        ReDim Preserve Rcv(Rcvlen) As Byte            重新定义并保存接收字符数组
           If Rcvlen >= 1 Then
              For i = LBound(Rcv)   1 To UBound(Rcv)
                 If Rcv(i) = &HA And Rcv(i - 1) = &HD Then   如果接收到回车换行符
                     RcvFinFlag = True                       报文接收完成标志置位
                     FinalDataLen = i                        保存接收到的最终数据长度
                     ReDim Preserve Rcv(FinalDataLen) As Byte    重新定义并保存接收字符数组
                     Rcvlen = -1                                 初始化接收到的数据的长度变量
                     Exit For
                 End If
              Next
        End If
        End Select
        End If
        If RcvFinFlag = True Then                            若报文接收结束
            If ReadFlag Then                                 若为定时读取数据命令
               If Rcv(0) = &H2 Then                          且报文以STX开始
                  t = RcvDataChk(Rcv)                        调用接收数据检查子程序
                  If t Then                                   若接收到的数据正确
                     Call RcvDataDisplay(Rcv, FinalDataLen)   显示
                     Call confirm(&H6)                        向 PLC发送ACK开始的确认报文
                     ReadFlag = False                         “读命令”复位标志
                     No2 = 0                                  重发计数次数复位
                     Timer2.Enabled = False                   关闭通讯超时定时器
                   Else
                     Call confirm(&H15)                       向PLC发送NAK开始的无法确认报文
                     Call ErrorHandle                         进行错误处理
                  End If
                Else
                   If Rcv(0) = &H15 Then Call confirm(&H15)    
                      Call ErrorHandle                         
                End If
            Else
               If Rcv(0) = &H6 And FinalDataLen = 6 Then       若PLC正确执行写命令
                  Timer2.Enabled = False                       关闭通讯超时定时器
                  No2 = 0                                      复位重发次数
                  Exit Sub
               Else
                Call ErrorHandle                              
               End If
            End If
        End If
End Sub

Private Sub Timer1_Timer()
    ReadFlag = True                       置位“读命令”标志
    inString = "00FFWRAD010001"          输入定时读取D100的命令字符串
    Call send(inString)                  调用发送子程序,形成命令帧发送
End Sub

Private Sub Timer2_Timer()
     Call ErrorHandle
End Sub


Private Sub send(inString As String)
    Dim length, i As Integer
If RcvFinFlag = True Then                               前以命令执行完毕,接收完成标志为True
    SaveString = inString                               保存命令字符串
    Rcvlen = -1                                         接收数据存放数组的下标初始化
    RcvFinFlag = False                                  接收完成标志复位
    length = Len(inString)                              求形参传递过来的字符串的长度
    ReDim Preserve outdata(0 To length) As Byte         重新定义发送数据数组,其元素个数为length 1
    outdata(0) = &H5                                    命令报文以控制代码“ENQ”开始
    For i = 1 To length                                 字符串转换为ASCII码,送入发送数组
        outdata(i) = Asc(Mid(inString, i, 1))           
    Next
    Call FCScheck(outdata)                              产生校验和,形成发送帧
    length = UBound(outdata)                            
    ReDim Preserve outdata(0 To length   2) As Byte     重新定义发送数据数组
    outdata(length   1) = &HD                           因为是传输格式4,最后添加回车换行符
    outdata(length   2) = &HA                           
    MSComm1.Output = outdata                            发送命令帧
    Timer2.Enabled = True                               开启超时判断定时器
Else
    MsgBox "前以命令尚未执行完毕", vbExclamation, "操作提示"
End If
End Sub

Private Sub FCScheck(outdata() As Byte)
   Dim BufLen As Integer, Buf As String                 定义字符串长度变量和字符串变量
   Dim i As Integer
   Dim CheckSum As Long                                 定义校验和变量
   BufLen = UBound(outdata)                             求outdata数组可用的最大下标
   CheckSum = 0                                         初始化校验和
   For i = LBound(outdata)   1 To UBound(outdata)       求和时不包括开始的控制代码
       CheckSum = (CheckSum   outdata(i)) And &HFF      对outdata数组的元素求和,只保留低位字节
   Next i
   Buf = IIf(Len(Hex(CheckSum)) = 1, "0" & Hex(CheckSum), Hex(CheckSum))
                                                        若校验和只有1位,则高位添零,补足位2位
   ReDim Preserve outdata(BufLen   2) As Byte
   outdata(BufLen   1) = Asc(Mid(Buf, 1, 1))            校验和转换为ASCII码,低位在前
   outdata(BufLen   2) = Asc(Mid(Buf, 2, 1))
End Sub

Private Sub ErrorHandle()
    If No2 >= 0 And No2 < No1 Then                     若重发次数小于重发设定值,则重发命令
          No2 = No2   1                                重发次数加1
          RcvFinFlag = True                            置位接收完成标志,准备重发
          Call send(SaveString)                        重发
          Exit Sub
    Else
          Timer1.Enabled = False                       关闭定时读取温度值定时器
          Timer2.Enabled = False                       关闭超时判断定时器
          MsgBox "请检查硬件连接及报文设置", vbExclamation, "通讯超时或通讯过程出错"
          No2 = 0                                       复位重发次数
          ReadFlag = False                              读命令标志复位
          RcvFinFlag = True                             接收完成标志置位
          Exit Sub
    End If
End Sub

Private Sub txtset_keypress(KeyAscii As Integer)
     Dim temp As String
     temp = (Chr(KeyAscii))
     If Not (temp Like "[0-9;.;-]" Or KeyAscii = 8 Or KeyAscii = 13) Then
         MsgBox "只允许输入0~9、小数点、负号或退格和火车", vbExclamation, "键盘输入字符错误"
         Exit Sub
     End If
End Sub

Private Function RcvDataChk(Cdata() As Byte) As Boolean
      Dim i As Integer
      Dim EndNo As Integer
      CheckFlag = False                      校验标志初始化
      For i = 0 To UBound(Cdata)
          If Cdata(i) = &H3 Then             如果找到接收到的报文中的ETX(文本结束符)
             EndNo = i                       保存ETX所在的数组元素的下标
             Exit For
          End If
      Next
      Dim Ddata() As Byte                   定义新数组
      ReDim Ddata(EndNo) As Byte
      For i = 0 To EndNo                    将接收到的报文中需要求和的部分存入该数组
          Ddata(i) = Cdata(i)
      Next
          Call FCScheck(Ddata)                调用求校验和子程序。校验和存放在数组末尾
      If Ddata(EndNo   1) = Cdata(EndNo   1) And Ddata(EndNo   2) = Cdata(EndNo   2) Then
          CheckFlag = True                     如果求校验和与接收到的相等,将校验标志置位
      End If
      RcvDataChk = CheckFlag                   返回校验结果
End Function

Private Sub RcvDataDisplay(Rcv() As Byte, Rcvlen As Integer)
    If Rcv(0) = &H2 Then                                          如果是发送读命令后PLC返回的报文
    Dim Shwtemp As Variant                                        定义用于显示字符串的变量
      Dim i As Integer
       Shwtemp = ""                                               显示字符串变量初始化
          For i = 5 To Rcvlen                                     I=5开始上传的数据
              If Rcv(i) <> &H3 Then                               如果不是文本结束的代码ETX
                  Shwtemp = Shwtemp & Chr(Rcv(i))                 接收到的字符送入显示字符串变量
              Else
                 Exit For
              End If
          Next
        Txtview = Format((Val("&h" & Shwtemp) / 10), "# #0.0")    按小数点后一位的格式显示温度值
        End If
End Sub
读命令接收数据响应子程序
Private Sub confirm(CodeByte As Byte)
     ReDim outdata(6) As Byte
     outdata(0) = CodeByte         报文以控制代码ACK或NAK开始
     outdata(1) = &H30             PLC的站号设置为0
     outdata(2) = &H30
     outdata(3) = &H46            FX系列PLC的标识FFH
     outdata(4) = &H46
     outdata(5) = &HD              因为送传送格式4,以回车换行符结束
     outdata(6) = &HA
    MSComm1.Output = outdata      发送确认或不能确认的报文
End Sub

ygmsenator | 2010-5-8 19:22:45 | 显示全部楼层

Re:三菱FX系列PLC编程口通信协议

现在正在看那个 modbus 协议 以前没接触过 正在学习中 哪位要是有资料的发个来 学习下 谢谢

ygmsenator | 2010-5-8 19:27:09 | 显示全部楼层

Re:三菱FX系列PLC编程口通信协议

 我这里有Q02HCPU  和 c24 模块 以前以为需要MODBUS模块呢 看来还是不了解 那个协议 现在积极研究中
yt5408 | 2010-5-8 22:57:09 | 显示全部楼层

Re:三菱FX系列PLC编程口通信协议

LOVEPLC的文章对此通讯讲得非常清楚,顶一下!

学习!

 

yt5408 | 2010-5-10 19:00:42 | 显示全部楼层

Re:三菱FX系列PLC编程口通信协议

三菱的没用过,好好学习一下!

另外,能否做成一个控件 FXCOMM.OCX,上位机调用时,直接通过对该控件里面的属性或方法使用,完成通讯参数设置、对PLC的位及寄存器的读写。这样的话,用户使用起来就方便多了,他可以完全不需了解底层的协议,嘿嘿

 

houyilin82 | 2010-5-16 04:13:19 | 显示全部楼层

Re:三菱FX系列PLC编程口通信协议

难度较高,从未用过~~~~~~~~~看了有点迷糊了,呵呵
2008l1377 | 2011-7-28 00:02:01 | 显示全部楼层

Re:三菱FX系列PLC编程口通信协议

各位好

JACK847070222 | 2013-6-3 21:50:16 | 显示全部楼层
loveplc 发表于 2010-5-8 18:24
该协议实际上适用于PLC编程端口以及 FX-232AW 模块的通信。
通讯格式:

谢谢,很有用,有时间好好读一读
jduzhe | 2013-12-19 14:46:50 | 显示全部楼层
JACK847070222 发表于 2013-6-3 21:50
谢谢,很有用,有时间好好读一读

没有自己编过通信代码,看的有点迷糊
您需要登录后才可以回帖 登录 | 注册哦

本版积分规则