wjun7610
级别: 略有小成
精华主题: 0
发帖数量: 127 个
工控威望: 249 点
下载积分: 708 分
在线时间: 31(小时)
注册时间: 2007-09-16
最后登录: 2024-11-08
查看wjun7610的 主题 / 回贴
楼主  发表于: 2007-09-22 12:49
一、最新三菱PLC FX系列PLC编程口通信协议动态链接库DLL(以下简称DLL),是为满足工业通信需要,针对工业领域要求上位机对PLC实时采集与控制的组态编程而设计。本DLL是采用Delphi语言开发的标准串口通讯库,具有以下特点:
1)、实时性、可靠性好,可根据通信数据量自行调整通信时间;
2)、适用于多PLC联网和上位机通信,满足多方面的需要;
3)、函数接口功能全,操作简单;
4)、附加实用转换与读取函数,易于快速开发(VC等非RAD开发环境的开发);
5)、支持USB、PC扩展卡等扩展串口号;
6)、支持多种操作系统win9x/win2000/winXP;
7)、可在多种编程环境下使用,例如VB、VC、Delphi等开发环境。

二、DLL函数说明

1、打开串口
Function ComOpen(nport:longint;User:Pchar):longint;stdcall;
参数:nport: 打开串口号,取值为1~8,代表COM1~COM8;
      User:DLL授权用户名;
返回值:长整型,操作成功返回1,否则返回0;打开串口不成功即返回0时的原因:1)、串口不存在或被占用; 2)、DLL试用过期; 3)、DLL授权注册不正确。

使用举例:
ComOpen(1,'wjun') , 打开COM1口。

2、关闭串口
Function ComClose(nport:longint):longint;stdcall;
参数:nport: 串口号,取值为1~8,代表COM1~COM8;
返回值:长整型,操作成功返回1,否则返回0;

使用举例:
ComClose(1) ,关闭打开的COM1口。

3、PLC存储器的预置(D)
Function ComDWrite(nport,address:longint;sendstr:pchar):longint;stdcall;
参数:
nport: 串口号,取值为1~8,代表COM1~COM8;
address: 寄存器地址,取值为0~4095(此值需根据不同的PLC操作选择合适的地址范围);
sendstr:  给寄存器写入的值,该值为4个一组的16进制字符串组其取值为0000~FFFF(整数值为0~65535),当要写入多个寄存器值时依次排列即可。如给D0与D1写值100和1000,先将100转成16进制字符串0064、1000转成16进制字符串03E8,则sendstr=006403E8;一次最多写32个寄存器即字符串长应小于等于128;
                            ---------- ----  ~  ---------
写字符串序列如:            | 0000 | FFFF | 0010 | 0064 |  ~    | 0010 | 0064 |
                            ---------- ----  ~  ---------
返回值:长整型,操作成功返回1,否则返回0;

使用举例:
ComDwrite(1,0,‘006403E8’),由串口1预置值D0=100,D1=1000。

4、PLC存储器的读取(C、D、T)
Function ComDRead(nport,address,Count:longint;element:Pchar):Pchar;stdcall;
参数:
nport: 串口号,取值为1~8,代表COM1~COM8;
address: 寄存器地址,取值为D区0~4095、C、T(定时器/计数器当前值)区0~255;
Count: 读取寄存器个数,最多读取32个寄存器;DLL读取的越界会自动舍去
element:元件名称,支持D、C、T;
返回值:字符串数据,字符串数据的终止符为"@";
使用举例:
ComDRead(1,0,4,'D') ,由COM1读取D0~D3四个寄存器的值,如返回值为“0001006403E809F0@”,则表示D0=0001, D1=0064,D2=03E8,D3=09F0;

                            ---------- ----  ---
读字符串序列如:            | 0001 | 0064 | 03E8 | 009F | | @ |
                            ---------- ----  ---
返回值为16进制字符串,可以将返回值如上四位一组再转换成“0~65535”的整数值。


5、PLC的多线圈强置(C、M、S、T)
Function ComEForce(nport,address:longint;element,sendstr:pchar):longint;stdcall;
参数:
nport: 串口号,取值为1~8, 代表COM1~COM8;
address: 位单元地址,定时器C:0~255、M:0~2047、S:0~999、T:0~255、特M: M8000~M8255;
element:元件名称,支持C、M、S、T;
sendstr:  给多线圈写入的值,该值为2个一组的16进制字符串组其取值为00~FF(整数值为0~255),当要写入多个值时依次排列即可。例如: 字节地址C0'(实际由位地址位C0~C7)、字节地址C1'(实际由位地址位C8~C15),如给C0'赋值80、C1'赋值CF,则sendstr=80CF;一次最多写64个字节,即字符串长应小于等于128;
                          ---------- - ~  ------
