责任编辑:万翀Consumer ElectronicsLinux环境下的USB摄像头驱动开发USB Camera Driver Development in Linux 李茂杰 柳寅秋 王勇 电子科技大学移动计算中心(四川 成都611731)摘要:嵌入式环境下的USB摄像头驱动需要充分利用USB总线与内核中的USB核心密切配合并且保证数据流的高速与稳定。本文从Linux内核的USB核心模块出发,遵循Video4Linux接口标准,采用urb策略与内存映射的方式以提高数据读取速度,设计开发了基于Linux环境下的USB摄像头驱动,并在ARM9实验平台上对该驱动程序进行了测试与分析。关键词:嵌入式Linux;USB摄像头驱动;urb;内存映射机制DOI: 10.3969/j.issn.1005-5517.2012.3.012用户的操作通过标准化的调用执行,经设备驱动程序映射到实际的硬件设备上。由于在不同的环境下通常需要不同的方式来操作硬件,所以在编写访问硬件的设备驱动程序时,不能给用户强加任何特定的策略(策略,既是如何使用这些功能[1])。驱动程序应该处理如何使硬件工作的问题,而将怎样使用硬件的问题留给上层应用程序,即驱动程序的作用在于提供机制(机制,既是需要提供什么功能[1])。USB子系统分析一个USB子系统是由几个点对点的连接方式构建而成的树状结构,在该系统中USB只是担当设备和主控制器之间通信通道的角色,对它所发送或者接受的数据没有任何特殊的内容和结构上的要求。Linux内核支持两种主要类型的USB驱动程序:宿主系统上的驱动程序和设备上的驱动程序[2]。宿主系统上的USB驱动程序控制插入其中的USB设备,而设备上的驱动程序则控制该设备如何与主机通信。 Linux内核提供引言嵌入式图像处理的应用范围随着嵌入式技术的不断发展而不断扩大,摄像头作为数字图像采集的基础,驱动程序的开发显得尤为重要。作为操作系统内核与硬件设备之间的接口,驱动程序是否优良,直接影响着硬件设备能否正常、高效运转。带有USB(通用串行总线)接口的摄像头因其具有支持热插拔、接口通用性好、实时性强、较高的传输速率、低成本、易于扩展等优点而备受青睐。但是嵌入式系统中支持USB摄像头的驱动程序很少,因此,研究和开发嵌入式环境下的USB摄像头驱动程序具有应用价值和现实意义。USB摄像头驱动程序构架设备驱动程序是计算机软、硬件之间存在的悖论的产物:软件和硬件不应该互相渗透到对方的领域。因此,设备驱动程序是介于操作系统与硬件之间,将硬件设备的具体工作细节对操作系统内核完全屏蔽的接口。图1 USB驱动程序概观图2 USB设备概观www.eepw.com.cn2012.32012.1694630消费电子表1 USB端点的不同数据传输类型类型说明控制控制端点用以对USB设备不同部分的访问,通常用以配置设备、获取设备信息、发送命令到设备或者从设备接收数据。中断中断端点用于在USB宿主要求设备传输数据时,以固定速率传送少量的数据。批量批量端点用以传输大量数据。等时等时端点用以传输大量数据,但是数据是否到达不能保证。了USB核心模块,该模块将不同类型始化做准备;的USB主控制器完全屏蔽,而为上层2. Release:释放Open分配的内存的USB设备驱动程序提供了用于访问空间;和控制USB设备的接口(如图1)。因此3. Read:将摄像头获取的图像数USB核心模块将USB主控制器和USB据拷贝到应用程序空间;设备驱动程序隔开,也同时将USB驱4. Mmap:内存映射使得进程之动程序的编写与具体的硬件隔开。间通过映射同一个普通文件实现内存驱动模块构架共享;每个硬件设备在操作系统内核5. Ioctl:提供一种执行设备特定中都对应一个驱动模块。USB摄像头命令的方法。设备在内核中表现为一个字符设备,并且属于视频类的硬件设备。Linux关键技术研究内核为视频类设备提供了内核接口USB驱动程序与USB核心、用户Video4Linux,该内核接口使得Linux应用层结合紧密,需要开发者对Linux环境下的上层应用程序可以像访问普的内核有很好的掌握,理解驱动程序通文件一样对其进行读写操作。因与USB设备的关联方式、系统内核与此该驱动模块提供的机制应该符合USB设备的通信方式、内存的映射机Video4Linux接口:制以及由于USB热插拔而引出的驱动1. Open:提供给驱动程序以初始探测、断开机制的实现对USB设备驱化的能力,从而为以后的操作完成初动开发有很好的指导作用。驱动程序与USB设备的关联Linux操作系统内核提供了USB核心模块用于处理USB设备的复杂操作。在USB核心中将USB设备划分为配置、接口和端点三个层次接口(如图2)。 1. 端点:USB通信最基本的形式是通过端点完成。USB端点只能往一个方向传送数据,从主机到设备(输出端点)或者从设备到主机(输入端点)。端点可以图3 一个urb的生命周期看做是一个单向的管道。70472012.12012.3www.eepw.com.cn责任编辑:万翀2. 接口:USB接口只处理一种USB逻辑连接。一些USB设备具有多个接口,一个USB接口代表了一个基本功能,而每个USB驱动程序控制一个接口。3. 配置:一个USB设备可以有多个配置,而且可以在配置之间切换以改变设备的状态。综上所述,端点是USB通信的最小单位,多个端点绑定成为一个接口,驱动程序是和接口绑定在一起的,一个接口对应一个驱动,在一个USB设备中可以有多个接口,既是可以有多个驱动与之绑定,多个接口对应于一个配置,用于调整USB设备的状态。系统内核与USB设备通信在Linux系统中,内核是通过urb(USB请求块)完成与USB设备的通信功能的。Urb被用来以一种异步的方式往/从特定的USB设备上的特定USB端点发送/接收数据。所以,urb是在Linux内核与USB设备之间的信息承载体,USB设备驱动程序通过该结构向USB设备发送操作信息或者从USB设备处接收硬件状态信息反馈。在Linux USB设备驱动中,可以为单个端点分配多个urb,也可以根据实际需求让多个不同端点重用单个urb。图3为一个urb数据模块的生命周期。1. 创建urb:为了遵循USB核心对urb使用的计数机制,urb结构体不能在驱动程序中或者另一个结构体中静态地创建。2. 初始化urb:由于urb结构是最终制定给特定的USB端点,通过端点进行数据通行的,因此需要根据端点的不同类型对urb结构进行初始化。责任编辑:万翀Consumer Electronics当USB设备接入宿主机并且USB解码帧数88164239334帧数(f/s)12101111表2 帧数测试数据测试时间(ms)7264164232175430418程序不再控制该设备,此时断开函数将被调用,该函数完成后期清理工作以防止USB核心对驱动程序进行不适当的数据访问。在断开函数中需要防止open函数与断开函数之间可能出现的并发竞争状态,使用锁机制可以很好的避免这一点[4]。static void skel_disconnect(struct usb_interface *interface){struct usb_skel *dev;int minor = interface->minor;lock_kernel();dev = usb_get_intfdata(interface);usb_set_intfdata(interface, NULL);usb_deregister_dev(interface, &skel_class);unlock_kernel();kref_put(&dev->kref, skel_delete);}内存映射USB摄像头驱动使用Video4Linux接口获取图像信息的方法有两种:直接读取图像数据和使用内存映射方法获取图像信息。本文采用后一种方式获取图像信息。使用内存映射,使得进程之间通过映射同一个内存数据空间,实现内存的共享,让进程可以像访问普通文件一样对硬件设备获取的数据进行访问[5]。该方法很好地提高了数据的访问速度,并且由于不使用read系统调用,大大降低了系统开销。通过以下主要代码实现内存的分配:…addr = (unsigned long) frame->data;while (size > 0) {page = vmalloc 下转54核心判定该USB设备驱动应该处理时,探测函数将被调用。在探测函数中,USB摄像头驱动程序完成初始化可用于控制USB设备的局部数据结构体,将所需要的与设备有关的信息保存到局部结构体中。探测函数还应该锁定摄像头的端点地址以及缓冲区大小,为操作系统与摄像头进行通信做好准备。在摄像头驱动的开发中,使用以下代码完成探测函数的主要工作:if (!dev->bulk_in_endpointAddr && (endpoint->bEndpointAddress & USB_DIR_IN) &&((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK)) {buffer_size = endpoint->wMaxPacketSize;dev->bulk_in_size = buer_size;dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;dev->bulk_in_buffer = kmalloc(buer_size, GFP_KERNEL);if (!dev->bulk_in_buer) {err(\"Could not allocate bulk_in_buer\");goto error;}}当把USB摄像头从宿主机上拔下时候,驱动在表1中列出了USB端点的四中不同类型的数据传送方式。对于USB摄像头,因其为实时数据收集设备,注重于保持一个恒定的数据流,可以容忍数据丢失的情况,所以该类设备选用等时模式进行数据传输:urb->pipe = usb_rcvisocpipe(gspca_dev->dev, ep->desc.bEndpointAddress);3. 提交urb:USB驱动程序在成功创建urb之后,就可以提交到USB核心以发送到USB设备:int usb_submit_urb(struct urb* urb,int mem_ags);4. 销毁urb:当urb结束之后,驱动程序将销毁该urb,此后该urb结构将不会被驱动程序访问:void usb_free_urb(struct urb* urb); 至此,通过urb数据请求模块完成了操作系统内核与USB设备(USB摄像头)的数据通信。当urb结束的时候,驱动程序将调用urb的结束处理例程结束USB核心对urb的处理,将对它的控制权返还给设备驱动程序。探测和断开实现细节图4 驱动程序测试平台www.eepw.com.cn2012.32012.1714830责任编辑:万翀Power Management结语随着DSP和FPGA技术的发展,开关电源的控制部分正在向数字化、智图9 数字电源与模拟电源稳定性曲线图一样,只是PID参数不一致,这个参数一般都凭经验在实际的调试过程中得出。对整个电源系统来说,控制部分是决定稳定性的一方面。功率部分还起着关键作用,比如供电电平扰动很大,那么我们可能采用恒压源+桥式电路,在控制回路前再加一级稳压源,也可能加大滤波电路等方式来处理。总而言之,要解决这些问题,基本上不是PID算法来解决,而是功率回路的设计来解决,核心还是在功率回路。因为其不在本文的重点讨论范围内,故不作深究。参考文献:[1] 刘和平,等.DSP原理及电机控制应用[M].北京:北京航空航天大学出版社,2006[2] 褚振勇,齐亮,等.FPGA设计及应用[M].西安:西安电子科技大学出版社,2006[3] 陈施华.PID控制器参数的自动整定[J].雷达与对抗,2005(3)[4] 李夕红.基于DSP和FPGA的数字化开关电源的实用化研究[D].全国优秀硕士论文2007-10[5] 刘勇,祝忠明,等.数字电源中基于FPGA+DSP的高精度数据采集系统设计[J].电子元器件应用,2008[6] Altera EP1C3T144C8N Datasheet[DB].Altera,2002只需要改变设定的参考电压就可以实现不同的电压的输出,从而增加了电源的灵活性。(2)电压稳定度测试-电阻负载在常规条件下,对数字电源和模拟电源用6位半带存储功能的数字表做稳定性测试。测试时间10小时,每1秒取一个采样点。将采样值转化为曲线。如图9所示。 图9中蓝色为模拟电源曲线,绿色曲线为数字电源曲线。从图9中可以看出,数字电源稳定性比较好,没有出现尖峰,模拟电源经常出现尖峰。从这点表明数字电源具有很高的稳定性。在实际应用中,对于常见的供电电平的扰动和阻性负载、感性负载或容性负载变化的问题,要达到系统的稳定性,对于PID算法本身来说都能化、零件的共通化方向发展。依靠现代数字化控制和数字信号处理新技术,数字化开关电源有着广阔的发展前景。本文从电源的精度和稳定度出发,提出了基于PID算法的数字化电源设计,从测试结果可以看出数字化电源比传统的模拟电源具有更高的精度和稳定度,更重要的是采用了PID算法以后,使得数字化电源变得更加灵活通用。上接48((void *) addr);…ARM9芯片、工作频率为400MHz,64M的SDRAM,3.5英寸LCD屏幕以及USB接口构成,能够满足USB摄像头驱动测试的硬件要求。 驱动程序在宿主机接上USB摄像头后,能够顺利完成初始化以及探测功能,运行用户应用软件后能够顺利获取图像,长时间工作性能稳定。在不同的时间间隔下测试摄像头的帧数情况,获取数据见表2。由表中数据可以看出,平均帧数在11左右,能够满足视频设备采集图像的基本需求。与USB设备之间的通信方式、USB摄像头的探测以及断开函数的设计技术以及内存映射机制,开发出了符合Video4Linux内核接口标准的设备驱动程序,通过测试数据可以看出该驱动程序能够满足嵌入式Linux环境下的视频图像采集要求。本文介绍的驱动程序的开发技术对于嵌入式Linux环境下的USB设备驱动开发有一定的借鉴意义。参考文献:[1] 魏永明,耿岳,钟书毅.Linux设备驱动程序[M].3版.北京:中国电力出版社,2005:10[2] 宋宝华.Linux设备驱动开发详解[M].北京:人民邮电出版社,2008:563[3] 李俊.嵌入式Linux设备驱动程序开发详解[M].北京:人民邮电出版社,2008:314[4] 陈莉君,康华,张波.Linux内核设计与实现[M].北京:机械工业出版社,2009.100[5] 胡希明,毛德操.Linux内核源代码情景分析[M].杭州:浙江大学出版社,2001:802[6] 陈葆珏,王旭,柳纯录,冯雪山.UNIX操作系统设计[M].北京:机械工业出版社,2007:216}…vma->vm_ops = &gspca_vm_ops;vma->vm_private_data = frame;gspca_vm_open(vma);…在代码中使用vmalloc内核函数分配内存,该函数分配的虚拟地址空间在物理上可能不是连续的,但是内核却认为它们在地址上是连续的[6]。该函数是Linux内存分配机制的基础,在如何使用硬件上没有区别。结语测试与性能分析嵌入式测试平台如图4,主要由本文介绍了驱动程序与USB设备之间的关联,详细说明了系统内核www.eepw.com.cn2012.3775430