摘 要: 提出了一种基于S3C2440 ARM处理器、USB摄像头硬件平台和嵌入式Linux操作系统的视频采集系统方案。研究了USB摄像头在Linux中的驱动开发,对系统总体结构、基于V4L2的视频采集以及视频动态显示应用程序等功能模块设计做了具体介绍。试验结果表明,本系统实现了在LCD显示器上动态显示USB摄像头采集的图像,且视频采集稳定,图像帧过渡平稳,提供了一种成本低、体积小、功耗低的图像采集方案,能够很好地应用在视频监控等系统中。
关键词: 视频采集;S3C2440;Linux;V4L2;Qtopia
随着嵌入式系统、网络和图像信息技术的渗透,以及对设备体积和无线的要求越来越多,以嵌入式体系为核心的图像采集系统已成为研究热点。其中,嵌入式系统具有功耗低、体积小和集成接口丰富等优点[1];嵌入式Linux系统具有源代码开放、内核稳定、可裁减性、支持硬件广泛及驱动丰富等特点,成为嵌入式系统领域不可或缺的操作系统之一;USB摄像头具有性能好、价格低、使用方便、易于集成到嵌入式系统等特点,在视频监控和网络视频会议等领域有广泛应用[2]。
本文以开发成本低、体积小、功耗低的嵌入式图像采集系统为目标,采用ARM9处理器S3C2440和USB接口的摄像头组成硬件体系,采用嵌入式Linux OS和Qtopia文件系统构建应用系统,通过编写USB摄像头驱动和Qt视频显示程序实现视频采集和视频显示程序,很好地完成了图像采集的功能。由于本系统成本低、扩展简单、体积小,能够很好地扩展到和视频相关的很多领域[3]。
1 系统硬件设计
系统硬件总体结构如图1所示,嵌入式处理器采用三星公司的基于ARM920T内核的16/32位RISC嵌入式微处理器S3C2440,其主频高达400 MHz,内置丰富的外设资源,包括存储器、LCD控制器、串口、I2C、I2S和USB等接口控制电路,主要面向高性价比、低功耗的应用,适合于图像和视频处理。Flash存储器采用64 MB NAND存储器用于存放Linux操作系统启动引导程序Bootloader、Linux系统内核、Qt文件系统及系统掉电后要保存的数据。SDRAM中用于存放Linux系统运行时的一些临时数据。LCD显示器大小为4.3英寸,480×272分辨率,并带有触摸屏[4]。
目前用于摄像头的控制芯片组主要有中芯微公司的ZC0301P和美国OV公司的OV511+芯片。国内市场上的USB摄像头基本上采用的是ZC0301P,并且中芯微公司的主流控制芯片都带有硬件JPEG编码模块,利用这个特性可以直接从摄像头得到经过压缩的图片格式,可以简化应用软件的设计,因此本设计采用ZC0301P芯片的130万像素的USB摄像头。
在硬件系统中,通过USB接口将摄像头与S3C2440相接。利用USB接口进行通信只需要在Linux内核中增加USB驱动支持和加载ZC0301P硬件驱动模块。这样的驱动设计充分地利用了Linux模块化的设计特点,同时也有利于软件的设计和调试。
2 嵌入式Linux系统设计
2.1 宿主机开发环境的建立
通常嵌入式系统的软件编译和运行是在两个不同平台上进行的,编译是在宿主机上,通常是PC;运行是在目标板上,即S3C2440平台。一般是在宿主机上通过跨平台交叉编译器把源文件编译成可在目标平台上执行的文件,再通过串口等方式下载到目标平台上的NAND或NOR存储器上,然后由目标机来运行此软件。本系统采用的交叉编译器为arm-linux-gcc-4.3.3。
在宿主机上首先要安装Linux操作系统,本文使用的是Fedora10。此外,在系统中建立了交叉编译环境。这样,嵌入式Linux的开发环境就搭建完成了[5]。
2.2 搭建嵌入式Linux系统平台
在目标平台上构建一个完整的嵌入式Linux系统一般需要Bootloader引导程序、Linux内核以及根文件系统3个内容。
2.2.1 Bootloader引导程序
Bootloader引导程序是嵌入式开发很重要的组成部分。Boofloader引导程序最基本的功能是对硬件系统的初始化和内核启动参数设置并启动内核。在嵌入式系统开发过程中,Bootloader还与主机通信,不断检测从主机传来的控制信息和数据信息,完成相应的操作。本系统中使用的是U-Boot引导程序。下载U-Boot源码后,在宿主机交叉编译器中编译出启动代码的可执行文件,下载到开发板中即可。
2.2.2 Linux操作系统的移植
下载linux-2.6.30内核,并解压Linux内核到linux-2.6.30目录。然后修改此目录下的Makefile文件,需改的主要内容是目标代码的类型和为编译内核指定一个编译器。注释掉以下内容:
#ARCH?=$(SUBARCH)
#CROSS-COMPILE?=
增加如下内容:
ARCH=arm
CROSS-COMPILE=arm-inux-
做完以上修改以后,内核编译以后就可在硬件板上运行了。
2.2.3 建立根文件系统
根文件系统一直以来是所有UNIX类操作系统的一个核心组成部分,在内核启动的最后阶段,所执行的操作之一就是挂载根文件系统。Linux的根文件系统包含内核所需的文件和可执行文件,还有用于系统管理的可执行文件。在网上下载YAFFS文件系统代码,并修改配置文件使之适用于本目标平台。配置Linux内核,使内核支持MTD(Memory Technology Devices)和YAFFS。重新编译内核并将内核下载到开发板的Flash中,然后把编译好的文件系统下载到开发板中。启动内核,就可以看到在LCD中显示Qt界面,这样一个完整的Linux系统平台就建立好了。
3 视频采集的实现
3.1 摄像头驱动及V4L2简介
在Linux下,设备驱动程序可以看成Linux内核与外部设备之间的接口。设备驱动程序向应用程序屏蔽硬件实现的细节,使得应用程序可以像操作普通文件一样来操作外部设备,可以使用和操作文件中相同的、标准的系统调用接口函数来完成对硬件设备的打开、关闭、读写和I/O控制操作,而驱动程序的主要任务也就是要实现这些系统调用函数。
对于USB口摄像头,其驱动程序中提供了基本的I/O操作接口函数open、read、write、close的实现以及对I/O通道的控制接口函数ioctl的实现等,并定义在struct file_operations中。
Video4Linux2(V4L2)是Linux中关于视频设备的内核驱动,它为针对视频设备的应用程序编程提供一系列接口函数,这些视频设备包括现今市场上流行的TV卡、视频捕捉卡和USB摄像头等。Linux内核提供Video4Linux2应用程序接口,在程序开发时首先是基于Video4Linux2 API函数来设计程序[6]。
3.2 Video4Linux2下的摄像头采集编程
在USB摄像头被驱动后,只需要再编写一个对视频采集的应用程序就可以了,根据嵌入式系统开发特征,先在宿主机上编写应用程序,再使用交叉编译器进行编译链接,生成在目标平台的可执行文件。宿主机与目标板通常采用打印终端的方式进行交叉调试,成功后移植到目标平台。本文编写的视频采集程序是在安装Linux操作系统的宿主机PC上进行的,下面是具体论述[7]。
程序中定义的重要数据结构为:
struct v4l2_capability cap;
struct v4l2_fmtdesc fmtdesc;
struct v4l2_format fmt;
struct v4l2_requestbuffers req;
struct v4l2_buffer buf;
这些数据结构都是Video4Linux2支持的,它们的用途如下。
(1)v4l2_capability包含摄像头的基本信息,例如设备名称、支持的最大及最小分辨率、信号源信息等,分别对应着结构体中成员变量name、maxwidth、maxheight、minwidth、minheight、channels、type等;
(2)struct v4l2_fmtdesc获取设备支持的视频格式;
(3)v4l2_format fmt设置视频捕获格式;
(4)v4l2_requestbuffers用于请求分配内存;
(5)v4l2_buffer代表驱动中的一帧。
Linux下视频图像采集流程如图2所示。
(1)打开视频设备,获得文件描述符
摄像头在系统中对应的设备文件为/dev/video0,采用系统调用函数video_fd=open(“/dev/video0”, O_RDWR,0),video_fd为设备打开后返回的文件描述符,以后的系统调用函数就可以使用它来对设备文件进行操作了。接着用ioctl(video_fd,VIDIOC_QUERYCAP,&cap)函数读取v4l2_capability中的有关摄像头的信息。该函数成功返回后,这些信息从内核空间拷贝到用户程序空间capability各成员分量中,使用printf函数就可以得到各成员分量信息。
(2)设置图片格式
将图片宽设为320,高设为240,其他参数保持默认。
struct v4l2_format fmt;
CLEAR(fmt);
fmt.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.width=320;
fmt.fmt.pix.height=240;
fmt.fmt.pix.field=V4L2_FIELD_INTERLACED;
fmt.fmt.pix.pixelformat=VIDEO_FORMAT;
if(ioctl(video_fd,VIDIOC_S_FMT,&fmt)<0)
{
exit(1);
}
(3)分配缓冲区
struct v4l2_requestbuffers req;
CLEAR(req);
req.count=4;
req.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory=V4L2_MEMORY_MMAP;
if(ioctl(video_fd,VIDIOC_REQBUFS,&req)<0)
{
exit(1);
}
然后通过调用ioctl(video_fd,VIDIOC_QUERYBUF,&buf)获得缓冲区的长度(buf.length)和偏移地址(fd,buf.m.offset),将这两个参量作为参数传给mmap函数:
buffers[num].start=mmap(NULL,buf.length,PROT_READ|PROT_WRITE,MAP_SHARED,fd,buf.m.offset);
缓冲区分配好之后,驱动并不会向里面写入数据,还需要将每个缓冲区放入视频采集队列:
ioctl(video_fd,VIDIOC_QBUF,&buf);
(4)启动采集过程,读取数据
完成以上初始化设置工作后,就可以对视频进行采集了,有直接读取read()和内存映射mmap()两种方法。read()通过内核缓冲区来读取数据;而mmap()通过把设备文件映射到内存中,绕过了内核缓冲区,最快的磁盘访问往往还是慢于最慢的内存访问,因此mmap()方式加速了I/O访问。另外,mmap()系统调用使得进程之间通过映射同一文件实现共享内存,各进程可以像访问普通内存一样对文件进行访问,访问时只需要使用指针而不用调用文件操作函数。因为mmap()的以上优点,所以在程序实现中采用内存映射方式。
应用程序调用VIDIOC_STREAMON来启动采集过程:
enum v4l2_buf_type type=
V4L2_BUF_TYPE_VIDEO_CAPTURE;
ioctl(fd,VIDIOC_STREAMON,&type);
采集过程开始以后,驱动会不停地将数据写入分配的缓冲区内,当一个缓冲区的数据准备就绪后,驱动就会将其放入输出队列,等待应用程序的处理。当所有的缓冲区都进入输出队列后,驱动将停止采集,并等待缓冲区重新放入采集队列。读取数据时,首先需要将一个缓冲区出队列:
struct v4l2_buffer buf;
ioctl(fd,VIDIOC_DQBUF,&buf);
驱动会从输出队列取出一个缓冲区,并将其序号赋值给buf.index,应用程序可以通过buffers[buf.index].start来访问缓冲区的数据。当处理完成后,需要将其重新放入采集队列:
ioctl(fd,VIDIOC_QBUF,&buf);
(5)停止采集
首先停止采集过程ioctl(fd,VIDIOC_STREAMOFF,&type),然后使用munmap函数删除映射,最后调用close(fd)函数关闭设备。
视频图像显示模块使用GUI软件提供的API函数的方法进行视频图像显示。视频显示程序基于Qt库来编写,原理是将buf的内容转为image再转为pixmap,然后显示出来。按照以上编写流程编写视频显示程序后,通过交叉编译器编译出目标平台能够执行的代码,下载到开发板中,即可实现视频的实时采集。
4 测试结果与分析
在目标平台上运行编写的视频采集程序,摄像头所采集到的实时动态图像的截图如图3所示。从图中可以看出,该系统所提供的视频画面图像质量较好,完全可以满足实际应用中对画面质量的要求。
采用内嵌ARM9核的S3C2440嵌入式微控制器和ZC0301P视频处理芯片,同时结合Linux的Video4Linux视频接口技术和视频流传输技术,实现了快速视频采集的应用[8]。实践表明,采用高性能的ARM9核的微控制器和嵌入式Linux操作系统,使视频采集系统的视频画面清晰流畅,其性能优越、稳定而且构建成本低。该方案可以广泛应用于视频通信和现场监测等智能控制领域,具有广阔的应用前景。
参考文献
[1] SAMSUNG ELECTRONICS. S3C2440 user′s manual(Revision 1.2)[Z], 2003.
[2] 杜春雷.ARM体系结构与编程[M].北京:清华大学出版社,2003.
[3] 孙天泽,袁文菊,张海峰.嵌入式设计及Linux驱动开发指南[M].北京:电子工业出版社,2005.
[4] RUBINI A. L INUX设备驱动程序[M].魏永明,译.北京:中国电力出版社,2002.
[5] 刘峥嵘,张智超,许振山.嵌入式Linux应用开发详解[M].北京:机械工业出版社,2004.
[6] COX A. Video 4 Linux Programming[EB/OL]. www.redhat. com.
[7] 王艳,吴旭光,赵勋峰.基于ARM的嵌入式手持扫描设备的系统设计[J].电子测量技术,2007(3):47-49.
[8] 贾明,严世贤.Linux下的C编程[M].北京:人民邮电出版社,2001.