写字符串序列如:          | 00 | FF | 10 | 64 |  ~  | 1F | 48 |
                          ---------- - ~  ------

实际字符串与位地址的数值应如下表:(将上例的字节C0'、C1'转换为位地址C0~C15由下表对应)
            ------------------------------------
位地址:    | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 |
            ------------------------------------ 
各位赋值:  | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1  | 1  | 0  | 0  | 1  | 1  | 1 | 1 |
            ------------------------------------
16进制串:  |      8      |      0      |        C        |        F      |
            ------------------------------------
         
返回值:长整型,操作成功返回1,否则返回0;

使用举例:
ComEForce(1,0,‘C’ ,‘80CF’),DLL将按上表给相应位强置值,这里地址address赋值0~7都是给C0~C15的强置值,因实际字节地址=address div 8 (0~7 div 8 都等于0,即都是给C0'、C1'强置值)。

字节地址如Mx' = address div 8 ,M、S、C、T都按此公式计算如何给实际的位地址强置值。

6、PLC的多线圈读取(C、M、S、T、X、Y)
Function ComERead(nport,address,Count:longint;element:Pchar):Pchar;stdcall;
参数:
nport: 串口号,取值为1~8,代表COM1~COM8;
address: 位单元地址,定时器C:0~255、M:0~2047、S:0~999、T:0~255、X:0~177(8进制)、Y:0~177(8进制)、特M: M8000~M8255;
Count: 读取字节个数,最多读取64个字节;
element:元件名称,支持C、M、S、T、X、Y;
位单元的字节组成按8个位一组,如56(16进制)则表示下表的位构造值。
              ----------------
位地址:      | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
              ----------------
各位赋值:    | 0 | 1 | 0 | 1 | 0 | 1 | 1 | 0 |
              ----------------
16进制串:    |      5      |      6      |
              ----------------
使用举例:
ComERead(1,0,1,‘M’),如返回56(16进制)则M0~M7的值为上表反应的结果。0~255的位地址映射成字节地址应为0~31。
即:实际地址=address / 8 ,如ComERead(1,11,2,‘C’)相应都得是C8~C15与C16~C23组合的两个字节的值,例如返回值为“870A@”
        -----------------------------------------
位地址:| 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | @ |
        ----------------------------------------- 
各位赋值:| 1 | 0  | 0  | 0  | 0  | 1  | 1 | 1 | 0  | 0  | 0  | 0  | 1  | 0  | 1  | 0  | @ |
        -----------------------------------------
16进制串:|        8        |        7      |        0        |        A        | @ |
        -----------------------------------------

"@"为字符串数据的终止符。

DLL还提供附加函数CinBin ,可以读出字节中相应位的值是1还是0。

对于X、Y区线圈地址可以按8进制地址转换成10进制后再参照上表的地址映射关系。


7、PLC的线圈置位(C、M、S、T)

Function ComESet(nport,address:longint;element:Pchar):longint;stdcall;
参数:
nport: 串口号,取值为1~8,代表COM1~COM8;
address: 位单元地址,定时器C:0~255、M:0~1023、S:0~999、T:0~255、X:0~177、Y:0~177及特M: M8000~M8255;
element:元件名称,支持C、M、S、T;
使用举例:
ComESet(1,0,‘C’),由COM1给C0置位;
ComESet(1,8010,‘M’), 由COM1给M8010置位;


8、PLC的线圈复位(C、M、S、T)
Function ComEReset (nport,address:longint;element:Pchar):longint;stdcall;
参数:
nport: 串口号,取值为1~8,代表COM1~COM8;
address: 位单元地址,定时器C:0~255、M:0~1023、S:0~999、T:0~255、X:0~177、Y:0~177及特M: M8000~M8255;
element:元件名称,支持C、M、S、T;
使用举例:
ComEReset(1,0,‘T’),给T0复位;
ComEReset (1,810,‘S’),给S810复位;

三、DLL附加函数说明

1、串口打开状态的读取
Function ComTrue(nport:longint):longint;stdcall;
参数:
noprt: 串口号,取值为1~8,代表COM1~COM8;
返回值:长整型,串口打开返回1,否则返回0;

