ov9650学习(3)

news/2024/10/18 16:53:48/

参考了飞凌公司提供的测试源码,自己稍加修改,可以实现摄像头的在LCD上显示

由于飞凌公司提供的测试源码功能很多所以一下子可能不能理解,所以我就想一步步拆开来做,先实现在LCD屏上的显示

———————————————————————————————————————————————————————————————————————

改动的地方:

                      1.我经过测试知道了摄像头输出的图像格式是YUV422,所以代码中我就没有再判断图像格式去选择哪一种显示方法,精简了一下代码。代码中存在一个格式转换的函数 show_cam_image(),还有一个转换公式的函数Conv_YCbCr_Rgb()。因为我们的LCD的图像显示格式是RGB565的,而我摄像头获得的数据是YUV422的(摄像头寄存器参数决定),所以想在LCD上显示必须得转换一下格式,这2个函数我还没看懂。

如果你的摄像头获得的图像格式是RGB的,那么你就不用转换,只需要将v4l  mmap获得的图像内存地址copy到LCD的framebuffer中即可。


                      2.增加了一个退出的功能,原来代码中是有的,不过比这个复杂,你只需要在屏幕上按e或者E即可退出摄像头,采用了select,这里我的tv设置了0,因为是while(1)循环,所以并没有大影响,如果你设置了一个时间,那么在屏幕上你会看见图像动得很慢,因为CPU花时间去轮询是否要去看是否要退出。原代码中用了一个方法去解决这个问题,跟根据驱动来的,目前我还没看懂,就是我注销的那一段。

======================================================================================================

还有需要注意的地方就是v4l的一些结构体:(具体可以去百度,还有很多结构体是代码中没用到的,是设置图像的一些其他参数的,这里只是在LCD上显示,所以没用到那么多)

        struct video_capability vc;
        struct video_window vw;
        struct video_capture vcp;
        struct video_picture vp;
        struct video_mbuf vm;

======================================================================================================

这里我就稍微说一下mmap

mmap()是实现了一个映射过程,将用户空间的一段内存与设备的内存相关联,当用户访问用户空间的这段地址范围实际上就转化为对设备的访问。对于摄像头这样的设备,mmap应该是最好的选择,可以直接将图像数据存放的内存映射到用户空间,然后将这段内存再和frambuffer去重合,就可以直接实现LCD显示,不用再调用read这个函数,很快,因为调用read这个系统调用,还是要先进内核,然后在内核中读取数据,再从内核空间搬运到用户空间,这样比只在用户空间操作慢很多。


上面这个过程和理解我想了很久,就是因为不理解用户空间和内核空间的概念,这里我再说一下我对这2个空间的理解吧

因为32位的linux系统(开启了MMU)每个进程有4G的内存空间,3G是用户空间,1G是内核空间,每个进程的用户空间的完全独立的,而内核空间是由内核负责映射的,它并不会跟着进程改变,而每个进程的用户空间和内核空间也是分开的(为了防止用户空间错误导致系统奔溃),用户进程通常情况下只能访问用户空间的虚拟地址,不能访问内核空间的虚拟地址。

所以用户空间是不可能直接访问设备的,但是我现在需要去操作设备呀,比如去点灯,打开摄像头,这里就可以利用系统调用(代表用户进程在内核态执行),来实现这个过程;系统调用在用户空间调用了一个功能函数,其实是调用file_operations里面我们定义的一些功能函数,通过这些功能函数我们再去操作设备。


上面说到了一个4G的空间,其实是我们虚拟出来的,实际上我们的SDRAM只有64M,它操作的大概方法是:假如我有一个1G的程序需要在内存中运行,但是我们的内存只有64M,但是我的虚拟内存有4G,它首先把这1G的程序搬到4G上,然后在这4G的空间里面,取一段,假如8M的程序放到我们64M中运行,当满了64M的时候就把前面的扔掉,再取,就这样一直循环,也有可能是边拿边扔,具体怎样我不知道,需要看算法。这里就出了问题了哪来的4G呢?这里就要说到MMU了,它里面是管理这些实际内存和虚拟内存映射的单元,它会建立一个映射表,哪些虚拟内存地址是对应到实际内存地址,CPU只需要处理这些虚拟地址。

