Android Camera Architecture

  • Post author:
  • Post category:其他


From:


http://blog.csdn.net/qikaibinglan/archive/2010/12/21/6088337.aspx


1





Camera


成像原理


介绍

Camera工作流程图


image

Camera的成像原理可以简单概括如下:

景物(SCENE)通过镜头(LENS)生成的光学图像投射到图像传感器(Sensor)表面上,然后转为电信号,经过A/D(模数转换)转换后变为数字图像信号,再送到数字信号处理芯片(DSP)中加工处理,再通过IO接口传输到CPU中处理,通过DISPLAY就可以看到图像了。


电荷耦合器件(CCD)



互补金属氧化物半导体(CMOS)

接收光学镜头传递来的影像,经模/数转换器(A/D)转换成数字信号,经过编码后存储。

流程如下:

1、CCD/CMOS将被摄体的光信号转变为电信号—电子图像(模拟信号)

2、由模/数转换器(ADC)芯片来将模拟信号转化为数字信号

3、数字信号形成后,由DSP或编码库对信号进行压缩并转化为特定的图像文件格式储存

数码相机的光学镜头与传统相机相同,将影像聚到感光器件上,即(光)电荷耦合器件(CCD) 。CCD替代了传统相机中的感光胶片的位置,其功能是将光信号转换成电信号,与电视摄像相同。

CCD是半导体器件,是数码相机的核心,其内含器件的单元数量决定了数码相机的成像质量——像素,单元越多,即像素数高,成像质量越好,通常情况下像素的高低代表了数码相机的档次和技术指标。


2





Android Camera


框架

Android的Camera子系统提供一个拍照和录制视频的框架。

它将Camera的上层应用与Application Framework、用户库串接起来,而正是这个用户库来与Camera的硬件层通信,从而实现操作camera硬件。


image


3、Android Camera的代码结构

Android的Camera代码主要在以下的目录中:


Camera的JAVA部分


packages/apps/Camera/。其中Camera.java是主要实现的文件。这部分内容编译成为目标是Camera.apk

com.android.camera这个包,几个主要的类文件如下:

PhotoViewer:GalleryPicker.java(所有图片集)—>ImageGallery.java(某个Folder下图片列表)—>ViewImage.java(看某张具体图片)

VideoPlayer:GalleryPicker.java(所有视频集) —>MovieView.java(看某一个视频)

Camera:Camera.java(Camera取景及拍照)

VideoCamera:VideoCamera.java(VideoCamera取景及摄像)






Camera







framework



供上层应用调用的部分





base/core/java/android/hardware/Camera.java

这部分目标是

framework.jar


Camera的JNI部分


frameworks/base/core/jni/android_hardware_Camera.cpp

这部分内容编译成为目标是libandroid_runtime.so。


Camera UI库部分


frameworks/base/libs/ui/camera

这部分的内容被编译成库libcamera_client.so。


Camera服务部分


frameworks/base/camera/libcameraservice/

这部分内容被编译成库libcameraservice.so。


Camera HAL层部分


hardware/msm7k/libcamera



vendor/qcom/android-open/libcamera2

为了实现一个具体功能的Camera,在HAL层需要一个硬件相关的Camera库(例如通过调用video for linux驱动程序和Jpeg编码程序实现或者直接用各个chip厂商实现的私有库来实现,比如Qualcomm实现的libcamera.so和libqcamera.so),实现CameraHardwareInterface规定的接口,来调用相关的库,驱动相关的driver,实现对camera硬件的操作。这个库将被Camera的服务库libcameraservice.so调用。

高通Android平台硬件调试之Camera

高通android平台上调试2款camera sensor,一款是OV的5M YUV sensor,支持jpeg out,同时也支持AF,调试比较比较简单,因为别的项目已经在使用了,只是把相关的驱动移植过来就好;另一款是Samsung的一款比较新的3M YUV FF sensor,在最新项目中要使用的,本文以调试该sensor为例,从底层驱动的角度分享一下高通android平台下调试camera的经验,而对于高通平台camera部分的架构以及原理不做过多的介绍。

一、准备工作

从项目中看,在硬件(板子)ready前,软件部分是要准备好的。单独从底层驱动来看,软件部分可以分为2个部分,一个是高通平台相关的,再一个就是sensor部分的,通常的做法就是把sensor相关的设定移植到高通平台的框架之中。这样就需要先拿到sensor的spec以及厂商提供的sensor register setting file。Spec的用途是清楚高通平台和sensor通讯(读写寄存器)的时序以及相关参数设定;而厂商提供的setting file则是在使用camera各个功能(preview、snapshot…)时候需要写入到sensor中的.

