Camera driver&V4L2驱动架构介绍

转自:http://www.cnblogs.com/AdiSky/archive/2012/09/05/2671963.html

大约一年前写的东西,介绍性部分是当时在网络上找的内容,后面的分析部分是基于当时的项目,基于Mavell Pxa920,希望对初学者有点点的帮助吧。转载请注明出。

 

1.       Camera相关介绍

1.1.  手机Camera介绍

手机的数码相机功能指的是手机通过内置数码相机进行拍摄静态图片或短片拍摄,作为手机的一项新的附加功能,手机的数码相机功能得到了迅速的发展。

手机摄像头分为内置与外置,内置摄像头是指摄像头在手机内部,更方便。外置手机通过数据线或者手机下部接口与数码相机相连,来完成数码相机的一切拍摄功能。

处于发展阶段的手机的数码相机的性能应该也处于初级阶段,带有光学变焦的手机目前国内销售的还没有这个功能,不过相信随着手机数码相机功能的发展,带有光学变焦的手机也会逐渐上市,但大部分都拥有数码变焦功能。

目前手机的数码相机功能主要包括拍摄静态图像,连拍功能,短片拍摄,镜头可旋转,自动白平衡,内置闪光灯等等。手机的拍摄功能是与其屏幕材质、屏幕的分辨率、摄像头像素、摄像头材质有直接关系。

 

1.2.  Camera技术指标

1.2.1.    图像压缩方式JPEG

    (joint photographic expert group)静态图像压缩方式。一种有损图像的压缩方式。压缩比越大,图像质量也就越差。当图像精度要求不高存储空间有限时,可以选择这种格式。目前大部分数码相机都使用JPEG格式。

 

1.2.2.   图像噪音

    指的是图像中的杂点干扰,表现为图像中有固定的彩色杂点。

 

1.2.3.    视角

    与人的眼睛成像是相似原理,简单说就是成像范围。

 

1.2.4.   白平衡处理技术(AWB)

    要求在不同色温环境下,照白色的物体,屏幕中的图像应也是白色的。色温表示光谱成份,光的颜色。色温低表示长波光成分多。当色温改变时,光源中三基色(红、绿、蓝)的比例会发生变化,需要调节三基色的比例来达到彩色的平衡,这就是白平衡调节的实际。

图象传感器的图象数据被读取后,系统将对其进行针对镜头的边缘畸变的运算修正,然后经过坏像处理后被系统送进去进行白平衡处理(在不同的环境光照下,人类的眼睛可以把一些“白”色的物体都看成白色,是因为人眼进行了修正。但是SENSOR没有这种功能,因此需要对SENSOR输出的信号进行一定的修正,这就是白平衡处理技术)。

 

1.2.5.   电源

    好的摄像头内部电源也是保证摄像头稳定工作的一个因素。

 

1.2.6.   彩色深度(色彩位数)

    反映对色彩的识别能力和成像的色彩表现能力,就是用多少位的二进制数字来记录三种原色。实际就是A/D转换器的量化精度,是指将信号分成多少个等级,常用色彩位数(bit)表示。彩色深度越高,获得的影像色彩就越艳丽动人。非专业的SENSOR一般是24位;专业型SENSOR至少是36位。24位的SENSOR,感光单元能记录的光亮度值最多有2^8=256级,每一种原色用一个8位的二进制数字来记录,最多记录的色彩是256×256×256约16,77万种。

36位的SENSOR,感光单元能记录的光亮度值最多有2^12=4096级,每一种原色用一个12位的二进制数字来记录,最多记录的色彩是4096×4096×4096约68.7亿种。

 

1.2.7.   输出/输入接口(IO)

    串行接口(RS232/422):传输速率慢,为115kbit/s。

    并行接口(PP):速率可以达到1Mbit/s。

    红外接口(IrDA):速率也是115kbit/s,一般笔记本电脑有此接口。

    通用串行总线USB:即插即用的接口标准,支持热插拔。USB1.1速率可12Mbit/s,USB2.0可达480bit/s。

     IEEE1394(火线)接口(亦称ilink):其传输速率可达100M~400Mbit/s。

 

1.2.8.   图像格式(image Format/ Color space)

 RGB24,I420是目前最常用的两种图像格式。RGB24:表示R、G、B三种颜色各8bit,最多可表现色。

 I420:YUV格式之一。

 

 其它格式有: RGB565,RGB444,YUV4:2:2等。

 

