usb camera

--------------------------------camera开发
WinCE USB驱动开发 错走冤枉路

1. 什么是WinCE设备驱动程序?

  (1)从驱动加载方式来区分
  在深入探讨Windows CE所支持的外围设备驱动程序之前,先了解在WinCE平台上使用的两种设备:内建设备和可安装设备。因此,从驱动加载方式来看WinCE可分为本机设备驱动(Built-In Driver)、可加载驱动(Loadable Driver)以及混合型驱动。

  ①本机设备驱动
  本机设备驱动即Native Device Drivers。WinCE设计成可直接使用内建设备,这些设备由本机驱动过程控制,而本机驱动程序又与WinCE的核心组件紧密相连。这些驱动对应的设备通常在系统启动时,在GWES的进程空间内被加载,因此它们不是以独立的DLL形式存在,也因此要求每一个本机驱动程序都必须与称为设备驱动程序接口(DDI)的特定接口一致。

  本机设备是指整合进平台的设备,其中包括显示、触摸面板、音频、串行埠、LED、电池和PC卡插座等。如果没有这些本机设备整个系统就不能和用户信息交流,例如触摸面板和显示等。本机驱动程序一般设计为动态链接库,但有两个例外:电池和LED驱动程序由于小而设计为静态库(当建立CE图像时与GWES模块链接)。这些设备相应的驱动程序是在WinCE平台开发过程中由OEM开发的,它们储存在ROM或闪存内。通常只有OEM才会对本机设备驱动程序进行修改,其它自由设备生产商只提供附加的硬件设备,对本机设备驱动程序不会有过多涉及。

  ②可加载设备驱动
  可加载设备是指可与平台连接和分离的第三方接口设备,可由用户随时安装和卸载。这种外围设备的驱动也被称为流驱动,这些驱动可以在系统启动时或者和启动后的任何时候由设备管理器动态加载。通常这类驱动是以DLL动态链接库的形式存在,系统加载后这些驱动程序也只是以用户态的角色运行。可加载驱动程序是通过文件操作API来从设备管理器和应用程序获得命令。在WinCE典型的可加载驱动有:PCMCIA driver(PCMCIA.dll)、Serial driver(SERIAL.dll)、ATAFLASH driver(ATA.dll)、Ethernet driver(NE2000.dll,SMSC100FD.dll)。

  与本机驱动程序不同的是,所有可加载流驱动程序都共享一个公用接口。该接口由每个驱动程序内的10个功能或记录点组成,这些功能与应用程序所用的文件API中的功能匹配。因此,控制可加载设备的流接口驱动程序一般由应用程序存取,流接口驱动程序由一个特殊文件来将设备功能展现给应用程序的,该文件可被打开、读取、写入和关闭。例如,用户将一个GPS设备与平台相连后,就可启动有GPS功能的应用程序来存取并使用该设备。WinCE是使用已有的API来让应用程序存取这些驱动程序,而不是建立新的API。

  (2)从驱动程序层次上分类
  一般可以分为独立驱动和层次型驱动两类。独立驱动程序是指将驱动程序编写成同时包含Model Device Driver(MDD)和Platform Dependent Driver(PDD)层的独立驱动。使用独立驱动的好处在于可以省去MDD和PDD层驱动之间的信息传递,这一点在实时处理中非常重要。独立驱动的代码包括中断服务例程和平台相关处理函数。另外,如果设备的操作和MDD驱动层的接口描述相吻合,用独立驱动程序可以提高处理性能。

  层次型驱动是指分为两层,较上层的MDD和比较下层的PDD。MDD实现的是和平台无关的功能,它描述了一个通用的驱动程序框架;而PDD是和硬件以及平台相关的代码组成。MDD调用PDD中特定的接口来获取硬件相关的信息。当使用层次型驱动的时候,一般只需要基于相近的样列驱动程序,针对特定的硬件只修改PDD程序,MDD建立的框架可继续使用。但由于层次间接口的层层调用以及消息的传递,使得处理速度相对于独立驱动程序要慢。因此,在嵌入式实时要求苛刻的环境下,层次型驱动显得不是很适合。

  简单的说,独立驱动是把PDD与MDD写在一起,没有做严格的区分,通常这种驱动比较简单,比如ATADISK。至于本机驱动和加载式流驱动是从驱动与系统其它模块(调用者)的接口形式上做的分类。所以,一个加载式驱动程序可以是独立的流式驱动,例如ATADISK;也可以是分层的流式驱动,例如OHCI。也就是说,独立和分层是驱动实现方式上的分类,而本机和加载流式则是驱动模型上的分类。所谓本机驱动就是操作系统有保留专门的接口,而加载流式驱动是指编写DLL文件导出各种流式接口函数的接口。

 
 2. USB加载式流接口驱动要点分析

  为了支持不同类型的外围设备,WinCE平台提供了具有定制接口的流接口驱动程序模型。因为大部分USB外围设备由于功能性更适合流接口驱动的结构,所以一般都采用加载式流接口驱动程序模型来开发USB设备驱动程序。

  (1)USB系统结构分析
  WinCE下USB系统软件由两层组成:较高USB设备驱动程序层和较低的USB函数层。较低的USB函数层本身又由两部分组成:较高的通用串行总线驱动程序(USBD)模块和较低的主控制器驱动程序(HCD)模块。通过HCD模块功能和USBD模块实现高层的USBD接口函数,USB设备驱动程序就能与外围设备进行通讯。

  在数据传输的过程中,操作流程通常按下列的次序进行:①USB设备驱动程序进行数据传输的初始化,即通过USBD接口函数给USBD模块发送数据传输的请求。②USBD模块将该请求分成一些单独的事务。③HCD模块排出事务次序。④主控制器硬件执行事务。这里需要提醒的是,所有的事务都是从主机发出的,外围设备完全是被动接受型的。

  (2)USB设备驱动程序入口点函数
  从结构分析我们可知,所有的USB设备驱动程序必须在它们的DLL库设置一定的入口点与USBD模块进行适当的交互。设置入口点函数有两个作用:一是使得 USBD 模块能与外部设备交互;二是使得驱动程序能创建和管理任何可能需要的注册键。

  下面简要介绍相关函数的作用:USBDeviceAttach是当 USB 设备连接到主计算机时运行,USBD模块会调用这个函数初始化USB设备,取得USB设备信息和配置USB设备,并且申请必需的资源。USBInstallDrive是在第一次加载USB设备驱动程序时首先被调用,它使得驱动程序能创建需要的注册键,用于将一个驱动程序所需的注册表信息写入到HKEY_LOCAL_MACHINE/Drivers/USB/ClientDrivers目录下,例如设备名称等。需要注意的是,USB设备驱动程序不使用标准的注册表函数,而是使用RegisterClientDriverID()、RegisterClientSettings()函数来注册相应的设备信息。

  USBUninstallDriver是在用户删除USB设备驱动程序时调用,负责删除注册键并释放其它相关资源。它通过调用UnRegisterClientSettings()和UnRegisterClientDriverID()函数来删除由驱动程序的USBInstallDriver()函数创建的所有注册键。因此,我们在驱动程序中就需要严格按照这三个函数的原型来实现,否则就不能为设备管理器所识别。
 