本项目中,高通平台为MSM7X27,camera为Samsung 5CA。从spec中知道,该sensor的I2C ID为0x78,I2C的通信采用双字节方式,另外也弄清楚了读写sensor寄存器的规则,从调试角度看这些基本上够用了。另外厂商提供的setting file,其实就是寄存器列表,告诉我们再什么时候将哪些寄存器写入什么值,通常是一个寄存器地址再加上一个寄存器的值,不过Samsung提供的是PC上调试使用的文本,需要自己转换成c语言中的二维数组。从文件中看,寄存器数据可以分为几个部分:初始化、IQ设定(tuning相关)、clk设定、preview设定、snapshot设定,基本上有这几个就够了,其他的比如调节亮度啦、设定特殊效果啦、设置白平衡啦等等都可以自己通过spec来完成。

Sensor部分的东西搞定后,接下来就是修改高通camera部分的驱动了,主要有:

Kernal部分:

1、检查Sensor的电源配置,并修改软件中的设定。本项目中使用2.8/1.8/1.5共3个电源。

2、检查并修改sensor reset设置。注意reset的时间设定,务必和spec中一致,否则会导致sensor无法工作。

3、修改I2C驱动,使用双字节读写的接口,并完成读取sensor ID的接口。这个用来检验I2C通讯是否OK

4、导入寄存器设定,分别在初始化、preview、snapshot等几个部分写入对应的寄存器值。

注意:reset以及写寄存器部分一定要按照spec的规定加入一些delay,否则会导致sensor工作异常

User空间部分:

这个部分主要是根据硬件的规格来配置VFE,如sensor输出数据的格式,接口方式、分辨率大小、同步信号模式等,比较简单,但一定要检查仔细,任何一个地方不对都会导致调试失败。

到这里为止,软件部分的准备已经告一段落了。

二、调试环境准备(板子出来了,但sensor sample还没到位)

首先,测试点的准备。

调试前就需要想好,如果sensor无法工作,要怎么去debug,这就需要去测量一些信号,比如power、reset、I2C、M/P CLK、H/V同步信号、数据信号等,要确保这些信号都可以测量到。

其次要选择软件的调试环境,这里选择在ADB环境中执行高通的mm-qcamera-test程序来调试,相关的trace都可以打印出来。

这样就万事俱备,只欠sensor了。

三、调试(sensor终于拿到了)

将sensor接到板子上,开机后,ADB中运行调试程序,preview画面并没有出来,失败,有点小失望,本来觉得可以一气呵成的,但毕竟这是一个全新的sensor,任何一个地方没有想到位做到位都会导致失败。那就找原因吧。

1、首先从trace得知,I2C已经读到了sensor的ID:0x05CA,这可以说明I2C通讯是没有问题的

2、接着检查Sensor的电源配置,测量了供给sensor的3个电源,都是OK的。

3、测量MCLK,这个是提供给sensor使用的,正常(24MHZ)

4、测量PCLK,这个是sensor输出的,正常(58MHZ,高通上限为96MHZ),和寄存器中配置的一致。

5、测量H/V同步信号,这个是sensor输出的,正常。和FPS和分辨率一致。

6、测量数据信号,这个是sensor输出的,正常。(数据信号,示波器上可以看到)

这样看来,sensor已经在正常工作了,但为何preview画面没有出来呢?继续检查高通这边的设定。

从trace看,高通的VFE已经reset并且start了,但一直接没有输出preview数据,这就奇怪了,sensor明明已经输出了,为什么VFE接收后并没有把数据吐出来呢,难道这个sensor输出的数据VFE无法识别?为了验证这个问题,我在另一块板子上测量了OV sensor输出数据的波形,主要是M/P clk、H/V同步信号,然后再拿来对比,不过并没有发现异常,只是H/V同步信号有所不同,主要高低的占空比不太一致,会不会是这样信号的问题呢?为了进一步验证,我同时测量了H/V 信号和数据信号,这时发现OV sensor输出的数据信号是包在V帧同步信号的低电平中;而Samsung 5CA输出的数据信号是包在V帧同步信号的高电平中,会不会是因为V信号极性设置不对导致VFE没有读取到sensor输出的数据呢?重新检查了一下高通VFE的设定,果然有一个参数是用来设定V信号极性的,这个参数默认是Active Low的,我这边并没有去修改它。接着把这个参数修改为Active High,重新build、download后,开机运行,Ok了,preview画面可以正常显示了。到这里为止sensor的硬件调试可以算作完成了,后续的其他功能也可以慢慢完善了。

FSL调试之Camera

fsl的camera hal层没有实现上层到下层的设置参数的接口,所以需要自己实现。好在从应用到hal层的参数已经弄好,否则工作量就更大了。

参数设置在hal层调用的函数是status_t CameraHal::setParameters(const CameraParameters& params)。在这个函数里实现对每个参数的设置。参数设置主要通过 CameraParameters这个类实现的。通过观察这个类发现,里面有个get()函数,可以分别得到各个参数。如

const char *white_balance = params.get(CameraParameters::KEY_WHITE_BALANCE);这个可以得到目前白平衡的参数即返回值。然后根据返回值判断是哪种情况,如

