wjun7610
级别: 略有小成
精华主题: 0
发帖数量: 127 个
工控威望: 249 点
下载积分: 708 分
在线时间: 31(小时)
注册时间: 2007-09-16
最后登录: 2024-11-08
查看wjun7610的 主题 / 回贴
楼主  发表于: 2013-04-13 12:43
S7200_PPI动态库DLL+实例.rar (490 K) 下载次数:1911    S7200_PPI.dll V3.2 通信协议串口通讯动态链接库DLL(以下简称DLL),是为满足工业通信需要,

针对工业领域要求上位机对西门子S7-200系列PLC通讯实时采集与控制的组态编程而设计。

本DLL是采用Delphi语言开发的标准串口通讯库,具有以下特点:

1)、遵循西门子PLC的PPI通讯协议;

2)、支持多串口并发操作,支持多线程串口通讯,将串口通讯对主程序的影响降至最低;

3)、实时性、可靠性好,通用性强;

4)、适用于多PLC(下位机)联网和上位机通信,满足多方面的需要;

5)、函数接口功能全,操作简单,支持PPI通讯协议的绝大部分地址的读写功能函数;

6)、支持USB、PC扩展卡等扩展串口号;

7)、支持多种操作系统win9x/win2000/winXP(标准Win32 DLL);

8)、易于快速开发(VC等非RAD开发环境的开发);

9)、更新原有函数调用方式采用数组调用数据发生与接收,初学者快速入门;

10)、可在多种编程环境下使用,例如VB、VC、Delphi、PB、Labview、易语言等开发环境;

11)、扩展了函数功能读取下位机中的双字、单精度浮点等类型数据,更加符合工业自动化领域的工控软件的开发。

软件版本更新及相关说明:

S7200_PPI.dll V3.2 串口通讯链接库,实现了对S7-200系列PLC的I、Q、M、V、S、、SM、C、T区软元件的读写功能

wangjun于2008年在原有S7200_PPI.dll V2.0版基础上的更新版,重写了函数结构,采用数据接收和发送缓冲区

(数组)方式传递,读出和写入单元数据使用更方便。对于16位整数和32位整数读取写入均按有符号整数处理,

小数的返回按单精度浮点型(2进制浮点)处理读取方式。提供扩展功能函数,方便用户实现混合数据读取。

能够同时满足32个串口并发情况下的正常使用。

S7200_PPI.dll V3.2 分为完美版和单机版两个版本。

注意:实例在使用时不能超出试用版的地址限制,否则无法正确通讯。

龙帅工作室为您提供专业串口通讯控件,也可为您订制

专用通讯协议串口控件。目前龙帅系列串口通讯协议

控件包括modbus_rtu、modbus_tcp、三菱FX PLC编程口、

欧姆龙HostLink、西门子PPI等多个系列产品。

淘宝店:http://shop34821629.taobao.com

电  话:13912935690

腾讯QQ:157610979

阿里旺旺: wj_longshuai

邮  箱:157610979@QQ.com        

        wjun7610@yahoo.com.cn

        chinawanglong@163.com
[ 此帖被wjun7610在2013-04-13 15:03重新编辑 ]
wjun7610
级别: 略有小成
精华主题: 0
发帖数量: 127 个
工控威望: 249 点
下载积分: 708 分
在线时间: 31(小时)
注册时间: 2007-09-16
最后登录: 2024-11-08
查看wjun7610的 主题 / 回贴
1楼  发表于: 2013-04-13 12:47
DLL串口操作函数包括:

  1)、S7200ComOpen打开串口;

  2)、S7200ComClose关闭串口;
      
  3)、S7200SetDelay串口通讯延时设定;
                    
  4)、S7200ComTrue判断串口是否打开成功;
            
  5)、S7200ComWork判断串口是否正在工作中;

S7200 PPI协议功能函数:

  1)、S7200BitRead    位读取函数

  2)、S7200ByteRead   字节读取函数

  3)、S7200WordRead   字读取函数

  4)、S7200DwordRead  双字读取函数

  5)、S7200FloatRead  浮点读取函数

  6)、S7200BitWrite   位赋值函数

  7)、S7200ByteWrite  字节赋值函数

  8)、S7200WordWrite  字赋值函数

  9)、S7200DwordWrite 双字赋值函数