2、整数转换成16进制字符串 (为VC等非RAD开发环境所增设)
Function CIntToHex(Cint,Digits:Longint):Pchar;stdcall;
参数:
Cint: 待转换整数,取值为(0~65535);
Digits: 转换的字符串位数,指定位数小于实际位数时按实际输出;

使用举例:
CIntToHex(200,2),则返回字符串“C8@”;
CIntToHex(200,4),则返回字符串“00C8@”;
CIntToHex(4500,4),则返回字符串“1194@”;
CIntToHex(4500,3),则返回字符串“1194@”,因为“194”不足以表示4500这个数所以按实际输出字符串“1194@”;

"@"为字符串数据的终止符。

3、16进制字符串转换成整数 (为VC等非RAD开发环境所增设)
Function CHexToInt(CHex:Pchar):Longint;stdcall;
参数:
CHex: 待转换字符串,取值为(0000~FFFF);
使用举例:
CHexToInt(‘03E8’),则返回整数1000;

4、抽取16进制字符串中某个位的值
Function CinBin(CHex:Pchar;Start:longint):longint;stdcall;
参数:
CHex: 待转换字符串,取值为(0~FFFF);
Start: 抽取的位,取值为(0~15);
使用举例:
CinBin(‘0F’,3),则返回值1;
CinBin(‘0F’,4),则返回值0;
CinBin(‘03E8’,6),则返回值1;
读取M8~M15组合成的字节值为“FC”时,要读取M10的值时,则调用CinBin(‘FC’,3)返回值1表示M10的值为1。

5、返回字符串Text左边的Count个字符  (为VC等非RAD开发环境所增设)
Function CLeftStr(Text:Pchar;Count:longint):Pchar;stdcall;
参数:
Text: 字符串原型;
Count: 指定返回左侧字符串个数;

使用举例:
CleftStr('123456', 3) = '123@';

"@"为字符串数据的终止符。

6、返回字符串Text右边的Count个字符  (为VC等非RAD开发环境所增设)
Function CRightStr(Text:Pchar;Count:longint):Pchar;stdcall;
参数:
Text: 字符串原型;
Count: 指定返回右侧字符串个数
使用举例:
CRightStr('123456', 3) = '456@';

"@"为字符串数据的终止符。

7、返回字符串Text从Start开始的Count个字符 (为VC等非RAD开发环境所增设)
Function CMidStr(Text:Pchar;Start,Count:longint):Pchar;stdcall;
参数:
Text: 字符串原型;
Start: 指定返回字符串的起始位置;
Count: 指定返回字符串个数;
使用举例:
CMidStr('123456',2,3) = '234@';

"@"为字符串数据的终止符。

8、字符串Substr开始于字符串S的位置 (为VC等非RAD开发环境所增设)
Function CinStr(S,Substr:Pchar):Longint;stdcall;
参数:
S: 字符串原型;
Substr: 查询的字符串;
返回值:长整型;

使用举例:
CinStr('1Tfdg23456','2') = 6

DLL中关于传出字符串值的函数都以"@"为字符串函数值终止符。

四、Delphi、VB、VC语言环境的开发使用说明

1、Delphi语言环境开发说明

在Delphi环境下将melsecfx.dll、serialfx.slip(许可文件)复制到应用程序目录下(即将上述文件与编译后的可执行文件方入同一文件夹内);

在工程文件的主程序窗体(pas)文件中声明:

Function ComOpen(nport:longint;User:pchar):longint;stdcall;External'melsecfx.dll';
Function ComClose(nport:longint):longint;stdcall;External'melsecfx.dll';
Function ComDWrite(nport,address:longint;sendstr:pchar):longint;stdcall;External'melsecfx.dll';
Function ComDRead(nport,address,Count:longint;element:Pchar):Pchar;stdcall;External'melsecfx.dll';
Function ComEForce(nport,address:longint;element,sendstr:pchar):longint;stdcall;External'melsecfx.dll';
Function ComERead(nport,address,Count:longint;element:Pchar):Pchar;stdcall;External'melsecfx.dll';
Function ComESet(nport,address:longint;Element:Pchar):longint;stdcall;External'melsecfx.dll';
Function ComEReset(nport,address:longint;element:Pchar):longint;stdcall;External'melsecfx.dll';
Function ComTrue(nport:longint):longint;stdcall; External'melsecfx.dll';
Function CinBin(CHex:Pchar;Start:longint):longint;stdcall; External'melsecfx.dll';