1.2.9.   分辨率(Resolution)

所谓分辨率就是指画面的解析度,由多少象素构成的数值越大,图像也就越清晰。分辨率不仅与显示尺寸有关,还会受到显像管点距、视频带宽等因素的影响。我们通常所看到的分辨率都以乘法形式表现的,比如1024*768,其中的1024表示屏幕上水平方向显示的点数,768表示垂直方向的点数。

 

QXGA (2048 X 1536)又称300万像素

UXGA (1600X 1200)又称200万像素

SXGA(1280 x1024)又称130万像素

XGA(1024 x768)又称80万像素

SVGA(800 x600)又称50万像素

VGA(640x480)又称30万像素(35万是指648X488)

CIF(352x288) 又称10万像素

SIF/QVGA(320x240)

QCIF(176x144)

QSIF/QQVGA(160x120)

 

1.3.  备注

1.3.1.   Mipi接口介绍:

MIPI=Mobile Industry Processor Interface, 是类似SMIA的一个LVDS的一种接口,主要用在手机Camera Module上居多。

        就CameraModule而言,现在Micorn和OV均推出支持MIPI接口的Sensor如Micorn的MT9D112,MT9T111和OV的OV2650等,对于低像素的Sensor似乎MIPI的优势不是很明显哦,但是在3MP以上就可能有些优势了。

        优势-1,Camera的布线大大减少。并口的数据接口,如果是YUV输出至少为8个数据Bit、2个Clock(MCLK和PCLK)、I2C两个、同步信号2个,再加地和电源等,如果换成MIPI的串口,可以减少2个同步信号,8个数据Bit变为DOUT_P、DOUT_N、CLK_P、CLK_N,PCLK也可以不要,卓实少了很多,布线自然方便许多。

       优势-2,Noise的减少。走线越多被干扰的可能就越多,走线少了于是干扰就少了,同时MIPI信号是DOUT_N和DOUT_P成对走线,需要考虑impedance,两根线从波形看是成反相,所以有外部干扰过来,就会被抵消很大部分,同时MIPI的信号属于LVDS(Low Voltage Differential Signaling:低压差分信号传输)底到MV的等级,于是他本身对于外部的干扰也是很小的。

       优势-3,传输速度极快,从并口到串口,当然要足够大的速度,MIPI的理论上的速度可以到80MB/s-1GB/s,实际也在600-800MB/s,而传统的并口再高也不过600MB/s了吧。

      优势-4,功耗低。并口的Camera,只要上电,给Clock于是PCLK就有输出,Data也会由输出,抓不到同步就成不了像,但是数据还是输出,于是就要功耗。而MIPI理论上静态是没有功耗的。

  

2.       Linux Camera Driver

2.1.   代码结构

其中以ov5640为例,其他carmera相关carmere driver 同理。

 

 

 

2.2.  重要接口实现说明

ov5640_mipi_probe()

{

    …..

    ccic_set_clock_mipi();  //ccic mipi clock 设置

pdata->power_on(1, 1);  //power on camera

i2c_set_clientdata(client, info); //初始化I2c资源

ov5640_mipi_detect(client);  //camera检测

ccic_sensor_attach(client);  //ccic host端与sensor driver绑定

…..

}

说明:camera 驱动启动时调用的初始化操作函数,包括camera上电、ccic控制器mpi clock设置、i2c端口初始化、camera检测与绑定等。

 

ov5640_mipi_comman()

{

v4l2_chip_ident_i2c_client () 

ov5640_mipi_reset()

ov5640_mipi_enum_fmsize () 

ov5640_mipi_querycap ()   

ov5640_mipi_enum_fmt () 

ov5640_mipi_try_fmt ()

ov5640_mipi_s_fmt()

ov5640_mipi_queryctrl()

ov5640_mipi_s_ctrl()

ov5640_mipi_g_ctrl()

ov5640_mipi_s_input()

ov5640_mipi_streamon()

ov5640_mipi_streamoff()

ov5640_mipi_g_register()

ov5640_mipi_s_register()

 

}

说明:camera iocontorl函数

v4l2_chip_ident_i2c_client() //为摄像头分配一个独立字段,由于双摄像头区分之用

ov5640_mipi_reset   //camera重启功能