3.USB设备流接口驱动的实现步骤

  从WinCE USB设备驱动模型及结构分析中,我们可以清晰的看到主机和外设之间的实现方式。在主机端,通过USBD模块和HCD模块使用默认的PIPE访问一个通用的逻辑设备,实际上就是说USBD和HCD是一组访问所有USB设备的逻辑接口,它们负责管理所有USB设备的连接、加载、移除、数据传输和通用配置。其中HCD是主机控制驱动,是为USBD提供底层的功能访问服务,USBD是USB总线驱动,位于HCD的上层,利用HCD的服务提供较高层次的功能。因此,实现USB加载流驱动程序大致需要完成以下步骤:

  (1)选择代表设备的文件名前缀。前缀非常重要,设备管理器在注册表中通过前缀来识别设备。同时,在流接口命名时也将这个前缀作为入口点函数的前缀,如果设备前缀为XXX,那么流接口对应为XXX_Close,XXX_Init等。

  (2)设置驱动的各个入口点函数。所谓入口点是指提供给设备管理器的标准文件I/O接口。在生成一个DLL后,就用设备文件名前缀替换名字中的XXX。因此,每个加载式流接口驱动程序必须实现XXX_Init()、XXX_IOControl()以及XXX_PowerUp()等一组标准的函数,用来完成标准的文件I/O函数和电源管理等。

  (3)建立.DEF文件。当设备管理器初始化USB设备编译出来的流接口函数后,还必须建立一个.def文件。DEF文件定义了DLL要导出的接口集,而且加载式流驱动大多是以DLL形式存在的,所以应将DLL和DEF的文件名统一起来。DEF文件告诉链接程序需要输出什么样的函数,最后将驱动程序编译到内核中去,这样这个USB设备流接口驱动程序就可以被应用程序调用。

  (4)在注册表中为驱动程序建立表项。在注册表中建立驱动程序入口点,这样设备管理器才能识别和管理这个驱动。此外,注册表中还能存储额外的信息,这些信息可以在驱动运行之后被使用到。

  在这次USB驱动开发过程中,错走许多冤枉路使我叫苦连天。我感受最深的是由于WinCE提供了通用串行总线驱动程序(USBD)模块、USBD接口函数全集、样本主机控制器驱动程序(HCD)模块。所以,我们只需要根据USB设备硬件特性,利用USBD提供的不同函数,实现流接口函数与外围设备的交互。在没有特别的情况下,我最大的收获经验是把这些公用的源程序照搬过来,能极大的缩短开发周期,从而能更快速地进行嵌入式开发。


 