其它附加函数Delphi有实用函数,建议用Delphi自带函数,如需使用声明参上例;

声明后可以在程序中使用这些函数,附加函数置中除ComTrue、CinBin外Delphi系统自带有类似功能函数。通信时必须先使用ComOpen函数打开串口,在串口打开后可以有效操作相关函数,为确保通信可在程序运行开始时打开串口,程序关闭前关闭串口。应用程序关闭之前请务必将关闭所有串口,如串口未关闭前关闭系统将抛出异常。确保应用程序在关闭释放前关闭打开的串口。解决方法,在form的OnDestroy事件中加入如下语句:
if ComTrue(1)=1 then ComClose(1);
在Delphi中给中给DLL中的函数传pchar值问题,参考下例:
读取多线圈M8~M23的值
Procedure TForm1.BitBtn1Click(Sender: TObject);
Var
  elementstr,Recstr:string
  Name:pchar;
  address,Count:word;
begin
  elementstr:=’M’;
  address:=8;  //地址给8~15都行,实际地址=address div 8
  Count:=2;
  // Recstr:= ComERead(1,address, Count,Pchar(elementstr));
  //以上的用法不建议是使用,推荐用下面的用法。
  try
    Name:=strAlloc(8);
Name:=strPCopy(Name, elementstr);  Recstr:=strpas(ComERead(1,address,Count,
Pchar(elementstr));
  finally;
    StrDispose(Name);
  end;
end; 
建议采用strPCopy()|string型转换到Pchar型,straps()|Pchar型转换到string型,不推荐使用直接转换法。

上述可以详细参照DEMO程序。

2、VB语言环境开发说明

在VB环境下将melsecfx.dll、serialfx.slip(许可文件)复制到应用程序目录下(即将上述文件与编译后的可执行文件方入同一文件夹内);

在工程文件中声明:

Private Declare Function ComOpen Lib "melsecfx.dll" (ByVal nport As Long, ByVal User As String) As Long
Private Declare Function ComClose Lib "melsecfx.dll" (ByVal nport As Long) As Long
Private Declare Function ComDWrite Lib "melsecfx.dll" (ByVal nport As Long, ByVal address As Long, ByVal sendstr As String) As Long
Private Declare Function ComEForce Lib "melsecfx.dll" (ByVal nport As Long, ByVal address As Long, ByVal element As String, ByVal sendstr As String) As Long
Private Declare Function ComDRead Lib "melsecfx.dll" (ByVal nport As Long, ByVal address As Long, ByVal Count As Long, ByVal element As String) As String
Private Declare Function ComERead Lib "melsecfx.dll" (ByVal nport As Long, ByVal address As Long, ByVal Count As Long, ByVal element As String) As String
Private Declare Function ComESet Lib "melsecfx.dll" (ByVal nport As Long, ByVal address As Long, ByVal element As String) As Long
Private Declare Function ComEReset Lib "melsecfx.dll" (ByVal nport As Long, ByVal address As Long, ByVal element As String) As Long
Private Declare Function ComTrue Lib "melsecfx.dll" (ByVal nport As Long) As Long
Private Declare Function CinBin Lib "melsecfx.dll" (ByVal Chex As String, ByVal Start As Long) As Long

其它附加函数VB有实用函数,建议用VB自带函数,如需使用声明参上例;
做完上述声明后,便可以在程序中使用相关函数了。本DLL是串口通信库,通信时必须先使用ComOpen函数打开串口,在串口打开后可以有效操作相关函数,为保证通信可以在程序运行开始时打开串口,程序关闭前关闭串口。在应用程序关闭之前请务必将关闭所有串口,如您的程序串口未关闭前被关闭系统将抛出异常。当出现这样的异常请更改您的程序,确保应用程序在关闭释放前关闭打开的串口。
解决方法,在form的Unload事件中加入如下例:
If ComTrue(1)=1 then
  ComClose(1)
End if
为确保Unload事件有效执行程序中应使用“End”语句,而尽量使用“Unload”语句释放所有窗体,因使用“End”语句系统不会执行正常的窗体释放等事件而直接退出程序,如程序中有form1,form2两个窗体,则使用下面语句:
Unload  form1
Unload  form2
VB会在所有窗体关闭后释放所有占用资源。
上述可以详细参照DEMO程序。

3、VC语言环境开发说明
在VC环境下将melsecfx.dll、serialfx.slip(许可文件)复制到应用程序目录下(即将上述文件与编译后的可执行文件方入同一文件夹内);

在工程主文件cpp中声明一个句柄:
HINSTANCE m_handle;
用来标识导入的动态链接库。

1)、导入动态链接库,如例所示:

  m_handle =:: LoadLibrary("melsecfx.dll");