if (strcmp(white_balance, CameraParameters::WHITE_BALANCE_AUTO) == 0) { //判断为自动白平衡

LOGV(“white_balance to ioctl is auto !/n”);

ctl.id = V4L2_CID_AUTO_WHITE_BALANCE; //自动白平衡命令,ctl为v4l2_control结构,该结构很有用

ctl.value = 1;

if (ioctl(camera_device, VIDIOC_S_CTRL, &ctl) < 0){ //通过 VIDIOC_S_CTRL把ctl结构体传下去

LOGE(“set control failed/n”);

//return -1;

}

}else if(strcmp(white_balance, CameraParameters::WHITE_BALANCE_INCANDESCENT) == 0){ //白炽灯模式

LOGV(“white_balance to ioctl is incandescent !/n”);

ctl.id = V4L2_CID_DO_WHITE_BALANCE; //其它白平衡情况都用该命令

ctl.value = 2; //根据用户自己定义的白平衡模式数目排列

if (ioctl(camera_device, VIDIOC_S_CTRL, &ctl) < 0){ //同样通过 VIDIOC_S_CTRL把ctl结构体传下去,然后在根据value值分情况讨论

LOGE(“set control failed/n”);

//return -1;

}

}

传到驱动的mxc_v4l2_capture.c文件的mxc_v4l_ioctl中,mxc_v4l_ioctl调用mxc_v4l_do_ioctl,mxc_v4l_do_ioctl对命令的解释如下

/*!

* V4l2 VIDIOC_S_CTRL ioctl

*/

case VIDIOC_S_CTRL: {


pr_debug(” case VIDIOC_S_CTRL/n”);

retval = mxc_v4l2_s_ctrl(cam, arg);

break;

}

这样就到了mxc_v4l2_s_ctrl。在mxc_v4l2_s_ctrl通过对ctl.id分情况调用

switch (c->id) {


……

case V4L2_CID_AUTO_WHITE_BALANCE:

ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi, true, true);

ret = vidioc_int_s_ctrl(cam->sensor, c); //该函数是v4l2对应ov7670驱动中的s_ctl

ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi, false, false);

break;

case V4L2_CID_DO_WHITE_BALANCE:

ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi, true, true);

ret = vidioc_int_s_ctrl(cam->sensor, c);

ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi, false, false);

break;

……

其中vidioc_int_s_ctrl()是v4l2对应ov7670驱动中的 ioctl_s_ctrl,具体代码怎么对应由于篇幅原因就不贴出来。

根据ctl结构体的id分情况去实现即可。

switch (vc->id) {


…..

case V4L2_CID_AUTO_WHITE_BALANCE:

retval = ov7670_autowhitebalance(vc->value);

break;

case V4L2_CID_DO_WHITE_BALANCE:

retval = ov7670_dowhitebalance(vc->value);

break;

……

下面是whitebalance函数的实现

static int ov7670_autowhitebalance(int value)

{


unsigned char v = 0;

int ret;

printk(“0v7670_autowhitebalance called/n”);

ret = ov7670_read(ov7670_data.i2c_client, REG_COM8, &v);

if (value)

v |= COM8_AWB; //自动白平衡

msleep(10); /* FIXME */

ret += ov7670_write(ov7670_data.i2c_client, 0x01, 0x56);

ret += ov7670_write(ov7670_data.i2c_client, 0x02, 0x44);

ret += ov7670_write(ov7670_data.i2c_client, REG_COM8, v);

return ret;

}

static int ov7670_dowhitebalance(int value)

{


unsigned char v = 0;

int ret;

printk(“0v7670_dowhitebalance called value:%d/n”,value);

ret = ov7670_read(ov7670_data.i2c_client, REG_COM8, &v);

if (value)

v &= ~COM8_AWB; //关闭自动白平衡

msleep(10); /* FIXME */

ret += ov7670_write(ov7670_data.i2c_client, REG_COM8, v);

if(value == 2) //INCANDESCENCE //这个值就是ctl的value值

{


ret += ov7670_write(ov7670_data.i2c_client, 0x01, 0x8c);

ret += ov7670_write(ov7670_data.i2c_client, 0x02, 0x59);

}else if(value == 3) //FLUORESCENT

{


ret += ov7670_write(ov7670_data.i2c_client, 0x01, 0x7e);

ret += ov7670_write(ov7670_data.i2c_client, 0x02, 0x49);

}else if(value == 4) //DAYLIGHT

{


ret += ov7670_write(ov7670_data.i2c_client, 0x01, 0x52);

ret += ov7670_write(ov7670_data.i2c_client, 0x02, 0x66);

}

return ret;

}

其中函数中ox01、0x02分别是蓝红通道的增益的寄存器。

上面是白平衡从hal层最终到sensor的参数设置过程。其它如色彩效果、取景模式等都是同样的过程。

取景模式根据具体的情况如夜间模式等设定具体的寄存器即可

色彩效果主要通过设置uv的值实现的