ov5640_mipi_enum_fmsize  // 枚举camera支持的priver and capture 的图像大小

ov5640_mipi_querycap   //对应hal层识别的camera device的关键字符串赋值

ov5640_mipi_enum_fmt  //枚举camera支持的图像格式

ov5640_mipi_try_fmt ()   //根据传入的图像格式及大小,计算图像的宽和高占用的字节数

ov5640_mipi_s_fmt()  //根据传入的图像格式及大小,对camera寄存器进行相应的设置

ov5640_mipi_s_ctrl() //预留函数,对camera的一些特效设置,进行对应的寄存器设置,现在这部分功能已经在hal层实现。

ov5640_mipi_g_ctrl()//预留函数,获取camera的一些特效设置对应的寄存器设置值,现在这部分功能已经在hal层实现。

ov5640_mipi_s_input() //camera寄存器初始化

ov5640_mipi_streamon()  //camera 数据流开

ov5640_mipi_streamoff()  //camera 数据关

ov5640_mipi_g_register()  //读取camera某一寄存器的值

ov5640_mipi_s_register()  //设置 camera某一寄存器的值

 

  

3.       Host控制器实现(基于V4L2)

3.1.  V4L2知识

Video4linux2(简称V4L2),是linux中关于视频设备的内核驱动。在Linux中,视频设备是设备文件,可以像访问普通文件一样对其进行读写,摄像头在/dev/video0下

 

3.1.1.   一般操作流程(视频设备):

1.打开设备文件。 int fd=open(”/dev/video0″,O_RDWR);

2.取得设备的capability,看看设备具有什么功能,比如是否具有视频输入,或者音频输入输出等。VIDIOC_QUERYCAP,struct v4l2_capability

3.设置视频的制式和帧格式,制式包括PAL,NTSC,帧的格式个包括宽度和高度等。

VIDIOC_S_STD,VIDIOC_S_FMT,struct v4l2_std_id,struct v4l2_format

4.向驱动申请帧缓冲,一般不超过5个。struct v4l2_requestbuffers

5.将申请到的帧缓冲映射到用户空间,这样就可以直接操作采集到的帧了,而不必去复制。mmap

6.将申请到的帧缓冲全部入队列,以便存放采集到的数据.VIDIOC_QBUF,struct v4l2_buffer

7.开始视频的采集。VIDIOC_STREAMON

8.出队列以取得已采集数据的帧缓冲,取得原始采集数据。VIDIOC_DQBUF

9.将缓冲重新入队列尾,这样可以循环采集。VIDIOC_QBUF

10.停止视频的采集。VIDIOC_STREAMOFF

11.关闭视频设备。close(fd);

 

3.1.2.   在进行V4L2开发中,一般会用到以下的命令标志符:

1 VIDIOC_REQBUFS:分配内存

2 VIDIOC_QUERYBUF:把VIDIOC_REQBUFS中分配的数据缓存转换成物理地址

3 VIDIOC_QUERYCAP:查询驱动功能

4 VIDIOC_ENUM_FMT:获取当前驱动支持的视频格式

5 VIDIOC_S_FMT:设置当前驱动的频捕获格式

6 VIDIOC_G_FMT:读取当前驱动的频捕获格式

7 VIDIOC_TRY_FMT:验证当前驱动的显示格式

8 VIDIOC_CROPCAP:查询驱动的修剪能力

9 VIDIOC_S_CROP:设置视频信号的边框

10 VIDIOC_G_CROP:读取视频信号的边框

11 VIDIOC_QBUF:把数据从缓存中读取出来

12 VIDIOC_DQBUF:把数据放回缓存队列

13 VIDIOC_STREAMON:开始视频显示函数

14 VIDIOC_STREAMOFF:结束视频显示函数

15 VIDIOC_QUERYSTD:检查当前视频设备支持的标准,例如PAL或NTSC。

这些IO调用,有些是必须的,有些是可选择的。

 

3.2.  Host控制器(video device)代码结构

这里主要介绍920 host端 camera video控制器实现,主要基于v4l2架构,code文件见pxa910_camera.c。

 

 

 

3.3.  部分重要接口实现说明

如3.2中结构图所示,camera host端是被当作标准的v4l2架构来注册的的,也就是说是按照 struct video_device 结构体(标准的视频设置)来向系统注册设备的。

 