10)、S7200FloatWrite 浮点赋值函数

11)、S7200BitEWrite  元件强制赋值函数

12)、S7200EbitCancel 取消位元件强制值函数

13)、S7200PlcRun     置PLC为运行模式

14)、S7200PlcStop    置PLC为停止模式

S7200 PPI协议DLL缓冲区传递功能函数包括:

  1)、S7200ClearBuf         清除复位DLL缓存区

  2)、S7200ByteReadBuf      字节读取函数  

  3)、S7200WordReadBuf      字读取函数

  4)、S7200DwordReadBuf     双字读取函数  

  5)、S7200IntDllBufRead    读DLL整数缓存区值;
  
  6)、S7200FloatReadBuf     浮点读取函数

  7)、S7200FloatDllBufRead  读DLL浮点缓存区值;

  8)、S7200WriteIntDllBuf   写DLL整数缓存区值;

  9)、S7200ByteWriteBuf     字节赋值函数  

10)、S7200WordWriteBuf     字赋值函数  

11)、S7200DwordWriteBuf    双字赋值函数

12)、S7200WriteFloatDllBuf 写DLL浮点缓存区值;  

13)、S7200FloatWriteBuf    浮点赋值函数

   这里,仅对PB开发环境使用DLL缓存区数据传递进行调用给出了

申明方式和调用举例,其他非VC、VB、Delphi等语言需要使用DLL

缓冲区进行数据传递时,调用DLL声明请参考本语言对Windows标准

DLL的规则和函数原型进行调用。

能够正确传递数组的开发环境如VB、VC、delphi不推荐使用该部分

扩展功能函数。

当需要进行8位整数、16位整数、32位整数、32位浮点数值进行混合读写时,所需拓展函数包括:

  1)、DecBitBin        字整数的位抽取;

  2)、Int16ToInt_8h     字到字节转换高位字节;  

  3)、Int16ToInt_8l     字到字节转换低位字节;

  4)、Int8ToInt16       字节到字的转换;

  5)、Int32ToInt_16h    双字到字转换高位字;  

  6)、Int32ToInt_16l    双字到字转换低位字;

  7)、Int16ToInt32      字到双字的转换;

  8)、Float32ToInt_16h  浮点到字转换高位字;

  9)、Float32ToInt_16l  浮点到字转换低位字;

  10)、Int16ToFloat32   字到浮点的转换;  

  11)、介绍如何实现数据的混合读取和写入;
wjun7610
级别: 略有小成
精华主题: 0
发帖数量: 127 个
工控威望: 249 点
下载积分: 708 分
在线时间: 31(小时)
注册时间: 2007-09-16
最后登录: 2024-11-08
查看wjun7610的 主题 / 回贴
2楼  发表于: 2013-04-13 12:47
在VC中使用DLL一般都是采用动态声明的方式,函数说明中给出的是Delphi的函数原型,

在VC中声明时只要注意一下类型的对应即可,Delphi中的longint类型对应VC中的int类型

Delphi中的Pchar对应VC中的char* ,下面给出主要函数的声明:

在使用的文件的cpp中声明一个句柄:

HINSTANCE hinstDLL;

用来标识导入的动态链接库。

1)、按下例说明声明相关各个函数:(在cpp文件的头处声明);

typedef int (_stdcall *pOpen)(int nport, int BaudRate, int DataBits, char* Parity, int StopBits, char* User);
typedef int (_stdcall *pClose)(int nport);
typedef int (_stdcall *pSetDelay)(int value);
typedef int (_stdcall *pComTrue)(int nport);
typedef int (_stdcall *pComWork)(int nport);

typedef int (_stdcall *pReadBit)(int nport, int node, char* element, int address, int Bit);
typedef int (_stdcall *pReadByte)(int nport, int node, char* element, int address, int Count, int* RxdBuffer);
typedef int (_stdcall *pReadInt)(int nport, int node, char* element, int address, int Count, int* RxdBuffer);
typedef int (_stdcall *pReadDInt)(int nport, int node, char* element, int address, int Count, int* RxdBuffer);
typedef int (_stdcall *pReadFloat)(int nport, int node, char* element, int address, int Count, float* RxdBuffer);

