320 likes | 554 Views
Windows CE.net USB 摄像头驱动开发 ( 初步 ). 型号 :Ominivision OV511+ (USB-Camera Bridge). 摄像头型号. 网眼 Webeye 2000 VID 0x05A9 PID 0xA511 输出格式: RAW-YUV 等. USB 驱动程序的功能. 1. 通过对 Windows CE.net 提供的 USBD 等函数的调用,控制设备以及读取设备数据; 2. 外加的摄像头做成流接口驱动比较方便,驱动程序要向系统注册设备接口,和应用程序交互。. OV511+ 芯片的控制.
E N D
Windows CE.net USB摄像头驱动开发(初步) 型号:Ominivision OV511+ (USB-Camera Bridge)
摄像头型号 网眼 Webeye 2000 VID 0x05A9 PID 0xA511 输出格式:RAW-YUV等
USB驱动程序的功能 1.通过对Windows CE.net提供的USBD等函数的调用,控制设备以及读取设备数据; 2.外加的摄像头做成流接口驱动比较方便,驱动程序要向系统注册设备接口,和应用程序交互。
OV511+芯片的控制 依据OV511的Datasheet,里面有10h到49h这样57个寄存器,通过对这些积存器赋值,就能够对Camera Interface、DRAM Interface和ISO FIFO等进行设置。 通过端点0进行控制传输,就可以读写这些寄存器,在Linux下,使用usb_control_msg函数(无URB的传输)。后面将讲解如何在Windows CE.net下实现。 /* Write an OV51x register */ static int reg_w(struct usb_ov511 *ov, unsigned char reg, unsigned char value) { … rc = usb_control_msg(ov->dev,usb_sndctrlpipe(ov->dev, 0),(ov->bclass == BCL_OV518)?1:2 /* REG_IO */, USB_TYPE_VENDOR | USB_RECIP_DEVICE,0, (__u16)reg, &ov->cbuf[0], 1, HZ); …
开发工具 测试系统为Windows CE.net 4.2,驱动程序形式为流接口驱动,基本框架是一个dll动态连接库。 使用Embedded Visual C++ 4.0或者Platform Builder 4.2都可以编译,推荐使用EVC,EVC用起来比较快,只要将调用USB函数需要的头文件以及Lib文件的路径设置好就可以。 开发板的硬件配置为Samsung ARM9 S3C2410,故连接的Lib为ArmV4Release版本的usbclient.lib。
流接口USB驱动程序 步骤: 1.建立一个WCE Dynamic-Link Library; 2.书写一个Export def文件(不是必要的,有这个可以保证符号的导出),输出各个驱动需要的函数符号; 3.依据USB驱动加载的流程,添加完成各个函数,并进行测试……
第一部分 使驱动程序能够被加载 这部分,将填写 USBInstallDriver USBDeviceAttach USBUnInstallDriver CAM_Init CAM_Deinit 这些函数正确填写之后,驱动程序就可以加载了。
Windows CE.net下流接口USB驱动程序加载的流程 1.设备插入后,系统在取得VID/PID后将自动在注册表中寻找是否有这一项,如果没有,则出现提示输入设备名称对话框; 2. 依据注册表或者输入名称找到驱动程序dll文件后,将首先调用USBInstallDriver函数,该函数使用USBD.dll中的函数注册设备,并依据VID/PID设置注册表项,这样在这个项不丢失的时候,下次插入将能够自动找到,如果这个函数的VID/PID和实际设备不符,将返回失败; 3.USBInstallDriver成功之后,系统才调用USBDeviceAttach函数;
Windows CE.net下流接口USB驱动程序加载的流程 4.对于USB设备,在USBDeviceAttach函数中,主要进行3个工作:(1)USB设备接口配置的枚举和保存,备后面传输工作使用;(2)调用ActiveDevice函数激活一个流接口,使应用程序可以和驱动交互;(3)调用RegisterNotificationRoutine函数注册一个设备状态回调函数,这里被注册的函数的主要功能是在设备移除时通知驱动程序停止设备,释放占用的资源; 5.ActiveDevice函数调用的时候,依据参数lpszDevKey找到驱动程序文件,在注册表的HKEY_LOCAL_MACHINE\Drivers\Active键中增加这个设备,并且将USB驱动程序的上下文指针放到这里(即该函数的第2个参数),并且指定一个索引(设备序号),将驱动程序加载到Device Manager的进程空间。这时Device Manager将发送一个新设备插入的消息,调用该流接口驱动的CAM_Init函数;
Windows CE.net下流接口USB驱动程序加载的流程 6.接着CAM_Init函数被调用,参数就是ActiveDevice函数的参数1,然后依据这个参数,在注册表中找到USB驱动程序的上下文,并返回,这样对该流接口驱动的操作,就可以找到USB驱动的上下文,并且交换数据。 以上这些工作完成之后,USB设备的信息被保存,并且USB驱动程序上下文也被流接口驱动所记载。流接口USB驱动的加载完成。我们就看见那个输入驱动程序名称的对话框消失了。如果前面6个步骤中任何一步不成功,将不会使用这个驱动程序,会反复弹出这个对话框,提示输入驱动程序名称,直到这些过程全部正确完成。 驱动加载完成后,用户程序再用CreateFile打开设备的时候,将返回驱动程序上下文给用户程序,这样用户程序和驱动就可以交互了,并且这个驱动程序上下文是含有该USB设备相关信息的,所以,可以使用USBD函数来操作USB设备,并且将USB设备的数据返回给应用程序。
Windows CE.net下流接口USB驱动程序卸载的流程 在加载的时候,我们已经注册了USBDeviceNotification回调函数,在USB设备移除的时候,将调用这个函数。注册回调时候使用的函数lpRegisterNotificationRoutine的第3个参数是驱动程序上下文指针,就是在调用这个回调函数时候的参数。 USBDeviceNotification传入的参数是驱动程序上下文,我们依据这个就可以向设备发送信息以停止设备,停止驱动程序正在进行的线程,关闭事件、临界区等信号,释放申请的内存。这样,就完成了驱动程序的卸载。 对于流接口驱动,在卸载的时候会调用CAM_Deinit函数,这个函数的参数也是驱动程序上下文,我们也可以在这里完成资源释放工作,这里选择在USBDeviceNotification释放,所以CAM_Deinit就不用填写了。 再设备资源释放之后,将调用USB_UninstallDriver函数,使用USBD里面的操作注册表的函数清理注册表中添加的HKEY_LOCAL_MACHINE\Drivers\Active中的项目。 注意:USB驱动程序不通过注册表API来操作注册表,而是通过USBD提供的函数。
第二部分 保存USB设备的信息 在第一部分,有一个函数尚未填写完成,USBDeviceAttach函数中的第一个工作——USB设备接口配置的枚举和保存。 这个主要和USB规范,以及OV511+的规格相关,所以在这个部分单独讲解。
Windows CE.net下USB设备接口的枚举和保存 USB主设备 USB协议本身很复杂,但方便在提供了统一的接口方式,使得驱动程序在使用设备的时候,工作简化到了类似操作串行接口。 USB设备可以看作提供了多个串口的设备,依据USB的规范,我们将每个串口称作端点(Endpoint),要和这个端点通信,我们就要打开到这个端点的连接,这个连接就是管道(Pipe)。 管道 端点 USB从设备
Windows CE.net下USB设备接口的枚举和保存 打开端点之后,就可以像串口一样进行数据传输了。USB有4种不同类型的传输方式:控制传输(Control Transfer),批量传输(Bulk Transfer),中断传输(Interrupt Transfer)和实时传输(IsochTransfer)。 对应每种传输, Windows CE.net下都提供了函数。
Windows CE.net下USB设备接口的枚举和保存 由于一个设备可能要适应多种情况,端点的设置会有多套,以备使用。端点设置称为接口(Interface)。USB设备展现给我们能够找到的东西就是这些Interface,我们选择要用的Interface,就可以找到Endpoint,再打开Endpoint,就可以传输数据了。所以,在驱动程序开始的时候,需要记录下这些Interface。 OV511+的端点0是控制端点,用来设置参数以及起停设备;端点1是实时传输端点,用来传输视频。 端点1有8套不同的设置,主要区别就在于一次传输的数据帧的大小,所以在USBDeviceAttach的时候,要记录这些设置到驱动程序中,后面才能够选用。
Windows CE.net下USB设备接口的枚举和保存 USBDeviceAttach函数传入的第一个参数LPCUSB_DEVICE lpUsbDev,里面就包含了这些设置信息,OV511+芯片在这里就有8个Interface,通过lpUsbDev指针,把这8个Interface的内容读出来,存到驱动程序的变量中,这个工作就完成了。 后面,传输视频的时候,就从驱动程序变量中选择一个Interface,然后依据这个Interface,就找到了端点,接着就可以打开管道,传输数据了。
Windows CE.net下USB设备接口的枚举和保存 OV511+这8个Interface设置见Datasheet USBDeviceAttach函数中,通过ParseStreamInterfaces函数来保存这些Interface。详细内容的参见代码。 主要结构如下: for (i = 0; i < lpUsbDev->lpConfigs->dwNumInterfaces; i++) { lpIF = &lpUsbDev->lpConfigs->lpInterfaces[i]; if ((lpIF->Descriptor.bInterfaceClass == bIFStrm) && (lpIF->Descriptor.bInterfaceSubClass == bIFSubStrm)) { //保存接口 } } 其中dwNumInterfaces为折纸总数,lpInterfaces[i]存放的就是设置的内容,本身是一个复杂的结构体,里面有描述端点等信息的相关内容。OV511+的InterfaceClass值为0xff,InterfaceSubClass值为0xff(见Datasheet)。
第三部分 控制OV511 OV511通过端点0来控制其寄存器。 Linux中,写寄存器函数如下: /* Write an OV51x register */ static int reg_w(struct usb_ov511 *ov, unsigned char reg, unsigned char value) { … rc = usb_control_msg(ov->dev,usb_sndctrlpipe(ov->dev, 0),(ov->bclass == BCL_OV518)?1:2 /* REG_IO */, USB_TYPE_VENDOR | USB_RECIP_DEVICE,0, (__u16)reg, &ov->cbuf[0], 1, HZ); … 这部分讲解如何在Windows CE.net下实现一样的功能。
Windows CE.net的控制传输 查看MSDN,IssueControlTransfer和IssueVendorTransfer是用来进行控制传输的,但两个函数有什么区别呢? 看MSDN,IssueControlTransfer的第一个参数是hPipe,就是说,在使用IssueControlTransfer之前,必须用OpenPipe打开管道。 OpenPipe函数需要端点描述作为参数。但是,端点0是没有这个描述可以用的(MSDN: However, endpoint zero (0) never has a USB_ENDPOINT structure)。 我没测试自己指定描述是否可以,但是,IssueVendorTransfer提供了更为方便的途径。这个函数默认从端点0进行控制传输。 如果是其他端点的控制传输,是要用IssueControlTransfer的。
Windows CE.net下读写控制管道0 这样,Windows CE.net写寄存器代码如下: int RegisterWrite(PDRVCONTEXT pDrv, unsigned char reg, unsigned char value) { USB_DEVICE_REQUEST req; ... // Get our pdd specific context PDEVICECONTEXT pPDD = (PDEVICECONTEXT)pDrv->dwDeviceContext; req.bmRequestType = USB_TYPE_VENDOR | USB_RECIP_DEVICE; req.bRequest = 2; req.wValue = 0; req.wIndex = (USHORT)reg; req.wLength = 1; unsigned char buf=value; dwBytes = 0; dwErr = USB_NO_ERROR; dw = IssueVendorTransfer (pDrv->lpUsbFuncs, pDrv->hDevice, DefaultTransferComplete, pPDD->hVendorEvent, USB_OUT_TRANSFER | USB_SHORT_TRANSFER_OK,&req, &buf, NULL, &dwBytes, 2000, &dwErr); ... }
第四部分 读取OV511视频数据 第二部分,已经保存了OV511的视频接口,现在,是用的时候了。 Linux下,实时传输通过创建URB,提交到USB传送队列来完成实时传输。 Windows CE.net下,URB不用我们自己创建,只要准备好数据缓冲,使用IssueIsochTransfer函数,系统就帮我们完成了这个过程。
Windows CE.net下的实时传输 先看Linux下的过程
Windows CE.net下的实时传输 Linux下通过填写一系列URB包,在包中指定传输参数,数据缓冲,最后usb_submit_urb将这些URB提交到系统的USB数据队列。 Windows CE.net的USBD层以下完成USB数据队列的功能,而呈现给驱动程序使用的函数就是IssueIsochTransfer。
Windows CE.net下的实时传输 IssueIsochTransfer函数需要的参数之一为USB_PIPE,所以,在进行传输之前,先要打开对应端点的管道。 在USBDeviceAttach的时候,已经保存了这个设备的若干Interface接口(包含了端点设置的信息),打开管道,就是选择一个Interface,找到端点描述符,然后OpenPipe。 这个部分的代码在程序的SetStreamInterface函数中。
Windows CE.net下的实时传输 IssueIsochTransfer函数需要的参数之一为USB_PIPE,所以,在进行传输之前,先要设置接口(SetInterface),并且打开到对应端点的管道(OpenPipe)。 在USBDeviceAttach的时候,已经保存了这个设备的若干Interface接口(包含了端点设置的信息)。依据这个信息,使用SetInterface设置接口,选择一个Interface ;使用OpenPipe打开管道,依据选择的Interface找到端点描述符,然后OpenPipe。 这个部分的代码在程序的SetStreamInterface函数中。
Windows CE.net下的实时传输 设置接口和打开管道完成之后,就可以进行实时传输了。 这个程序里面IssueIsochTransfer是来源于微软的一个例子程序,这个函数是仿照usbclient.cpp里面的IssueVendorTransfer写出来的。
Windows CE.net下的实时传输 和Linux下类似,首先要准备足够的缓冲,这里只做了每次传输961字节,一次IssueIsochTransfer传输10次这样的编写。因为实时传输的帧本身也包含丰富的信息和设置工作。这里先不做深入的讨论,只从OV511的角度来讲一些使用。 在程序的Ov51xReadOneFrame函数中,pDataBuff申请了9610字节的空间,dwFrameLen[10]为10次传输每次的长度,这里都设置为961。实测的时候并不是每次传输都能够有9610字节的。这个问题可以放到后面的应用程序中处理。
Windows CE.net下的实时传输 在IssueIsochTransfer中,pUsbFuncs->lpIssueIsochTransfer可能是同步或者异步的。 对于同步的情况,不用判断是否传输超时,直接用GetIsochResults来取得传输结果,然后统计传输的字节数等数据。 NotifyContext和NotifyRoutine指定的情况下,是异步的,在调用后会立即返回,我们要等待传输完成,即等待调用时传入的一个事件NotifyContext。这个时候,如果成功返回,那么就和同步类似处理;如果超时或者其他情况,则认为传输失败,数据是无效的。 详细的过程全部在IssueIsochTransfer函数中。
第五部分 视频数据的处理和显示 这个示范程序中,设置画面大小为320X240,使用接口7,输出格式为RAW-YUV420。后面的部分是参考一个VC+WinDriver下的程序得来。 这样,至少一次传输240250个字节,便于在里面查找帧开始标记。应用程序通过DeviceIoControl读取视频数据,在读取的时候,驱动程序循环运行25次GetFrame,得到足够的数据,并复制到应用程序的缓冲中。 再进行RAW-YUV420->标准YUV420->RGB,就可以显示了。 全部代码都在测试应用程序TestOv511中,为了调试快速起见用MFC写的。
需要继续完善 前面的工作,我只是使摄像头工作并且取得了数据,但是时间仓促,有很多细节还未完善。例如驱动的事件和临界区还没有回收,以及停止设备代码等等都有相关问题,后面的问题,将在以后的时间陆续细致解决。 这个PPT主要是作为一个开发的笔记,给准备做这个以及做这个遇到问题的同行一点参考,本人才疏学浅,难免有错漏之处,还望不吝赐教,批评指正! 欢迎大家和我交流! E-mail:ghsnc@163.com
参考资料 这个工作花了我将近一个星期的空闲时间,如果没有丰富的参考资料和周围多个高手的帮助,是不可能完成的。在此对提供资料和帮助我的人表示深深的感谢! 我也提供自己的程序全部代码,希望能够发挥到一些参考作用! 参考资料: 1. Microsoft Windows CE .NET MSDN 2. 《Linux设备驱动(第三版)》 3. Linux2.4.18内核源代码(Ov511.c Ov511.h) 4. Windows CE.net的Logitech摄像驱动代码(http://www.gotdotnet.com/workspaces/workspace.aspx?id=0eb87e35-13e4-4fa3-9fde-71e9136f47de) 5. 《初试驱动---OV511+摄像头驱动开发》(http://www.vckbase.com/code/listcode.asp?mclsid=13&sclsid=1311的解码部分) 6. OV511 Datasheet