嵌入式系统是一个很不错的选择。但是由于嵌入式硬件平台的多样性,以及WinCE对USB设备驱动开发只提供了底层支持,再加上许多摄像头厂商尚未提供WinCE下的USB摄像头驱动,这对初级开发人员在开发WinCEUSB摄像头程序时是一个难点。
前段时间,公司委派我负责一个嵌入式项目,项目要求是在WinCE平台上集成USB摄像头驱动和视频采集程序。这个项目的关键是要集成USB摄像头驱动,并高效的把摄像头设备进行初始化以取得一幅完整的图像。幸好我以前开发过WinCEUSB的主从设备的驱动程序。但虽然如此,我还是花了一些时间来调整系统的稳定性和可靠性。在这里我分享在这次项目实践中得到的经验和教训,希望大家能少走弯路。
一.什么是USB设备驱动程序开发?
随着USB设备的普及,USB设备驱动开发在嵌入式系统变得越来越重要了。为了支持不同类型的硬件可以连接到WinCE平台上,微软提供了具有定制接口的流接口驱动程序模型。WinCE的USB外围设备一般是使用流接口驱动程序。流接口驱动程序是指通过系统提供的文件系统API与应用程序交互;WinCE内核系统会通过设备管理器来完成对流接口驱动程序的加载、卸载等管理工作;而流接口驱动程序则会通过调用USBD模块提供的接口函数实现与底层USB设备通信。因此,在进行USB设备驱动程序开发之前,我们必须先了解USB设备驱动的结构和分类。
(1)主机与USB摄像头的通讯结构
USB摄像头驱动程序主要是利用系统提供的底层接口配置设备和摄像头设备进行通讯。因此,WinCE的USB摄像头驱动分为两层:USBClient设备驱动程序和底层的WinCE函数实现层。而底层的函数层本身又由两部分组成,即通用串行总线驱动程序(USBD)模块和较低层的主控制器驱动程序(HCD)模块。HCD负责最底层的处理,USBD模块实现较高的USBD函数接口。因此,USB摄像头驱动主要是利用USBD接口函数和外围USB摄像头打交道。
一般来说,主机和USB外设之间的通讯是由在主机端通过USBD模块和HCD模块使用的PIPE访问一个通用的逻辑设备来完成。也就是说,USBD和HCD是一组抽象出来用于访问USB设备的逻辑接口,它们主要是负责管理USB外设的连接、加载、移除、数据传输和通用的配置。其中HCD是由主机控制和驱动的,是为USBD提供底层的功能访问服务。而USBD则是由USB总线驱动的,位于HCD的上层,是利用HCD的服务提供较高层次抽象的功能。
由于HCD和USBD都是面向一致的逻辑设备接口,因此如果嵌入式系统中拥有多种USB物理外设的话,那么就需要有唯一对应的外设驱动程序,也就是要有最上层的PIPE所连接的物理设备和USB设备驱动程序。有了对这个结构的认识,那么我们在进行USB设备驱动程序开发时首先要写的就是最上端的USB摄像头客户端驱动程序,在WinCE的样例程序中它也被称为USBClientDriver。它是工作于USBD之上,所以实际上我们的工作就变成了利用USBD提供的接口针对特定的物理设备来完成USB设备驱动程序。(见图)
(2)流驱动程序的分类和函数结构
WinCE驱动程序是介于内核系统和物摄像头驱动理设备之间的一个代码层,它的主要作用是为内核系统提供一个接口用来操作不同的外围设备,包括物理设备和虚拟设备。驱动程序提供给内核系统的接口一般可以分为:本地驱动(NativeDrivers)和流驱动(StreamDrivers)。我从这次项目实践中得到的经验是,WinCE下的所有驱动都可以归类到这两个里面,二者必居其一。
流驱动是指通过为内核系统提供流接口函数来实现驱动外围设备,如XXX_Init()、XXX_Open()、XXX_Read()、XXX_Write()、XXX_Close()等。这一类的驱动由DeviceManager来管理,它是通过调用ActivateDeviceEx()函数来实现加载流驱动的。ActivateDeviceEx()的参数是注册表中相应的键,用来设定加载流驱动的属性,如Index、Order、Prefix等等。流驱动加载成功后,应用程序就可以通过调用CreateFile()、ReadFile()、WirteFile()等函数来访问流驱动设备了。而与流驱动相反,软装,本地驱动提供给内核系统的不是标准的流接口,而是事先约定好的特定接口。因此不同的本地驱动设备,接口也是不一样的。在WinCE中,常见的本地驱动有LCD显示驱动、触摸屏驱动、鼠标和键盘驱动及打印机驱动等。从这里可以看出,本地驱动主要是涉及与人机界面相关的驱动。它们是由GWES来管理的,由于他们在注册表中有各自相应的配置信息,因此它们会在系统启动时自动加载。简单的说,就是本地驱动是由内核系统操作和调用的,一般的应用程序是不能访问和调用的