typedef int (_stdcall *pBitWrite)(int nport, int node, char* element, int address, int Bit, int value);
typedef int (_stdcall *pBitEWrite)(int nport, int node, char* element, int address, int Bit, int value);
typedef int (_stdcall *pEbitCancel)(int nport, int node, char* element, int address, int Bit);

typedef int (_stdcall *pWriteByte)(int nport, int node, char* element, int address, int Count, int* TxdBuffer);
typedef int (_stdcall *pWriteInt)(int nport, int node, char* element, int address, int Count, int* TxdBuffer);
typedef int (_stdcall *pWriteDInt)(int nport, int node, char* element, int address, int Count, int* TxdBuffer);
typedef int (_stdcall *pWriteFloat)(int nport, int node, char* element, int address, int Count, float* TxdBuffer);

typedef int (_stdcall *pPlcRun)(int nport, int node);
typedef int (_stdcall *pPlcStop)(int nport, int node);

typedef int (_stdcall *pBitBin)(int value, int Bitaddress);
typedef int (_stdcall *p16I_8h)(int value);
typedef int (_stdcall *p16I_8l)(int value);
typedef int (_stdcall *p8I_16I)(int valueH, int valueL);
typedef int (_stdcall *p32I_16h)(int value);
typedef int (_stdcall *p32I_16l)(int value);
typedef int (_stdcall *p16I_32I)(int valueH, int valueL);
typedef int (_stdcall *p32f_16h)(float value);
typedef int (_stdcall *p32f_16l)(float value);
typedef float (_stdcall *p16I_32f)(int valueH, int valueL);

2)、建立动态链接库的新函数名:(在cpp文件的头处声明,上面的声明之后)

pOpen mOpen;
pClose mClose;
pSetDelay mSetDelay;
pComTrue mComTrue;
pComWork mComWork;

pReadBit mReadBit;
pReadByte mReadByte;
pReadInt mReadInt;
pReadDInt mReadDInt;
pReadFloat mReadFloat;

pBitWrite mBitWrite;
pBitEWrite mBitEWrite;
pEbitCancel mEbitCancel;

pWriteByte mWriteByte;
pWriteInt mWriteInt;
pWriteDInt mWriteDInt;
pWriteFloat mWriteFloat;

pPlcRun mPlcRun;
pPlcStop mPlcStop;

pBitBin mBitBin;
p16I_8h m16I_8h;
p16I_8l m16I_8l;
p8I_16I m8I_16I;
p32I_16h m32I_16h;
p32I_16l m32I_16l;
p16I_32I m16I_32I;
p32f_16h m32f_16h;
p32f_16l m32f_16l;
p16I_32f m16I_32f;

3)、导入动态链接库,如例所示:(在cpp文件的OnInitDialog过程建立):

hinstDLL = LoadLibrary("S7200_PPI.dll");

4)、判断dll文件是否存在并声明并建立动态链接库中的函数与新函数名的对应关系,

如下:(在cpp文件的OnInitDialog过程建立):

