camera调试过程借鉴
在android的模拟器上面调用系统的摄像机会出现异常的现象,会自动退出,这可能和模拟器有关,模拟器无法使用系统的硬件,就像重力感应和方向感应系统一样都无法使用,甚至是GPS
导航系统也无法使用,好像有个第三方的组织,已经模拟出来模拟器上使用重力感应。但是我没有真正的看到,下面的这几段是一本书上面写的android上面使用模拟器的例子。
这个示例将显示预览图像,在图像上点击则触发拍照操作,拍照成功后显示所拍的照片,然后点击Start菜单可以继续拍照。
首先通过OPhone开发工具(ODT)创建一个OPhone项目,填写基本信息后ODT会自动创建一个\src\org\goodev\camera\CameraActivity.java文件和res\layout\main.xml文件。
把main.xml文件内容修改为如下:
view plaincopy to clipboardprint?
1. <?xml version="1.0" encoding="utf-8"?>
2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
3. android:orientation="vertical"
4. android:layout_width="fill_parent"
5. android:layout_height="fill_parent"
6. >
7. <SurfaceView android:id="@+id/camera"
8. android:layout_width="fill_parent"
9. android:layout_height="fill_parent"/>
10. <ImageView android:id="@+id/image"
11. android:layout_width="fill_parent"
12. android:layout_height="fill_parent"/>
13. </LinearLayout>
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <SurfaceView android:id="@+id/camera" android:layout_width="fill_parent" android:layout_height="fill_parent"/> <ImageView android:id="@+id/image" android:layout_width="fill_parent" android:layout_height="fill_parent"/> </LinearLayout>
SurfaceView 用来预览图像,ImageView 用来显示所拍的照片。这里也需读者会问,为什么要使用SurfaceView 来预览图像?使用其他的View不可以吗?要回答这个问题,需要了解下SurfaceView 的作用,关于SurfaceView 的详细介绍已超出了本教程的范围,我们会在后续教程中来详细介绍SurfaceView ,这里只做简要介绍。
在通常情况下,OPhone程序中的View都是在同一个GUI线程中绘制的,该线程也是接收用户交互事件的线程(例如:按钮点击事件)。从另外的线程修改GUI元素是不可以的,如果要迅速的更新UI显示该如何办?显然在主线程中还需要处理其他事件,不适合做这件事情,所以OPhone提供了SurfaceView 来满足这种需求。一个SurfaceView 包装一个Surface对象(通过SurfaceHolder操作该对象)而不是Canvas对象,这就是关键所在,Surface可以在其他线程中绘制,这对于周期性更新和要求高帧率的场景来说是很有用的,特别是在游戏开发中。Surface中包含了当前UI的原生数据(raw data),在不同的软件和硬件条件下对这些数据的处理是不一样的,这就可以通过一些设置来加速图形的绘制,可以通过SurfaceHolder的setType函数来设置,目前接收如下的参数:
· SURFACE_TYPE_NORMAL:用RAM缓存原生数据的普通Surface
· SURFACE_TYPE_HARDWARE:适用于DMA(Direct memory access )引擎和硬件加速的Surface
· SURFACE_TYPE_GPU:适用于GPU加速的Surface
· SURFACE_TYPE_PUSH_BUFFERS:表明该Surface不包含原生数据,Surface用到的数据由其他对象提供,在Camera图像预览中就使用该类型的Surface,有Camera负责提供给预览Surface数据,这样图像预览会比较流畅。如果在这里设置了上面三种类型则可以发现不会出现预览图像,在和Camera底层的预览机制实现有关,如果对预览有特殊要求的可以现实PreviewCallback 接口来自己处理。
关于SurfaceView先简单介绍到这里,下面来继续看看如何在代码中实现。
修改CameraActivity类使其实现如下接口:
view plaincopy to clipboardprint?
1. android.view.SurfaceHolder.Callback
2. android.view.View.OnClickListener
原创 android 之摄像头
用google查了一下camera方面的资料,前几页的检索内容居然一模一样的,真是浪费时间,严重鄙视一下。
既然要用到硬件肯定要牵涉到权限,
在 中加入camera的权限:
<uses-permission android:name="android.permission.CAMERA"></uses-permission>
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
调用camera最简单的办法是调用系统的功能,然后通过onActivityResult方法获得图像数据。
不是太习惯用android的xml配置文件,但是为了代码简单,先加一个layout.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView android:text="Camera Demo" android:id="@+id/TextView01"
android:layout_width="fill_parent" android:layout_height="wrap_content"></TextView>
<RelativeLayout android:id="@+id/FrameLayout01" android:layout_weight="1"
android:layout_width="fill_parent" android:layout_height="fill_parent"></RelativeLayout>
<Button android:text="test" android:id="@+id/Button01"
android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center"></Button>
</LinearLayout>
系统camera的uri为:
android.media.action.IMAGE_CAPTURE
final int TAKE_PICTURE = 1;
ImageView iv;
private void test1(){
iv = new ImageView(this);
((FrameLayout)findViewById(R.id.FrameLayout01)).addView(iv);
Button buttonClick = (Button)findViewById(R.id.Button01);
buttonClick.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View arg0) {
startActivityForResult(new Intent("android.media.action.IMAGE_CAPTURE"), TAKE_PICTURE);
}
});
}
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == TAKE_PICTURE) {
if (resultCode == RESULT_OK) {
Bitmap b = (Bitmap) data.getExtras().get("data");
iv.setImageBitmap(b);
}
}
}
系统功能很简单,呵呵,不过不能满足俺小小的控制欲,看看camera类能干些什么。
首先扫描了一下camera,感觉camera主要是用到几个接口:
1.需要SurfaceHolder类来显示图像,并获取SurfaceHolder类传递给Camera,Camera以后通过该Holder对图像进行处理。
所以程序中需要SurfaceView子类,并实现SurfaceHolder.Callback 接口:
public void surfaceChanged(SurfaceHolder holder, int format, int width,int height)
public void surfaceCreated(SurfaceHolder holder)
public void surfaceDestroyed(SurfaceHolder holder)
如:public class Preview extends SurfaceView implements SurfaceHolder.Callback
2.拍摄相片主要用到如下方法:
public final void takePicture(ShutterCallback shutter, PictureCallback raw, PictureCallback jpeg)
方法中的参数是几个回调接口:
①ShutterCallback
void onShutter();
拍照时调用该接口,用于按下拍摄按钮后播放声音等操作
②PictureCallback
void onPictureTaken(byte[] data, Camera camera);
拍照时调用该接口,data为拍摄照片数据,camera为Camera类自身
takePicture方法中有两个PictureCallback,看参数名好像一个是原始数据,一个是jpeg数据。
3.还有一个预览方式
PreviewCallback
void onPreviewFrame(byte[] data, Camera camera);
该接口可以获取摄像头每一帧的图像数据
此外此外还有几个辅助方法:
startPreview()
stopPreview()
previewEnabled()
4.其它方法:
①自动对焦 AutoFocusCallback
void onAutoFocus(boolean success, Camera camera);
摄像头自动对焦,success表示自动对焦是否成功
② ErrorCallback
void onError(int error, Camera camera)
摄像头发生错误是调用该接口,
CAMERA_ERROR_UNKNOWN
CAMERA_ERROR_SERVER_DIED 表示媒体服务已经当掉,需要释放Camera重新启动
③setParameters(Parameters params)
设置摄像头参数
先来做一个最简单的测试:
用来表现图像的SurfaceView子类,Android的例子里面有一个,直接拿过来用用:
class camerView extends SurfaceView implements SurfaceHolder.Callback{
SurfaceHolder mHolder;
Camera mCamera;
public camerView(Context context) {
super(context);
mHolder = this.getHolder();
mHolder.addCallback(this);
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
Camera.Parameters parameters = mCamera.getParameters();
parameters.setPreviewSize(width, height);
mCamera.setParameters(parameters);
mCamera.startPreview();
}
public void surfaceCreated(SurfaceHolder holder) {
mCamera = Camera.open();
try {
mCamera.setPreviewDisplay(holder);
} catch (IOException exception) {
mCamera.release();
mCamera = null;
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
mCamera.stopPreview();
mCamera.release();
mCamera = null;
}
public void draw(Canvas canvas){
super.draw(canvas);
Log.d("===>", "draw");
}
}
内容比较简单,Camera的管理都跟camerView 的几个接口绑在一块。
下来把View加到Active中去,同时用用 takePicture方法:
private void test2(){
cv = new camerView(this);
RelativeLayout relay = (RelativeLayout)findViewById(R.id.FrameLayout01);
relay.addView(cv);
buttonClick = (Button)findViewById(R.id.Button01);
buttonClick.setOnClickListener(new OnClickListener(){
public void onClick(View arg0) {
cv.mCamera.takePicture(
new ShutterCallback(){
public void onShutter() {
Log.d("===>", "onShutter");
}},
new PictureCallback(){
public void onPictureTaken(byte[] data, Camera camera) {
Log.d("===>", "raw:" + (data == null ? "null" : data.length));
}},//raw
new PictureCallback(){
public void onPictureTaken(byte[] data, Camera camera) {
Log.d("===>", "postview:" + (data == null ? "null" : data.length));
}}, //postview
new PictureCallback(){
public void onPictureTaken(byte[] data, Camera camera) {
Log.d("===>", "jpeg:" + (data == null ? "null" : data.length));
}} // jpeg
);
}
});
}
这样所有的代码就完成了,在模拟器上点击test按钮,在log中可以看到:
===>onShutter
===>raw:null
===>jpeg:18474
很奇怪的是 camerView中的 ===>draw没有输出,说明View不进行绘制,那么摄像图像是怎么出来的呢?
源代码中是调用本地方法,懒得去看C代码,不想钻得太深,娱乐而已。
上网查了一半天也没有搞明白,感觉是通过SurfaceHolder获得View的Canvas对象,直接进行绘制,Holder中没有View的引用,当然不会再去调用View的draw方法了。
最后在网上搜到文章一篇,对这个原因有一点说明,Copy之,以防后面忘记:
在通常情况下,OPhone程序中的View都是在同一个GUI线程中绘制的,该线程也是接收用户交互事件的线程(例如:按钮点击事件)。从另外的 线程修改GUI元素是不可以的,如果要迅速的更新UI显示该如何办?显然在主线程中还需要处理其他事件,不适合做这件事情,所以OPhone提供了 SurfaceView 来满足这种需求。一个SurfaceView 包装一个Surface对象(通过SurfaceHolder操作该对象)而不是Canvas对象,这就是关键所在,Surface可以在其他线程中绘 制,这对于周期性更新和要求高帧率的场景来说是很有用的,特别是在游戏开发中。Surface中包含了当前UI的原生数据(raw data),在不同的软件和硬件条件下对这些数据的处理是不一样的,这就可以通过一些设置来加速图形的绘制,可以通过SurfaceHolder的 setType函数来设置,目前接收如下的参数:
SURFACE_TYPE_NORMAL :用RAM缓存原生数据的普通Surface
SURFACE_TYPE_HARDWARE :适用于DMA(Direct memory access )引擎和硬件加速的Surface
SURFACE_TYPE_GPU :适用于GPU加速的Surface
SURFACE_TYPE_PUSH_BUFFERS :表明该Surface不包含原生数 据,Surface用到的数据由其他对象提供,在Camera图像预览中就使用该类型的Surface,有Camera负责提供给预览Surface数 据,这样图像预览会比较流畅。如果在这里设置了上面三种类型则可以发现不会出现预览图像,在和Camera底层的预览机制实现有关,如果对预览有特殊要求 的可以现实PreviewCallback 接口来自己处理 。
如果想在图像上叠加一些文字等透明信息的时候,总不能也像j2me一样地处理吧。
后面看到一篇文章介绍,直接将一个View叠加到Camera上就可以了,开始还不相信,后面实在找不到其它办法,试一试看看:
在test2()中加入
TextView tv = new TextView(this);
tv.setText("test");
RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
lp.addRule(RelativeLayout.CENTER_IN_PARENT);
relay.addView(tv, lp);
果然可以。呵呵,分了一下神,再回来看看Camera。
既然jpeg数据有输出,看看jpeg是什么内容,
new PictureCallback(){
public void onPictureTaken(byte[] data, Camera camera) {
Log.d("===>", "jpeg:" + (data == null ? "null" : data.length));
}} // jpeg
在jpeg的回调接口中添加内容
Log.d("===>", "jpeg:" + (data == null ? "null" : data.length));
cv.setVisibility(View.INVISIBLE);
ImageView iv = new ImageView(test.this);
Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
iv.setImageBitmap(bitmap);
relay.addView(iv);
其中的test是类名,另外需要把relay改成final变量:
final RelativeLayout relay = (RelativeLayout)findViewById(R.id.FrameLayout01);
呵呵,又看到那幅熟悉的图片了,帅。
raw数据没有输出,网上也有人提问,外网被屏蔽了看不到详细的信息,那就此处不表下次再说了。
看看能不能设置一下参数就可以有输出了,在好奇的驱使下,又试了一下设置参数。
从log中可以看到Parameters预设的参数:
picture-format=jpeg
picture-preview=yuv422sp
很可惜,设置为其它的参数系统都报错,玩不转,郁闷,看来要在摄像头这一块抱太多的遗憾了。
算了,看看Camera最后一点功能吧,获取帧数据:
mCamera.setPreviewCallback(new PreviewCallback(){
public void onPreviewFrame(byte[] data, Camera camera) {
Log.d("===>", "onPreviewFrame");
}
});
其中的data是yuv格式的,需要对其解码:
static public void decodeYUV420SP(byte[] rgbBuf, byte[] yuv420sp, int width, int height) {
final int frameSize = width * height;
if (rgbBuf == null)
throw new NullPointerException("buffer 'rgbBuf' is null");
if (rgbBuf.length < frameSize * 3)
throw new IllegalArgumentException("buffer 'rgbBuf' size "
+ rgbBuf.length + " < minimum " + frameSize * 3);
if (yuv420sp == null)
throw new NullPointerException("buffer 'yuv420sp' is null");
if (yuv420sp.length < frameSize * 3 / 2)
throw new IllegalArgumentException("buffer 'yuv420sp' size " + yuv420sp.length
+ " < minimum " + frameSize * 3 / 2);
int i = 0, y = 0;
int uvp = 0, u = 0, v = 0;
int y1192 = 0, r = 0, g = 0, b = 0;
for (int j = 0, yp = 0; j < height; j++) {
uvp = frameSize + (j >> 1) * width;
u = 0;
v = 0;
for (i = 0; i < width; i++, yp++) {
y = (0xff & ((int) yuv420sp[yp])) - 16;
if (y < 0) y = 0;
if ((i & 1) == 0) {
v = (0xff & yuv420sp[uvp++]) - 128;
u = (0xff & yuv420sp[uvp++]) - 128;
}
y1192 = 1192 * y;
r = (y1192 + 1634 * v);
g = (y1192 - 833 * v - 400 * u);
b = (y1192 + 2066 * u);
if (r < 0) r = 0; else if (r > 262143) r = 262143;
if (g < 0) g = 0; else if (g > 262143) g = 262143;
if (b < 0) b = 0; else if (b > 262143) b = 262143;
rgbBuf[yp * 3] = (byte)(r >> 10);
rgbBuf[yp * 3 + 1] = (byte)(g >> 10);
rgbBuf[yp * 3 + 2] = (byte)(b >> 10);
}
}
}
摄像头这一块android虽然给了一个接口,但是实现还是各个厂家自己实现的,所以不同的机型处理方式还不一致,
android camera 源码分析(基于应用)
这里主要是针对Ophone进行介绍的,当然 结合了android的源码(以下出现均为android2.2源码)。
首先在Ophone中也是通过android.hardware.Camera类来控制摄像头设备的,要使用只有通过 android.hardware.Camera.open()来打开。 try { mCameraDevice = android.hardware.Camera.open(); }
catch (RuntimeException e) { Log.e(TAG, "fail to connect Camera", e);
throw new CameraHardwareException(e); }
另外Ophone还提供了一些接口来给予回调,控制Camera的状态,
分别是: 1.android.hardware.Camera.ErrorCallback:摄像头出错的时候调用,这个接口具有一个void onError(int error,Camera camera)函数;其中,
前者表示数据类型,取值是Camera类中的常量CAMERA_ERROR_UNKNOWN或者是 CAMERA_ERROR_SERVICE_DIED;
前者是不明错误,后者是表示服务已经关闭,
在这种情况下需要释放当前的Camera对象,然后再初始化一个。
private static final class ErrorCallback implements android.hardware.Camera.ErrorCallback { public void onError(int error, android.hardware.Camera camera) { if (error == android.hardware.Camera.CAMERA_ERROR_SERVER_DIED) { mMediaServerDied = true; Log.v(TAG, "media server died"); } } }
2.android.hardware.camera.PreviewCallback:在图像预览时调用,这个接口有一个void onPreviewFrame(byte[] data,Camera camera);参数data为每帧图像的数据流。我们可以根据实际需要来实现这个接口。
3.android.hardware.Camera.ShutterCallback:在图像预览的时候调用,这个接口具有一个void onShutter();可以在改函数中通知用户快门已经关闭,例如播放一个声音。 private final class ShutterCallback implements android.hardware.Camera.ShutterCallback { public void onShutter() { mShutterCallbackTime = System.currentTimeMillis(); mShutterLag = mShutterCallbackTime - mCaptureStartTime; Log.v(TAG, "mShutterLag = " + mShutterLag + "ms"); clearFocusState(); } }
4.android.hardware.Camera.PictureCallback:当拍摄相片的时候调用,该接口具有一个void onPictureTaken(byte[] data,Camera camera)函数;参数和预览的一样。在android中主要有三个类实现了这个接口,分别是PostViewPictureCallback、 RawPictureCallback、JepgPictureCallback。我们可以根据需要定义自己需要的类。
5.android.hardware.Camera.AutoFocusCallback:当自动对焦时候调用,该接口具有一个void onAutoFocus(boolean focused,Camera camera)函数;
private final class AutoFocusCallback implements android.hardware.Camera.AutoFocusCallback { public void onAutoFocus(boolean focused, android.hardware.Camera camera) { mFocusCallbackTime = System.currentTimeMillis();
mAutoFocusTime = mFocusCallbackTime - mFocusStartTime; Log.v(TAG, "mAutoFocusTime = " + mAutoFocusTime + "ms");
if (mFocusState == FOCUSING_SNAP_ON_FINISH) { // Take the picture no matter focus succeeds or fails. No need // to play the AF sound if we're about to play the shutter // sound. if (focused) { mFocusState = FOCUS_SUCCESS; }
else { mFocusState = FOCUS_FAIL; } mImageCapture.onSnap(); }
else if (mFocusState == FOCUSING)
{ // User is half-pressing the focus key. Play the focus tone. // Do not take the picture now. ToneGenerator tg = mFocusToneGenerator; if (tg != null)
{ tg.startTone(ToneGenerator.TONE_PROP_BEEP2); }
if (focused) { mFocusState = FOCUS_SUCCESS; }
else { mFocusState = FOCUS_FAIL; } }
else if (mFocusState == FOCUS_NOT_STARTED)
{ // User has released the focus key before focus completes. // Do nothing. } updateFocusIndicator(); } }
6.还提供了放大缩小的监听器android.hardware.Camera.OnZoomChangeListener。
private final class ZoomListener implements android.hardware.Camera.OnZoomChangeListener { public void onZoomChange(int value, boolean stopped, android.hardware.Camera camera) { Log.v(TAG, "Zoom changed: value=" + value + ". stopped=" + stopped); mZoomValue = value; // Keep mParameters up to date. We do not getParameter again in // takePicture.
If we do not do this, wrong zoom value will be set. mParameters.setZoom(value);
// We only care if the zoom is stopped. mZooming is set to true when
// we start smooth zoom.
if (stopped && mZoomState != ZOOM_STOPPED)
{ if (value != mTargetZoomValue) { mCameraDevice.startSmoothZoom(mTargetZoomValue); mZoomState = ZOOM_START; }
else { mZoomState = ZOOM_STOPPED; } } } }
当取得照片的数据流后可以通过BitmapFactory的decodeByteArray()函数来解析图片。
另外还可以通过Camera对象的getParameters()函数来得到一个android.hardware.Camera.Parameters 对象,Parameters提供了一些接口来设置Camera的属性:
1.setPictureFormat(int pixel_format):设置图片的格式,其取值为PixelFormat YCbCr_420_SP、PixelFormatRGB_565或者PixelFormatJPEG。
2.setPreviewFormat(int pixel_format):设置图片的预览格式,取值如上。
3.setPictureSize(int width,int height):设置图片的高度和宽度,单位为像素。
4.setPreviewSize(int width,int height):设置预览的高度和宽度,取值如上。
5.setPreviewFrameRate(int fps):设置图片预览的帧速。 在设置好Camera的参数后,可以通过函数void startPreview()开始预览图像、void stopPreview()结束预览,通过autoFocus(AutoFocusCallback cb)来自动对焦,最后可以通过takePicture(ShutterCallback shutter, PictureCallback raw, PictureCallback jpeg)函数来拍照。
该函数有三个参数,分别为快门回调接口、原生图像数据接口和压缩格式图片数据接口。
如果数据格式不存在的话数据流为空,如果不需要实现这些接口则这些参数取值可以为null。
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的硬件调试可以算作完成了,后续的其他功能也可以慢慢完善了。
Note:V同步即帧同步信号,代表一帧数据的开始,VFE会根据该信号来读取一帧数据。
H同步即行同步信号,代表一行数据的开始,VFE会根据该信号来读取一行数据。H信号一定是包在V同步中的。