4.软件设计
WinDriver开发驱动程序一般分以下几个步骤:安装、选择硬件、测试硬件、生成驱动框架代码、编译及运行。
ISA板卡与PC机接口采用8位ISA总线结构,控制方式为I/O空间访问,与外部设备的连接采用62针D型插座。电路板的I/O基地址,采用8位DOP开关(U5)设置。出厂时设置为0X200H。

打开WinDriver软件新建一个工程,选择ISA CARD设备,弹出定义设备资源窗口,如图所示,由于我们的板卡只访问IO空间,寻址空间为0-7,无内存、中断和寄存器。假设基地址为0x200,则定义IO范围为0x200~0x207。然后点击Next,然后选择需要WinDriver生成何种开发环境的框架代码。在这里我们选择微软的Microsoft Visual C++ 6.0,WinDriver自动生成一个Dll工程框架代码。用户只需要简单的在框架代码基础上稍微修改,即可生成自己需要的驱动接口,主要函数及其功能如下:
•设置板卡基地址函数
extern "C" __declspec(dllexport) void __stdcall DO_SetAdd(WORD ADDR)
用户在调用任何驱动函数前先调用此函数设置板卡的基地址,地址范围为:0100H~03FFH,需要跟板卡的实际拨码开关相一致,否则板卡不会被片选。
此函数声明表明导出函数采用__stdcall(pascal)调用方式,由调用者负责参数从右往左入栈,被调用者清理堆栈。函数命名方式为C标准的命名方式,用Depends.exe观察导出函数的名称实际上就是“DO_SetAdd”,这对于C++编译器中调用此接口函数的时候,声明函数原型特别重要,否则会导致连接函数错误,因为C++为了重载函数的需要,所有的函数编译后的函数名称都进行了修饰。
•设备初始化函数
extern "C" __declspec(dllexport) int _stdcall DO_Device_Initialize(DO_HANDLE *hDO)
用户调用此dll导出函数来初始化硬件资源,并返回一个指向设备的句柄,DO_HANDLE *hDO是一个双指针,在函数内部动态分配一个设备句柄对象,返回该设备句柄的指针给用户,传递给后面其它函数使用。此函数中仅仅是调用两个内部函数DO_RegisterWinDriver和DO_Open,来完成软件的注册、版本冲突检测,板卡资源的分配、注册等。
•关闭设备
extern "C" __declspec(dllexport) void _stdcall DO_Close(DO_HANDLE hDO)
调用此函数来关闭设备句柄,释放初始化的时候动态分配的设备资源。
•获取最近一次操作失败的原因
extern "C" __declspec(dllexport) char * _stdcall DO_GetErrStr()
如果调用任何函数时候发生错误,可以通过此函数获取错误的详细说明。
•读取IO空间或者内存空间的内部函数
void DO_ReadWriteBlock(DO_HANDLE hDIDO, DO_ADDR addrSpace,
DWORD dwOffset, BOOL fRead, PVOID buf, DWORD dwBytes, DO_MODE mode)

所有的对IO口线的读写或者内存的读写都是通过此内部函数来完成的。addrSpace表明需要使用空间,该空间可以是IO空间也可以是一个内存空间,在板卡初始化的时候就会注册这些空间,dwOffset表明基于该空间的偏移量,fRead表明是读还是写,buf需要传输的数据缓冲区地址,dwBytes需要传输的数据长度,DO_MODE表明一次是传输1Byte传输,2Byte传输还是4Byte。
基本驱动框架生成后,用户可以再此基础上添加自己的函数,来实现自己的特定功能,比如向IO通道写控制字或者读IO通道的数字开关量或者AD值。
•自定义写IO的函数
extern "C" __declspec(dllexport)
void _stdcall DO_WriteByte (DO_HANDLE hDO, DO_ADDR addrSpace, DWORD dwOffset, BYTE data)
此函数向特定偏移地址写一个控制字,控制字根据实际控制输出信号来决定。
写完所有的用户代码后,编译工程会生成.dll、.lib文件,并定义好包含函数声明的头文件。
LabWindows/CVI是一个标准的C开发平台,调用驱动接口中的函数,可以包含其头文件,在编译条件中加入其.lib文件或者将其.lib库文件包含到工程中即可。由于LabWindows是一个标准的C编译器和开发平台,故extern "C"关键字可以省略,但是调用方式__stdcall不能省略,因为标准C的调用方式是基于C调用方式,入栈方式和堆栈平衡跟pascal(__stdcall)调用都不相同,如果不加可能导致程序崩溃。

在使用驱动前必须先设置板卡基地址空间,先将所有的输出关闭。代码片断如下:

5.结论
对于自制数据采集卡或者其它硬件设备的驱动程序都可以通过WinDriver生成驱动框架代码,然后根据自己的控制信号和硬件设计,很容易的写出特定功能的驱动程序代码,封装成标准的DLL文件就可以在任何开发平台下就像使用Windows Api那样使用,达到跟特定语言不相关,可以达到事倍功半的效果,特别适合面向项目的开发模式。