摘 要:本文介绍了Windows下可安装的设备驱动程序的消息和框架,并给出了调用设备驱动程序的的方法,我们运用该方法将DOS环境下的实时数据采集中断驻留程序移植到Windows下,取得了较理想的实时数据采集效果。
关键字:Windows设备驱动程序 中断驻留 实时数据采集
一、引言
Windows操作系统以其外观新颖精致、用户界面友好、操作方式简便快捷、能够处理多媒体信息、从底层支持网络性能,而逐步成为目前主流的操作系统。除此之外,Windows环境下的界面设计环境优美、使用方便,吸引了大量的应用程序开发者。Windows操作系统为支持其多任务功能,在系统运行后,Windows接管了计算机的所有资源,如键盘、鼠标等。同样,Windows也接管了计算机系统的中断服务,使得以前我们很容易实现的中断驻留程序无法在Windows环境下运行,这对于利用中断驻留程序实现实时数据采集的系统来说无疑是不幸的。为了能够在Windows环境下实现实时数据采集,我们设计了一种Windows环境下的设备驱动程序框架,将原来DOS环境下的中断驻留程序很方便地移植到Windows下,作为Windows的一种可安装驱动程序。本文所要讲述的就是可安装的设备驱动程序的设计与实现,以及它的应用。
二、Windows环境设备驱动程序的分类
Windows环境下,设备驱动程序分为两类:设备驱动程序和虚拟设备驱动程序。其中设备驱动程序又可分为两种,一种是标准的设备驱动程序,如键盘、鼠标等;另一种是可安装的设备驱动程序,它主要是用来处理非标准的硬件设备,如数据采集卡等用户自己设计的硬件设备。
三、设备驱动程序中的消息
Windows环境下,可安装设备驱动程序可能收到的消息和说明如表1所示。
表1 可安装设备驱动程序的消息
序号 |
消 息 |
说 明 |
1 |
DRV_LOAD |
在驱动程序第一次装载后,Windows发给它的消息就是DRV_LOAD消息,此时可以初始化实例数据。 |
2 |
DRV_ENABLE |
当驱动程序首次装载后,Windows向它发DRV_ENABLE消息,利用该消息设置硬件并挂接中断。 |
3 |
DRV_OPEN |
当驱动程序被OpenDriver调用打开时,Windows向它发该消息。 |
4 |
DRV_CLOSE |
当驱动程序因CloseDriver调用而关闭时,Windows向驱动程序发出一个DRV_CLOSE消息。驱动程序常使用DRV_CLOSE通知来释放特定实例的数据结构。 |
5 |
DRV_DISABLE |
当驱动程序被释放或从Windows进入DOS时,向驱动程序发DRV_DISABLE消息。因此,从Windows切换出来时,设备驱动程序不挂接任何中断,并复位硬件。 |
6 |
DRV_FREE |
在驱动程序被丢弃前,Windows发给它的最后一个消息就是DRV_FREE。 |
7 |
DRV_EXITAPPLICATION |
当应用程序退出时,Windows向驱动程序发出该消息。 |
8 |
DRV_EXITSESSION |
当Windows退出前,向所有打开的驱动程序发出该消息。 |
9 |
DRV_PORWER |
在符合Advanced Power ManageMent(APM)标准的系统中,当系统将进入或退出挂起模式时,设备驱动程序将收到一个DRV_PORWER消息。 |
10 |
DRV_INSTALL |
在驱动程序安装过程中,它将收到该消息。 |
11 |
DRV_QUERYCONFIGURE |
驱动程序的安装程序利用该消息来确定驱动程序是否能由用户配置。对于对该消息返回TURE的驱动程序,安装程序会使能“configure”或“Setup”按钮。 |
12 |
DRV_CONFIG |
当驱动程序完成初始化安装,以及每当用户要求重新配置驱动程序时,驱动安装程序会发送该消息。 |
13 |
DRV_REMOVE |
当驱动程序将从安装的驱动程序列表中删除时,将会收到该消息。 |
14 |
DRV_USER |
用户自定义的消息。 |
四、设备驱动程序框架
Windows的设备驱动程序实质上也是一个动态连接库,它也有LibMain、_WEP函数,利用这两个函数,可以完成驱动程序的加载和释放。可安装的设备驱动程序与一般的DDL的不同在于它有一个驱动程序事件处理过程DriverProc,有了DriverProc,Windows就可以向驱动程序发送消息和从驱动程序得到消息。下面是DriverProc的框架实例,程序给出的中断号为0x0d(对应IRQ5),我们开发的硬件设备的定时中断信号就是接在此中断号,定时信号每5ms引起一次中断,采集一个数据。
1.DriverProc的框架
#include <windows.h>
#include <stdio.h>
#include <io.h>
#include "instdrv.h"
unsigned int data[800]; ①数据区
unsigned int int_num = 0x0d; ②中断向量(对应IRQ5)
HWND Hand_Send=NULL;
HWND Hand_Rec=NULL;
void interrupt (*oldhandler)(void);
void interrupt handler(void);
int _export WINAPI LibMain(HINSTANCE hInstance,WORD wDataSeg,WORD cbHeapSize,LPSTR lpszCmdLine)
{ return(1);}
int _export WINAPI _WEP(int bSystemExit)
{ return(1);}
LRESULT CALLBACK DriverProc(DWORD dwDrvid,HDRVR hDrv,UINT msg,LPARAM lParam1,LPARAM lParam2)
{
HWND Hand=NULL;
switch( msg )
{
case DRV_LOAD: oldhandler = getvect(int_num);return (TRUE);
③保护旧的中断向量
case DRV_ENABLE:
setvect(int_num, handler);
④设置新的中断向量
asm{ cli
in al,21h
and al,11011111B
⑤打开IRQ5中断
out 21h,al
sti }
return (TRUE);
case DRV_OPEN: return( TRUE );
case DRV_CLOSE: return( TRUE );
case DRV_FREE: return( TRUE );
case DRV_DISABLE:
asm{ cli
in al,21h
or al,00100000B
⑥关闭IRQ5中断
out 21h,al
sti }
setvect(int_num,oldhandler);
return (TRUE);
case DRV_QUERYCONFIGURE: return( FALSE );
case WM_SEND:
⑦WM_SEND应用程序发过来的消息
switch((WORD)lParam1)
{
case GET_BUF_PTR:
⑧得到缓冲区地址指针
Hand = (HWND)lParam2;
SendMessage(Hand,WM_INT, SBUF_PTR ,(LPARAM)(int far *)data);
break;
case RESET:
⑨此处可以控制采集设备复位
break;
}
return (TRUE);
default: return DefDriverProc(dwDrvid,hDrv,msg,lParam1,lParam2);
}
}
void interrupt handler(void)
{
…
此处加入中断处理程序(实时数据采集程序),编写方法与DOS下相同。
…
}
2.驱动程序的模块定义文件
LIBRARY instdrv
DESCRIPTION 'Installable Device Driver '
EXETYPE WINDOWS
STUB 'WINSTUB.EXE'
CODE PRELOAD MOVEABLE DISCARDABLE
DATA PRELOAD MOVEABLE SINGLE
HEAPSIZE 1024
EXPORTS
WEP @1 RESIDENTNAME
DriverProc @2 RESIDENTNAME
五、设备驱动程序的使用
前面给出了一个完整的可安装设备驱动程序的框架,我们可以用下面的三个函数使用可安装设备驱动程序:
1.OpenDriver函数:在应用程序使用可安装设备驱动程序之前,用该函数打开驱动程序。
2. CloseDriver函数:在应用程序不再需要该驱动程序时,用该函数关闭它。
3. SendDriverMessage函数:驱动程序打开之后通过该函数与之通讯。
一个简单的使用上述设备驱动程序的框架如下所示:
#include <windows.h>
HINSTANCE hInst;
HDRVR hDrv = NULL;
HWND hWND = NULL;
int far* pointer = NULL; ①指向数据区的长指针
int PASCAL WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpszCmd,int nCmdShow)
{
static char szAppName[] = "usedrv";
HWND hwnd ;
MSG msg ;
WNDCLASS wndclass ;
if( !hPrevInstance ){
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = (WNDPROC)WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon( NULL, IDI_APPLICATION );
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wndclass.lpszMenuName = "MainMenu";
wndclass.lpszClassName = szAppName;
RegisterClass (&wndclass); }
hwnd = CreateWindow (szAppName, "Example",WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,
CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,hInstance,NULL);
ShowWindow( hwnd, nCmdShow );
UpdateWindow(hwnd);
while(GetMessage( &msg, NULL, 0, 0))
{ TranslateMessage(&msg);
DispatchMessage(&msg);}
return msg.wParam;
}
long FAR PASCAL _export WndProc(HWND hWnd, WORD message,WORD wParam,LONG lParam)
{
HDC hdc;
HWND = hWnd;
switch (message)
{
case WM_DESTROY: ②关闭驱动程序
if( hDrv ){ CloseDriver( hDrv,0,0 ); hDrv=NULL;}
PostQuitMessage(0);
return(0);
case WM_INT: ③收到WM_INT消息后,得到数据区指针
pointer = (int far *)lParam;
…
数据处理程序。
…
break;
case WM_COMMAND:
switch( wParam )
{
case IDM_OPEN: ④打开驱动程序
if( !hDrv ) { hDrv = OpenDriver(InstDrvName,NULL,NULL);}
break;
case IDM_CLOSE: ⑤关闭驱动程序
if( hDrv ){ CloseDriver(hDrv,0,0); hDrv = NULL; PostQuitMessage(0);}
break;
case IDM_SEND: ⑥发出消息WM_SEND给驱动程序,需要得到数据区指针
if( hDrv ) SendDriverMessage(hDrv,WM_SEND,GET_BUF_PTR,(LPARAM)hWND);
break;
default: return(DefWindowProc(hWnd,message,wParam,lParam));
}
return (0);
}
return(DefWindowProc(hWnd,message,wParam,lParam));
}
上述程序中设备驱动程序与应用程序之间是通过消息来传递信息的,当要进行数据采集时,应用程序首先打开设备驱动程序,然后发送WM_SEND(GET_BUF_PTR)消息给设备驱动程序,通知设备驱动程序将数据区地址指针传送给应用程序;设备驱动程序收到WM_SEND消息后,向应用程序发送WM_INT消息,同时将数据区指针传送给应用程序。应用程序收到数据区地址指针后,即可从数据区读实时采集的数据进行显示。
六、结束语
在Windows环境下开发用户自己的驱动程序,是许多硬件开发者所面临的最大困难。本文给出了一种简单实用的设备驱动程序框架,在实际工作中,我们将原来在DOS环境下的设备驱动程序移植到Windows环境,实现了原来实时采集数据的功能,同时在Windows下编写的界面程序比以前更加方便美观,达到了预期的效果。
参考文献
1.Microsoft Windows API大全 北京科海培训中心
2.Asael Dror著 Windows 奥秘 学苑出版社 1994年9月
3.傅 勇等 发动机自动测试台架的数据采集系统 工业控制计算机1998年第6期
|