资源描述
资料内容仅供您学习参考,如有不当之处,请联系改正或者删除。
嵌入式课程设计报告
一、 课程设计目的
1.1 掌握linux开发环境的搭建;
1.2巩固嵌入式交叉编译的开发思想;
1.3掌握嵌入式GUI软件设计技。,.
二、 课程设计要求
输入信号为 1 路 AV 视频信号,要求系统能对 1 路输入信号进行实时采集,数字化 处理,压缩,存储,要保证一定的录像质量. 根据设计题目的要求,选择确定 ARM 芯片型号,视频采集芯片型号,完成系统硬件设 计和程序设计.
三、 课程设计内容
设计原理 ARM10 系列微处理器为低功耗的 32 位 RISC 处理器,最适合用于对价位和功耗要 求较高的消费类应用.
ARM10 系列微处理器的主要应用领域为:工业控制,Internet 设备,调制解 调器设备,移动电话等多种多媒体和嵌入式应用.
视频监控系统总体设计 首先需要对系统进行总体规划,将系统划分成几个功能模块,确定各个模块的实现 方法.整个视频监控系统采用 C/S 结构,从主体上分为两部分:服务器端和客户端.服 务器端主要包括 S3C4510 平台上运行的采集,压缩,传输程序,客户端是 PC 机上运行 的接收,解压,回放程序.视频监控终端从摄像头捕获实时的视频信息,压缩之后经过 以太网传输到视频监控服务器上.
视频图像采集和打包发送在服务器端完成,图像的接收 解包和回放将在客户端完成. 采集图像 数据压缩 打包发送 接收
系统的硬件设计 系统采用模块化设计方案,主要包括以下几个模块:主控制器模块,储存电路模块, 外围接口电路模块,电源和复位电路,
S3C4510 主控器模块
主控器模块是整个系统的核心,采用的 S3C4510B 处理器.Samsung 公司的 S3C45 10B 是基于以太网应用系统的高性价比 16/32 位 RISC 微控制器,内含一个由 ARM 公司设计的 16/32 位 ARM7TDMI RISC 处理器核,ARM7TDMI 为低功耗,高性能的 16/32 核,
系统存储电路模块
主控器还需一些外围存储单元如 Nand Flash,和 SDRAM.Nand Flash 中包含 Lin ux 的 Bootloader,系统内核,文件系统,应用程序以及环境变量和系统配置文件等;S DRAM 读写速度快,系统运行时把它作为内存单元使用.
外围电路模块
外围电路主要是以下几个电路,复位电路图,电源电路图以及 JTAG 电路,
三、 课程设计设备及工具
硬件: UP-NETARM2410-S嵌入式实验仪、 PC机、 ov511摄像头;
软件: PC机操作系统REDHAT LINUX 9.0、 MINICOM、 AMR-LINUX开发环境。
四、 设计方案
本次课程设计采用arm10开发平台。该平台采用Samsung公司的处理器S3C2410。该处理器内部集成了ARM公司 ARM920T处理器核的32位微控制器, 资源丰富, 带独立的16KB的指令Cache和16KB数据Cache、 LCD控制器、 RAM控制器、 NAND 闪存控制器、 3路UART、 4路DMA、 4路带PWM的Timer、 并行I/O口、 8路10位ADC、 Touch Screen接口、 I2C接口、 I2S接口、 2个USB接口控制器、 2路SPI, 主频最高可达203MHz。在处理器丰富资源的基础上, 还进行了相关的配置和扩展, 平台配置了16MB 16位的Flash和64MB 32位的SDRAM。经过以太网控制器芯片DM9000E扩展了一个网口, 另外引出了一个HOST USB接口。经过在USB接口上外接一个带USB口的摄像头, 将采集到的视频图像数据放入输入缓冲区中。然后, 对缓冲区中的视频数据进行压缩成帧, 并把每一帧图片在网页中显示出来, 每秒钟刷新两次, 得到一个动态的视频界面, 然后经过局域网访问该主机的网页, 得到网络视频信息。本次课程设计主要是完成得到视频图片及网页刷新的过程。
本次设计要求在局域网环境中进行。
五、 开发环境配置
5.1 摄像头驱动加载
在Linux下, 设备驱动程序能够看成Linux内核与外部设备之间的接口。设备驱动程序向应用程序屏蔽了硬件实现了的细节, 使得应用程序能够像操作普通文件一样来操作外部设备, 能够使用和操作文件中相同的、 标准的系统调用接口函数来完成对硬件设备的打开、 关闭、 读写和I/O控制操作, 而驱动程序的主要任务也就是要实现这些系统调用函数。本系统平台使用的嵌入式arm-Linux系统在内核主要功能上与 Linux操作系统没本质区别。
Video4Linux(简V4L)是Linux中关于视频设备的内核驱动, 它为针对视频设备的应用程序编程提供一系列接口函数, 这些视频设备包括现今市场上流行的TV卡、 视频捕捉卡和USB摄像头等。
ov511的驱动, 2.4的内核中就有, 因此我们只需重新编译内核, 将下边的选项都选上, 然后重新烧写内核就能够了。也就是直接静态加载。
( 1) 在arm linux的kernel目录下make menuconfig。
( 2) 首先(*)选择Multimedia device->下的Video for linux。加载video4linux模块, 为视频采集设备提供编程接口;
( 3) 然后在usb support->目录下(*)选择support for usb和usb camera ov511 support。这使得在内核中加入了对采用OV511接口芯片的USB数字摄像头的驱动支持。
( 4) 保存配置退出。
( 5) make dep; make zImage就生成了带有ov511 驱动的内核。
接下来就经过uboot将内核烧到flash里去。这时你将摄像头插上, 系统就会提示发现摄像头-ov511+, 这说明驱动正常。:-), 驱动加载就成功了。
5.2 安装jpeg库
在地址ftp://
# tar -zxvf jpegsrc.v6b.tar.gz //解压库文件
# cd jpeg-6b
# ./configure --prefix=/usr/local/jpeg6--enable-shared //生成MAKEFILE文件
# make //编译安装文件
#mkdir /usr/local/jpeg6
#mkdir /usr/local/jpeg6/include
#mkdir /usr/local/jpeg
#mkdir /usr/local/jpeg/lib
#mkdir /usr/local/jpeg/bin
#mkdir /usr/local/jpeg/man
#mkdir /usr/local/jpeg/man/man1 //建立生成安装文件的目录
#make install -lib /生成安装文件
#make install //开始安装文件
六、 设计过程
6.1 硬件设计
系统的主体设计思想是将视频前端和嵌入式Web服务器整合在一起, 摄像头经过USB接口和嵌入式系统板的USB HOST相连, 摄像头采集来的视频信号经过压缩后, 经过内部总线传送到内置的Web服务器, 客户端采用浏览器/服务器结构( 即B/S结构) , 能够经过IE浏览器访问视频Web服务器, 以便查看所监控的视频画面, 而且, 用户还能够控制平台对镜头的动作或对系统进行配置控制。
网络视频采集系统是由视频监控终端和视频监控中心组成, 终端使用嵌入式系统加上USB摄像头, 在Linux操作系统上运行USB摄像头的驱动和相应的视频采集处理传输程序, 得到视频监控画面, 并画面经过网络传输, 监控中心运行监控软件端, 经过Internet浏览器来浏览终端送来的监控画面, 其硬件连接图如下所示:
网络视频采集硬件连接图
从图中能够看出, PC机部分能够直接采用IE浏览器, 不需要在PC机上运行其它的软件, 主要设计工作是开发板端的设计。视频监控终端主要有两个部分组成, 硬件部分包括摄像头和开发平台的选择; 软件部分包括系统级软件( Boot Loader, 嵌入式操作系统, USB驱动程序) 与用户级软件( 应用程序) 。
6.2 软件设计
Video4linux为应用程序提供了一系列的接口函数, 经过这些函数, 能够执行打开、 读写、 关闭等基本操作。设备驱动提供了read、 write、 open、 close等函数的具体实现, 在内核这些函数都能够调用。视频采集流程图如下所示:
视频采集流程图
利用V4L编写的视频采集程序重要代码如下, 下列代码包含采集编码程序一些关键函数。
(1) 程序中定义的数据结构
struct _v4l_struct
{
int fd; // 保存打开视频文件的设备描述符
struct video_capability capability;
struct video_picture picture;
struct video_mmap mmap;
struct video_mbuf mbuf;
unsigned char *map; // 用于指向图像数据的指针
int frame_current;
int frame_using[VIDEO_MAX_FRAME]; // 这两个变量用于双缓冲
}; typedef struct _v4l_struct v4l_device;
这些数据结构都是由Video4Linux支持的, 它们的用途如下:
*video_capability包含摄像头的基本信息, 例如设备名称、 支持的最大最小分辨率、 信号源信息等;
*voide_picture包含设备采集图像的各种属性, 如brightness(亮度)、 hue(色调)、 contrast(对比度)、 whiteness(色度)、 color(深度)等;
*video_mmap用于内存映射;
*voido_mbuf利用mmap进行映射的帧信息, 实际上是输入到摄像头存储器缓冲中的帧信息, 包括size( 帧的大小) 、 frames( 最多支持的帧数) 、 offsets( 每帧相对基址的偏移) 。
程序中用到的主要系统调用函数有: open("/dev/voideo0",int flags)、 close(fd)、 mmap(void *start,size_t length,int prot,int flags,int fd,off_t offset)、 munmap(void *start,size_tlength)和ioctl(int fd,int cmd,…)。
前面提到Linux系统中把设备看成设备文件, 在用户空间能够经过标准的I/O系统调用函数操作设备文件, 从而达到与设备通信交互的目的。当然, 在设备驱动中要提供对这些函数的相应支持。这里说明一下ioctl(int fd,int cmd,…)函数, 它在用户程序中用来控制I/O通道, 其中, fd代表设备文件描述符, cmd代表用户程序对设备的控制命令, 省略号一般是一个表示类型长度的参数, 也可没有。
( 2) 采集程序实现过程
首先打开视频设备, 摄像头在系统中对应的设备文件为/dev/video0, 采用系统调用函数grab_fd =open ("/dev/video0",O_RDWR),grab_fd是设备打开后返回的文件描述符( 打开错误返回-1) , 以后的系统调用函数就可使用它来对设备文件进行操作了。接着, 利用ioct1( grab_fd,VIDIOCGCAP,&grab_cap) 函数读取struct video_capability中有关摄像头的信息。该函数成功返回后, 这些信息从内核空间拷贝到用户程序空间grab_cap各成员分量中, 使用 printf函数就可得到各成员分量信息, 例如printf("maxheight=%d",grab_fd.maxheight)获得最大垂直分辨率的大小。不规则用ioct1(grab_fd,VIDIOCGPICT,&grab_pic)函数读取摄像头缓冲中voideo_picture信息。在用户空间程序中能够改变这些信息, 具体方法为先给分量赋新值, 再调用VIDIOCSPICT ioct1函数, 例如:
grab_fd.depth=3;
if(ioct1(grab_fd,VIDIOCSPICT,&grab_pic)<0)
{perror("VIDIOCSPICT");return -1;};
完成以上初始化设备工作后, 就能够对视频图像截取了, 有两种方法: 一种是read()直接读取; 另外一种 mmap()内存映射。Read ()经过内核缓冲区来读取数据; 而mmap()经过把设备文件映射到内存中, 绕过了内核缓冲区, 最快的磁盘访问往往还是慢于最慢的内存访问, 因此mmap ()方式加速了I/O访问。另外, mmap()系统调用使得进程之间经过映射同一文件实现共享内存, 各进程能够像访问普通内存一样对文件进行访问, 访问时只需要使用指针而不用调用文件操作函数。因为mmap()的以上优点, 因此在程序实现中采用了内存映射方式, 即mmap()方式。
利用mmap()方式视频裁取具体进行操作如下。
①先使用ioct1(grab_fd,VIDIOCGMBUF,&grab_vm)函数获得摄像头存储缓冲区的帧信息, 之后修改voideo_mmap中的设置, 例如重新设置图像帧的垂直及水平分辨率、 彩色显示格式。可利用如下语句
grab_buf.height=240;
grab_buf.width=320; 字串8
grab_buf.format=VIDEO_PALETTE_RGB24;
②接着把摄像头对应的设备文件映射到内存区, 具体使用grab_data=(unsigned char*) mmap(0,grab_vm.size,PROT_READ|PROT_WRITE,MAP_SHARED,grad_fd,0)操作。这样设备文件的内容就映射到内存区, 该映射内容区可读可写而且不同进程间可共享。该函数成功时返回映像内存区的指针, 挫败时返回值为-1。
下面对单帧采集和连续帧采集进行说明:
*单帧采集。在上面获取的摄像头存储缓冲区帧信息中, 最多可支持的帧数( frames的值) 一般为两帧。对于单帧采集只需设置 grab_buf.frame=0, 即采集其中的第一帧, 使用ioctl(grab_fd,VIDIOCMCAPTURE,&grab_buf) 函数, 若调用成功, 则激活设备真正开始一帧图像的截取, 是非阻塞的。接着使用ioct1(grab_fd,VIDIOCSYNC,&frame) 函数判定该帧图像是否截取完毕, 成功返回表示截取完毕, 之后就可把图像数据保存成文件的形式。
七、 测试与实现
( 1) 仔细检查设备的连接, 打开电源;
( 2) 在PC机上的浏览器中地址栏输入开发板地址, 网页自动跳到开发板的首页;
( 3) 在页面上可见摄像头采集到的视频信息。
经验证, 本次设计基本达到设计要求。
八、 设计心得
本次课程设计经过运用嵌入式技术, 图形处理技术等, 完成了网络视频的采集过程, 进一步熟悉了交叉编译的开发方法, 在开发的过程中, 遇到很多问题, 如驱动无法加载, 程序调试出错等, 经过重新编译开发板内核、 安装图形处理库等, 解决了多个问题, 在解决问题的过程中, 我们对嵌入式的开发过程多了一些细节的认识, 为以后的持续学习巩固了基础。
在设计过程中, 我们分工合作, 一起进步, 不只从理论上对嵌入式有了更深的认识, 同时从实践中验证了学习的理论知识, 对嵌入式有了更深一步的认识。
九、 源程序清单
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <errno.h>
#include <linux/videodev.h>
#include <jpeglib.h>
#include <unistd.h>
#include <math.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#include <pthread.h>
#include <sys/time.h>
#include <sys/mman.h>
#include <getopt.h>
#define WIDTH 640
#define HEIGHT 480
#define DEFAULT_DEVICE "/dev/v4l/video1"
int write_jpeg(char *filename,unsigned char *buf,int quality,int width, int height, int gray)
{struct jpeg_compress_struct cinfo;
struct jpeg_error_mgr jerr;
FILE *fp;
int i;
unsigned char *line;
int line_length;
if (NULL == (fp = fopen(filename,"w")))
{
fprintf(stderr,"grab: can't open %s: %s\n",filename,strerror(errno));
return -1;
}
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_compress(&cinfo);
jpeg_stdio_dest(&cinfo, fp);
cinfo.image_width = width;
cinfo.image_height = height;
cinfo.input_components = gray ? 1: 3;
cinfo.in_color_space = gray ? JCS_GRAYSCALE: JCS_RGB;
jpeg_set_defaults(&cinfo);
jpeg_set_quality(&cinfo, quality, TRUE);
jpeg_start_compress(&cinfo, TRUE);
line_length = gray ? width : width * 3;
for (i = 0, line = buf; i < height; i++, line += line_length)
jpeg_write_scanlines(&cinfo, &line, 1);
jpeg_finish_compress(&(cinfo));
jpeg_destroy_compress(&(cinfo));
fclose(fp);
/////////////////////////////////////////////////////////////////////
char fn1[30]="/var/www/pic001.jpg";
char fn2[30]="/var/www/pic002.jpg";
if (NULL == (fp = fopen(fn1,"r"))){
fclose(fp);
remove(fn1);
}
rename(fn2,fn1);
/////////////////////////////////////////////////////////////////////
return 0;
}
//这个函数很通用, 它的作用是把buf中的数据压缩成jpeg格式。
struct _v4l_struct
{
int fd; // 保存打开视频文件的设备描述符
struct video_capability capability;
struct video_picture picture;
struct video_mmap mmap;
struct video_mbuf mbuf;
unsigned char *map; // 用于指向图像数据的指针
int frame_current;
int frame_using[VIDEO_MAX_FRAME]; // 这两个变量用于双缓冲
}; typedef struct _v4l_struct v4l_device;
extern int v4l_open(char *, v4l_device *);
extern int v4l_close(v4l_device *);
extern int v4l_get_capability(v4l_device *);
extern int v4l_get_picture(v4l_device *);
extern int v4l_get_mbuf(v4l_device *);
extern int v4l_set_picture(v4l_device *, int, int, int, int, int);
extern int v4l_grab_picture(v4l_device *, unsigned int);
extern int v4l_mmap_init(v4l_device *);
extern int v4l_grab_init(v4l_device *, int, int,int);
extern int v4l_grab_frame(v4l_device *, int);
extern int v4l_grab_sync(v4l_device *);
int v4l_open(char *dev , v4l_device *vd)
{
dev= DEFAULT_DEVICE;
if((vd->fd=open(dev,O_RDWR))<0){perror("v4l_open");return -1;}
if(v4l_get_capability(vd))return -1;
if(v4l_get_picture(vd))return -1; // 这两个函数就是即将要完成的获取设备信息的函数
return 0;
}
int v4l_get_capability(v4l_device *vd)
{
if (ioctl(vd->fd, VIDIOCGCAP, &(vd->capability)) < 0) {
perror("v4l_get_capability:");
return -1;
}
return 0;
}
int v4l_get_picture(v4l_device *vd)
{
if (ioctl(vd->fd, VIDIOCGPICT, &(vd->picture)) < 0) {
perror("v4l_get_picture:");
return -1;
}
return 0;
}
int v4l_get_mbuf(v4l_device *vd)
{
if (ioctl(vd->fd, VIDIOCGMBUF ,&(vd->mbuf)) < 0) {
perror("v4l_get_mbuf:");
return -1;
}
return 0;
}
int v4l_set_picture(v4l_device *vd,int br,int hue,int col,int cont,int white)
{
/* if(br)*/ vd->picture.brightness=25000;
/* if(hue)*/ vd->picture.hue=25000;
/* if(col)*/ vd->picture.colour=65535;
/* if(cont)*/ vd->picture.contrast=30000;
/* if(white)*/ vd->picture.whiteness=25000;
if(ioctl(vd->fd,VIDIOCSPICT,&(vd->picture))<0)
{perror("v4l_set_picture: ");return -1;}
return 0;
}
int v4l_grab_picture(v4l_device *vd, unsigned int size)
{
if(read(vd->fd,&(vd->map),size)==0)return -1;
return 0;
}
int v4l_mmap_init(v4l_device *vd)
{
if (v4l_get_mbuf(vd) < 0)
return -1;
if ((vd->map = mmap(0, vd->mbuf.size, PROT_READ|PROT_WRITE, MAP_SHARED, vd->fd, 0)) < 0) {
perror("v4l_mmap_init:mmap");
return -1;
}
return 0;
}
int v4l_grab_init(v4l_device *vd, int width, int height,int pal)
{
vd->mmap.width = width;
vd->mmap.height = height;
pal= vd->picture.palette;
vd->mmap.format=pal;
vd->frame_current = 0;
vd->frame_using[0] = FALSE;
vd->frame_using[1] = FALSE;
return v4l_grab_frame(vd, 0);
}
//真正获得图像的函数 extern int v4l_grab_frame(v4l_device *, int);
int v4l_grab_frame(v4l_device *vd, int frame)
{
if (vd->frame_using[frame]) {
fprintf(stderr, "v4l_grab_frame: frame %d is already used.\n", frame);
return -1;
}
vd->mmap.frame = frame;
if (ioctl(vd->fd, VIDIOCMCAPTURE, &(vd->mmap)) < 0) {
perror("v4l_grab_frame");
return -1;
}
vd->frame_using[frame] = TRUE;
vd->frame_current = frame;
return 0;
}
//在截取图像后还要进行同步操作, 就是调用 extern int v4l_grab_sync(v4l_device *); 该函数如下
int v4l_grab_sync(v4l_device *vd)
{
if (ioctl(vd->fd, VIDIOCSYNC, &(vd->frame_current)) < 0) {
perror("v4l_grab_sync");
}
vd->frame_using[vd->frame_current] = FALSE;
return 0;
}
int v4l_close(v4l_device *vd)
{
close(vd->fd);
return 0;
}
//该函数返回 0 说明你想要获取的图像帧已经获取完毕。
//图像的位置, 存在 vd.map+vd.mbuf.offsets[vd.frame_current] 处。其中 vd.frame_current=0 , 为
//第一帧的位置, vd.frame_current=1 , 为第二帧的位置。
void bufBRExchange(unsigned char * buf, int bufLength)
{
int i=0;
printf("ECHG SUC\n");//bufLength-=2;
unsigned char temp;
for(i=15;i<921615;i+=3)
{
temp=buf[i];
buf[i]=buf[i+2];
buf[i+2]=temp;
}
}
int main()
{v4l_device vd;
unsigned char* buf;// unsigned char nbuf[1843216];
v4l_open(DEFAULT_DEVICE,&vd);
v4l_mmap_init(&vd);
v4l_grab_init(&vd,640,480,0);
v4l_grab_sync(&vd); // 此时就已经获得了一帧的图像, 存在 vd.map 中
//buf=(unsigned char*) vd.map+vd.mbuf.offsets[vd.frame_current];
while(TRUE)
{
buf=vd.map+vd.mbuf.offsets[vd.frame_current];
printf("%d\n",vd.mbuf.size);
bufBRExchange(buf,vd.mbuf.size);
//unsigned char echg;
vd.frame_current ^= 1;
v4l_grab_frame(&vd, vd.frame_current);
v4l_grab_sync(&vd);
printf("FRAME 1 OK!\n");
if(-1 == (write_jpeg("/var/www/pic002.jpg",buf,30,640,480,0)))
{
printf("write_jpeg error\n");
exit(1);
}
printf("FRAME 2 OK!\n");
//图像处理函数( vd.map+vd.mbuf.offsets[vd.frame_current] ) ; //循环采集
// 其中 vd.map+vd.mbuf.offsets[vd.frame_current] 就是图像所在位置。
}
v4l_close(&vd);
}
展开阅读全文