亚控公司驱动部 DLL 开发包 使用手册 北京亚控自动化软件科技有限公司 2001-9-21(v1.0)
概述 : 本开发包采用微软标准的 COM 组件技术, 采用该技术, 在创建接口时, 可以创建多个互相独立对象, 每个对象都可以拥有自己的变量 最后的结果是一个 DLL 文件 接口中的各函数, 是被组态王的两个应用程序 ---TouchExplorer.exe 和 TouchVew.exe 调用的 如果这两个应用程序都不启动, 函数中的代码将永远没有机会执行 组台王的驱动程序是组态王和硬件设备连接的桥梁, 本开发包用于开发组态王的驱动程序 只要拥有硬件的通讯协议, 即可以使用本开发包开发组态王的驱动程序 本开发包只能使用 VC++ 开发 本开发包包括两个 VC 的头文件 :IcomPro.h 和 datatype.h; 一个 demo 项目及一个制作安装文件的项目代码 1 快速创建程序框架 : 1) 选择向导中的 Project 选项卡, 选择 MFC AppWizard(dll) 2) 命名原则 : 可读性强, 根据设备的型号 厂家或客户的公司名字, 应能和其他的硬件 设备厂家或客户的公司名字区分开 3) 下一步的选项如下所示 4) 加一个新类, 2
5) 加入新类时各选项如下 : 6) 新类的名字应是在项目的名字后加 Pro 7) 打开另一个驱动的项目, 打开 XXXXPro.h 文件, 3
8) 将下图所示部分拷入新的项目中 9) 将文件 XXXXPro.cpp 打开, 将下图所示部分拷入新的项目中 10) 将下图所示部分直至文件结尾, 全部拷入拷入新的项目中 11) 选择 Replace All 将类名全部替换 4
12) 根据协议, 修改新的项目 2 有关函数使用的说明 : 各函数简介 : 1) QueryInterface(REFIID iid, void** ppvobj) // 函数名称 : QueryInterface // 函数说明 : 无须理睬 2) ULONG AddRef() // 函数名称 : AddRef // 函数说明 : 无须理睬 // 返回值 : ULONG 3) ULONG Release() // 函数名称 : Release // 函数说明 : 无须理睬 // 返回值 : ULONG 4) BOOL GetRegisters( char *szdevicename, LPVOID *ppreg, int *pregnum ) // 函数名称 : GetRegisters // 函数说明 : 得到由 szdevicename 确定的仪表的寄存器 // 的名字和个数 // 返回值 : BOOL: 如果 szdevicename 有效, 则返回 TRUE, // 否则 FALSE // 参数 : const char* szdevicename // 设备的唯一名字 // 参数 : LPVOID * ppregs // 返回寄存器信息的指针数组的指针 如果 // szdevicename 无效, 则返回 NULL // 参数 : int *pregnum // 返回寄存器的个数, 如果 szdevicename 无效, // 则返回 0 5) BOOL TryConnect( char* pdevicename, int nunitaddr, LPVOID lpdevaddr ) 5
// 函数名称 : TryConnect // 函数说明 : 与地址为 UnitAddr 的设备尝试进行通讯, 用于故障侦探及恢复尝试. // 返回值 : BOOL: 如果 szdevicename 有效, 则返回 TRUE, // 否则 FALSE // 参数 : char* pdevicename // 设备的唯一名字 // 参数 : int nunitaddr // 需要进行故障侦探及恢复尝试的设备的地址 // 参数 : LPVOID lpdevaddr // 应将此参数转换为结构 --- PDEVADDR 此结构详情参阅 // DATATYPE.H 文件 // 将 lpdbitem 内的有关下位机的内容进行转换并填充到 lpvar 中 6) WORD ConvertUserConfigToVar( LPVOID lpdbitem, LPVOID lpvar ) // 函数名称 : ConvertUserConfigToVar // 函数说明 : 将用户的配置字符串转换为组态王变量结构 // 返回值 : WORD: 如果转换成功则返回 0, 否则返回非零的错误代码 // 参数 : LPVOID lpdbitem: 指向结构 MiniDbItem 的指针, // 注意成员变量 szregsiter 和 szdevname, 这两个 // 变量决定了返回的变量 // 参数 : LPVOID lpvar: 指向结构 PLCVAR 的指针, 返回变量 // 就存放在这里 7) int LoadDeviceInfo( const char *sproducer, const char *sdevicename, int ntype) // 函数名称 : LoadDeviceInfo // 函数说明 : 加载设备的特殊信息, 板卡中使用较多 // 返回值 : int // 加载信息成功, 返回 TRUE; 加载信息失败, 返回 FALSE; // 参数 : const char *sproducer // 版卡配置文件的名字 // 参数 : const char *sdevicename // 设备的唯一名字 // 参数 : int ntype // 备用 8) BOOL OpenComDevice( int ndevicetype, LPVOID lpinitdata ) // 函数名称 : OpenComDevice // 函数说明 : 初始化通讯设备如 :( 串口或板卡等 ) // 返回值 : BOOL: 成功则返回 TRUE, 否则返回 FALSE // 参数 : int ndevicetype: 仪表类型或者 PLC 类型 // 参数 : LPVOID lpinitdata: 一个指向 COMM_CONFIG 结构的指针 9) BOOL CloseComDevice() // 函数名称 : CloseComDevice // 函数说明 : 关闭该通讯设备 // 返回值 : BOOL: 成功则返回 TRUE, 否则返回 FALSE 10) BOOL InitialDevice(char* pdevicename, int nunitaddr, LPVOID 6
lpdevaddr ) // 函数名称 : InitialDevice // 函数说明 : 初始化设备, 设置该设备的初始化状态 // 返回值 : BOOL: 如果 szdevicename 有效, 则返回 TRUE, // 否则 FALSE // 参数 : char* pdevicename // 设备的唯一名字 // 参数 : int nunitaddr // 需要进行故障侦探及恢复尝试的设备的地址 // 参数 : LPVOID lpdevaddr // 应将此参数转换为结构 --- PDEVADDR 此结构详情参阅 // DATATYPE.H 文件 // 判断变量是否能够加入到该包中一起采集 : 如果可以加入, 返回 TRUE 并修改包的 起止地址 ; 否则返回 FALSE. 11) BOOL AddVarToPacket( LPVOID lpvar, int nvaraccesstype, LPVOID lppacket) // 函数名称 : AddVarToPacket // 函数说明 : 确认变量是否能够与一个包里的其他变量一起 // 进行采集, 以进行变量的打包, 如果可以加入, 返回 TRUE 并修改包的 // 起止地址 ; 否则返回 FALSE. // 返回值 : BOOL:FALSE 表示不能打包, 否则能 // 参数 : LPVOID lpvar: 变量指针 // 参数 : int nvaraccesstype: 读写方式 // 参数 : LPVOID lppacket: 包指针 // 根据协议处理包, 若处理成功则返回真且将数据存入 varlist 中, 否则返回 FALSE. 12) int ProcessPacket( LPVOID lppacket ) // 函数名称 : ProcessPacket // 函数说明 : 根据协议及包状态信息进行相应的处理, 例如进行 // 读写处理, 如果读成功则把数据写入 ppac->varlist // 返回值 : BOOL:TRUE if 成功 否则 FALSE // 参数 : LPVOID lppacket: 包指针 // 返回最近一次的错误信息. 13) char * GetLastError() // 函数名称 : GetLastError // 函数说明 : 返回最近一次的错误信息. // 返回值 : char *: 将最近一次的错误信息返回给组态王 14) BOOL,StrToDevAddr)(const char* str,lpvoid lpdevaddr) // 函数名称 : LoadDeviceInfo // 函数说明 : 此函数实现对地址输入的整理, 判断用户输入的地址是否合法, 并 将地址的数值传给组态王, 如果必要, 也可以将地址以字符串的形 式传给组态王 // 返回值 : BOOL 将字符串转换为设备地址, 若成功则返回 TRUE, 若地址字符 串不合理则返回 FALSE // 参数 (in) : const char* str, 将地址栏 ( 见下图 ) 中操作员输入的字符传下来 7
// 参数 (out) : LPVOID lpdevaddr, 应将此参数转换为结构 --- PDEVADDR 此结构 详情参阅 DATATYPE.H 文件 组态王开发环境启动后, 1) 定义一个设备时, 要调用 BOOL,StrToDevAddr)(const char* str,lpvoid lpdevaddr) 当点击下一步时, 就会调用此函数 const char* str,lpvoid lpdevaddr 2) 当定义一个变量时, 8
当点击 连接设备 的下拉列表并选中一个设备时, 会调用函数 BOOL,GetRegisters)(char *szdevicename,lpvoid *ppregs, int *pregnum), 之后, 组态王将根据从驱动中得到的寄存器列表和寄存器的数量载寄存器下拉列表中显示若干寄存器 当点击确定时, 会调用函数 WORD,ConvertUserConfigToVar)(LPVOID lpdbitem, LPVOID lpvar) 组态王运行环境启动后, 1) 会调用 BOOL,StrToDevAddr)(const char* str,lpvoid lpdevaddr) 和 WORD,ConvertUserConfigToVar)(LPVOID lpdbitem, LPVOID lpvar) 2) 若以上两个函数无误, 则调用 (BOOL,OpenComDevice)(int ndevtype,lpvoid lpinitdata); (int, LoadDeviceInfo)(const char *sprod, const char *sdevname, int ntype); 和 (BOOL,InitialDevice)(char* DevName,int nunitaddr/*int ndevtype*/,/*lpvoid pdevaddr*/lpvoid lpinitdata); 对于目前我们的驱动, 大部分都只是使用 (BOOL,OpenComDevice)(int ndevtype,lpvoid lpinitdata); 对于其他两个函数, 直接返回 TRUE 即可 3) 接下来每当采集频率的时间到, 即调用 : (BOOL,AddVarToPacket)(LPVOID lpvar, int nvaraccesstype, LPVOID lppacket); 和 (int, ProcessPacket)(LPVOID lppacket); 4) 当 (int, ProcessPacket)(LPVOID lppacket); 函数返回 FALSE 时, 则调用 (BOOL,TryConnect)(char* devname,int naddr,lpvoid lpdevaddr) 5 ) 若 (BOOL,TryConnect)(char* devname,int naddr,lpvoid lpdevaddr) 函数返回 TRUE, 则转至 3); 6 ) 若 (BOOL,TryConnect)(char* devname,int naddr,lpvoid lpdevaddr) 函数返回 FALSE, 则再次调用 (BOOL,TryConnect)(char* devname,int naddr,lpvoid lpdevaddr) 7) 若本次 (BOOL,TryConnect)(char* devname,int naddr,lpvoid lpdevaddr) 函数返回 9
TRUE, 则转至 3); 8) 若本次 (BOOL,TryConnect)(char* devname,int naddr,lpvoid lpdevaddr) 函数返回 FALSE, 则等待 尝试恢复间隔 时间, 时间到, 调用 (BOOL,TryConnect)(char* devname,int naddr,lpvoid lpdevaddr) 函数转至 7); 9 ) 若最长恢复时间到,(BOOL,TryConnect)(char* devname,int naddr,lpvoid lpdevaddr) 函数仍没有返回 TRUE, 则不再与此设备通讯 10 ) 在开发环境中调用的函数, 不论哪一个返回 FALSE, 组态王均调用 (char*,getlasterror)(void); 函数 11) 组态王退出时, 调用 (BOOL,CloseComDevice)(); 函数 3 如何修改 Devlst.dat 文件 : 1) 我们在使用组态王时, 首先要定义一个设备 当定义一个设备时, 组态王将显示 一个对话框, 如下所示 : 其中的设备列表, 即存储在 Devlst.dat 中 2) 我们修改 Devlst.dat 文件时, 应先使文件 Devman.exe 和文件 Devlst.dat 在同一个文 件夹下 启动 Devman.exe, 如下所示 : 10
3) 新建一个设备时, 选中 PLC 智能仪表 智能模块 板卡或变频器之一, 例如 : 智能模块, 点击 新建 按钮即可 若设备已存在, 修改时, 点击编辑 按钮 即可 4) 其中描述一项, 应填入 串口 专用卡 或 网络卡 之一 11
如果描述中选择专用卡或网卡, 则向导页中的串口项应取消 其他项可由程序员酌情 增减 5) 协议 ID 一项, 应从 VC++ 代码中复制, 填入此处 12
4 最终产品 : 我们最终交付给客户的产品中, 应包含以下四个文件 : Devlst.dat: 其中存储我们新建的设备信息 XXXX.dll: 我们最终的设备的驱动程序 ReadMe.txt: 对我们的驱动程序的说明, 及如何使用此驱动程序 SetupXXX.exe: 我们提供给客户的安装程序 Setup 程序代码随开发鲍赠送 在其中找到如下所示的位置, 修改相应的文件名字即 可 ( 应使用不带向导的代码 ) 5 如何调试 : 1) 针对刚刚完成的 dll 文件, 在组态王中建立一个项目, 要求至少有一个 I/O 变量和本驱动程序驱动的设备相关联 2) 将要调试的项目设定为 debug, 并编译 3) 在 VC++ 中的 Tools>Register control 注册该项目中 debug 文件夹下的 dll 文件 4) 将调用驱动的 exe 文件设好 5) 按 F5 键调试即可, 组态王将自动启动 6 快速创建 XXX.reg 文件 : 找一个我们以往的项目中的注册文件, 修改名字, 作为新驱动的注册文件的名字 编辑该文件, 如下图 : 13
将文件 Hart.reg 中的第 2 行 第 3 行中的 Hart.HartPro 用下图中第 8 行的 Spi.spjproto 替换 将文件 Hart.reg 中的第 2 行中的 Hart 用下图中第 8 行的 Spi 替换 将文件 Hart.reg 中的第 3 行中的 HartProtocol 用下图中第 8 行的 Spjproto 替换 将文件 Hart.reg 中的第 3 4 5 行中的驱动 ID 号用下图中第 7 行的驱动 ID 号替换 将文件 Hart.reg 中的第 5 行中的路径部分用 C:\Program Files\Kingview\Driver\spi.dll 替换 ( 注意将驱动的名字替换为 spi.dll) 将文件 Hart.reg 的文件名改为 Spi.reg 14