if (hinstDLL)
{
   mOpen = (pOpen)GetProcAddress (hinstDLL,"S7200ComOpen");
   mClose = (pClose)GetProcAddress (hinstDLL,"S7200ComClose");
   mSetDelay = (pSetDelay)GetProcAddress (hinstDLL,"S7200SetDelay");
   mComTrue = (pComTrue)GetProcAddress (hinstDLL,"S7200ComTrue");
   mComWork = (pComWork)GetProcAddress (hinstDLL,"S7200ComWork");

   mReadBit = (pReadBit)GetProcAddress (hinstDLL,"S7200BitRead");
   mReadByte = (pReadByte)GetProcAddress (hinstDLL,"S7200ByteRead");
   mReadInt = (pReadInt)GetProcAddress (hinstDLL,"S7200WordRead");
   mReadDInt = (pReadDInt)GetProcAddress (hinstDLL,"S7200DwordRead");
   mReadFloat = (pReadFloat)GetProcAddress (hinstDLL,"S7200FloatRead");

   mBitWrite = (pBitWrite)GetProcAddress (hinstDLL,"S7200BitWrite");
   mBitEWrite = (pBitEWrite)GetProcAddress (hinstDLL,"S7200BitEWrite");
   mEbitCancel = (pEbitCancel)GetProcAddress (hinstDLL,"S7200EbitCancel");
      
   mWriteByte = (pWriteByte)GetProcAddress (hinstDLL,"S7200ByteWrite");
   mWriteInt = (pWriteInt)GetProcAddress (hinstDLL,"S7200WordWrite");
   mWriteDInt = (pWriteDInt)GetProcAddress (hinstDLL,"S7200DwordWrite");
   mWriteFloat = (pWriteFloat)GetProcAddress (hinstDLL,"S7200FloatWrite");
  
   mPlcRun = (pPlcRun)GetProcAddress (hinstDLL,"S7200PlcRun");
   mPlcStop = (pPlcStop)GetProcAddress (hinstDLL,"S7200PlcStop");
      
   mBitBin = (pBitBin)GetProcAddress (hinstDLL,"DecBitBin");
   m16I_8h = (p16I_8h)GetProcAddress (hinstDLL,"Int16ToInt_8h");
   m16I_8l = (p16I_8l)GetProcAddress (hinstDLL,"Int16ToInt_8l");
   m8I_16I= (p8I_16I)GetProcAddress (hinstDLL,"Int8ToInt16");
   m32I_16h = (p32I_16h)GetProcAddress (hinstDLL,"Int32ToInt_16h");
   m32I_16l = (p32I_16l)GetProcAddress (hinstDLL,"Int32ToInt_16l");
   m16I_32I= (p16I_32I)GetProcAddress (hinstDLL,"Int16ToInt32");
   m32f_16h = (p32f_16h)GetProcAddress (hinstDLL,"Float32ToInt_16h");
   m32f_16l = (p32f_16l)GetProcAddress (hinstDLL,"Float32ToInt_16l");
   m16I_32f= (p16I_32f)GetProcAddress (hinstDLL,"Int16ToFloat32");
  
   AfxMessageBox("S7200_PPI.dll已成功载入!");
}
else
{
  AfxMessageBox("没找到S7200_PPI.dll!");
  SendMessage(WM_CLOSE);
}

注:双引号中为动态链接库中的原有函数名。

函数中用到了char*型参数,这里介绍下char*与Cstring的相互转换的函数:

(1)char*->CString

char* sz;
CString str;
str.Format("%s",sz);  //可以用此函数将读取的值转成字符串

(2) CString -> char*

CString str;
char* sz = str.GetBuffer(0);//可将字符串转成char*给函数赋值


5)、当不再需要使用DLL时记得关闭串口及释放动态链接库,(在OnDestroy事件中释放)

if(hinstDLL)
{
   int k = mComTrue(mnport);
   if (k==1)
   {
      mClose(mnport);
   }    
   FreeLibrary(hinstDLL);
}
wjun7610
级别: 略有小成
精华主题: 0
发帖数量: 127 个
工控威望: 249 点
下载积分: 708 分
在线时间: 31(小时)
注册时间: 2007-09-16
最后登录: 2024-11-08
查看wjun7610的 主题 / 回贴
3楼  发表于: 2013-04-13 12:47
西门子PPI通讯协议

    通过硬件和软件侦听的方法,分析PLC内部固有的PPI通讯协议,然后采用上位机编程,遵循PPI通讯协议,读写PLC数据,实现人机操作

任务。这种通讯方法,与一般的自由通讯协议相比,省略了PLC的通讯程序编写,只需编写上位机的通讯程序资源S7-226的编程口物理层为

RS-485结构,SIEMENS提供MicroWin软件,采用的是PPI(Point to Point)协议,可以用来传输、调试PLC程序。在现场应用中,当需要PLC

与上位机通讯时,较多的使用自定义协议与上位机通讯。在这种通讯方式中,需要编程者首先定义自己的自由通讯格式,在PLC中编写代码,

利用中断方式控制通讯端口的数据收发。采用这种方式,PLC编程调试较为烦琐,占用PLC的软件中断和代码资源,而且当PLC的通讯口定义

为自由通讯口时,PLC的编程软件无法对PLC进行监控,给PLC程序调试带来不便。

    SIEMENS S7-200PLC的编程通讯接口,内部固化的通讯协议为PPI协议,如果上位机遵循PPI协议来读写PLC,就可以省略编写PLC的通讯

