首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

Linux系统下USB摄像头驱动开发 02

Linux系统下USB摄像头驱动开发 02

1.2 使用双URB轮流通信

众所周知,USB1.1总线标准定义了控制、中断、批量、等时等四种管道。对于时间性极强但是准确度要求不高的视频捕捉应用来说,摄像头应当使用等时传输方式。为了尽可能快地得到图像数据,应当在URB中指定USB_ISO_ASAP标志。

urb->transfer_flags = USB_ISO_ASAP; //尽可能快地发出本URB

Linux系统中任何USB传输都通过URB实现。为提高速度,可以考虑扩大URB的缓冲,这样可以降低每个USB事务中握手信息所占比例,提高有效数据的传输速度。但是受限于总线带宽和具体的USB设备芯片,单纯扩大URB的缓冲不能无限制地解决问题。具体分析一下USB传输在操作系统中的实现:每次传输都要包括URB的建立、发出、回收、数据整理等阶段,这些时间不产生有效数据。因此可以建立两个URB,在等待一个URB被回收时,也就是图像正在被传感器采集时,处理、初始化另一个URB,并在回收后立刻将其发出。两个URB交替使用,大大减少了额外时间。工作流程如图1所示。

这个过程是在URB的完成例程中实现的,有两点需要注意:首先处理再次初始化的代码时间不能长,否则会造成完成例程的重入,如果确实来不及,可以在完成例程中设定标志,例如“数据采集好”旗语,由应用程序使用阻塞ioctl()来查询该旗语并做处理;其次由于CPU可能会在完成例程中停留较长时间,系统负担较大,可以在 .open 函数中初始化两个URB并将其发出,有限度地减轻系统负担。

1.3 使用双帧缓冲提高效率

Linux系统中,文件操作通常是由read、write等系统调用来完成。这些系统调用在驱动中的解决方法就是用copy_to_user() 、copy_from_user() 等函数在核态、户态内存空间中互相拷贝。但是对于大批量的图像数据,采用拷贝的方法显然会增加时间开销,因此用内存映射的方法解决。首先使用vmalloc()申请足够大的核态内存,将其作为图像数据缓冲空间,两个URB带回的图像数据在这里暂存;然后使用remap_page_range()  函数将其逐页映射到用户空间中。户态的图像处理程序使用mmap() 函数,直接读写核态图像缓冲内存,大大减少额外开销。

图像数据的处理可能要花费比较长的时间,不同的算法对于数据保留时间的要求也不一样。因此可以申请两帧图像缓冲,在处理一帧图像的同时,将两个URB带回的数据全部填充到另一帧缓冲中,这样可以免去时间冲突上的麻烦。

值得注意的是:这种方法要求时刻持有当前帧的序号、每一帧的起始地址等信息,不能将两帧图像混淆。这些信息可以保存在保留内存中,当前帧的数据整理、序号改变在URB完成例程中实现。

2 V4L标准的改进

V4L标准目前已经发展到第二版V4L2,其基本思路与V4L相同。

2.1 标准分析

根据V4L标准,户态程序在需要一帧图像时,CPU的走向如图2。CPU按照123456的顺序完成一个循环。在这里,有一个细节被忽略:在完成例程中,也就是图2中步骤6,该URB被立刻发出,但是由于这时用户程序正在阻塞等待,没办法再次提出获得图像的申请,因此在判断有无新请求时,判断的结果必然是当前无请求,导致下一个URB带回的数据被驱动丢弃;由于核态、户态的切换需要一定的时间,加上户态多进程同步等开销,等到应用程序能够再次发出获得一副图像的申请时,已经有不止一个URB带回的数据被丢弃掉,这些URB包含的数据正好是新一帧图像的开始部分。因此驱动必须等到再下一帧图像才能保存数据、缓冲。这样凭白损失了一帧图像,帧速最少下降一半。



2.2 改进思路:不间断采集解决这个问题,可以改进V4L标准作,使其增加新的功能:通过新的参数,让ioctl()函数通知驱动不间断采集、缓冲图像数据,轮流保存在两帧缓冲区中,并在一帧图像采集好后,
返回列表