pxa910_camera_probe()

{

   ……

platform_set_drvdata(pdev, cam);

init_waitqueue_head()

  request_irq()  // ccic irq 中断注册

ccic_ctlr_init()    //ccic 控制器初始化

video_register_device()

   ……

 

}

说明:camera video 设备启动时调用的初始化操作函数

platform_set_drvdata(pdev, cam) ://连接设备对象与设备驱动

init_waitqueue_head()  ://申明等待事件,处理见ccic_v4l_poll函数

request_irq():ccic irq 中断注册,对应中断处理函数见 ccic_irq()函数

ccic_ctlr_init() :ccic 控制器初始化

video_register_device ://v4l2 video 设备注册

 

v4l2 video主要需实现ccic_v4l_fops : (struct v4l2_file_operations) 和ccic_ioctl_ops : (struct v4l2_ioctl_ops)

static const struct v4l2_file_operations ccic_v4l_fops = {

       .owner = THIS_MODULE,

       .open = ccic_v4l_open,  //open files

       .release = ccic_v4l_release, //release files resource

       .poll = ccic_v4l_poll,  //files poll 功能对于接口

       .mmap = ccic_v4l_mmap, //files 内存映射接口

       .ioctl = ccic_v4l_ioctl, //files 对应iocontorl操作接口

};

说明:v4l2 video作为文件接口对应的相关操作

 

struct v4l2_ioctl_ops ccic_ioctl_ops = {

         .vidioc_querycap   = ccic_vidioc_querycap,

         .vidioc_enum_fmt_vid_cap   = ccic_vidioc_enum_fmt_cap,

         .vidioc_try_fmt_vid_cap        = ccic_vidioc_try_fmt_cap,

         .vidioc_s_fmt_vid_cap   = ccic_vidioc_s_fmt_cap,

         .vidioc_g_fmt_vid_cap  = ccic_vidioc_g_fmt_cap,

         .vidioc_enum_input      = ccic_vidioc_enum_input,

         .vidioc_g_input         = ccic_vidioc_g_input,

         .vidioc_s_input         = ccic_vidioc_s_input,

         .vidioc_s_std           = ccic_vidioc_s_std,

         .vidioc_reqbufs         = ccic_vidioc_reqbufs,

         .vidioc_querybuf        = ccic_vidioc_querybuf,

         .vidioc_qbuf            = ccic_vidioc_qbuf,

         .vidioc_dqbuf           = ccic_vidioc_dqbuf,

         .vidioc_streamon        = ccic_vidioc_streamon,

         .vidioc_streamoff       = ccic_vidioc_streamoff,

         .vidioc_queryctrl       = ccic_vidioc_queryctrl,

         .vidioc_g_ctrl          = ccic_vidioc_g_ctrl,

         .vidioc_s_ctrl          = ccic_vidioc_s_ctrl,

         .vidioc_g_parm          = ccic_vidioc_g_parm,

         .vidioc_s_parm          = ccic_vidioc_s_parm,

         .vidioc_cropcap               = ccic_vidioc_cropcap,

         .vidioc_g_register      = ccic_vidioc_g_register,

         .vidioc_s_register      = ccic_vidioc_s_register,

}

说明:v4l2 video设备io contorl对应的处理函数。

ccic_vidioc_querycap() //查询驱动功能,获取camera属性

ccic_vidioc_enum_fmt_cap()  获取当前驱动支持的视频格式

ccic_vidioc_try_fmt_cap()  //对应调用ov5640_mipi_try_fmt ()的接口,功能见上一章

ccic_vidioc_s_fmt_cap()  //设置当前驱动的频捕获格式

ccic_vidioc_g_fmt_cap()  //读取当前驱动的频捕获格式

ccic_vidioc_enum_input()   //支持的camera检测,容错处理

ccic_vidioc_g_input()  //获取camera id(30万sensor_selected 为0 ,500万sensor_selected为1)

ccic_vidioc_s_input()  //设置要切换的camera id

ccic_vidioc_reqbufs()   //分配buf内存

ccic_vidioc_querybuf, //把ccic_vidioc_reqbufs中分配的数据缓存转换成物理地址

ccic_vidioc_qbuf()  //把缓冲放回缓存队列

ccic_vidioc_dqbuf()  //把数据从缓存中读取出来

ccic_vidioc_streamon()  //开始视频显示函数