代码。如何获得PPI协议?可以在PLC的编程软件读写PLC数据时,利用第三个串口侦听PLC的通讯数据,或者利用软件方法,截取已经打开

且正在通讯的端口的数据,然后归纳总结,解析出PPI协议的数据读写报文。这样,上位机遵循PPI协议,就可以便利的读写PLC内部的数据,

实现上位机的人机操作功能。

西门子的PPI(Point to Point)通讯协议采用主从式的通讯方式,一次读写操作的步骤包括:首先上位机发出读写命令,PLC作出接收正确

的响应,上位机接到此响应则发出确认申请命令,PLC则完成正确的读写响应,回应给上位机数据。这样收发两次数据,完成一次数据的读写

(从这里可以看出PPI协议的通讯效率并不好,一次读写需收发两次数据^_^)。

其通讯数据报文格式大致有以下几类:

1)、读写申请的数据格式如下:

SD LE LER SD DA SA FC DASP SSAP DU FCS ED

SD:(Start Delimiter)开始定界符(68H)

LE:(Length)报文数据长度

LER:(Repeated Length)重复数据长度

SD: (Start Delimiter)开始定界符(68H)

SA:(Source Address)源地址,指该地址;

DA:(Destination Address)目标地址,指该地址;

FC:(Function Code)功能码

DSAP:(Destination Service Access Point)目的服务存取点

SSAP:(Source Service Access Point)源服务存取点

DU:(Data Unit)数据单元

FCS:(Frame Check Sequence)校验码

ED:(End Delimiter)结束分界符(16H)

报文数据长度和重复数据长度为自DA至DU的数据长度,校验码为DA至DU数据的和校验,只取其中的末字节值。

读写一般数据的功能码为 6CH,读CT数据、置位、复位、强制等的功能码为 7CH,确认命令功能码5CH。

2)、PLC接收到读写命令,校验后正确,返回的数据格式为 E5H

3)、确认读写命令的数据格式为:

SD SA DA FC FCS ED  

其中SD为起始符,为10H

SA为数据源地址

DA为目的地址

FC为功能码,取5CH

FCS为SA+DA+FC的和的末字节

ED为结束符,取16H

4)、数据类型码

04:S
05:SM
06:AI
07:AQ
1E: C
81:I
82:Q
83:M
84:V
1F: T

5)、读写功能数据命令实例

1、给写VW3值1200

68 21 21 68 02 00 6C 32 01 00 00 00 00 00 0E 00 06 05 01 12 0A 10 02 00 02 00 01 84 00 00 18 00 04 00 10 04 B0 50 16

2、读取IB0的数据值

68 1B 1B 68 02 00 6C 32 01 00 00 00 00 00 0E 00 00 04 01 12 0A 10 02 00 01 00 00 81 00 00 00 64 16

感兴趣的朋友给你根据上述例子用串口精灵测试一下,当发送命令成功PLC返回E5,你再发送10 02 00 5C 5E 16,就可以完成一次读写命令。

(记得接收和发送采用16进制方式 o(∩_∩)o...哈哈)

2.2 S7-200PLC CPU内存地址范围(DLL所能寻址的范围)

被存取:       内存类型      CPU 221        CPU 222         CPU 224                   CPU 226

位(字节.位)       V          0.0 ~ 2047.7  0.0 ~  2047.7  0.0 ~  5119.7  V 1.22    0.0 ~  5119.7   V 1.23
                                                            0.0 ~  8191.7  V 2.00    0.0 ~ 10239.7  V 2.00
                                                            0.0 ~  10239.7 XP

                   I          0.0 ~ 15.7   0.0 ~ 15.7     0.0 ~  15.7              0.0 ~  15.7

                   Q          0.0 ~ 15.7   0.0 ~ 15.7     0.0 ~ 15.7               0.0 ~ 15.7
                  
                   M          0.0 ~ 31.7   0.0 ~ 31.7     0.0 ~ 31.7               0.0 ~ 31.7

                   S          0.0 ~ 31.7   0.0 ~ 31.7     0.0 ~ 31.7               0.0 ~ 31.7

                   SM         0.0 ~ 179.7  0.0 ~ 299.7    0.0 ~ 549.7              0.0 ~ 549.7

