引言
嵌入式系统由于其体积小可便携,较 PC机而言很有优势。随着计算机技术的发展,一些基于PC机的应用也得以在嵌入式系统上实现。由于 USB 接口摄像头价格低廉,性能较好,即插即用,加上Linux下有Video4Linux标准支持对其编程,很容易集成到嵌入式系统,因此嵌入式系统视频采集设备通常用 USB 接口摄像头。
1 芯片介绍
S3C2440 处理器采用ARM920t内核,0.13um的CMOS 标准宏单元和存储器单元,支持高速总线和异步总线模式;具有 1G字节寻址空间;支持外部等待信号延长总线周期;支持掉电时SDRAM 自刷新模式;支持从 NAND flash存储器启动;采用 4KB 内部缓冲器进行启动引导。采用写穿式(write-through)或写回式(write-back)cache 操作来更新主存储器;写缓冲器可以保存 16个字的数据和 4 个地址[1]。
OV511 是高性能摄像机到 USB 接口单片控制芯片,它极大的简化了单片 CMOS 图像传感器和USB 的接口,其片上增加256K DRAM 和一个 USB 收发器,可以很容易构成基于USB 的视频子系统。OV511 最大视频传输设计使系统能够以更加实时的方式获取大量的视频信息[2]。
OV7650是高集成度高分辨率的 COMS 图像传感器,它将所有摄像功能和矩阵处理功能都集成在片上。其像矩阵位 640*480 像素(30 万像素),支持VGA,QVGA ,CIF,QCIF四种分辨率,可进行编程控制[3]。
2 摄像头硬件
视频采集部分由 OV511 和OV7650组成。通过 SCCB 总线初始化OV7650和OV511 ,将OV7650设置为 CIF 采集,输出YUV422 数据流;将 OV511 配置为YUV422 格式输入,输出为YUV420 数据流。OV511 提供OV7650所需的控制信号,并接受来自 OV7650的同
步输出信号并将输入图像数据通过内置的 USB 控制器和外接的 USB 收发器经USB 总线送入ARM,然后进行压缩编码、发送等处理,其硬件框图见图 1。
3 USB 摄像头驱动
设备驱动程序可以看成 Linux内核与外部设备之间的接口。设备驱动程序向应用程序屏蔽了硬件实现了的细节,使得应用程序可以像操作普通文件一样来操作外部设备,可以使用和操作文件中相同的、标准的系统调用接口函数来完成对硬件设备的打开、关闭、读写和 I/O控制操作,而驱动程序的主要任务也就是要实现这些系统调用函数[4]。
Linux 视频采集设备的正常运行依赖于对 Video4Linux标准的支持。Video4Linux设备的驱动程序需要提供基本的 I/O 操作接口函数 open ,read ,write和对中断处理的实现、内存映射功能以及对I/O 通道的控制接口函数 ioctl 的实现等,并把它们定义在 struct video_device中。所以首先在驱动程序中声明一个 video_device 结构,并为其指定文件操作函数指针数组fops 向系统注册。应用程序发出文件操作的相关命令时,Linux 内核根据这些指针调用相应函数,并将该结构作为参数传递给它们,实现驱动与内核之间的通信。
Linux 内核是依据设备号来操作设备文件的,内核中摄像头对应的设备文件名为/dev/video,主设备号是81,次设备号根据摄像头数目来确定,本系统中仅使用一个摄像头。因此没有此设备号,所以可以通过mknod /dev/video0 c 81 0来创建节点,驱动程序原理如图2。
Linux系统通过URB实现USB 传输。为提高有效数据的传输速度可扩大 URB的缓冲来降低每个USB 事务中握手信息所占比例。每次 USB 传输都需要在操作系统中进行URB的建立、发出、回收和数据整理等阶段。可建立两个URB,当等待一个 URB被回收时,也就是图像正在被传感器采集时同时处理、初始化另一个 URB,并在回收后立刻将其发出。两个URB交替使用,大大提高了系统处理的时间效率。
在编译器部分将Linux下的摄像头驱动程序中Makefile 文件中相关行修改为CC=/opt/host/armv4l/bin/armv4l-unknown-gcc-linux,修改 Config.in 文件,这样在配置内核的时候才可以看见驱动程序的名称。同时对下列与处理器相关的部分进行修改,即可实现对S3C2440 的USB 驱动的移植。
(1)PCI 接口处理
由于S3C2440 的USB 主机控制器不包含 PCI 接口,因此需要删除 usb-ochi.c中与PCI接口相关的代码。
(2)寄存器地址设置
在usb-ochi.c中,使S3C2440 的USB 主机控制器寄存器的起始地址(0x49000000) 初始化ochi->regs。
(3)主机控制器中断设置
在usb-ochi.c中,使S3C2440 的USB 主机控制器寄存器的中断向量初始化 ochi->irq 。
(4)根HUB端口数目设置
在usb-ochi.c 中,定义根HUB的下行端口数目为 2(#define MAX_ROOT_PORTS
2),MAX_ ROOT_PORTS 的默认值为150。
(5)修改 Makefile和Config.in 文件
修改完成后执行 make命令,即可生成所需要的带有.o 后缀的驱动文件。
驱动程序设计完成并编译成功后,使用动态加载的方法添加到内核中。首先在宿主机上交叉编译好驱动模块,然后通过串口下载到开发板上,再使用 insmod 命令将驱动挂载,摄像头驱动便可成功添加,通过 lsmod 命令可以查看当前驭动添加的情况。
4 视频采集
系统软件基于VFL 开发,基本流程原理如图3。其中最关键的步骤就是视频数据的采集,一般有两种实现方法,一是直接读取,二是内存映射的方法。
1) 定义数据结构
程序中需要定义一些数据结构,如:video_capability ,包含摄像头的基本信息;video_picture,包含设备采集图像的各种属性;video_mmap,用于内存映射;video_mbuf ,利用mmap 进行映射的帧信息,实际上是输入到摄像头存储器缓冲中的帧信息;video_Window,包括设备采集窗口的各种参数。
Linux系统中把设备看成设备文件,在用户空间可以通过标准的 I/O 系统调用函数操作设备文件,从而达到与设备通信交互的目的。用 ioctl 函数来控制1/O 通道。
2) 采集程序实现过程
1. 打开视频设备
在linux中视频设备对应的设备文件为/dev/video0 ,采用open 函数来打开视频设备。
2. 获取设备信息和视频信息并进行设置
开启设备文件后,通过调用 camera_get_capability() 和camera_get_picture() 函数来实现对设备信息以及图像信息的获取。这两个函数均通过调用ioctl() 函数来取得设备和图像的相关信息,并将取得的信息放到 video_capability 结构里。若需对图像信息进行设置时,先给video_picture 数据结构对象中所要修改的变量进行重新赋值,然后通过 ioctl 函数的VIDIOCGPICT来进行设置。通过调用 ioctl VIDIOCGPICT可设置所采集图像的属性。
3. 设置窗口的高度和宽度
编码器输入的是 CIF 格式的YUV420 码流,故将采集窗口的高度设置为 288,宽度为352。
4. 获取视频帧
使用mmap()(内存映射) 方式截取视频,mmap()系统调用使得进程之间通过映射同一个普通文件实现共享内存。[5]
主要部分介绍如下:
a. 初始化及设置
使用ioctl(camera_fd,VIDIOCGMBUF,&camera_mbuf) 函数初始video_mbuf,获得摄像头存储缓冲区的帧信息,之后修改 video_mmap和帧状态的设置。
b. 实现摄像头设备文件到内存区的映射
调用buf=void *mmap(void *addr,size_t len,int prot,int flags,int fd,off_t offset) 函数,将设备文件的内容映射到内存区。
c. 数据采集
调用ioctl(fd,VIDIOCMCAPTURE,&camera_buf)截取图像,失败将返回-1 ,若函数成功调用,便开始一帧图像数据的截取,并将当前帧号按缓冲区总帧数的模加上1,为下一帧截取作准备。然后调用 ioctl(fd,VIDIOCSYNC,&frame) 函数,成功返回则表示图像截取已完成,可以开始作下一帧图像的采集。图像捕捉函数 v41_frame_grab()是mmap内存映射方式捕捉视频数据的具体实现,每次采集一帧YUV420P格式的原始图像数据。在使用双缓冲区轮换采集时,对于每个缓冲区进行连续帧采集,通过外加循环控制对摄像头帧缓冲区采集的次数来实现,以达到提高效率的目的[6]。
在此基础上也可实现连续帧的采集,Video4Linux最多支持一次采集32帧,首先需要设置采集的帧数 camera_buf.frame,并将data+camera_mbuf.offsets[frame] 定义每一帧数据在内存中的起始位置,利用 ioctl(fd,VIDIOCGMBUF,&camera_mbuf) 便可获得camera_mbuf 的信息。除此之外还要设置数据缓冲区的大小,然后利用 ioctl VIDIOCMCAPTURE 操作进行数据的连续采集,直到缓冲区中的剩余空间无法保存一个完整的数据帧。当缓冲区中没有可利用的空间时,系统调用 ioctl VIDIOCSYNC 来检查视频采集过程是否完成。若完成时,应用程序为数据帧分配地址,使缓冲区的数据帧可被安全用于其他进程。
4. 关闭视频设备
在采集完成后,需要关闭设备,并收回系统资源。如果是采用内存映射方法进行视频采集,在系统任务完成后必须用munmap 函数关闭映射内存,close函数可关闭视频设备文件。
5 视频采集系统的多线程设计
在采集和处理模块的设计中创建图像采集和图像处理两个线程,并开辟两个缓冲区轮换采集图像帧,以便解决视频采集模块与编码模块的同步。在采集程序写满缓冲区1 后,改变线程等待条件,释放被阻塞的图像处理线程对该缓冲区数据进行编码输出。同时采集线程转到缓冲区2,若此时图像处理线程已完成对缓冲区 2 的处理,则将采集获得的帧图像覆盖,保存至缓冲区 2,否则阻塞。两个缓冲轮流使用,不丢弃任何帧,并且图像采集与处理同步进行,提高了效率。
6 结论
本文给出了远程监控系统中视频采集技术的分析和研究并得到实验结果。S3C2440 处理器USB Host控制器兼容USB1.1标准,支持低速 1.5Mbps 和全速12Mbps USB 设备。实验表明,视频采集程序对 CIF\QVGA 格式的图像采集效率最高,采集速率分别达到 9fps ,12fps以上,接近全速模式下的极限速率。对 QCIF格式的图像采集效率较低,距离 USB1.1全速传输的理论值相差较远,这既与摄像头的硬件特性有关(包括图像传感器的特性以及 DSP 桥接芯片对图像格式的处理) ,也与驱动程序的实现有关。不过单从采集帧速率上看,9fps 的CIF 采集速度和24fps的QCIF采集速度己经可以满足一般嵌入式实时应用的要求。