======================================================================================================

这个结构体是用来存放v4l设备mmap相关参数的,因为v4l驱动对于设备的内存访问使用到了mmap

struct video_mbuf

{

int size; 可映射的摄像头内存大小 

int frames; 摄像头可同时存储的帧数 

int offsets[VIDEO_MAX_FRAME];每一帧图像的偏移量

};

用户空间操作:

ioctl(fd, VIDIOCGMBUF, &vm)

buf = (__u8 *)mmap(0, vm.size, PROT_READ, MAP_SHARED, fd, 0);//这里就调用mmap实际是去内核中调用我们定义的mmap函数

注意:在用户空间进行mmap摄像头数据之间都需要调用VIDIOCGMBUF的ioctl

PROT_READ //页内容可以被读取

MAP_SHARED//对映射区域的写入数据会复制回文件内,而且允许其他映射该文件的进程共享


V4L提供了2种读取摄像头数据的方式,一个是直接读取,一个是mmap,我查了一下两者最大的区别在于写的速度mmap会快一点,对于读取数据方面应该差不多的(我改了它的程序都试过一下)。

不过大多都是用后者(应该是会节约一点CPU时间),使用后者的时候它给的测试程序会先调用一个VIDIOCGMBUF的ioctl,内核中主要是将摄像头采集的数据放到一个video_mbuf结构体类型的变量中(用来保存摄像头的buf),然后copy_to_user将数据传到用户空间中去,然后再调用mmap,对于内核具体是如何将这段摄像头buf和mmap的内存空间重叠的还没看懂。(我怀疑是配置了摄像头接口的寄存器,将采集数据的目的地址进行了修改吧)



======================================================================================================

内核会进行如下处理:(这里就是我前一个文章提到的深水区,在内核中看到了VMA的操作)
1.在进程的虚拟空间查找一块VMA
2.将这块VMA进行映射
3.如果设备驱动程序或者文件系统的file_operations定义了mmap()操作,则调用它
4.将这个VMA插入到进程的VMA链表中

======================================================================================================

OK,我们已经通过了mmap获取了图像数据内存地址,然后再将它和frambuffer重合即可,可以使用mmecpy,不过我上面就提到了由于我们获得的图像格式和frambuffer图像显示格式不一样,所以我们需要进行转换,这就是show_cam_image()这个函数所要做的事情,具体函数的操作我还没看懂~


今天就分析到这里了,代码在下面。