USB设备流接口驱动的实现步骤

  从WinCE USB设备驱动模型及结构分析中,我们可以清晰的看到主机和外设之间的实现方式。在主机端,通过USBD模块和HCD模块使用默认的PIPE访问一个通用的逻辑设备,实际上就是说USBD和HCD是一组访问所有USB设备的逻辑接口,它们负责管理所有USB设备的连接、加载、移除、数据传输和通用配置。其中HCD是主机控制驱动,是为USBD提供底层的功能访问服务,USBD是USB总线驱动,位于HCD的上层,利用HCD的服务提供较高层次的功能。因此,实现USB加载流驱动程序大致需要完成以下步骤:

  (1)选择代表设备的文件名前缀。前缀非常重要,设备管理器在注册表中通过前缀来识别设备。同时,在流接口命名时也将这个前缀作为入口点函数的前缀,如果设备前缀为XXX,那么流接口对应为XXX_Close,XXX_Init等。

  (2)设置驱动的各个入口点函数。所谓入口点是指提供给设备管理器的标准文件I/O接口。在生成一个DLL后,就用设备文件名前缀替换名字中的XXX。因此,每个加载式流接口驱动程序必须实现XXX_Init()、XXX_IOControl()以及XXX_PowerUp()等一组标准的函数,用来完成标准的文件I/O函数和电源管理等。

  (3)建立.DEF文件。当设备管理器初始化USB设备编译出来的流接口函数后,还必须建立一个.def文件。DEF文件定义了DLL要导出的接口集,而且加载式流驱动大多是以DLL形式存在的,所以应将DLL和DEF的文件名统一起来。DEF文件告诉链接程序需要输出什么样的函数,最后将驱动程序编译到内核中去,这样这个USB设备流接口驱动程序就可以被应用程序调用。

  (4)在注册表中为驱动程序建立表项。在注册表中建立驱动程序入口点,这样设备管理器才能识别和管理这个驱动。此外,注册表中还能存储额外的信息,这些信息可以在驱动运行之后被使用到。

  在这次USB驱动开发过程中,错走许多冤枉路使我叫苦连天。我感受最深的是由于WinCE提供了通用串行总线驱动程序(USBD)模块、USBD接口函数全集、样本主机控制器驱动程序(HCD)模块。所以,我们只需要根据USB设备硬件特性,利用USBD提供的不同函数,实现流接口函数与外围设备的交互。在没有特别的情况下,我最大的收获经验是把这些公用的源程序照搬过来,能极大的缩短开发周期,从而能更快速地进行嵌入式开发。

 

这是一个USB接口的流式驱动。

对于USB驱动,必须实现的接口函数为
USBInstallDriver
USBUnInstallDriver
USBDeviceAttach
USBDeviceNotificationCallback
这几个函数实现USB设备的系统注册安装,最后使设备管理器加载对应的DLL,调用该DLL的XXX_INIT函数


对于流式驱动必须实现的接口函数为
DWORD CAM_Init (DWORD dwContext);
BOOL  CAM_Deinit (DWORD dwContext);
DWORD CAM_Open (DWORD dwContext, DWORD dwAccess, DWORD dwShare);
BOOL  CAM_Close (DWORD dwOpen);
DWORD CAM_Read (DWORD dwOpen, LPVOID pBuffer, DWORD dwCount);
DWORD CAM_Write (DWORD dwOpen, LPVOID pBuffer, DWORD dwCount);
DWORD CAM_Seek (DWORD dwOpen, long lDelta, WORD wType);
DWORD CAM_IOControl (DWORD dwOpen, DWORD dwCode,
                     PBYTE pIn, DWORD dwIn,
                     PBYTE pOut, DWORD dwOut,
                     DWORD *pdwBytesWritten);
void CAM_PowerDown (DWORD dwContext);
void CAM_PowerUp (DWORD dwContext);

插入USB摄像头,系统检测到设备插入后会提示寻找相应的驱动(第一次时会指定,后面会自动查找),
此时指定该驱动DLL文件,设备管理器会调用驱动中的USBInstallDriver,该函数使用USBD.dll中的函数注册设备,并依据VID/PID设置注册表项,这样在这个项不丢失的时候,下次插入将能够自动找到,如果这个函数的VID/PID和实际设备不符,将返回失败;

USBInstallDriver成功之后,系统才调用USBDeviceAttach函数

