参考gooogleman大兄的文档及ouravr一位兄弟整理出来的··实验无问题
一、编写流驱动程序有两种方法:一种是使用Platform Builder60的动态库生成向导生成器生成一个动态链接库工程;另一种是手工建立工程。
手工建立工程的方法举例,一个流接口驱动程序源文件一般包括以下5个文件:
1、 驱动程序源代码文件。例如GPIODriver.cpp,该文件包含了实现流接口函数的具体代码。
2、 驱动程序头文件。 例如GPIODriver.h,该文件为 GPIODriver.cpp 的头文件。
3、 动态库导出文件。 例如GPIODriver.def,该文件定义了需要导出的函数名称。
4、 makefile文件。文件名固定makefile,该文件指出了驱动程序的编译和链接方法,但是实际上盖文件并不做什么事情,具体的方法由sources文件进行设置。
5、 链接和编译文件,文件名固定为sources,该文件用于设置连接器和编译器,指出驱动程序的编译和链接方法。
二、实验步骤
1、在C:\WINCE600\PLATFORM\TQ2440\Src\Drivers目录下建一个目录GPIOdriver。
2、用文本编辑器建立5个文本文件文件名分别为GPIODriver.cpp、GPIODriver.h、GPIODriver.def、makefile、sources。完成以上步骤后,
GPIOdriver目录下文件如图所示。
3、编写GPIODriver.cpp文件。首先必须包含相关的文件及定义需要用到的全局变量,并编写流驱动程序动态入口函数,在该文件中需完成以下流设备接口驱动。
1. DWORD XXX_Init(LPCTSTR pContext, DWORD dwBusContext):
该函数用于初始化一个流设备驱动,在设备被加载的时候调用,调用成功后会返回一个句柄。
pContext:在Active注册表键路径下的一个字符串
dwBusContext:不常用,这里可以设为0
2. BOOL XXX_Deinit(DWORD hDeviceContext):
卸载一个设备驱动。
hDeviceContext:设备驱动的句柄,在XXX_Init调用时返回的
3. DWORD XXX_Open(DWORD hDeviceContext, DWORD AccessCode, DWORD ShareMode):
打开一个设备。
hDeviceContext:设备驱动的句柄,在XXX_Init调用时返回的
AccessCode:访问权限代码,一般是只读或者只写或者读写
ShareMode:共享模式,是否支持共享或者独享
4. BOOL XXX_Close(DWORD hOpenContext):
关闭一个设备。
hDeviceContext:设备驱动的句柄,在XXX_Open调用时返回的
5. DWORD XXX_Read(DWORD hOpenContext, LPVOID pBuffer, DWORD Count):
从设备上面读取数据。
hDeviceContext:设备驱动的句柄,在XXX_Open调用时返回的
pBuffer:存放数据的Buffer
Count:读取数据的字节数
6. DWORD XXX_Write(DWORD hOpenContext, LPCVOID pBuffer, DWORD Count):
写数据到设备上面。
hDeviceContext:设备驱动的句柄,在XXX_Open调用时返回的
pBuffer:存放数据的Buffer
Count:写入数据的字节数
7. DWORD XXX_Seek(DWORD hOpenContext, long Amount, WORD Type):
移动设备中的数据指针。
hDeviceContext:设备驱动的句柄,在XXX_Open调用时返回的
Amount:移动的字节数
Type:FILE_BEGIN表示从头移动
FILE_CURRENT表示从当前位置移动
FILE_END表示从末尾往前移动
8. void XXX_PowerUp(DWORD hOpenContext):
打开设备电源。
hDeviceContext:设备驱动的句柄,在XXX_Open调用时返回的
9. void XXX_PowerDown(DWORD hOpenContext):
关闭设备电源。
hDeviceContext:设备驱动的句柄,在XXX_Open调用时返回的
10. BOOL XXX_IOControl(DWORD hOpenContext, DWORD dwCode, PBYTE pBufIn, DWORD dwLenIn, PBYTE pBufOut, DWORD dwLenOut, PDWORD pdwActualOut):
设备IO控制操作函数。
hDeviceContext:设备驱动的句柄,在XXX_Open调用时返回的
dwCode:操作码
pBufIn:输入Buffer
dwLenIn:输入Buffer的size
pBufOut:输出Buffer
dwLenOut:输出Buffer的size
pdwActualOut:实际输出的字节数
11. BOOL XXX_PreClose(DWORD hOpenContext):
标记一个正要关闭的句柄为无效,并唤醒所有正在休眠的线程
hDeviceContext:设备驱动的句柄,在XXX_Init调用时返回的
12. BOOL XXX_PreDeinit(DWORD hDeviceContext):
标记一个设备实例为无效,并唤醒所有休眠的线程
hDeviceContext:设备驱动的句柄,在XXX_Init调用时返回的
要操作一个平台的GPIO,在其对应的BSP中按照基地址,并且找到方便操作这个地址的数据结构,关键函数就是VirtualAlloc和VirtualCopy。并且WINCE的方便之处就是用户态的应用程序仍然可以使用这两个函数来访问所有这些虚拟空间,对于不太复杂的程序,甚至可以省略写驱动直接在应用程序中操作,其实在CE6之前,这些驱动也是工作在用户态的。
下面以操作Samsung S3C2440的GPIO为例,讲述这个步骤:
1、 首先在BSP中的s3c2440a_ioport.h文件,找到虚拟地址映射以及操作GPIO的寄存器结构体。
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft end-user
// license agreement (EULA) under which you licensed this SOFTWARE PRODUCT.
// If you did not accept the terms of the EULA, you are not authorized to use
// this source code. For a copy of the EULA, please see the LICENSE.RTF on your
// install media.
//
//------------------------------------------------------------------------------
//
// Header: s3c2440a_ioport.h
//
// Defines the Input/Output Ports (IOPORT) control registers and associated
// types and constants.
//
#ifndef __S3C2440A_IOPORT_H
#define __S3C2440A_IOPORT_H
#if __cplusplus
extern "C" {
#endif
//------------------------------------------------------------------------------
typedef struct {
UINT32 GPACON; // Port A - offset 0
UINT32 GPADAT; // Data
UINT32 PAD1[2];
UINT32 GPBCON; // Port B - offset 0x10
UINT32 GPBDAT; // Data
UINT32 GPBUP; // Pull-up disable
UINT32 PAD2;
UINT32 GPCCON; // Port C - offset 0x20
UINT32 GPCDAT; // Data
UINT32 GPCUP; // Pull-up disable
UINT32 PAD3;
UINT32 GPDCON; // Port D - offset 0x30
UINT32 GPDDAT; // Data
UINT32 GPDUP; // Pull-up disable
UINT32 PAD4;
UINT32 GPECON; // Port E - offset 0x40
UINT32 GPEDAT; // Data
UINT32 GPEUP; // Pull-up disable
UINT32 PAD5;
UINT32 GPFCON; // Port F - offset 0x50
UINT32 GPFDAT;
UINT32 GPFUP;
UINT32 PAD6;
UINT32 GPGCON; // Port G - offset 0x60
UINT32 GPGDAT;
UINT32 GPGUP;
UINT32 PAD7;
UINT32 GPHCON; // Port H - offset 0x70
UINT32 GPHDAT;
UINT32 GPHUP;
UINT32 PAD8;
UINT32 MISCCR; // misc control reg - offset 0x80
UINT32 DCLKCON; // DCLK0/1 control reg
UINT32 EXTINT0; // external interrupt control reg 0
UINT32 EXTINT1; // external interrupt control reg 1
UINT32 EXTINT2; // external interrupt control reg 2
UINT32 EINTFLT0; // reserved
UINT32 EINTFLT1; // reserved
UINT32 EINTFLT2; // external interrupt filter reg 2
UINT32 EINTFLT3; // external interrupt filter reg 3
UINT32 EINTMASK; // external interrupt mask reg
UINT32 EINTPEND; // external interrupt pending reg
UINT32 GSTATUS0; // external pin status
UINT32 GSTATUS1; // chip ID
UINT32 GSTATUS2; // reset status
UINT32 GSTATUS3; // inform register
UINT32 GSTATUS4; // inform register
UINT32 FLTOUT; // C0
UINT32 DSC0;
UINT32 DSC1;
UINT32 MSLCON;
UINT32 GPJCON; // D0
UINT32 GPJDAT;
UINT32 GPJUP;
UINT32 PDA9;
} S3C2440A_IOPORT_REG, *PS3C2440A_IOPORT_REG;
//------------------------------------------------------------------------------
#if __cplusplus
}
#endif
#endif // __S3C2440A_IOPORT_H
注:在以上代码中
#if defined(__cplusplus)
extern "C"
{
#endif
//代码
#if defined(__cplusplus)
}
#endif
C语言中有些定义在C++中会被解释成C++的定义的,这样程序编译就会发生错误,上面那个宏就是为了避免那种情况,当程序是按C++标准编译的时候,extern "C"{}之间的代码用C的标准来声明。
2、 按照驱动程序里面的操作方法应用程序中写GPIO操作函数
(1)、定义1个结构体变量
volatile S3C2440A_IOPORT_REG *v_pIOPregs ;
(2)、给这个变量分配空间并且映射到寄存器的空间上
bool InitializeAddresses(VOID)
{
bool RetValue = TRUE;
/* IO Register Allocation */
v_pIOPregs = (volatile S3C2440A_IOPORT_REG *)VirtualAlloc(0, sizeof(S3C2440A_IOPORT_REG), MEM_RESERVE, PAGE_NOACCESS);
if (v_pIOPregs == NULL)
{
ERRORMSG(1,(TEXT("For IOPregs : VirtualAlloc faiGPIO!\r\n")));
RetValue = FALSE;
}
else
{
If (!VirtualCopy((PVOID)v_pIOPregs, (PVOID)(S3C2440A_BASE_REG_PA_IOPORT >> 8), sizeof(S3C2440A_IOPORT_REG), PAGE_PHYSICAL | PAGE_READWRITE | PAGE_NOCACHE))
{
ERRORMSG(1,(TEXT("For IOPregs: VirtualCopy faiGPIO!\r\n")));
RetValue = FALSE;
}
}
if (!RetValue)
{
if (v_pIOPregs)
{
VirtualFree((PVOID) v_pIOPregs, 0, MEM_RELEASE);
}
v_pIOPregs = NULL;
}
return(RetValue);
}
这两步骤以后,对S3C2440A_IOPORT_REG的操作将直接和GPIO的寄存器相关联
例如:设置GPB的控制寄存器为全部Output
v_pIOPRegs->rGPBCON=0x155555;
设置GPB的数据寄存器输出高电平
v_pIOPRegs->rGPBDAT=0x3FF;
3、 编写流接口驱动程序动态库入口函数DllEntry(),此动态链接库与应用程序所用的库差别并不是很大,源文件可以是C、C++、甚至汇编,只要它可以实现以下函数
DllEntry(HINSTANCE DllInstance, INT Reason, LPVOID Reserved )
这个函数是动态链接库的入口,每个动态链接库都需要输出这个函数,他只在动态库被加载和卸载时被调用,也就是设备管理器调用LoadLibrary而引起它被装入内存和调用UnloadLibrary将其从内存释放时被调用, 因而它是每个动态链接库最早被调用的函数,一般用它做一些全局变量的初始化。
参数:
DllInstance:DLL的句柄,与一个EXE文件的句柄功能类似,一般可以通过它在得到DLL中的一些资源,例如对话框,除此之外一般没什么用处。
Reason:
一般我们只关心两个值:DLL_PROCESS_ATTACH与DLL_PROCESS_DETACH,Reason等于前者是动态库被加载,等于后者是动态库被释放。
所以,我们可以在Reason等于前者是初始化一些资源,等于后者时将其释放。
程序清单如下:
BOOL WINAPI DllEntry(HANDLE hinstDLL, DWORD dwReason, LPVOID /* lpvReserved */)
{
switch(dwReason)
{
case DLL_PROCESS_ATTACH: //动态库加载
DEBUGREGISTER((HINSTANCE)hinstDLL); //
return TRUE;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH: //动态库卸载
break;
#ifdef UNDER_CE
case DLL_PROCESS_EXITING:
break;
case DLL_SYSTEM_STARTED:
break;
#endif
}
return TRUE;
}
4、 接下来就是那具体的12个函数了
DWORD XXX_Init( LPCTSTR pContext, LPCVOID lpvBusContext);
它是驱动程序的动态库被成功装载以后第一个被调用的函数。其调用时间仅次与DllEntry,而且,当一个库用来生成多于一个的驱动程序实例时仅调用一次DllEntry,而xxx_Init会被调用多次。驱动程序应当在这个函数中初始化硬件,如果初始化成功,就分配一个自已的内存空间(通常用结构体表示),将自已的状态保存起来,并且将此内存块的地址做为一个DWORD值返回给上层。设备管理器就会用在调用XXX_Open时将此句柄传回,我们就能访问自已的状态。如果初始化失败,则返回0以通知这个驱动程序没有成功加载,先前所分配的系统资源应该全部释放,此程序的生命即告终至。
当这个函数成功返回,设备管理器对这个程序就不做进一步处理,除非它设置了更多的特性。至此一个各为XXX的设备就已经加载成功,当用户程序调用CreateFile来打开这个设备时,设备管理器就会调XXX_Open函数。
参数:
pContext:系统传入的注册表键,通过它可以讲到我们在注册表中设置的配置信息。
lpvBusContext:一般不用,在这先不做讲解
实际上,很多程序中将这个函数写成了DWORD XXX_Init( DWORD pContext )
我们只需要将pContext转化成LPCTSTR即可。
DWORDl XXX_Open(DWORD hDeviceContext,DWORD dwAccess, DWORD dwShareMode);
当用户程序调用CreateFile打开这个设备时,设备管理器就会调用此驱动程序的XXX_Open函数。
参数:
hDeviceContext XXX_Init 返回给上层的值,也就是我们在XXX_Init中分配的用来记录驱动程序信息的那个结构体的指针,我们可以在这个函数中直接将其转化成所定义的结构,从而获取驱动程序的信息。
dwAccess 上层所要求的访问方式,可以是读或者写,或者是0,即不读也不写
dwShareMode 上层程序所请求的共享模式,可以是共享读、共享写这两个值的逻辑或,或者是0,即独占式访问。
系统层对设备文件的存取权限及共享方法已经做了处理,所以在驱动程序中对这两个参数一般可以不用理会。
这个函数一般不用做太多处理,可以直接返回hDeviceContext表示成功,对于一个不支持多个用户的硬件,在设备已经打开后,应该总是返回0以至失败,则CreateFile调用不成功。
l DWORD XXX_Close( DWORD hDeviceContext );
当用户程序调用CloseHandle关闭这个设备句柄时,这个函数就会被设备管理器调用。
参数:
hDeviceContext 为XXX_Open返回给上层的那个值。
这个函数应该做与XXX_Open相反的事情,具体包括:释放XXX_Open分配的内存,将驱动程序被打开的记数减少等。
l DWORD XXX_Deinit( DWORD hDeviceContext );
这个函数在设备被卸载时被调用,它应该实现与XXX_Init相反的操作,主要为释放前者占用的所有系统资源。
参数:
hDeviceContext XXX_Init函数返回给上层的那个句柄
void XXX_PowerUp( DWORD hDeviceContext );l
voidl XXX_PowerDown(DWORD hDeviceContext );
正如其名称中体现的那样,这两个函数在系统PowerUp与PowerDown时被调用,这两个函数中不能使用任何可能引起线程切换的函数,否则会引起系统死机。所以,在这两个函数中,实际上几乎是什么做不了,一般在PowerDown时做一个标志,让驱动程序知道自已曾经被Power Down过。在Power Down/On的过程中硬件可能会掉电,所以,尽管Power On以后,原来的IO操作仍然会从接着执行,但可能会失败。这时,当我们发现一次IO操作失败是因为程序曾经进入过Power Down状态,就重新初始化一次硬件,再做同样的IO操作。
BOOL XXX_IOControl(l
DWORD hDeviceContext,
DWORD dwCode,
PBYTE pBufIn,
DWORD dwLenIn,
PBYTE pBufOut,
DWORD dwLenOut,
PDWORD pdwActualOut
);
几乎可以说一个驱动程序的所有功能都可以在这个函数中实现。对于一类CE自身已经支持的设备,它们已经被定义了一套IO操作定,我们只需按照各类设备已经定义的内容去实现所有的IO操作。但当我们实现一个自定义的设备时,我们就可以随心所欲定义我们自已的IO操作。
参数:
hDeviceContext XXX_Open返回给上层的那个句柄,即我们自已定义的,用来存放程序所有信息的一个结构。
dwCode IO操作码,如果是CE已经支持的设备类,就用它已经定义好码值,否则就可以自已定义。
pBufIn 传入的Buffer,每个IO操作码都会定义自已的Buffer结构
dwLenIn pBufIn以字节记的大小
pBufOut,dwLenOut分别为传出的Buffer,及其以字节记的大小
pdwActualOut 驱动程序实际在pBufOut中填入的数据以字节记的大小
其中,前两个参数是必须的,其它的任何一个都有可能是NULL或0。
所以,当给pdwActualOut赋值时应该先判断它是否为一个有效的地址
具体代码如下:
/*
当设备管理器卸载本驱动时,就会调用该函数,用户在此添加一些如释放内存、恢复全局变量值、置硬件回到默认状态的代码。
*/
BOOL GIO_Deinit(DWORD hDeviceContext) //驱动程序卸载函数
{
BOOL bRet = TRUE;
RETAILMSG(1,(TEXT("GPIO_Control: GIO_Deinit\r\n"))); //提示驱动卸载
return TRUE;
}
/*
当ce的设备管理器加载本驱动时,该函数 将被调用,用户可在该函数做一些初始化的工作,例如对全局初始化变量等。
*/
DWORD GIO_Init(DWORD dwContext) // 驱动程序初始化函数
{
RETAILMSG(1,(TEXT("GPIO Initialize ..."))); //提示驱动加载
if (!InitializeAddresses())
return (FALSE);
v_pIOPregs->GPBCON = (v_pIOPregs->GPBCON &~(3 << 10)) | (1<< 10); // GPB5 == OUTPUT.
v_pIOPregs->GPBCON = (v_pIOPregs->GPBCON &~(3 << 12)) | (1<< 12); // GPB6 == OUTPUT.
v_pIOPregs->GPBCON = (v_pIOPregs->GPBCON &~(3 << 14)) | (1<< 14); // GPB7 == OUTPUT.
v_pIOPregs->GPBCON = (v_pIOPregs->GPBCON &~(3 << 16)) | (1<< 16); // GPB8 == OUTPUT.
mInitialized = TRUE;
RETAILMSG(1,(TEXT("OK !!!\n")));
return TRUE; //返回表示成功
}
/*
编写I/O控制函数,当应用程序使用DeviceIOControl函数对驱动程序进行一些特殊请求时,wince将调用这个函数。
*/
BOOL GIO_IOControl(DWORD hOpenContext,
DWORD dwCode,
PBYTE pBufIn,
DWORD dwLenIn,
PBYTE pBufOut,
DWORD dwLenOut,
PDWORD pdwActualOut)
{
switch(dwCode)
{
case IO_CTL_GPIO_1_ON:
v_pIOPregs->GPBDAT=v_pIOPregs->GPBDAT&~(0x1<<5);
break;
case IO_CTL_GPIO_2_ON:
v_pIOPregs->GPBDAT=v_pIOPregs->GPBDAT&~(0x1<<6);
break;
case IO_CTL_GPIO_3_ON:
v_pIOPregs->GPBDAT=v_pIOPregs->GPBDAT&~(0x1<<7);
break;
case IO_CTL_GPIO_4_ON:
v_pIOPregs->GPBDAT=v_pIOPregs->GPBDAT&~(0x1<<8);
break;
case IO_CTL_GPIO_ALL_ON:
v_pIOPregs->GPBDAT=v_pIOPregs->GPBDAT&~(0xF<<5);
break;
case IO_CTL_GPIO_1_OFF:
v_pIOPregs->GPBDAT=v_pIOPregs->GPBDAT|(0x1<<5);
break;
case IO_CTL_GPIO_2_OFF:
v_pIOPregs->GPBDAT=v_pIOPregs->GPBDAT|(0x1<<6);
break;
case IO_CTL_GPIO_3_OFF:
v_pIOPregs->GPBDAT=v_pIOPregs->GPBDAT|(0x1<<7);
break;
case IO_CTL_GPIO_4_OFF:
v_pIOPregs->GPBDAT=v_pIOPregs->GPBDAT|(0x1<<8);
break;
case IO_CTL_GPIO_ALL_OFF:
v_pIOPregs->GPBDAT=v_pIOPregs->GPBDAT|(0xF<<5);
break;
default:
break;
}
RETAILMSG(1,(TEXT("GPIO_Control:Ioctl code = 0x%x\r\n"), dwCode));
return TRUE;
}
/*
编写打开驱动程序函数,当应用程序使用文件操作函数CreateFile()打开本驱动程序时,WCE
将调用本函数,函数先检查是否有其他应用程序已打开本驱动,如果有,则不允许再打开,返回0表示打开失败,如果没有其他应用程序打开驱动,则打开计数器加1,然后返回一个不为空的句柄表示打开成功。
*/
DWORD GIO_Open(DWORD hDeviceContext, DWORD AccessCode, DWORD ShareMode)
{
v_pIOPregs->GPBDAT=v_pIOPregs->GPBDAT|(0xF<<5); //默认设置
RETAILMSG(1,(TEXT("GPIO_Control: GPIO_Open\r\n"))); //提示驱动打开
return TRUE;
}
/*编写关闭驱动程序函数,当应用程序使用CloseHandle()函数关闭本驱动时,wce讲调用此函数*/
BOOL GIO_Close(DWORD hOpenContext)
{
v_pIOPregs->GPBDAT=v_pIOPregs->GPBDAT&(0xF<<5); //默认设置
RETAILMSG(1,(TEXT("GPIO_Control: GPIO_Close\r\n"))); //提示驱动关闭
return TRUE;
}
void GIO_PowerDown(DWORD hDeviceContext)
{
RETAILMSG(1,(TEXT("GPIO_Control: GPIO_PowerDown\r\n")));
}
/*编写电源管理函数*/
void GIO_PowerUp(DWORD hDeviceContext)
{
RETAILMSG(1,(TEXT("GPIO_Control: GPIO_PowerUp\r\n")));
}
/*
编写读操作函数,当应用程序调用读文件函数ReadFile()时,wce将调用本函数读取,本函数返回读取的字节数。
*/
DWORD GIO_Read(DWORD hOpenContext, LPVOID pBuffer, DWORD Count)
{
RETAILMSG(1,(TEXT("GPIO_Control: GPIO_Read\r\n"))); //提示执行读函数
return TRUE;
}
/*
编写移动设备数据指针函数,当应用程序调用写文件函数SetFilePointer()时,wce将调用本函数
*/
DWORD GIO_Seek(DWORD hOpenContext, long Amount, DWORD Type)
{
RETAILMSG(1,(TEXT("GPIO_Control: GPIO_Seek\r\n")));
return 0;
}
/*
编写写操作函数,当应用程序调用写文件函数WriteFile()时,wce将调用这个函数写入数据,本函数返回实际写入的字节数。
*/
DWORD GIO_Write(DWORD hOpenContext, LPCVOID pSourceBytes, DWORD NumberOfBytes)
{
RETAILMSG(1,(TEXT("GPIO_Control: GPIO_Write\r\n")));
return 0;
}
总算讲完一个文件,休息一下先··接下来是
5、 编写GPIODriver.h文件
#define IO_CTL_GPIO_1_ON 0x01
#define IO_CTL_GPIO_2_ON 0x02
#define IO_CTL_GPIO_3_ON 0x03
#define IO_CTL_GPIO_4_ON 0x04
#define IO_CTL_GPIO_ALL_ON 0x05
#define IO_CTL_GPIO_1_OFF 0x06
#define IO_CTL_GPIO_2_OFF 0x07
#define IO_CTL_GPIO_3_OFF 0x08
#define IO_CTL_GPIO_4_OFF 0x09
#define IO_CTL_GPIO_ALL_OFF 0x0a
这个函数就不多说了··
6、 编写完以上这些函数后,如何让wce知道本驱动有这些函数捏,这就需要在GPIODriver.def文件中指出需要导出的函数了。具体函数如下:
LIBRARY GPIO
EXPORTS
GIO_Close
GIO_Deinit
GIO_Init
GIO_IOControl
GIO_Open
GIO_PowerDown
GIO_PowerUp
GIO_Read
GIO_Seek
GIO_Write
其中LIBRARY GPIO表示该驱动的动态库和静态库名称。EXPORTS段后面列出要从驱动的动态库文件中导出的函数名称。
这里导出所有流驱动函数。
7、 除了编辑GPIODriver.cpp和GPIODriver.def文件外,还需要编辑sources文件的内容,告诉编译器和链接器如何编译及连接本驱动程序。程序清单如下:
!if 0
File: sources
Copyright (c) 1995-2002 Microsoft Corporation. All rights reserved.
!endif
RELEASETYPE=PLATFORM //发布类型:BSP包中的一个驱动
TARGETNAME=GPIODriver //驱动动态库名称
TARGETTYPE=DYNLINK //目标类型:动态库
DLLENTRY=DllEntry //动态库入口函数名称:DllEntry
TARGETLIBS= \
$(_COMMONSDKROOT)\lib\$(_CPUINDPATH)\coredll.lib \ //需要连接的库
MSC_WARNING_LEVEL = $(MSC_WARNING_LEVEL) /W3 /WX //需要连接的库
INCLUDES= \
$(_TARGETPLATROOT)\src\inc; \
$(_COMMONOAKROOT)\inc; \
$(_PUBLICROOT)\common\oak\inc;$(_PUBLICROOT)\common\sdk\inc;$(_PUBLICROOT)\common\ddk\inc; \
..\..\inc
SOURCES= \
GPIODriver.cpp //要编译的源代码
以上代码等号左边的都是Platform Builder所能识别的编译变量。Sources就是给每个变量赋值,告诉编译器和连接器如何编译和连接本驱动程序。其中红色标记部分表示连接生成目标动态库时需要包含内核库coredll.lib,当要调用内核函数DisableThreadLibraryCalls()、MapPtrToProcess()和GetCallerProcess()时就需要该内核库。
8、 接下来是makefile,就一句话
!INCLUDE $(_MAKEENVROOT)\makefile.def
什么意思不知道···
9、完成以上步骤后,用PB打开平台,在窗体中选择File View选项卡,然后单击GPIODriver(excluded from build),在弹出的菜单选项中Clean Before Building,然后再选择Build Current Project,开始编译驱动。编译过程及结果可在信息框中看到,如果编译结果没有错误,也没有警告,将会在C:\WINCE600\PLATFORM\TQ2440\target\ARMV4I\retail目录下可以找到驱动程序目标文件GPIODriver.dll.
转载请注明出处。作者:四级管。广西师范大学 电子工程学院大学生科技创新基地 邮箱: [email protected]。