作 者 :华中科技大学 控制科学与工程系 吴力炜 叶念渝
关键词: 组态软件 设备驱动程序 RS-485串行通信
1 引言
组态软件设备驱动程序用于提供了连接计算机硬件的软件接口,在装入后成为操作系统内核的-部分,也就是说它成为操作系统的信任部分。因此,任何设备驱动程序的细小错误都可能引起操作系统的崩溃,要避免这样的事情发生,只有反复测试所写的代码,遵循编写驱动程序的规则。
最简单的方法是购买现成的驱动程序来直接处理我们的设备,但这样的驱动程序存在连接与嵌入错误的风险。另外,可以通过修改各种商业通用驱动程序与许多简单的设备打交道。如果自己编写驱动程序的话,尽量使用某个标准的总线驱动程序或者类驱动程序,因为它们通常实现了我们所需要的大量功能。如果设备只有一个专门的用途,则为它编写一个单一驱动程序来处理所有设备的请求可能是最简单的解决方案。
三维力控组态软件提供了力控I/O驱动程序接口软件开发工具包FIOS SDK,FIOS SDK提供了标准的开发接口,开发人员仅需要根据I/O设备的具体通信协议或驱动接口说明,填写几个扫描函数的实现代码,进行必要的调试与测试,即可完成一种FIOS的开发。
2 课题背景
目前笔者接手的项目是X机场助航灯光计算机监控系统。当机场灯光实施II类运行时,其能见度在800m至400m之间,要求灯光设备可靠性高、故障少,但往往灯具在运行过程中产生故障是随机的,其故障率达到某一个值时,将不能满足II类运行要求[1][2]。为保证监控系统运行的稳定性和可靠性,采用双机容错模式与力控工业控制组态软件相结合的方法,通过硬件冗余、软件组态的可靠设计、Visual C++与力控组态软件的结合,并以RS-485总线作为网络连接线,实现分布式的数据库配置,设计了基于力控组态环境的双机容错系统。开发此系统的任务之一,就是自主开发驱动程序,将自定义的通信协议写入驱动程序之中。而三维力控组态软件提供了I/O驱动程序接口开发工具包(FIOS SDK),因此,开发驱动的主要工作就是利用组态软件提供的接口函数和输入输出类库编写Ioapi中的代码,以达到I/O设备的控制要求[3]。由于此项目的开发在Visual C++ 6.0环境中进行,学习Windows WDM驱动程序设计对于深入理解内核态驱动程序开发、编写相应的接口程序也很有帮助。
3 开发工具--FIOS SDK简介[5][6]
FIOS SDK开发环境完全基于32位Windows平台。它使用动态链接库(DLL)技术将开发人员开发的代码整合到力控组态
软件系统中。FIOS SDK提供给程序员的开发接口为API函数和C++类库。
FIOS SDK主要由4部分组成:设备组态接口(Iodevcfg)、数据连接组态接口(Ioitemui)、I/O监控接口Ioapi和I/O服务器程序Ioserver。其中,Iodevcfg:负责管理设备组态过程;Ioitemui:负责管理数据连接组态过程;Ioapi:负责完成与I/O设备间的数据交换,实现对设备的监控过程,包括:对通信协议的解析、数据格式的转换等;Ioserver:由FIOS SDK提供,用于完成对Ioapi 的动态装载,调用并执行Ioapi实现的导出函数。它封装了大部分开发人员不必关心的技术细节,如完成与I/O设备的底层通信(串口通信、网络通信等)、设备超时处理、设备故障诊断等。Ioserver还完成与实时数据库DB之间的通信,它把从I/O设备采集到的数据经Ioapi解析转换后提交给DB,或者将DB下置给I/O设备的数据经Ioapi解析转换后写入I/O设备。
因此,开发人员仅需要开发Iodevcfg、Ioitemui、Ioapi三部分的代码。下面就以这三个部分的代码、界面开发为例,说明在力控组态软件中进行驱动开发的步骤。
4 开发实例
以C8051单片机实现数据采集为例,针对下层RS-485总线通信开发驱动程序。
4.1 设备组态接口(Iodevcfg)开发
(1) I/O描述文件
在使用力控进行组态时,一般都要涉及I/O设备的定义过程。I/O描述文件的文件名为Iodesc.txt。文件内容规定的格式如下(其中,表示回车换行):
类别;厂商或I/O程序描述;IOID
子类型1;类型号;缺省通信方式;提供设备地址
子类型2;类型号;缺省通信方式;提供设备地址
……
因此,在本项目中的I/O描述文件定义为:
"X计算机监控系统;华中科技大学控制系;CCMSController;0;1;0"表示类型号为0,RS-485通信,不设置设备地址。
(2) 开发Iodevcfg.dll
开发人员在编写Iodevcfg接口程序时主要完成两部分功能,一是为用户进行设备组态时提供一个界面;另外就是将用户组态的设备参数信息保存起来,以便在开发编程接口Ioapi时使用。如果力控提供的标准设备组态接口能够完整地描述设备的有关信息,就不需要再编写Iodevcfg接口程序。标准界面如图1所示,并对串口进行相关设置,如图2所示。
图1 标准设备组态界面
图2 串口设置界面
4.2 数据连接组态接口(Ioitemui)开发
在用力控进行组态时,把实时数据库DB中的点参数与某种设备的具体信道建立连接的过程被为数据连接过程。Ioitemui要以DLL形式提供,该DLL必须是MFC扩展DLL,其缺省文件名称为Ioitemui.dll。
数据连接过程对于不同的I/O设备,其形式和内容可能完全不同。因此,必须针对不同的I/O设备设计相应的数据连接形式以保存各种参数信息。
开发人员在编写Ioitemui接口程序时也是主要完成两部分功能,一是为用户进行数据连接组态时提供一个界面;二是将用户组态的设备参数信息保存在数据连接项结构(以下简称连接项结构)IOITEMDEF中,以便在开发I/O监控接口Ioapi时使用。这个连接项结构是一个通用结构,由开发人员自己赋值,自己解释。界面如图3所示:
图3 数据连接组态界面
4.3 I/O监控接口Ioapi开发
Ioapi是FIOS提供的最重要的一个编程接口,开发人员的主要工作就是开发Ioapi部分的程序代码。Ioapi提供了一组API函数(扫描函数)和一些C++类库(IOC, Input Output Class)。这些IOC类库全部以纯虚类的形式提供,并且只有成员函数,没有成员变量,主要包括5个类:CItem(数据项类)、CPacket(数据包类)、CDevice(设备类)、CChannel(信道类)和CManager(管理器类)。以层级的结构来管理点、包、设备、信道,实现对设备组态、数据组态、参数信息的获取和与实时数据库DB之间的数据交换。
Ioapi.dll程序中主要完成如下的导出函数(对于不需要的函数,一定要删除,这样可以使程序的运行效率提高):
OnCreate-载入Ioapi.dll后立即调用,这时信道、设备、包、点都不存在,一般用于设置程序的标题,使用户比较清楚程序的用途;
OnSortItem-所有点导入完毕后调用,这时候没有任何包建立;
OnItemToPacket-循环多次添加所有的点到各个包中,基本原则是能一次处理的点打成一个包,使打包合理化;
OnBeforeScan-打包完成,正式调度之前调用一次,遍历管理器,信道,设备,包,点;
OnCreateDeviceLink-设备的初始化工作;
OnCreatePacketLink-包的初始化工作;
OnReadData-IOSCAN程序循环扫描调度,形成数据采集命令串;
OnWriteData-发生数据下送时调用,形成数据下置命令串;
OnIsResponseComplete-异步方式下判断是否完成数据的接收,是否将数据设置到DB中;
OnAfterSend-发送数据后调用一次,调试时可以查看发送的命令串是否正确;
OnTimeout-发生超时的时候调用;
OnUnloadPacket-包析构时调用;
OnClose-CManager析构时调用一次。
开发人员可根据项目中自定义的通信协议,将通信协议写入函数OnReadData()、OnWriteData()中,实现数据的采集和下置。下面给出OnReadData函数的部分代码:
INT OnReadData( CPacket* pPacket, LPTSTR lpszSendString, INT& nSendStringLen )
{
pPacket->SetReady(TRUE);
//生成采集命令字符串
CDevice* pDevice = pPacket->GetDevice();
//得到设备指针
CChannel * pChannel = pDevice->GetChannel();
//得到通道指针
pChannel->ClearAcceptBuffer();
//清空接收缓冲区
CString csDeviceAddr = pDevice->GetAddr();
//得到实际设备的地址
IOITEMDEF * pItemStru = pPacket->GetItem(0)->GetItemStru();
//得到包的数据
BYTE ucIoType = pItemStru->n[IO_TYPE];
//得到发送命令的I/O类型
BYTE ucDeviceAddr = pItemStru->n[DEVICE_ADDR];//得到用户输入的设备地址
……
//需要根据实际的通信协议书写有关的程序
CString csCommand = "";
csCommand = BYTE(FRAME_HEAD);
//写入帧信息头
……
csCommand += BYTE(QUERY);
//定义帧信息类型
csCommand += BYTE(0x01);
//有效信息长度为 1 字节
csCommand += BYTE(ucDeviceAddr);
//写入设备的地址
csCommand += LRCCheck(temp_que, 2);
//写入帧尾LRC校验码
nSendStringLen = csCommand.GetLength();
CString csPmessage;
csPmessage.Format("读取数据:设备地址%X", ucDeviceAddr);
pDevice->ShowProcessMessage(csPmessage);
pDevice->ShowEventMessage(csPmessage, FALSE);
//调试用,用以显示触发事件
for ( int i = 0; i < nSendStringLen; i++ )
lpszSendString[i] = csCommand[i];
return SEND;
}
4.4 驱动程序的调试
通过Visual C++ 6.0编写的程序,如:Iodevcfg.dll、Ioitemui.dll、Ioapi.dll,必须生成Release版本,否则调试时,I/O Server调用时将会出错,并立即跳出程序的执行。调试显示过程如附表所示。
附表 设备驱动程序调试过程信息
附表 设备驱动程序调试过程信息
调试结果表明该驱动程序可以实现与C8051单片机的异步通信,完成数据的采集,进而实现对助航灯光的控制。
5 结束语
组态软件作为现场生产数据采集和过程控制的专用软件,在许多行业领域都有应用。但任何组态软件的驱动程序不可能囊括所有设备的驱动程序,特别是对通信协议有特定要求的设备,因此,就需要开发人员根据自己的通信协议编写相应的驱动程序代码,并结合实际的设备进行调试。一个好的驱动程序设计应该是可配置的、可移植的、可抢占式的、可中断的和多处理器安全的[4]。若想能够很好地掌握驱动程序的开发,最根本的是阅读驱动文档并自己动手编写驱动程序。