在USBDeviceAttach函数中,主要进行3个工作:
(1)USB设备接口配置的枚举和保存,备后面传输工作使用;
(2)调用ActiveDevice函数激活一个流接口,使应用程序可以和驱动交互;
(3)调用RegisterNotificationRoutine函数注册一个设备状态回调函数,这里被注册的函数的
主要功能是在设备移除时通知驱动程序停止设备,释放占用的资源

5.ActiveDevice函数调用的时候,依据参数lpszDevKey找到驱动程序文件,在注册表的HKEY_LOCAL_MACHINE/Drivers/Active
键中增加这个设备,并且将USB驱动程序的上下文指针放到这里(即该函数的第2个参数),并且指定一个索引(设备序号),
将驱动程序加载到Device Manager的进程空间。这时Device Manager将发送一个新设备插入的消息,
调用该流接口驱动的CAM_Init函数;

接着CAM_Init函数被调用,参数就是ActiveDevice函数的参数1,然后依据这个参数,
在注册表中找到USB驱动程序的上下文,并返回,这样对该流接口驱动的操作,
就可以找到USB驱动的上下文,并且交换数据.

至此,USB摄像头加载并初始化完成。如果上述函数中有任一函数失败,驱动加载都会失败。
对于普通的流式驱动,不会有USB设备注册这一过程,都是通过XXX_INIT时加载驱动。因为USB驱动必须注册在USB设备的键值下,所以不一样。

然后在应用程序中使用参数"CAM1:"(前面的函数使用这一参数注册了该设备并关联DLL驱动文件)CreateFile打开设备,就会调用到驱动中的cam_open,得到一个设备句柄。
此时在应用程序中调用DeviceIoControl,传入ioctrl码,驱动中的CAM_IOControl就会根据相应的ioctrl码作相应的动作或填充相应的缓冲区。
比如:
  case IOCTL_CAMERA_DEVICE_CAPTURE_ONE_FRAME:
   RETAILMSG(1, (TEXT("Init isoc!/r/n")));
   startaddress=0;
   for(i=0;i<25;i++)
   {
    bytesRead=Ov51xReadOneFrame(pDrv,pOut,startaddress);
    startaddress+=bytesRead;
   }
   *pdwBytesWritten=startaddress;
   break;
会在DeviceIoControl传入的指针内填充帧数据,应用程序就可以得到图像数据进行转换解析。


具体的ioctl动作是控制摄像头的寄存器来实现的,这个要查摄像头的DATASHEET,
比如:OV511的datasheet中关于寄存器50H的描述(网上可以下到0V511的datasheet)

50h RST[6:0] RW Bit 0 : Software reset for UDC

Bit 1 : Software reset for IC
Bit 2 : Software reset for ISO FIFO
Bit 3 : Software reset for OmniCE
Bit 4 : Software reset for DRAM interface
Bit 5 : Software reset for camera interface
Bit 6 : Software reset for OV511 & registers

在驱动中调用Ov51xReset(pDrv, 0x3F);
//重置OV511
int Ov51xReset(PDRVCONTEXT pDrv,unsigned char reset_type)
{
 int rc;
 rc = RegisterWrite(pDrv, 0x50, reset_type);
 rc = RegisterWrite(pDrv, 0x50, 0);

 if(0==rc)
 {
  return 1;
 }
 else
 {
  return 0;
 }
}
就是直接往寄存器0x50写入0x3F值,将50h的低6位全部置1进行重置(硬件上检测到各位变1后会有相应的重置动作)。RegisterWrite通过调用USB的传输函数IssueVendorTransfer写入到硬件寄存器。
整个驱动的流程就是应用通过IOCTRL调用驱动,驱动通过读写寄存器器控制硬件。


关于驱动文件如何build到镜像中去,由于做成的是流式驱动,不是builtin的驱动(builtin的驱动是启动后就加载的,必须存在于image中),所以实际上是可以不用加到image中去的。
只需要在插入摄像头时指定该DLL为摄像头的驱动文件即可(此时DLL可以放在任意位置进行)。然后通过应用程序进行调用来调试驱动。所以流式的可以做成安装包来安装,就像PC上的
驱动安装包一样。所谓安装也就是完成向系统注册该设备,然后插入摄像头时不需要手动查找。系统通过注册表和设备的PID匹配来自动调用对应的驱动文件。

如果要build到image中,需要设置好注册表(也就是摄像头插入后会通过注册表找到该驱动),另外还要设置好bib文件(也就是定义dll驱动存放的位置)就OK了。
如果要把驱动放到pb的目录下进行编译,需要编写该驱动对应的source文件和makefile文件(定义整个驱动工程要用到的头文件,库和编译类型等等)。

------------------camera开发

你可能感兴趣的:(manager,嵌入式,dll,平台,WinCE,CAM)