ccic_vidioc_streamoff() // 结束视频显示函数

ccic_vidioc_queryctrl()  //对应调用ov5640_mipi_queryctrl()的接口,功能见上一章

ccic_vidioc_g_ctrl()  //对应调用ov5640_mipi_ g_ctrl ()的接口,功能见上一章

ccic_vidioc_s_ctrl()  //对应调用ov5640_mipi_ s_ctrl ()的接口,功能见上一章

ccic_vidioc_cropcap()   //预留接口,查询驱动的修剪能力

ccic_vidioc_g_register()  //对应调用ov5640_mipi_g_registe ()的接口,功能见上一章

ccic_vidioc_s_register()  //对应调用ov5640_mipi_ s_register()的接口,功能见上一章

 

 

3.4. 备注

Camera视频数据流流动有以下主要相关函数:

ccic_vidioc_reqbufs()   //分配部分内存

ccic_vidioc_querybuf, //把ccic_vidioc_reqbufs中分配的数据缓存转换成物理地址

ccic_vidioc_qbuf() //把缓冲放回缓存队列

ccic_vidioc_dqbuf()  //把数据从缓存中读取出来

ccic_irq() //ccic中断处理函数

ccic_v4l_poll,  //files poll 功能对于接口

check_jpeg_header //检测抓取数据是否为jpeg文件

 

具体数据如何流动,见下图:(来自marrvel提供的PXA920 CCIC sensor bring up guide.pdf)

 

说明:VIDIOC_QBUF对应函数ccic_vidioc_qbuf()

      VIDIOC_QBUF对应函数ccic_vidioc_dqbuf()

      Dma_map_page //将dma buffer 数据映射到kernel内存空间

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Qt中使用V4L2驱动摄像头需要以下步骤: 1. 打开摄像头设备文件 ``` int fd = open("/dev/video0", O_RDWR); if (fd == -1) { perror("Failed to open device"); return -1; } ``` 2. 设置摄像头参数 ``` struct v4l2_format format; format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; format.fmt.pix.width = 640; format.fmt.pix.height = 480; format.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; if (ioctl(fd, VIDIOC_S_FMT, &format) == -1) { perror("Failed to set format"); return -1; } ``` 3. 请求缓冲区 ``` struct v4l2_requestbuffers req; req.count = 4; req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; req.memory = V4L2_MEMORY_MMAP; if (ioctl(fd, VIDIOC_REQBUFS, &req) == -1) { perror("Failed to request buffers"); return -1; } ``` 4. 映射缓冲区 ``` struct buffer { void *start; size_t length; }; struct buffer *buffers = (struct buffer *)calloc(req.count, sizeof(*buffers)); for (int i = 0; i < req.count; i++) { struct v4l2_buffer buf; buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; buf.index = i; if (ioctl(fd, VIDIOC_QUERYBUF, &buf) == -1) { perror("Failed to query buffer"); return -1; } buffers[i].length = buf.length; buffers[i].start = mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.offset); if (buffers[i].start == MAP_FAILED) { perror("Failed to map buffer"); return -1; } } ``` 5. 将缓冲区入队 ``` for (int i = 0; i < req.count; i++) { struct v4l2_buffer buf; buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; buf.index = i; if (ioctl(fd, VIDIOC_QBUF, &buf) == -1) { perror("Failed to queue buffer"); return -1; } } ``` 6. 开始采集 ``` enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; if (ioctl(fd, VIDIOC_STREAMON, &type) == -1) { perror("Failed to start streaming"); return -1; } ``` 7. 循环读取数据 ``` while (true) { fd_set fds; FD_ZERO(&fds); FD_SET(fd, &fds); if (select(fd + 1, &fds, NULL, NULL, NULL) == -1) { perror("Failed to select"); return -1; } if (!FD_ISSET(fd, &fds)) { continue; } struct v4l2_buffer buf; buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; if (ioctl(fd, VIDIOC_DQBUF, &buf) == -1) { perror("Failed to dequeue buffer"); return -1; } // 处理数据 process_buffer(buffers[buf.index].start, buffers[buf.index].length); if (ioctl(fd, VIDIOC_QBUF, &buf) == -1) { perror("Failed to queue buffer"); return -1; } } ``` 完整代码:https://github.com/chenxinyang2006/qt-v4l2-camera

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值