贴一下代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <termios.h>
#include <linux/fb.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <linux/videodev.h>
#include <errno.h>#define	FIXED_SOURCE_WIDTH	640
#define	FIXED_SOURCE_HEIGHT	480#define	VIDEO_START		0
#define VIDEO_PALETTE_YUV422P  13#define	YCbCrtoR(Y,Cb,Cr)	(1000*Y + 1371*(Cr-128))/1000
#define	YCbCrtoG(Y,Cb,Cr)	(1000*Y - 336*(Cb-128) - 698*(Cr-128))/1000
#define	YCbCrtoB(Y,Cb,Cr)	(1000*Y + 1732*(Cb-128))/1000
#define min(x1, x2)     (((x1)<(x2))?(x1):(x2))static unsigned short image_width;
static unsigned short image_height;
//static unsigned short image_format;static int fb_xres;
static int fb_yres;
static int fb_bpp;/*  
static struct 
{   int palette;    char *name;
} 
optional_image_format[] = 
{    {       VIDEO_PALETTE_YUV422P,      "YCbCr422 planar",    },{       VIDEO_PALETTE_RGB565,       "RGB565",   },  {       VIDEO_PALETTE_RGB24,        "RGB24",    },
};
*/
__u32 Conv_YCbCr_Rgb(__u8 y0, __u8 y1, __u8 cb0, __u8 cr0)
{// bit order is// YCbCr = [Cr0 Y1 Cb0 Y0], RGB=[R1,G1,B1,R0,G0,B0].int r0, g0, b0, r1, g1, b1;__u16 rgb0, rgb1;__u32 rgb;#if 1 // 4 frames/s @192MHz, 12MHz ; 6 frames/s @450MHz, 12MHzr0 = YCbCrtoR(y0, cb0, cr0);g0 = YCbCrtoG(y0, cb0, cr0);b0 = YCbCrtoB(y0, cb0, cr0);r1 = YCbCrtoR(y1, cb0, cr0);g1 = YCbCrtoG(y1, cb0, cr0);b1 = YCbCrtoB(y1, cb0, cr0);#endifif (r0>255 ) r0 = 255;if (r0<0) r0 = 0;if (g0>255 ) g0 = 255;if (g0<0) g0 = 0;if (b0>255 ) b0 = 255;if (b0<0) b0 = 0;if (r1>255 ) r1 = 255;if (r1<0) r1 = 0;if (g1>255 ) g1 = 255;if (g1<0) g1 = 0;if (b1>255 ) b1 = 255;if (b1<0) b1 = 0;// 5:6:5 16bit formatrgb0 = (((__u16)r0>>3)<<11) | (((__u16)g0>>2)<<5) | (((__u16)b0>>3)<<0);	//RGB565.rgb1 = (((__u16)r1>>3)<<11) | (((__u16)g1>>2)<<5) | (((__u16)b1>>3)<<0);	//RGB565.rgb = (rgb1<<16) | rgb0;return(rgb);
}
static char yuv_interval[] = {0, 2, 4, 8, 16};static void show_cam_img(void *scr, __u8 *y_buf, __u8 *cb_buf, __u8 *cr_buf)
{__u16 x, y, w, h, i, f;__u16 *fb_buf = (__u16 *)scr;__u32 rgb_data;for(i=0; i<4; i++) {	//0,1,2,3if((image_width>>i)<=fb_xres) {f = 0;w = min(image_width>>i, fb_xres);h = min(image_height>>i, fb_yres);break;}if((image_height>>i)<=fb_yres) {f = 1;w = min(image_width>>i, fb_yres);h = min(image_height>>i, fb_xres);break;}}if(i>=4)return;if(!f) {for(y=0; y<h; y++) {for(x=0; x<w; x+=2) {	//calculate 2 timesif(i) {fb_buf[x]   = Conv_YCbCr_Rgb(y_buf[x<<i],y_buf[(x<<i)+1],cb_buf[(x<<i)>>1],cr_buf[(x<<i)>>1]);fb_buf[x+1] = Conv_YCbCr_Rgb(y_buf[(x<<i)+yuv_interval[i]],y_buf[(x<<i)+1+yuv_interval[i]],cb_buf[((x<<i)+yuv_interval[i])>>1],cr_buf[((x<<i)+yuv_interval[i])>>1]);} else {rgb_data = Conv_YCbCr_Rgb(y_buf[x<<i],y_buf[(x<<i)+1],cb_buf[(x<<i)>>1],cr_buf[(x<<i)>>1]);fb_buf[x]   = rgb_data;fb_buf[x+1] = rgb_data>>16;}}fb_buf += fb_xres;y_buf += image_width<<i;cb_buf += (image_width<<i)>>1;cr_buf += (image_width<<i)>>1;}} else {for(y=0; y<h; y++) {for(x=0; x<w; x+=2) {if(i) {fb_buf[(fb_yres-x-1)*fb_xres+y] = Conv_YCbCr_Rgb(y_buf[x<<i],y_buf[(x<<i)+1],cb_buf[(x<<i)>>1],cr_buf[(x<<i)>>1]);fb_buf[(fb_yres-x-2)*fb_xres+y] = Conv_YCbCr_Rgb(y_buf[(x<<i)+yuv_interval[i]],y_buf[(x<<i)+1+yuv_interval[i]],cb_buf[((x<<i)+yuv_interval[i])>>1],cr_buf[((x<<i)+yuv_interval[i])>>1]);} else {rgb_data = Conv_YCbCr_Rgb(y_buf[x<<i],y_buf[(x<<i)+1],cb_buf[(x<<i)>>1],cr_buf[(x<<i)>>1]);fb_buf[(fb_yres-x-1)*fb_xres+y] = rgb_data;fb_buf[(fb_yres-x-2)*fb_xres+y] = fb_buf[x+1] = rgb_data>>16;}}y_buf += image_width<<i;cb_buf += (image_width<<i)>>1;cr_buf += (image_width<<i)>>1;}}}int main(int argc, char *argv[])
{int i, fd, fbfd;struct fb_var_screeninfo vinfo;struct fb_fix_screeninfo finfo;__u8 *fb_buf;__u32 screensize;struct video_capability vc;struct video_window vw;struct video_capture vcp;struct video_picture vp;struct video_mbuf vm;__u8 *buf = NULL;fd_set rfds;struct timeval tv;fbfd = open("/dev/fb0", O_RDWR);if (fbfd < 0) {fbfd = open("/dev/fb/0", O_RDWR);if(fbfd<0) {printf("Error: cannot open framebuffer device.\n");return -1;}}if (ioctl(fbfd, FBIOGET_FSCREENINFO, &finfo)) {		printf("Error reading fixed information.\n");close(fbfd);return -1;}if (ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo)) {printf("Error reading variable information.\n");close(fbfd);return -1;}fb_xres = vinfo.xres;fb_yres = vinfo.yres;fb_bpp  = vinfo.bits_per_pixel;screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8;fb_buf = (char *)mmap(0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED,fbfd, 0);if ((int)fb_buf == -1) {printf("Error: failed to map framebuffer device to memory.\n");close(fbfd);return -1;}fd = open("/dev/video0", O_RDONLY);	//rd&wrif(fd<0) {fprintf(stderr, "Open camera fail!\n");close(fbfd);return -1;} elsefprintf(stdout, "Open camera success\n");if(ioctl(fd, VIDIOCGCAP, &vc)<0)printf("VIDIOCGCAP fail\n");elseprintf("max width %d, height %d\nmin width %d, height %d\n",vc.maxwidth, vc.maxheight, vc.minwidth, vc.minheight);vw.width  = FIXED_SOURCE_WIDTH;	vw.height = FIXED_SOURCE_HEIGHT;if(ioctl(fd, VIDIOCSWIN, &vw)<0)printf("VIDIOCSWIN fail\n");if(ioctl(fd, VIDIOCGWIN, &vw)<0)printf("VIDIOCGWIN fail\n");elseprintf("current width %d, height %d\n",	vw.width, vw.height);if(!image_width||!image_height) {image_width  = fb_xres;//vw.width;image_height = fb_yres;//vw.height;}vcp.width  = image_width;vcp.height = image_height;if(ioctl(fd, VIDIOCGCAPTURE, &vcp)<0)			       printf("VIDIOCGCAPTURE fail\n");		elseprintf("capture width %d, height %d\n", vcp.width, vcp.height);vp.palette = VIDEO_PALETTE_YUV422P;//modify by lzj //vp.palette = optional_image_format[image_format].palette;if(ioctl(fd, VIDIOCSPICT, &vp)<0)printf("VIDIOCSPICT fail\n");if(ioctl(fd, VIDIOCGPICT, &vp)<0)printf("VIDIOCGPICT fail\n");elseprintf("current palette %d\n",	vp.palette);if(ioctl(fd, VIDIOCGMBUF, &vm)<0) printf("VIDIOCGMBUF fail\n");else {printf("current camera buffer size %d, total frames %d\n",vm.size, vm.frames);buf = (__u8 *)mmap(0, vm.size, PROT_READ, MAP_SHARED, fd, 0);if((int)buf==-1) {printf("mmap camera fail!\n");} elseputs("mmap camera ok.\n");}printf("buffer at 0x%08x\n", (int)buf);printf("now start capture...\n");printf("please input 'e'or'E' to exit\n");if(ioctl(fd, VIDIOCCAPTURE, VIDEO_START)<0) {printf("VIDIOCCAPTURE fail\n");}while(1){       FD_ZERO(&rfds);FD_SET(0, &rfds);//FD_SET(fd, &rfds);tv.tv_sec = 0;tv.tv_usec = 0;select(1, &rfds, NULL,  NULL, &tv);if(FD_ISSET(0, &rfds)) {char cmd;scanf("%c",&cmd);getchar();if(cmd=='e'||cmd=='E')break;			} /*  //就是这一段,没看懂,因为驱动里面的read还没看懂if(FD_ISSET(fd, &rfds)){i = read(fd, buf, 0);if(i<0){printf("read fail!%d\n", i);break;}}*/ //modify by lzjshow_cam_img(fb_buf, buf,buf+image_width*image_height,buf+(image_width*image_height*3)/2);					}close(fd);munmap(buf, vm.size);munmap(fb_buf, screensize);close(fbfd);return 0;
}


http://www.ppmy.cn/news/152481.html

相关文章

linux 0v9650驱动分析

学习了裸机OV9650的P通道LCD直接显示程序&#xff0c;作为这点基础开始分析OV9650在linux设备驱动程序。昨天看了点这个驱动程序&#xff0c;让我很郁闷的是写这个程序的人是有毛病还是怎么回事&#xff0c;简简单单的IO口功能引脚的定义&#xff0c;整出了一个套一个的定义&am…

OV9650----摄像头调试笔记

http://blog.chinaunix.net/uid-24486720-id-1664850.html 过4天的调试,摄像头终于可以拍照片保存到电脑上来了&#xff0c;ov9650的调试走了不少弯路&#xff0c;一些教训总结如下&#xff1a; 1:OV9650是OmniVision公司的COMS摄像头&#xff0c;号称有130万像素&#xff0c;…

OV9650摄像头驱动略析

首先要明确一下摄像头工作方式&#xff1a; 一、摄像头是怎么把数据传送给mini2440的呢&#xff1f; 这个摄像头有10个数据口&#xff0c;mini2440通过这些数据口采集摄像头的数据。 二、硬件以什么样的方式交换采集数据呢&#xff1f; 摄像头将采集到的图像数据以一些标准…

ov9650 实时显示app 平台:i.mx6ul linux 3.14.38

本文先简单记录&#xff1a; 1.ov9650采集 摄像头输出yuyv格式 2.写入到framebuffer显示 3.贴yuv2rgb.c的代码如下&#xff1a; tips: 查表法/bpp32 table略 void yuv2rgb(unsigned char *pyuv,unsigned char *prgb,int width, int height){ unsigned char *py p…

ov9650摄像头驱动之——linux内核v4l2架构分析3

NO.3 V4L2的API和数据结构 V4L2是V4L的升级版本&#xff0c;为linux下视频设备程序提供了一套接口规范。包括一套数据结构和底层V4L2驱动接口。 1、常用的结构体在内核目录include/linux/videodev2.h中定义 struct v4l2_requestbuffers //申请帧缓冲&#xff0c;对应命令VIDIOC…

ov9650 的配置

经过4天的调试摄像头终于可以拍照片保存到电脑上来&#xff0c;ov9650的调试走了不少弯路&#xff0c;一些教训总结如下&#xff1a; 1:OV9650是OmniVision公司的COMS摄像头&#xff0c;号称有130万像素&#xff0c;但是实际效果感觉不如CCD的&#xff0c;特别是远处的背景更糟…

基于mini2440的ov9650摄像头裸机测试

mini2440提供了一个摄像头接口&#xff0c;可以输出RGB24,RGB16的原始图像还可以输出编码的如YUV格式的图像&#xff0c;并提供了偏移翻转&#xff0c;放大缩小的功能。与开发板配套的摄像头模块为CAM130,采用ov9650的芯片。操作摄像头接口实现视频的采集与显示&#xff0c;主要…

ov9650学习(2)

已经在2.6.24的内核将飞凌公司提供的ov9650的驱动和测试源码运行成功&#xff0c;不过之前那个天嵌提供的驱动和测试程序还没调通&#xff0c;先mark一下吧。 10月8号更新了第3篇文章实现了解决了很多第1,2篇中的问题&#xff1a;http://blog.csdn.net/liuzijiang1123/article…