《通信原理》课程设计说明书
视频监控系统设计
学 院: 电气与信息工程学院 学生姓名: 冼达康 指导教师: 张峰 职称: 助教 专 业: 通信工程 班 级: 通信1302班 学 号: 1330440216 完成时间: 2016年12月
湖南工学院《通信原理》课程设计课题任务书
学院:电气与信息工程学院 专业:通信工程
指导教师 课题名称 一、设计任务 在GEC210开发板上接入摄像头,实时获取并显示视频流、截图拍照并保存。 二、设计内容 根据控制要求,明确设计任务,拟定设计方案与进度计划,运用所学的理论知识,进行视频监控系统原理设计、程序框架设计、创新设计,提高理论知识、系统调试能力、分析问题与解决问题的能力。主要内容包括: 1.嵌入式交叉开发环境搭建。 2.Linux操作系统移植。 3.根据需求确定外围模块设计并完成相应驱动的开发。 4.应用程序原理及框架设计。 5.Linux下应用程序编写及移植。 内内容及任务 6.系统总体功能调试。 7.编写设计说明书。 张峰 学生姓名 视频监控系统设计 冼达康 三、要求 1.为GEC210开发板接入摄像头并完成V4L2驱动框架移植。 2.编写应用程序打开摄像头获取实时视频流。 3.完成可以保存任意一张图片到本地目录的功能。 4.实现效果如下图所示:
I
[1]马忠梅,ARM & Linux嵌入式系统教程(第3版)[M]。北京:北京航空航天大学出版社,2014 主。北京:电子工业出版要[2]曹忠明,程姚根。从实践中学嵌入式Linux操作系统[M]参社,2012 考[3]宋宝华,Linux设备驱动开发详解(第二版)[M]。北京:人民邮电出版社,2010。 资[4]韦东山,嵌入式Linux应用开发完全手册[M]。北京:人民邮电出版社,2008 料 [5] 陆文周,Qt5开发及实例[M]。北京:电子工业出版社,2015 [6] Stephen Prata,C Primer Plus[M]。北京:人民邮电出版社,2005 教研室 意 见 教研室主任: 年 月 日
II
摘 要
随着网络通信技术和嵌入式技术的发展,基于嵌入式Web服务器的视频监控系统已成为监控领域的发展趋势。
本设计通过搭建嵌入式环境,编写程序并将程序导入开发板中并实现视频监控的功能。设计中首先对视频监控系统现阶段的发展及课题的意义进行了概述,其次对GEC210开发板进行了简介并介绍QT平台的搭建步骤。然后分别介绍了在设计中用到的三个主要技术,最后把程序导入开发板,实现了视频监控的功能。
关键词:嵌入式;GEC210;QT
III
目录
1 绪论 ................................................................... 1
1.1 课题的研究意义 ................................................... 1 1.2 课题的国内发展状况 ............................................... 1
1.2.1 模拟视频监控系统 ........................................... 1 1.2.2 本地数字视额监控系统 ....................................... 1 1.2.3 远程网络视频监控系统 ....................................... 2 1.3 课题的研究内容 ................................................... 2 2 GEC210的简介及QT平台的搭建 ........................................... 3
2.1 GEC210的简介 .................................................... 3 2.2 QT平台的搭建 .................................................... 4 3 设计的主要技术 ......................................................... 7
3.1 FrameBuffer ...................................................... 7 3.2 YUYV转换RGB ..................................................... 8 3.3 video4linux ...................................................... 9 4 功能实现测试 .......................................................... 15 结束语 ................................................................... 17 参考文献 ................................................................. 18 致 谢 ................................................................... 19 附录A 程序代码 ......................................................... 20
IV
1 绪论
1.1 课题的研究意义
随着网络通信技术和嵌入式技术的发展,基于嵌入式Web服务器的视频监 控系统已成为监控领域的发展趋势,采用B/S架构的嵌入式Web服务器,监控者无需到现场操作,系统成本低廉,设备体积小巧,安装方便,故障维护升级简单,安全可靠,满足了现代消费者的需求。
视频监控系统的产生和发展有两个方面的原因:一方面,随着人们安全防范观念的加强,人们迫切希望通过监控的方式来预防和减少公共场所的破坏及犯罪活动。另一方面,视频技术和网络通信技术日益成熟,为它的实现和应用奠定了基础;而计算机技术和多媒体技术的迅速发展,将自动控制和多媒体技术融入到视频监控系统中来,促进了它的普及和应用。
网络通信和微电子技术的快速发展和人民物质生活水平的提高,使得人们的生产行为,生活方式发生了重大的变化,视频监控以其直观,方面和内容丰富等特点日益受到人们的重视。安防监控从银行到交通路口,从连锁超市到小区范围,都会看到监控产品的存在,大量犯罪案件在安防监控产品的帮助下破获。人们意识到视频监控对生产生活的安全非常重要,而网络视频监控凭借其先进的技术和贴近用户习惯的使用方式,逐步走进人们的生活当中。视频监控系统在保护安全,预防犯罪方面起到不可低估的作用,监控市场领域不断扩大。据权威部门统计,1998年全国图像监控系统的市场总额为650亿元,今年来一直保持1﹪至30﹪的速度快速增长。随着摄像设备价格逐渐下降,计算机计算能力增强和网络速度的提高,视频监控技术得到了快速的发展。
1.2 课题的国内发展状况
1.2.1 模拟视频监控系统
采用模拟方式传输,最简单的就是将图像信号直接送入视频电缆进行传输。每一监控点的视频信号都通过一条电缆线连接到中心控制室的多路切换器上,以手动切换方式选择监控点图像,使之显示在监视器上。模拟视频监控有以下弊端:运行及维护成本高;视频图像的质量不能在任意时刻调整;无法联网,只能以点对点的方式监控现场,使得布线工程量极大;检索困难;难以做到无人值守。 1.2.2 本地数字视额监控系统
进入20世纪80年代,随着数字化技术的发展,人们从过去的模拟处理方式转向数字处理方式,开始使用计算机处理图像信号。利用计算机的数据处理能力与显示器的高清晰度,将视频信号通过视频采集卡采集到计算机中,显示在显示器上,大大提高了图
1
像的画质,但是控制与切换仍采用传统的单片机方式,只是计算机串口与之相连,完成控制工作。尽管它有良好的人机界面,较好的稳定性,但它不能称为真正的数字图像监控系统。其根本原因在于系统设计的出发点不是基于计算机,而是基于传统模式。大部分时间里,计算机只是充当一个外部监视器的角色,并且传统单片机系统固有的弊端并没有克服,通信协议的多样化与专用化很难统一,导致现有的计算机资源远远满足不了多种设备的要求。另外计算机的运行速度较低,而数字视频的数据量又很大,这样就限制了利用一台计算机同时处理更多数字视频信号的能力。很难组建大型监控系统。 1.2.3 远程网络视频监控系统
第三代视频监控系统——远程网络视频监控系统。远程网络视频监控系统的关键设备是网络视频信号采集终端(也被称为视频服务器),网络视频信号采集终端采用嵌入式实时多任务操作系统。摄像机送来的视频信号在网络视频信号采集终端数字化后由高效压缩芯片压缩,通过内部总线送到网络接口发送到网络上,网络上用户可以直接用在PC机上用浏览器观看网络视频信号采集终端传送过来的摄像机所拍摄的图像,授权用户还可以通过计算机网络透过网络视频信号采集终端控制摄像机镜头和云台的动作或对系统进行配置操作。由于把视频压缩和网络功能集中到一个体积很小的设备内,可以直接连入局域网,达到即插即用,省掉多种复杂的电缆,安装方便,仅需设置一个IP地址,用户也无需安装任何硬件设备,仅通过PC机用浏览器即可观看。数字网络视频监控系统与其它监控系统的比较:布控区域广阔。数字网络视频监控系统的网络视频信号采集终端直接连入网络,没有线缆长度和信号衰减的限制,同时网络是没有距离概念的,彻底抛弃了地域的概念,扩展布控区域;系统具有几乎无限的无缝扩展能力。所有设备都以IP地址进行标识,增加设备只是意味着IP地址的扩充;可组成非常复杂的监控网络。采用基于网络视频信号采集终端为核心的系统,在组网方式上与以往的监控方式有极大的不同,由于视频信号采集终端输出已完成模拟到数字的转换并压缩,采用统一的协议在网络上传输,支持跨网关、跨路由器的远程视频传输;性能稳定可靠,无需专人管理。视频信号采集终端实际上基于嵌入式电脑技术,采用嵌入式实时多任务操作系统,又由于视频压缩和网络功能集中到一个体积很小的设备内,直接连入局域网或广域网,即插即看,系统的实时性、可靠性大大提高,也无需专人管理,非常适合无人值守的环境;当需要同时观看多路监控视频时,对网络带宽就会有一定的要求。
1.3 课题的研究内容
本文研究的主要内容,通过QT软件,编写程序并导入GEC210嵌入式开发板中去,开发板外接摄像头,从而实现视频实时监控的功能
2
2 GEC210的简介及QT平台的搭建
2.1 GEC210的简介
GEC210是一款高性能的Cortex A8核心板,它由广州粤嵌股份设计、生产和发行销售。它采用三星S5PV210作为主处理器,运行主频可高1GHz。S5PV210内部集成了 PowerVR SGX540高性能图形引擎,支持3D图形流畅运行,并可流畅播放1080P大尺寸视频。
GEC210主要采用了2.0mm间距的双排针,引出CPU大部分常用功能引脚,,另外还根据S5PV210芯片的特性,分别引出了标准的miniHDMI接口,如图所示。
GEC210板载512M DDR2内存,可流畅运行Android, Linux和WinCE6等高级操作系统。它非常适合开发高端物联网终端,广告多媒体终端,智能家居,高端监控系统,游戏机控制板等设备。
粤嵌GEC210+7寸触屏Cortex-A8 S5PV210开发板的功能特性 集成USB 2.0 Hub: 扩展出2个USB Host口
串口: 4路,其中1路已经转换为RS232,并通过DB9座引出(COM0),同时通过4pin的排针将其他3路引出
中断式按键: 8个,分别定义不同的功能
RJ45网口: 网卡采用DM9000AEP,10/100M自适应
音频输入与输出: 采用WM8960专业音频芯片,在板麦克风,内含Class D功放,并支持HDMI音频和视频同步输出
USB Slave: 1路,符合USB 2.0规范 红外接收头:1个
ADC输入:其中AIN0连接了在板可掉电阻,用于测试 蜂鸣器:1路,由PWM0控制
EEPROM: AT24C08(256 Byte),用于I2C测试
LCD接口:2个LCD接口,均为40 Pin 0.5mm间距贴片座, SDIO接口:用于连接SD WiFi模块等 CMOS Camera: 用于连接CMOS摄像头 RTC时钟:带备份电池
PCB层数:2 PCB尺寸:180 x 130mm,可方便固定各种LCD模块,尤其是7寸LCD模块
供电:DC-5V
3
2.2 QT平台的搭建
Qt 是一个完整的 C++应用程序开发框架。它包含一个类库,和用于跨平台开发及国际化的工具。Qt API 在所有支持的平台上都是相同的,Qt 工具在这些平台上的使用方式也一致, 因而 Qt 应用的开发和部署与平台无关。
Qtopia 是一个面向嵌入式 Linux 的全方位应用程序开发平台,同时也是用于基于 Linux 的 PDA(个人数字助理),智能电话(Smartphone)以及其他 移动设备的用户界面。简单地说,Qtopia 实质上是一组关于 PDA 和智能电话的应用程序结合,如果需要开发这类产品可以在这组程序的基础上迅速构建出 PDA 或者智能电话。Qtopia 实质上依赖 Qt/Embedded。Qt/Embedded 是一个完整的自包含 GUI 和基于 Linux 的嵌入式平台开发工具。
首先,使用 qt-x11 提供的库和开发工具开发出qt应用程序,然后使用qt-embedded 关于 x86 库和工具再次编译我们开发 qt 应用程序,这是所得到的可执行文件就可以在 qvfb 上运行了,最后,使用qt-embeddedd 的arm库再次编译就可以得到在 arm 上能运行的可执行程序。
具体步骤:
qt-x11版本可以产生 Qt开发工具,如 designer等等,最重要的是可以得到 qvfb,嵌入式的开发有了 qvfb,就可以不需要实际的开发板,也可以开发 Qt 应程序。 qt-embedded 版本就是专门用于嵌入式方面的版本。 tar xjvf qt-x11-opensource-src-4.5.0.tar.bz2 cd qt-x11-opensource-src-4.5.0 ./configure make make install
历经漫长的编译过程,约一个半小时,默认安装在/usr/local/Trolltech/Qt-4.5.0 下。编译完成后源程序不要删除。
编译及安装qt-embedded-linux-opensource-src-4.5.0,在用户目录下建立一个 src 目录,用于存放编译源文件qt-embedded-linux-opensource-src- 4.5.0.tar.bz2。将其解压得到qt-embedded-linux-opensource-src-4.5.0,
重命名为 qt-embedded-linux-opensource-src-4.5.0-x86.
再次解压qt-embedded-linux-opensource-src-4.5.0.tar.bz2,并重命令为qt-embedded- linux-opensource-src-4.5.0-arm。前者是适合 x 架构的,后者适合 arm 架构的,如果有开发板的话,可以只编译 qt-embedded 版本成 arm 架构的就可以了,但是很多时候用 qvfb 的话,开发会方便很多,所以多编 译一个 x86 架构的也不错。
4
mkdir ~/src
mv qt-embedded-linux-opensource-src-4.5.0.tar.bz2 ~/src tar xjvf qt-embedded-linux-opensource-src-4.5.0.tar.bz2 mv qt-embedded-linux-opensource-src-4.5.0 qt-embedded-linux-opensource-src-4.5.0-x86 tar xjvf qt-embedded-linux-opensource-src-4.5.0.tar.bz2 mv qt-embedded-linux-opensource-src-4.5.0 qt-embedded-linux-opensource-src-4.5.0-arm cd qt-embedded-linux-opensource-src-4.5.0-x86
./configure -prefix /usr/local/Trolltech/QtEmbedded-4.5.0-x86 -embedded x86 -qvfb make make install
-------OK!qt-embedded-x86 被安装在 /usr/local/Trolltech/QtEmbedded-4.5.0-x86 下. cd qt-embedded-linux-opensource-src-4.5.0-arm
./configure -prefix /usr/local/Trolltech/QtEmbedded-4.5.0-arm -embedded arm make make install
-------OK!qt-embedded-arm 被安装在 /usr/local/Trolltech/QtEmbedded-4.5.0-arm 下。 至此我们已经安装好了 qt-x11 版,即在 PC 机上运行的 QT,qt-embedded-x86 版(x86 体系的嵌入式板子上运行的 QT)和 qt-embedded-arm(arm 板子上运行的QT)。下一步:设置环境变量。
(1)qt-x11: vi setenv.sh 添加如下内容:
PATH=/usr/local/Trolltech/Qt-4.5.0/bin:$PATH
LD_LIBRARY_PATH=/usr/local/Trolltech/Qt-4.5.0/lib:$LD_LIBRARY_PATH 保存退出.移到/usr/local/Trolltech/Qt-4.5.0 中。 (2)qt-embedded-x86: vi setenv.sh 添加如下内容:
QTEDIR=/usr/local/Trolltech/QtEmbedded-4.5.0-x86 PATH=/usr/local/Trolltech/QtEmbedded-4.5.0-x86/bin:$PATH
LD_LIBRARY_PATH=/usr/local/Trolltech/QtEmbedded-4.5.0-x86/lib:$LD_LIB
RARY_PATH
5
保存退出。移到/usr/local/Trolltech/QtEmbedded-4.5.0-x86 中。 (3)qt-embedded-arm:vi setenv.sh 添加如下内容:
QTEDIR=/usr/local/Trolltech/QtEmbedded-4.5.0-arm PATH=/usr/local/Trolltech/QtEmbedded-4.5.0-arm/bin:$PATH
LD_LIBRARY_PATH=/usr/local/Trolltech/QtEmbedded-4.5.0-arm/lib:$LD_LIB RARY_PATH
保存退出.移到/usr/local/Trolltech/QtEmbedded-4.5.0-arm 中。我们在运行的时候分别运行对应的脚本即可,比如我们要用 qt-x11: cd /usr/local/Trolltech/Qt-4.5.0
source setenv.sh
然后就可以用 qmake designer 等工具了。
5.做到这一步还有qvfb没有编译,到qt-x11-opensource-src-4.5.0中重新编译 qvfb 即可。
cd qt-x11-opensource-src-4.5.0 cd /tools/qvfb
make --会在 qt-x11-opensource-src-4.5.0/bin 下生成 qvfb,我们将它 copy 至 /usr/local/Trolltech/QtEmbedded-4.5.0-x86/bin 下
cp ../..bin/qvfb /usr/local/Trolltech/QtEmbedded-4.5.0-x86/bin 6.做一个测试 cd /usr/local/Trolltech/QtEmbedded-4.5.0-x86
6
3 设计的主要技术
3.1 FrameBuffer
FrameBuffer 是出现在 2.2.xx 内核当中的一种驱动程序接口。
Linux是工作在保护模式下,所以用户态进程是无法象DOS那样使用显卡BIOS里提供的中断调用来实现直接写屏,Linux抽象出FrameBuffer这 个设备来供用户态进程实现直接写屏。Framebuffer机制模仿显卡的功能,将显卡硬件结构抽象掉,可以通过Framebuffer的读写直接对显存进行操 作。用户可以将Framebuffer看成是显示内存的一个映像,将其映射到进程地址空间之后,就可以直接进行读写操作,而写操作可以立即反应在屏幕上。这种操 作是抽象的,统一的。用户不必关心物理显存的位置、换页机制等等具体细节。这些都是由Framebuffer设备驱动来完成的。
但Framebuffer本身不具备任何运算数据的能力,就只好比是一个暂时存放水的水池.CPU将运算后的结果放到这个水池,水池再将结果流到显示器.中间不会对数据做处理. 应用程序也可以直接读写这个水池的内容.在这种机制下,尽管Framebuffer需要真正的显卡驱动的支持,但所有显示任务都有CPU完成,因此CPU负担很重.
framebuffer的设备文件一般是 /dev/fb0、/dev/fb1 等等。 可以用命令: #dd if=/dev/zero of=/dev/fb 清空屏幕.
如果显示模式是 1024x768-8 位色,用命令:$ dd if=/dev/zero of=/dev/fb0 bs=1024 count=768 清空屏幕;
用命令: #dd if=/dev/fb of=fbfile 可以将fb中的内容保存下来; 可以重新写回屏幕: #dd if=fbfile of=/dev/fb;
在使用Framebuffer时,Linux是将显卡置于图形模式下的.
在应用程序中,一般通过将 FrameBuffer 设备映射到进程地址空间的方式使用,比如下面的程序就打开 /dev/fb0 设备,并通过mmap 系统调用进行地址映射,随后用 memset 将屏幕清空(这里假设显示模式是 1024x768-8 位色模式,线性内存模式):
int fb;
unsigned char* fb_mem; fb = open (\"/dev/fb0\ fb_mem = mmap (NULL, 1024*768,
PROT_READ|PROT_WRITE,MAP_SHARED,fb,0);
memset (fb_mem, 0, 1024*76;
FrameBuffer 设备还提供了若干ioctl命令,通过这些命令,可以获得显示设备的一
7
些固定信息(比如显示内存大小)、与显示模式相关的可变信息(比如分辨率、象素结构、每扫描线的字节宽度),以及伪彩 色模式下的调色板信息等等。
通过 FrameBuffer设备,还可以获得当前内核所支持的加速显示卡的类型(通过固定信息得到),这种类型通常是和特定显示芯片相关的。比如目前最新的内核(2.4.9)中,就包含有 对S3、Matrox、nVidia、3Dfx 等等流行显示芯片的加速支持。在获得了加速芯片类型之后,应用程序就可以将 PCI设备的内存I/O(memio)映射到进程的地址空间。这些memio
一般是用来控制显示卡的寄存器,通过对这些寄存器的操作,应用程序就可以控制特定显卡的加速功能。
PCI设备可以将自己的控制寄存器映射到物理内存空间,而后,对这些控制寄存器的访问,给变成了对物理内存的访问。因此,这些寄存器又被称为\"memio\"。一旦被映 射到物理内存,Linux的普通进程就可以通过 mmap 将这些内存 I/O 映射到进程地址空间,这样就可以直接访问这些寄存器了。
当然,因为不同的显示芯片具有不同的加速能力,对memio的使用和定义也各自不同,这时,就需要针对加速芯片的不同类型来编写实现不同的加速功能。比如大多数芯片都提供了对矩形填充的硬件加速支持,但不同的芯片实现方 式不同,这时,就需要针对不同的芯片类型编写不同的用来完成填充矩形的函数。
FrameBuffer 只是一个提供显示内存和显示芯片寄存器从物理内存映射到进程地址空间中的设备。所以,对于应用程序而言,如果希望在FrameBuffer 之上进行图形编程,还需要自己动手完成其他许多工作。
3.2 YUYV转换RGB
YUYV格式到RGB格式的转换:
为什么YUYV格式要转到RGB格式,视频的显示调用的多数API都是基于RGB格式,所以需要进行格式的转换。
YUYV格式如下:
Y0U0Y1V0 Y2U1Y3V1.......... 。说明:一个Y代表一个像素,而一个Y和UV组合起来构成一个像素,所以第0个像素Y0和第一个像素Y1都是共用第0个像素的U0和V0。而每个分量Y,U,V都是占用一个字节的存储空间。所以Y0U0Y1V0相当于两个像素,占用了4个字节的存储空间,平均一个像素占用两个字节。
RGB格式:
R0G0B0 R1G1B1.........
说明:一个像素由三个分量构成,即一个像素占用三个字节。 YUV到RGB的转换有如下公式: R = 1.164*(Y-16) + 1.159*(V-128);
8
G = 1.164*(Y-16) - 0.380*(U-128)+ 0.813*(V-128); B = 1.164*(Y-16) + 2.018*(U-128))。
3.3 video4linux
Mmap就是所谓内存映射。很多设备带有自己的数据缓冲区,或者驱动本身在内核空间中维护一片内存区域,为了让用户空间程序安全地访问,内核往往要从设备内存或者内核空间内存复制数据到用户空间。这样一来便多了复制内存这个环节,浪费了时间。因此mmap就将目标存储区域映射到一个用户空间的一片内存,这样用户进程访问这片内存时,内核将自动转换为访问这个目标存储区。这种转换往往是地址的线性变化而已(很多设备的存储空间在所谓外围总线地址空间(X86)或者总的地址空间(ARM)上都是连续的),所以不必担心其转换的效率。
现在开始叙述Video4Linux2的使用。 /* 打开设备并进行错误检查 */ int fd = open (\"/dev/video\if (fd==-1){
perror (\"Can't open device\"); return -1; }
/* 查询设备的输出格式 */ struct v4l2_format format; memset (&format,0,sizoef(format));
format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; if (-1==ioctl(fd,VIDIOC_G_FMT,&format)){ perror (\"While getting format\"); return -2; } /*
* 这里要将struct v4l2_format结构体置零,然后将
* format.type设定为V4L2_BUF_TYPE_VIDEO_CAPTURE, * 这样在进行 VIDIOC_G_FMT 的ioctl时,驱动就会知 * 道是在捕获视频的情形下获取格式的内容。 * 成功返回后,format就含有捕获视频的尺寸大小及格 * 式。格式存储在 format.fmt.pix.pixelformat这个32
9
* 位的无符号整数中,共四个字节,以小头序存储。这里 * 介绍一种获取的方法。 */
char code[5]; unsigned int i; for (i=0;i<4;i++) {
code[i] = (format.fmt.pix.pixelformat & (0xff<>i*8; } code[4]=0;
/* 现在的code是一个以\\0结束的字符串。很多摄像头都是以格式MJPG输出视频的。
* MJPG是Motion JPEG的缩写,其实就是一些没填霍夫曼表的JPEG图片。 */
/* 请求一定数量的缓冲区。
* 但是不一定能请求到那么多。据体还得看返回的数量 */
struct v4l2_requestbuffers req; memset (&req,0,sizeof(req)); req.count = 10;
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; req.memory = V4L2_MEMORY_MMAP; if (-1==ioctl(fd,VIDIOC_REQBUFS,&req)){ perror (\"While requesting buffers\"); return -3; }
if (req.count < 5){
fprintf (stderr, \"Can't get enough buffers!\\n\"); return -4; }
/* 这里请求了10块缓存区,并将其类型设为MMAP型。 */
10
/* 获取缓冲区的信息
* 在操作之前,我们必须要能记录下我们 * 申请的缓存区,并在最后使用munmap释放它们 * 这里使用结构体 * struct buffer { * void * start; * ssize_t length; * } 以及buffer数量 * static int nbuffer * 来表示 */
struct buffer * buffers = (struct buffer *)malloc (nbuffer*sizeof(*buffers)); if (!buffers){
perror (\"Can't allocate memory for buffers!\"); return -4; }
struct v4l2_buffer buf;
for (nbuffer=0;nbuffer if (-1==ioctl(fd,VIDIOC_QUERYBUF,&buf)){ perror (\"While querying buffer\"); return -5; } buffers[nbuffer].length = buf.length; buffers[nbuffer].start = mmap ( NULL, buf.length, PROT_READ, /* 官方文档说要加上PROT_WRITE,但加上会出错 */ 11 MAP_SHARED, fd, buf.m.offset ); if (MAP_FAILED == buffers[nbuffer].start) { perror (\"While mapping memory\"); return -6; } } /*这个循环完成后,所有缓存区都保存在 *了buffers这个数组里了,完了就再将它们munmap即可。 */ /* 打开视频捕获 */ enum v4l2_buf_type type; type = V4L2_BUF_TYPE_VIDEO_CAPTURE; if (-1==ioctl(fd,VIDIOC_STREAMON,&type)){ perror (\"While opening stream\"); return -7; } /* 与内核交换缓冲区 */ unsigned int i; i=0; while(1) { memset (&buf,0,sizeof(buf)); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; buf.index = i; if (-1==ioctl(fd,VIDIOC_DQBUF,&buf)){ perror (\"While getting buffer's data\"); return -8; 12 } /* 现在就得到了一片缓冲区的数据,发送处理 */ process_image ( buffers+buf.index,buf.index ); /* 将缓冲区交还给内核 */ if (-1==ioctl(fd,VIDIOC_QBUF,&buf)){ perror (\"While returning buffer's data\"); return -9; } i = (i+1) & nbuffer; } 这就是所有获取的过程了。至于图像的处理,则是由解码函数和Qt来处理。现在先进行一些思路的设计。设想在进行图像的转换时,必须提供一块内存区域来进行,我们当然可以在转换时使用malloc来进行动态分配,转换完成并显示后,再将它free。然而这样做对内核而言是一个不小的负担:每次为一整张图片分配内存,最少也有上百KB,如此大的分配量和释放量,很容易造成内存碎片加重内核的负担。由于仅是每转换一次才显示一次图像,所以这片用于转换的内存区域可以安全地复用,不会同时由两个线程操作之。因此在初始化时我们为每一块内存映射缓冲区分配一块内存区域作为转换用。对于MJPEG到JPEG的转换,使用相同的内存大小。代码就不在此列出了。这片假设这个内存区域的起始指针为convertion_buffers,在process_image (struct buffer * buf, int index ) 中,有 void process_image (struct buffer *buf, int index){ struct * buffer conv_buf = convertion_buffers+index; do_image_conversion ( buf->start, buf->length, /* 要转换的区域 */ conv_buf->start, conv_buf->length, /* 保存转换数据的区域 */ ); /* 现在就可以把数据取出并交给QPixmap处理 * 要在一个QWidget里作图,必须重载paintEvent * 函数并用QPainter作画。然而paintEvent * 是由事件驱动层调用的,我们不能手工, * 所以在我们自己的的重载类里要保存一个全局 13 * 的QPixmap。这里设为 QPixmap * m_pixmap */ m_pixmap -> loadFromData (conv_buf->start,conv_buf->length); /* 立即安排一次重绘事件 */ repaint (); } /* 重载的paintEvent示例 */ MyWidget::paintEvent (QPaintEvent * evt) { QPainter painter(this); painter.drawPixmap (QPoint(0,0),*m_pixmap); QWidget::paintEvent(evt); } 这里讲Pixmap画到了(0,0)处。 考虑的改进之处: 虽然上述程序已经可以工作了,但是有一些细节可以改进。比如图像转换之处,可能相当耗时。解决的办法之一可以考虑多线程,用一个线程进行数据的收集,每收集一帧数据便通知显示的进程。显示的进程使用一个FIFO收集数据,用一个定时器,在固定的时间到时,然后从FIFO中取出数据进行转换然后显示。两个线程互不干扰,可以更有效地利用CPU,使收集、转换和显示协调地工作。 14 4 功能实现测试 (1) 连接完成后,打开开关,出现如图1所示界面。 图1 开始界面 (2) 触屏按下开始,出现图2画面。 图2 开始摄像画面 15 (3) 转动摄像头,画面随着摄像头的移动而发生改变,如图3所示。 图3 转动摄像头后的画面 (4) 按下保存后,出现图4界面。 图4 按下保存后的界面 16 结束语 在张老师的指导下,经过好多天的奋斗与坚持下,终于完成了设计任务,设计过程中,我充分的利用了网络资源,在网络上搜索需要的各种信息,同时也通过翻阅书本,获取更多的信息。更加深入地学习了嵌入式系统。 在设计过程中,我发现原来理论和实际有着很大的差别。我们在课堂上学到的东西,距离实际应用还有一定的距离,在实际操作中遇到了很大问题,而找不到其中原因。这种种,都坚定的告诉我们,我们要学以致用,我们的知识不是用来忘记的,而是要用到实际生活中的。有了这种思想,在以后的学习中会更加注重理论于实际的联系,使我们所学的知识能够尽可能的用到实处,而不仅仅是为了应付考试。 随着社会的发展,视频通信广泛应用于现代通信当中,成为我们生活中必不可少的一部分。 17 参考文献 [1] 樊昌信,曹丽娜.通信原理[M].北京:国防工业出版社,2015. [2] 刘晓东,董辰辉.MATLAB从入门到精通[M].北京:人民邮电出版社,2010. [3] 常华,袁刚,常敏嘉.仿真软件教程.北京:清华大学出版社,2006. [4] http://wk.baidu.com/view/17338d1733687e21af45a9c8?Pcf=2#6,2015-12-14. [5] 朱阳燕.基于MATLAB的2PSK系统仿真[J].科技信息,2008(17):82. 18 致 谢 通过十几天的努力之下,我终于完成了设计任务。 首先,我要感谢我的父母,在我累了的时候,打个电话回家总能得到父母的支持和安慰,其次要感谢老师们的悉心教导,在碰到一些不能理解的问题的时候,都要麻烦到老师,老师都不厌其烦地一一解释,使我在课堂中遗漏的知识得到充分的复习。最后要感谢我的室友和同学们,在课程设计过程中,我们互相帮助,营造了很长一阵的良好学习氛围。 通过这次课程设计,让我懂得了做事要坚持,不要轻言放弃,不能因为一点挫折就抱怨任务繁重,只要你肯坚持,结果总会是美好的。也让我学会了在以后的生活中,要秉持做课程设计的这种坚持不懈,保持乐观的精神。 最后,祝愿同学们学习进步,祝愿老师工作顺利,祝愿父母身体健康。 19 附录A 程序代码 /****************************************************************** 课 题 名:嵌入式系统 编 写 者:冼达康 指导老师:张峰 时 间:2016.12.24 班 级:通信1302班 *******************************************************************/ clear all close all i=10; j=5000; fc=5; fm=i/5; B=2*fm; t=linspace(0,5,j); a=round(rand(1,i)); st1=t; for n=1:10 if a(n)<1; for m=j/i*(n-1)+1:j/i*n st1(m)=0; end else for m=j/i*(n-1)+1:j/i*n st1(m)=1; end end end figure(1); subplot(411); plot(t,st1); title('基带信号st1'); 20 axis([0,5,-1,2]); st2=t; for k=1:j; if st1(k)>=1; st2(k)=0; else st2(k)=1; end end; subplot(412); plot(t,st2); title('基带信号反码st2'); axis([0,5,-1,2]); st3=st1-st2; subplot(413); plot(t,st3); title('双极性基带信号st3'); axis([0,5,-2,2]); s1=sin(2*pi*fc*t); subplot(414); plot(s1); title('载波信号s1'); e_psk=st3.*s1; figure(2); subplot(511); plot(t,e_psk); title('e_2psk'); noise=rand(1,j); psk=e_psk+noise; subplot(512); plot(t,psk); title('加噪后波形'); psk=psk.*s1; subplot(513); plot(t,psk); 21 title('与载波s1相乘后波形'); Rp=0.1;Rs=50;Wp=5/100;Ws=10/100; [n,Wn]=ellipord(Wp,Ws,Rp,Rs); [b,a]=ellip(n,Rp,Rs,Wn); X1=filter(b,a,psk); subplot(514); plot(t,X1); title('低通滤波后波形'); for m=0:i-1; if psk(1,m*500+250)<0; for j=m*500+1:(m+1)*500; psk(1,j)=0; end else for j=m*500+1:(m+1)*500; psk(1,j)=1; end end end subplot(515); plot(t,psk); axis([0,5,-1,2]); title('抽样判决后波形') 22 因篇幅问题不能全部显示,请点此查看更多更全内容