字节               VB         0 ~ 2047     0 ~ 2047       0 ~ 5119   V 1.22        0 ~ 5119        V 1.23
                                                            0 ~ 8191   V 2.00        0 ~ 10239       V 2.00
                                                            0 ~ 10239  XP

                   IB         0 ~ 15       0 ~ 15         0 ~ 15                   0 ~ 15

                   QB         0 ~ 15       0 ~ 15         0 ~ 15                   0 ~ 15

                   MB         0 ~ 31       0 ~ 31         0 ~ 31                   0 ~ 31

                   SMB        0 ~179       0 ~299         0 ~ 549                  0 ~ 549

                   SB         0 ~ 31       0 ~ 31         0 ~ 31                   0 ~ 31

字                 VW         0 ~2046      0 ~ 2046       0 ~5118   V 1.22         0 ~ 5118        V 1.23
                                                            0 ~ 8190   V 2.00        0 ~ 10230       V 2.00
                                                            0 ~ 10230  XP
                  
                   T          0 ~ 255      0 ~ 255        0 ~ 255                  0 ~ 255

                   C          0 ~ 255      0 ~ 255        0 ~ 255                  0 ~ 255

                   AIW        0 ~ 30       0 ~ 30         0 ~ 62                   0 ~ 62

                   AQW        0 ~ 30       0 ~ 30         0 ~ 62                   0 ~ 62

双字               VD         0 ~ 2044     0 ~ 2044      0 ~ 5116   V 1.22       0 ~ 5116        V 1.23
                                                           0 ~ 8188   V 2.00       0 ~ 10236       V 2.00
                                                           0 ~ 10236  XP

说明:S7-200PLC的字、双字地址和字节地址实际上是重叠的,下面做个说明如:

VB0  VB1  VB2  VB3  VB4  VB5  VB6  VB7  VB8  VB9  VB10  VB11  VB12  VB13  VB14  VB15

————  ————  ————  ————  ————  ————    ————     ————
  VW0       VW2       VW4       VW6       VW8       VW10        VW12         VW14
—————————  ————————— —————————    ———————————
       VD0                 VD4                 VD8                     VD12

从上例可以看出PLC中字节、字与双字寻址的关系,当然如果你想使用VW1(VB1 VB2)、VD3(VB3 VB4 VB5 VB6)等寻址方式也是可以的,

但为了减少不必要的寻址错误造成程序的非法执行建议:

字寻址时使用(V、I、Q、M等等)0、2、4、6、8、10 ……,(这从PLC的模拟量输入/输出(AIW/AQW)的寻址就可以看出奇数地址是不允许的)

双字寻址时使用(V、I、Q、M等等)0、4、8、12、16、20、24 …………………………,同时还要注意字节地址寻址最好也能不重叠;

C、T区当前值读取时的地址按实际地址0~255来。
huoshi
为所当为
级别: 网络英雄

精华主题: 0
发帖数量: 2618 个
工控威望: 21425 点
下载积分: 12982 分
在线时间: 2741(小时)
注册时间: 2010-12-27
最后登录: 2024-11-18
查看huoshi的 主题 / 回贴
4楼  发表于: 2013-04-14 08:50
谢谢下了学习学习
kccjason
级别: 探索解密
精华主题: 0
发帖数量: 43 个
工控威望: 144 点
下载积分: 600 分
在线时间: 33(小时)
注册时间: 2011-04-26
最后登录: 2024-07-12
查看kccjason的 主题 / 回贴
5楼  发表于: 2013-04-25 23:10
取下謝謝
11yewang
级别: 探索解密
精华主题: 0
发帖数量: 3 个
工控威望: 121 点
下载积分: 523 分
在线时间: 1(小时)
注册时间: 2013-01-28
最后登录: 2015-01-30
查看11yewang的 主题 / 回贴
6楼  发表于: 2013-10-23 09:23
好东西啊
yahk1984
级别: 探索解密
精华主题: 0
发帖数量: 7 个
工控威望: 125 点
下载积分: 576 分
在线时间: 7(小时)
注册时间: 2013-04-19
最后登录: 2024-11-01
查看yahk1984的 主题 / 回贴
7楼  发表于: 2013-11-05 11:49
下不了