2)、按下例说明声明相关各个函数:

typedef long (CALLBACK* pOpen)(long nport, char* User);
typedef long (CALLBACK* pClose)(long nport);
typedef long (CALLBACK* pDWrite)(long nport,long address, char* sendstr);
typedef long (CALLBACK* pEForce)(long nport,long address, char* element, char* sendstr);
typedef char* (CALLBACK* pDRead)(long nport,long address,long Count, char* element);
typedef char* (CALLBACK* pERead)(long nport,long address,long Count,char* element);
typedef long (CALLBACK* pSet)(long nport,long address, char* element);
typedef long (CALLBACK* pReset)(long nport,long address char* element);
typedef long (CALLBACK* pTrue)(long nport);
typedef char* (CALLBACK* pIntHex)(long Cint,long Digits);
typedef long (CALLBACK* pHexInt)( char* CHex);
typedef long (CALLBACK* pBin)( char* Chex, long Start);
typedef char* (CALLBACK* pLeft)( char* Text, long Count);
typedef char* (CALLBACK* pRight)( char* Text, long Count);
typedef char* (CALLBACK* pMid)( char* Text, long  Start, long Count);
typedef long (CALLBACK* pinstr)( char* S, char* Substr);

3)、声明并建立动态链接库中的函数与新函数名的对应关系,如下:

pOpen cOpen = (pOpen)GetProcAddress(m_handle,"ComOpen");
pClose cClose = (pClose)GetProcAddress(m_handle,"ComClose");
pDWrite cDWrite = (pDWrite)GetProcAddress(m_handle," ComDWrite");
pDRead cDRead = (pDRead)GetProcAddress(m_handle," ComDRead");
pEForce cEForce = (pEForce)GetProcAddress(m_handle,"ComEForce");
pERead cERead = (pERead)GetProcAddress(m_handle,"ComERead");
pSet cSet = (pSet)GetProcAddress(m_handle," ComESet");
pReset cReset = (pReset)GetProcAddress(m_handle," ComEReset");
pTrue cTrue = (pTrue)GetProcAddress(m_handle," ComTrue");
pIntHex cIntHex = (pIntHex)GetProcAddress(m_handle," CIntToHex");
pHexInt cHexInt = (pHexInt)GetProcAddress(m_handle," CHexToInt");
pBin cBin = (pBin)GetProcAddress(m_handle," CinBin");
pLeft cLeft = (pLeft)GetProcAddress(m_handle," CLeftStr");
pRight cRight = (pRight)GetProcAddress(m_handle," CRightStr");
pMid cMid= (pMid)GetProcAddress(m_handle," CMidStr");
pinstr instr=(pinstr)GetProcAddress(m_handle," CinStr");

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

4)、接下来就可以自由使用动态链接库中的函数了,如:

cOpen(参数略);
cClose(参数略);
cDWrite(参数略);
cDRead(参数略);
cEForce(参数略);
cERead(参数略);
cSet(参数略);
cReset(参数略);
cTrue(参数略);
cIntHex(参数略);
cHexInt(参数略);
cBin(参数略);
cLeft(参数略);
cRight(参数略);
cMid(参数略);
instr(参数略);

注:函数中用到了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时记得关闭串口及释放动态链接库,如:

关闭串口
if cTrue(1)==1 then
{
  cClose(1);
}

6)、释放DLL

FreeLibrary(m_handle);

注:对于各位使用VC及其他开发环境的朋友,我表示歉意因为我不太使用这类软件所以就不再写DEMO程序了,VC的参照上述声明我做过测试是可以使用的,其他开发环境我没有测试不能保证您可以使用。

作者:  wjun7610
QQ:    157610979
Email: wjun7610@yahoo.com.cn
淘宝店:http://shop34821629.taobao.com
本帖最近评分记录:
  • 下载积分:+5(taoyu001) 楼主辛苦了!
  • 下载积分:+5(YMMFA)
    heyijun
    级别: 探索解密
    精华主题: 0
    发帖数量: 39 个
    工控威望: 141 点
    下载积分: 626 分
    在线时间: 36(小时)
    注册时间: 2010-11-18
    最后登录: 2024-11-09
    查看heyijun的 主题 / 回贴
    1楼  发表于: 2011-01-27 11:55
    值得学习!感谢分享!