8、WINCE驱动开发介绍及实践
这一部分,对于一个项目来说比较重要的一个部分,如果我们有驱动开发的能力,那么硬件设计和软件的稳定性会有很大的保障。这一部分涉及的内容相对较难,不但要对系统的东西有深刻的认识,还要对硬件比较熟悉。学会了这样的开发能力,想要做的系统增加任何一个外设,开发周期都会变的短很多。我敢说,国内比较缺乏能够写驱动的人才。
驱动和我们平时写的硬件程序有些不同,它和系统的关系比较紧密,而且有着严格的调用机制。这一章,在课堂上只能说一个引导的作用,大部分的工作量还需下去同学们自己好好的研究和交流,请大家打起精神,查阅各种书籍及网络资源,来慢慢了解驱动,培养自己的开发能力。
由于这部分内容实在太多,我只以一个简单的流驱动作为例子,给大家讲解。
其余内容我把这部分课件上传到网上,大家可以下载阅读,或是去图书馆借书查阅。
参考资料:课件(http://d.download.csdn.net/down/3539921/liujianning)
我的例子参考资料:天嵌科技出品--WinCE开发完全手册(V2.5) (这个在天嵌论坛和上就可以下载的到)
(以下参考http://blog.csdn.net/nanjianhui/article/details/2674753)
流设备驱动程序又称为可安装的驱动程序,也就是说,它们是可以后续安装的。它们是由设备管理器(Device)动态加载的用户模式的DLL。
流设备驱动的架构:
在WinCE启动的时候,OAL(OAL.exe)首先加载kernel.dll,然后kernel.dll加载device.dll,device.dll会加载devmgr.dll,devmgr.dll实际上就是Device Manager模块,他会负责流设备的加载,卸载和交互操作。
一般应用程序要通过文件系统接口来访问设备。首先调用CreateFile打开设备并获得相应的句柄,然后通过文件系统接口调用ReadFile或者WriteFile来访问相应的流设备驱动,或者通过DeviceIoControl直接访问。无论哪种方式,都是要通过Device Manager才能访问到相应的设备驱动。
下面介绍一下流设备驱动的接口函数:
函数名 |
说明 |
XXX_Close |
关闭以hOpenContext标识的设备上下文。 |
XXX_Deinit |
由设备管理器调用来删除对某一设备的初始化信息。 |
XXX_Init |
由设备管理器调用来对某一设备进行初始化。 |
XXX_IOControl |
向设备发送命令,以命令设备做一些事情。 |
XXX_Open |
打开一个设备以进行读、写或者既读又写。 |
XXX_PowerDown |
停止向设备供电。供可使用软件控制关闭的设备关闭自身电源。 |
XXX_PowerUp |
恢复向设备供电 |
XXX_Read |
从设备读取数据 |
XXX_Seek |
在设备中移动数据指针 |
XXX_Write |
向设备写数据 |
以下是依次详细介绍这些接口函数。
1、 XXX_Init接口函数
完成的功能:
① 在驱动被加载时,该函数被调用
② 初始化所需要的硬件资源,并在本接口处理中被分配
③ 创建内存映射
接口的原型:
DWORD XXX_Init(DWORD dwContext):
参数说明:
dwContext:指向一个字符串,它描述注册表中一个流设备接口
本接口的处理返回时,设备管理器就在注册表中查找驱动的Ioctl子键,如果存在,设备管理器将调用XXX_IOControl()接口将dwcode参数传入驱动接口点。
2、 XXX_Deinit接口函数
完成的功能:
卸载一个设备驱动,释放被占用的资源,停止该设备的运行。
接口的原型:
BOOL XXX_Deinit(DWORD hDeviceContext)
参数说明:
hDeviceContext:指设备上下文的句柄,应由XXX_Init()返回。
3、 XXX_Open接口函数
完成功能:
当应用程序调用createfile()函数时,文件系统会自动调用该函数,打开一个存在的设备文件,当这个接口函数被调用后,也就为读/写做好了准备。
接口的原型:
DWORD XXX_Open(DWORD hDeviceContext, DWORD AccessCode, DWORD ShareMode):
参数说明:
hDeviceContext:指设备上下文的句柄,应由XXX_Init()返回。
AccessCode,打开设备的权限描述符,包括只读,只写,可读写。
4、 XXX_Close()接口函数
完成功能:
关闭一个设备,对应于closehandle接口
接口的原型:
BOOL XXX_Close(DWORD hOpenContext):
参数说明:
hDeviceContext:设备驱动的句柄,在XXX_Open调用时返回的
5、 XXX_Read()接口函数
完成功能:
应用程序中调用readfile()函数时,设备管理器会调用相应的接口。
接口的原型:
DWORD XXX_Read(DWORD hOpenContext, LPVOID pBuffer, DWORD Count):
参数说明:
hOpenContext:设备驱动的句柄,在XXX_Open调用时返回的
pBuffer:存放数据的Buffer,单位:字节(B)
Count:读取数据的长度
6、 XXX_Write()接口函数
完成的功能:
应用程序中调用writefile()函数时,设备管理器会调用相应的接口。
接口的原型:
DWORD XXX_Write(DWORD hOpenContext, LPCVOID pBuffer, DWORD Count):
参数说明:
hOpenContext:设备驱动的句柄,在XXX_Open调用时返回的
pBuffer:写入数据的Buffer,单位:字节(B)
Count:读取数据的长度
7、 XXX_Seek()接口函数
完成功能:
定义I/O指针时被调用,当应用程序调用setfliepointer()函数时,,设备管理器相应的调用该接口。
接口原型:
DWORD XXX_Seek(DWORD hOpenContext, long Amount, WORD Type)
参数说明:
hDeviceContext:设备驱动的句柄,在XXX_Open调用时返回的
Amount:移动的字节数,指针要移动的距离
Type:描述起始点位置,FILE_BEGIN表示从头移动,FILE_CURRENT表示从当前位置移动FILE_END表示从末尾往前移动
8、 XXX_IOControl()接口函数
完成功能:
主要传递包括读写命令在内的I/O控制命令给设备。
I/O控制字可以用来识别命令类型,普通的读写操作(XXX_Read和XXX_Write)通常能满足要求的,而I/O控制字可以与硬件相关。
接口原型:
BOOL XXX_IOControl(DWORD hOpenContext, DWORD dwCode, PBYTE pBufIn, DWORD dwLenIn, PBYTE pBufOut, DWORD dwLenOut, PDWORD pdwActualOut):
参数说明:
hDeviceContext:设备驱动的句柄,在XXX_Open调用时返回的
dwCode:由驱动程序指定,一般会在该驱动的说明文件中,提供相关的定义或通过头文件形式提供给应用程序。
pBufIn:输入Buffer
dwLenIn:输入Buffer的size
pBufOut:输出Buffer
dwLenOut:输出Buffer的size
pdwActualOut:实际输出的字节数
9、 XXX_PowerDown()接口函数
完成功能:
在停止对设备供电时被调用,完成关闭电源的安全性检查。
接口的原型:
void XXX_PowerDown(DWORD hOpenContext):
参数说明:
hDeviceContext:设备驱动的句柄,在XXX_Open调用时返回的
10、XXX_PowerUp()接口函数
完成功能:
恢复设备供电用。
接口的原型:
void XXX_PowerUp(DWORD hOpenContext)
参数说明:
hDeviceContext:设备驱动的句柄,在XXX_Open调用时返回的
下面,我们将介绍一下一个简单的GPIO的流驱动的开发流程。首先讲一个流驱动的一些知识
一个驱动至少需要包含:(.cpp、.def、.makefile、.sources、.reg、.bib)
.cpp是驱动源码,其中包含了头文件的函数调用,驱动代码和驱动接口。
.def提供驱动接口,内容大致如下:
LIBRARY XXX
EXPORTS
XXX_CLOSE
XXX_Deinit
XXX_IOControl
XXX_Open
XXX_PowerDown
XXX_PowerUp
XXX_Read
XXX_Seek
XXX_Write
MakeFlie文件内容大致如下:
!INCLUDE $(_MAKEENVROOT)\makefile.def
Sources文件内容:
TARGETNAME=指定要生成的动态库的名称
TARGETTYPE=DYNLINK
TARGETLIBS=\
$(_COMMONSDKROOT)\lib\$(_CPUINDPATH)\coredll.lib \
$(_SYSGENOAKROOT)\lib\$(_CPUINDPATH)\ceddk.lib \
(这个库需要自行添加)
DLLENTRY=DllEntry
DEFFILE=刚才提到的.def
SOURCES= \
(刚才见到的驱动源码)
.reg提供注册表信息,写入到Platform.reg中,内容大致如下:
[HKEY_LOCAL_MACHINE\Drivers\BuiltIn\驱动名称]
"Prefix"="前缀"
"Dll"="驱动的动态库文件"
"Index"=dword:1
"Order"=dword:30
Platform.bib中添加镜像:
驱动动态库文件 $(_FLATRELEASEDIR)\驱动动态库文件 NK SHK
以上就是一个流驱动拥有的主要文件的改写,下面我们开始介绍添加一个驱动的流程。
1、 首先在BSP包DRIVERS目录下建立一个“mydrivers”的目录。
2、 进入到该目录下,新建“mydrivers.cpp”,完善接口函数内容
3、 新建“mydrivers.def”添加上面讲的内容,这里XXX为“MYD”
4、 新建makefile,内容如前面所示
5、 新建sources,内容如下:
TARGETNAME=mydrivers
TARGETTYPE=DYNLINK
TARGETLIBS=\
$(_COMMONSDKROOT)\lib\$(_CPUINDPATH)\coredll.lib \
$(_SYSGENOAKROOT)\lib\$(_CPUINDPATH)\ceddk.lib
DLLENTRY=DllEntry
DEFFILE=mydrivers.def
SOURCES= \
Mydrivers.cpp
6、 直接修改注册表信息
[HKEY_LOCAL_MACHINE\Drivers\BuiltIn\Mydrivers]
"Prefix"="MYD"
"Dll"="mydrivers.dll"
"Index"=dword:1
"Order"=dword:30
7、 修改BSP包中的Platform.bib
mydrivers.dll $(_FLATRELEASEDIR)\ mydrivers.dll NK SHK
剩下的就是编译链接,调试驱动了。