Android6.0 camera个数探测_android camerax 检测camera数量-程序员宅基地

技术标签: android frameworks  camera  

近期在排查打开相机黑屏的问题,然后就跟了一下android camera的框架。根据log分析发现是camera个数为0,然后就进行代码流程跟踪。由于开发平台是mtk平台,log提示上层报错的对应代码逻辑如下图。
在这里插入图片描述
mCameraService是ICameraService接口类型。
在这里插入图片描述
获取服务的名称如下:
在这里插入图片描述
并且是通过aidl跟底层c++通信。
frameworks/base/core/java/android/hardware/ICameraService.aidl

/*
 * Copyright (C) 2013 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.hardware;

import android.hardware.ICamera;
import android.hardware.ICameraClient;
import android.hardware.camera2.ICameraDeviceUser;
import android.hardware.camera2.ICameraDeviceCallbacks;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.utils.BinderHolder;
import android.hardware.ICameraServiceListener;
import android.hardware.CameraInfo;

/**
 * Binder interface for the native camera service running in mediaserver.
 *
 * @hide
 */
interface ICameraService
{
    
    /**
     * Keep up-to-date with frameworks/av/include/camera/ICameraService.h
     */
    int getNumberOfCameras(int type);

    // rest of 'int' return values in this file are actually status_t

    int getCameraInfo(int cameraId, out CameraInfo info);

    int connect(ICameraClient client, int cameraId,
                    String opPackageName,
                    int clientUid,
                    // Container for an ICamera object
                    out BinderHolder device);

    int connectDevice(ICameraDeviceCallbacks callbacks, int cameraId,
                              String opPackageName,
                              int clientUid,
                              // Container for an ICameraDeviceUser object
                              out BinderHolder device);

    int addListener(ICameraServiceListener listener);
    int removeListener(ICameraServiceListener listener);

    int getCameraCharacteristics(int cameraId, out CameraMetadataNative info);

    /**
     * The java stubs for this method are not intended to be used.  Please use
     * the native stub in frameworks/av/include/camera/ICameraService.h instead.
     * The BinderHolder output is being used as a placeholder, and will not be
     * well-formatted in the generated java method.
     */
    int getCameraVendorTagDescriptor(out BinderHolder desc);

    // Writes the camera1 parameters into a single-element array.
    int getLegacyParameters(int cameraId, out String[] parameters);
    // Determines if a particular API version is supported; see ICameraService.h for version defines
    int supportsCameraApi(int cameraId, int apiVersion);

    int connectLegacy(ICameraClient client, int cameraId,
                    int halVersion,
                    String opPackageName,
                    int clientUid,
                    // Container for an ICamera object
                    out BinderHolder device);

    int setTorchMode(String CameraId, boolean enabled, IBinder clientBinder);

    /**
     * Notify the camera service of a system event.  Should only be called from system_server.
     *
     * Callers require the android.permission.CAMERA_SEND_SYSTEM_EVENTS permission.
     */
    oneway void notifySystemEvent(int eventId, in int[] args);
    // atc aosp enhancement: cvbs camera , cvbs format detect.
    int atcSetCameraInfoUpdateFlag(int flag);

}

当时我们公司的应用开发工程师打开相机时会进行摄像头探测,通过调用framework的aidl接口去调用mCameraService.getNumberOfCameras()探测camera个数,总的流程会通过三部分讲解。分别为framework层,native层,driver层三部分。

一:framework层

在这里插入图片描述
注意:CarmerServiceDetector中的mCameraService是直接通过aidl调用到c++的CameraSevice服务的。具体代码如下:

private static final String CAMERA_SERVICE_BINDER_NAME = "media.camera";
......//代表中间有很多代码
mCameraService = ICameraService.Stub.asInterface(ServiceManager.getService(CAMERA_SERVICE_BINDER_NAME));

我们看下CameraSevice.cpp是如何启动注册服务以及完成ICameraService.aidl的接口调用的。

//代码路径:frameworks/av/media/mediaserver/main_mediaserver.cpp
int main(int argc __unused, char** argv)
{
    
		......//代表中间走过了很多代码
		//相机服务开始初始化并注册
        CameraService::instantiate();
        .....
        ProcessState::self()->startThreadPool();
        IPCThreadState::self()->joinThreadPool();
    }
}
class CameraService :
    public BinderService<CameraService>,
    public BnCameraService,
    public IBinder::DeathRecipient,
    public camera_module_callbacks_t
{
    
    friend class BinderService<CameraService>;
public:
    class Client;
    class BasicClient;
	......
    // 获取注册服务的名字,提供上层aidl找到该服务
    static char const* getServiceName() {
     return "media.camera"; }
    .....
}

注意:在instantiate函数中,将CameraService注册到系统的binder service列表中,这样以后上层java就可以通过aid调到CameraService提供的方法了。并且我们可以通过注册CameraService时会调用getServiceName名字注册跟上层ServiceManager.getService(CAMERA_SERVICE_BINDER_NAME)时的名字都是media.camera可得知CameraService便正是上层通过aidl调用c++服务。

二:HAL层

经过上面的调用过程最终调用到mtk的carmer hal层,至于怎么调用到这里的,写过adroid hal层的都应该大概知道。

//path:vendor/mediatek/proprietary/hardware/mtkcam/legacy/module_hal/module/module.h
static
int
get_number_of_cameras(void)
{
    
    return  NSCam::getCamDeviceManager()->getNumberOfDevices();
}
//path:vendor/mediatek/proprietary/hardware/mtkcam/common/include/device/ICamDeviceManager.h
#ifndef _MTK_HARDWARE_INCLUDE_MTKCAM_DEVICE_ICAMDEVICEMANAGER_H_
#define _MTK_HARDWARE_INCLUDE_MTKCAM_DEVICE_ICAMDEVICEMANAGER_H_
//
#include <utils/Errors.h>
#include <utils/StrongPointer.h>
//
#include <hardware/camera_common.h>
#include <common.h>
.....
namespace NSCam {
    

class ICamDevice;

class ICamDeviceManager
{
    
...
};
ICamDeviceManager*  getCamDeviceManager();//返回一个ICamDeviceManager类型对象
};  //namespace NSCam
#endif  //_MTK_HARDWARE_INCLUDE_MTKCAM_DEVICE_ICAMDEVICEMANAGER_H_

查看具体getCamDeviceManager实现,可知返回就只有返回CamDeviceManagerImp类型指针。

//path:vendor/mediatek/proprietary/hardware/mtkcam/legacy/platform/mt3561m/devicemgr/CamDeviceManagerImp.cpp
namespace
{
    
    CamDeviceManagerImp gCamDeviceManager;
}   //namespace

namespace NSCam {
    
ICamDeviceManager*
getCamDeviceManager()
{
    
    return  &gCamDeviceManager;//返回CamDeviceManagerImp类型函数
}
}

CamDeviceManagerImp的声明,继承CamDeviceManagerBase

//path:vendor/mediatek/proprietary/hardware/mtkcam/legacy/platform/mt3561m/devicemgr/CamDeviceManagerImp.h
class CamDeviceManagerImp : public CamDeviceManagerBase
{
    
public:                             Instantiation.
                                        CamDeviceManagerImp();

protected:                          Operations.
    virtual android::status_t           validateOpenLocked(
                                            int32_t i4OpenId,
                                            uint32_t device_version
                                        ) const;

    virtual int32_t                     enumDeviceLocked();

    virtual android::status_t           setTorchModeLocked(
                                            int const deviceId,
                                            bool enabled,
                                            bool notifyEvent);

    virtual android::status_t           setTorchAvailableLocked(
                                            int const deviceId,
                                            bool available);
};

从上面CamDeviceManagerImp的类看出,其并没有定义方法getNumberOfDevices,但是继承了CamDeviceManagerBase,所以很有可能getNumberOfDevices是getNumberOfDevices的方法。跟踪代码发现如下,果然如此。

//path:vendor/mediatek/proprietary/hardware/mtkcam/legacy/module_hal/devicemgr/CamDeviceManagerBase.cpp
int32_t
CamDeviceManagerBase::
getNumberOfDevices()
{
    
    RWLock::AutoWLock _l(mRWLock);
    //
    if  ( 0 != mi4DeviceNum )
    {
    
        MY_LOGD("#devices:%d", mi4DeviceNum);//返回支持摄像头个数。一般来说初始化过一次后就得到支持的camera个数了。
    }
    else
    {
    
    	//第一次开机启动一般通过走这里去获取支持camera的数量
        Utils::CamProfile _profile(__FUNCTION__, "CamDeviceManagerBase");
        mi4DeviceNum = enumDeviceLocked();
        _profile.print("");
    }
    //
    return  mi4DeviceNum;
}

下面我们通过uml图来说明下调用流程:
在这里插入图片描述

最后camera hal层会通过ioctl去访问底层驱动,并且检测camera的sensor id是否有效,有效数目便是支持返回的camera个数。代码逻辑如下:

//path:vendor/mediatek/proprietary/hardware/mtkcam/legacy/platform/mt3561m/hal/sensor/imgsensor_drv.cpp
MINT32
ImgSensorDrv::impSearchSensor(pfExIdChk pExIdChkCbf)
{
    
......
     for (i = 0; i < MAX_NUM_OF_SUPPORT_SENSOR; i++) {
    
            //end of driver list
            if (m_pstSensorInitFunc[i].getCameraDefault == NULL) {
    
                LOG_MSG("m_pstSensorInitFunc[i].getCameraDefault is NULL: %d \n", i);
                break;
            }
                //set sensor driver
            id[KDIMGSENSOR_INVOKE_DRIVER_0] = (SensorEnum << KDIMGSENSOR_DUAL_SHIFT) | i;
            LOG_MSG("set sensor driver id =%x\n", id[KDIMGSENSOR_INVOKE_DRIVER_0]);
			//通过ioctl初始化camera sensor驱动
            err = ioctl(m_fdSensor, KDIMGSENSORIOC_X_SET_DRIVER,&id[KDIMGSENSOR_INVOKE_DRIVER_0] );
                if (err < 0) {
    
                    LOG_ERR("ERROR:KDCAMERAHWIOC_X_SET_DRIVER\n");
                }

			//通过ioctl去检测camera sensor是否有效
                //err = open();
                err = ioctl(m_fdSensor, KDIMGSENSORIOC_T_CHECK_IS_ALIVE);


                if (err < 0) {
    
                    LOG_MSG("[impSearchSensor] Err-ctrlCode (%s) \n", strerror(errno));
                }
  }
......
}

三:内核driver层

1.利用cmd命令KDIMGSENSORIOC_X_SET_DRIVER通过ioctl初始化sensor驱动,

//hal层的代码调用
kernel-3.18/drivers/misc/mediatek/imgsensor/src/mt3561/kd_sensorlist.c
 err = ioctl(m_fdSensor, KDIMGSENSORIOC_X_SET_DRIVER,&id[KDIMGSENSOR_INVOKE_DRIVER_0] );
static long CAMERA_HW_Ioctl(struct file *a_pstFile,
			    unsigned int a_u4Command, unsigned long a_u4Param)
{
    
	pIdx = (u32 *) pBuff;
	switch (a_u4Command) {
    

	case KDIMGSENSORIOC_X_SET_DRIVER:
		i4RetValue = kdSetDriver((unsigned int *)pBuff);
	}
}
int kdSetDriver(unsigned int *pDrvIndex)
{
    
	......
	//(1)初始化pSensorList指针,指向sensor驱动数组ACDK_KD_SENSOR_INIT_FUNCTION_STRUCT     //kdSensorList[MAX_NUM_OF_SUPPORT_SENSOR+1] 
	if (0 != kdGetSensorInitFuncList(&pSensorList)) {
    
		PK_ERR("ERROR:kdGetSensorInitFuncList()\n");
		return -EIO;
	}
	
//(2)调用pSensorList指向的ACDK_KD_SENSOR_INIT_FUNCTION_STRUCT类型中的SensorInit进行初始化。
pSensorList[drvIdx[i]].SensorInit(&g_pInvokeSensorFunc[i]);
......
}

分两部讲,第一步就是(1)初始化pSensorList指针,指向sensor驱动数组ACDK_KD_SENSOR_INIT_FUNCTION_STRUCT kdSensorList[MAX_NUM_OF_SUPPORT_SENSOR+1] ,来看下实现具体过程

UINT32 kdGetSensorInitFuncList(ACDK_KD_SENSOR_INIT_FUNCTION_STRUCT **ppSensorList)
{
    
	if (NULL == ppSensorList) {
    
		PK_ERR("[kdGetSensorInitFuncList]ERROR: NULL ppSensorList\n");
		return 1;
	}
	*ppSensorList = &kdSensorList[0];//执行kdSensorList数组的标号0,也就是开始地址。
	return 0;
} /* kdGetSensorInitFuncList() */

可以看到传进来的参数指针指向了kdSensorList数组的标号0,也就是该数组的开始地址,kdSensorList的定义如下:

//path:kernel-3.18/drivers/misc/mediatek/imgsensor/inc/kd_imgsensor_define.h
ACDK_KD_SENSOR_INIT_FUNCTION_STRUCT的声明:
typedef struct {
    
	MUINT32 SensorId;
	MUINT8 drvname[32];
	MUINT32(*SensorInit)(PSENSOR_FUNCTION_STRUCT *pfFunc);
} ACDK_KD_SENSOR_INIT_FUNCTION_STRUCT, *PACDK_KD_SENSOR_INIT_FUNCTION_STRUCT;

//path:kernel-3.18/drivers/misc/mediatek/imgsensor/src/mt3561m/kd_sensorlist.h
ACDK_KD_SENSOR_INIT_FUNCTION_STRUCT kdSensorList[MAX_NUM_OF_SUPPORT_SENSOR+1] =
{
    
#if defined(OV8825_MIPI_RAW)
    {
    OV8825_SENSOR_ID, SENSOR_DRVNAME_OV8825_MIPI_RAW, OV8825_MIPI_RAW_SensorInit},
#endif
......
#if defined(RN6752_YUV)
    {
    RN6752_SENSOR_ID, SENSOR_DRVNAME_RN6752_YUV, RN6752_YUV_SensorInit},
#endif
......
/*  ADD sensor driver before this line */
    {
    0,{
    0},NULL}, //end of list
};

可以看出kdSensorList是各个sensor型号驱动初始化结构体的数组。例如上面支持的RN6752_YUV型号的sensor驱动的初始化函数RN6752_YUV_SensorInit。

//RN6752_YUV sensor驱动的操作集合
SENSOR_FUNCTION_STRUCT SensorFuncRN6752 =
{
    
	RN6752Open,
	RN6752GetInfo,
	RN6752GetResolution,
	RN6752FeatureControl,
	RN6752Control,
	RN6752Close,
	RN6752_Get_SignalStatus
};
//RN6752_YUV sensor 驱动初始化
UINT32 RN6752_YUV_SensorInit(PSENSOR_FUNCTION_STRUCT *pfFunc)
{
    
	/* To Do: check sensor status here */
	//LOG_INF("Sensor init enter\n");
	if (pfFunc != NULL)
		*pfFunc = &SensorFuncRN6752;
	return ERROR_NONE;
} /* SensorInit() */

(2)上面第一步对pSensorList赋值后,接下来就可以调用sensor驱动的初始化获取sensor驱动的操作集合,如下:

pSensorList[drvIdx[i]].SensorInit(&g_pInvokeSensorFunc[i]);

最终便会执行到上面kdSensorList数组里面对应初始化函数,例如RN6752_YUV型号的sensor驱动的初始化函数RN6752_YUV_SensorInit,从下面初始化函数的逻辑得出执行完初始化函数后参数g_pInvokeSensorFunc数组中将会储存者RN6752_YUV型号的sensor驱动操作集合SensorFuncRN6752的地址。记住g_pInvokeSensorFunc这个变量后面会用到。

UINT32 RN6752_YUV_SensorInit(PSENSOR_FUNCTION_STRUCT *pfFunc)
{
    
	/* To Do: check sensor status here */
	//LOG_INF("Sensor init enter\n");
	if (pfFunc != NULL)
		*pfFunc = &SensorFuncRN6752;
	return ERROR_NONE;
} /* SensorInit() */

2.利用KDIMGSENSORIOC_T_CHECK_IS_ALIVE命令通过ioctl来获取sensor id值。

上面说完了sensor驱动的初始化完后,将会调用KDIMGSENSORIOC_T_CHECK_IS_ALIVE命令通过ioctl来检测sensor id的值。

err = ioctl(m_fdSensor, KDIMGSENSORIOC_T_CHECK_IS_ALIVE);

驱动层代码如下:

case KDIMGSENSORIOC_T_CHECK_IS_ALIVE:
	i4RetValue = adopt_CAMERA_HW_CheckIsAlive();
static inline int adopt_CAMERA_HW_CheckIsAlive(void)
{
    
......
	if (g_pSensorFunc) {
    
		for (i = KDIMGSENSOR_INVOKE_DRIVER_0; i < KDIMGSENSOR_MAX_INVOKE_DRIVERS; i++) {
    
			if (DUAL_CAMERA_NONE_SENSOR != g_invokeSocketIdx[i]) {
    
				//次数通过sensorID变量获取sensor id的值
				err = g_pSensorFunc->SensorFeatureControl(g_invokeSocketIdx[i], SENSOR_FEATURE_CHECK_SENSOR_ID, (MUINT8 *)&sensorID, &retLen);
				if (sensorID == 0) {
        /* not implement this feature ID */
					PK_DBG(" Not implement!!, use old open function to check\n");
					err = ERROR_SENSOR_CONNECT_FAIL;
				} else if (sensorID == 0xFFFFFFFF) {
      /* fail to open the sensor */
					PK_DBG(" No Sensor Found");
					err = ERROR_SENSOR_CONNECT_FAIL;
				} else {
    

					PK_INF(" Sensor found ID = 0x%x\n", sensorID);
					snprintf(mtk_ccm_name, sizeof(mtk_ccm_name), "%s CAM[%d]:%s;", mtk_ccm_name, g_invokeSocketIdx[i], g_invokeSensorNameStr[i]);
					err = ERROR_NONE;
				}
				if (ERROR_NONE != err) {
    
					PK_DBG("ERROR:adopt_CAMERA_HW_CheckIsAlive(), No imgsensor alive\n");
				}
			}
		}
	}
......
}

上面主要通过
err = g_pSensorFunc->SensorFeatureControl(g_invokeSocketIdx[i], SENSOR_FEATURE_CHECK_SENSOR_ID, (MUINT8 *)&sensorID, &retLen);获取sensor id的值,查找g_pSensorFunc的定义赋值如下:

//path:kernel-3.18/drivers/misc/mediatek/imgsensor/src/mt3561/kd_sensorlist.c
static MULTI_SENSOR_FUNCTION_STRUCT2 *g_pSensorFunc = &kd_MultiSensorFunc;
MULTI_SENSOR_FUNCTION_STRUCT2  kd_MultiSensorFunc = {
    
	kd_MultiSensorOpen,
	kd_MultiSensorGetInfo,
	kd_MultiSensorGetResolution,
	kd_MultiSensorFeatureControl,
	kd_MultiSensorControl,
	kd_MultiSensorClose
};
//MULTI_SENSOR_FUNCTION_STRUCT2的类型声明
//path:kernel-3.18/drivers/misc/mediatek/imgsensor/inc/kd_imgsensor_define.h
typedef struct {
    
	MUINT32(*SensorOpen)(void);
	MUINT32(*SensorGetInfo)(MUINT32 *pScenarioId[2], MSDK_SENSOR_INFO_STRUCT * pSensorInfo[2], MSDK_SENSOR_CONFIG_STRUCT *pSensorConfigData[2]);
	MUINT32(*SensorGetResolution)(MSDK_SENSOR_RESOLUTION_INFO_STRUCT * pSensorResolution[2]);
	MUINT32(*SensorFeatureControl)(CAMERA_DUAL_CAMERA_SENSOR_ENUM InvokeCamera, MSDK_SENSOR_FEATURE_ENUM FeatureId, MUINT8 *pFeaturePara, MUINT32 *pFeatureParaLen);
	MUINT32(*SensorControl)(CAMERA_DUAL_CAMERA_SENSOR_ENUM InvokeCamera, MSDK_SCENARIO_ID_ENUM ScenarioId, MSDK_SENSOR_EXPOSURE_WINDOW_STRUCT *pImageWindow, MSDK_SENSOR_CONFIG_STRUCT *pSensorConfigData);
	MUINT32(*SensorClose)(void);
} MULTI_SENSOR_FUNCTION_STRUCT2, *PMULTI_SENSOR_FUNCTION_STRUCT2;

从上面可以的得知g_pSensorFunc->SensorFeatureControl调用的是kd_MultiSensorFeatureControl函数。kd_MultiSensorFeatureControl定义如下:

MUINT32
kd_MultiSensorFeatureControl(
	CAMERA_DUAL_CAMERA_SENSOR_ENUM InvokeCamera,
	MSDK_SENSOR_FEATURE_ENUM FeatureId,
	MUINT8 *pFeaturePara,
	MUINT32 *pFeatureParaLen)
{
    
	MUINT32 ret = ERROR_NONE;
	u32 i = 0;
	KD_MULTI_FUNCTION_ENTRY();
	for (i = KDIMGSENSOR_INVOKE_DRIVER_0; i < KDIMGSENSOR_MAX_INVOKE_DRIVERS; i++) {
    
		if (g_bEnableDriver[i] && g_pInvokeSensorFunc[i]) {
    

			if (InvokeCamera == g_invokeSocketIdx[i]) {
    
				.....
				//这里是主要的调用,获取sensor id值
				ret = g_pInvokeSensorFunc[i]->SensorFeatureControl(FeatureId, pFeaturePara, pFeatureParaLen);
				if (ERROR_NONE != ret) {
    
					PK_ERR("[%s]\n", __func__);
					return ret;
				}
			}
		}
	}
	KD_MULTI_FUNCTION_EXIT();
	return ERROR_NONE;
}

上面可以看出主要是通过ret = g_pInvokeSensorFunc[i]->SensorFeatureControl(FeatureId, pFeaturePara, pFeatureParaLen);来获取sensor id值,注意g_pInvokeSensorFunc这个指针是不是很熟悉,没错就是前面我们初始化sensor驱动的时候那个指针,所以这里调用 g_pInvokeSensorFunc[i]->SensorFeatureControl其实调用了具体的sensor型号的操作结合里面的SensorFeatureControl。上面我们所说的RN6752_YUV型号的sensor,那么这里调用的是RN6752FeatureControl。

//RN6752_YUV sensor驱动的操作集合
SENSOR_FUNCTION_STRUCT SensorFuncRN6752 =
{
    
	RN6752Open,
	RN6752GetInfo,
	RN6752GetResolution,
	RN6752FeatureControl,
	RN6752Control,
	RN6752Close,
	RN6752_Get_SignalStatus
};

具体代码如下:这里在获取sensor id的时候传进来的FeatureId是SENSOR_FEATURE_CHECK_SENSOR_ID,因此会调用到RN6752GetSensorId(pFeatureData32)。

UINT32 RN6752FeatureControl(MSDK_SENSOR_FEATURE_ENUM FeatureId,
        UINT8 *pFeaturePara,UINT32 *pFeatureParaLen)
{
    
	UINT16 *pFeatureReturnPara16 = (UINT16 *) pFeaturePara;
	UINT16 *pFeatureData16 = (UINT16 *) pFeaturePara;
	UINT32 *pFeatureReturnPara32 = (UINT32 *) pFeaturePara;
	UINT32 *pFeatureData32 = (UINT32 *) pFeaturePara;
	//unsigned long long **pp64FeaturePara = (unsigned long long **)pFeaturePara;
	MSDK_SENSOR_CONFIG_STRUCT *pSensorConfigData = (MSDK_SENSOR_CONFIG_STRUCT *) pFeaturePara;
	MSDK_SENSOR_REG_INFO_STRUCT *pSensorRegData = (MSDK_SENSOR_REG_INFO_STRUCT *) pFeaturePara;
	
	switch (FeatureId) {
    
		case SENSOR_FEATURE_GET_RESOLUTION:
			*pFeatureReturnPara16++ = input_source_width;
			*pFeatureReturnPara16 = input_source_height;
			*pFeatureParaLen = 4;
			break;
		case SENSOR_FEATURE_GET_PERIOD:
			*pFeatureReturnPara16++ = input_source_width;
			*pFeatureReturnPara16 = input_source_height;
			*pFeatureParaLen = 4;
			break;
		case SENSOR_FEATURE_GET_PIXEL_CLOCK_FREQ:
			*pFeatureReturnPara32 = 27000000;
			*pFeatureParaLen = 4;
			break;
		case SENSOR_FEATURE_SET_ESHUTTER:
			break;
		case SENSOR_FEATURE_SET_NIGHTMODE:
			RN6752NightMode((BOOL) *pFeatureData16);
			break;
		case SENSOR_FEATURE_SET_GAIN:
			break;
		case SENSOR_FEATURE_SET_FLASHLIGHT:
			break;
		case SENSOR_FEATURE_SET_ISP_MASTER_CLOCK_FREQ:
			RN6752_isp_master_clock = *pFeatureData32;
			break;
		case SENSOR_FEATURE_SET_REGISTER:
			RN6752_write_cmos_sensor(pSensorRegData->RegAddr, pSensorRegData->RegData);
			break;
		case SENSOR_FEATURE_GET_REGISTER:
			pSensorRegData->RegData = RN6752_read_cmos_sensor(pSensorRegData->RegAddr);
			break;
		case SENSOR_FEATURE_GET_CONFIG_PARA:
			memcpy(pSensorConfigData, &RN6752SensorConfigData, sizeof(MSDK_SENSOR_CONFIG_STRUCT));
			*pFeatureParaLen = sizeof(MSDK_SENSOR_CONFIG_STRUCT);
			break;
		case SENSOR_FEATURE_SET_CCT_REGISTER:
		case SENSOR_FEATURE_SET_ENG_REGISTER:
		case SENSOR_FEATURE_GET_REGISTER_DEFAULT:
		case SENSOR_FEATURE_CAMERA_PARA_TO_SENSOR:
		case SENSOR_FEATURE_SENSOR_TO_CAMERA_PARA:
		case SENSOR_FEATURE_GET_GROUP_COUNT:
			*pFeatureReturnPara32++ = 0;
			*pFeatureParaLen = 4;
			break;
		case SENSOR_FEATURE_GET_GROUP_INFO:
		case SENSOR_FEATURE_GET_ITEM_INFO:
		case SENSOR_FEATURE_SET_ITEM_INFO:
		case SENSOR_FEATURE_GET_ENG_INFO:
			break;
		case SENSOR_FEATURE_GET_LENS_DRIVER_ID:
			// get the lens driver ID from EEPROM or just return LENS_DRIVER_ID_DO_NOT_CARE
			// if EEPROM does not exist in camera module.
			*pFeatureReturnPara32 = LENS_DRIVER_ID_DO_NOT_CARE;
			*pFeatureParaLen = 4;
			break;
		case SENSOR_FEATURE_SET_YUV_CMD:
			RN6752YUVSensorSetting((FEATURE_ID)*pFeatureData32, *(pFeatureData32+1));
			break;
		case SENSOR_FEATURE_SET_VIDEO_MODE:
			RN6752YUVSetVideoMode(*pFeatureData16);
		case SENSOR_FEATURE_CHECK_SENSOR_ID:
			RN6752GetSensorId(pFeatureData32);
			break;
		case SENSOR_FEATURE_GET_DELAY_INFO:
			//LOG_INF("SENSOR_FEATURE_GET_DELAY_INFO\n");
			//RN6752YUVGetDelayInfo((uintptr_t)*pp64FeaturePara);
			break;
		case SENSOR_FEATURE_GET_INTERLACE_FLAG:
			if (source_format == PAL || source_format == NTSC){
    
				*pFeatureReturnPara32 = 1;
			} else {
    
				*pFeatureReturnPara32 = 0;
			}
			*pFeatureParaLen = 8;
			LOG_INF("*pFeatureReturnPara32: %d\n", *pFeatureReturnPara32);
			break;
		case SENSOR_FEATURE_SET_VIDEO_CHANNEL:
			RN6752_Set_Video_Channel(*pFeatureData16);
			LOG_INF("Richard *pFeatureData16: %d\n", *pFeatureData16);
			break;
		default:
			break;
	}
	
	return ERROR_NONE;
}

RN6752GetSensorId(pFeatureData32);的实现:

UINT32 RN6752GetSensorId(UINT32 *sensorId)
{
    
	int retry = 3;
	do {
    
		//获取sernsor id。主要是通过i2c获取0xfe寄存器的值与0xfd寄存器的值
		*sensorId = ((RN6752_read_cmos_sensor(0xfe) << 8) | RN6752_read_cmos_sensor(0xfd));
		LOG_ERR("WFH: sensor id = 0x%x", *sensorId);
		if (*sensorId == SENSOR_ID)
			break;
		retry--;
	} while (retry > 0);

	if (*sensorId != SENSOR_ID) {
    
		*sensorId = 0xFFFFFFFF;
		return ERROR_SENSOR_CONNECT_FAIL;
	}

	if (false == bRN6752_Inputsource_Format_Detected) {
    
        //RN6752ForceUpdateCVBSFormat();
		RN6752_PrestoreFormat();
        bRN6752_Inputsource_Format_Detected = true;
    }
	return ERROR_NONE;
}

也正是这里,因为获取sensor id的值失败,导致返回上层的camera个数为0,后来从log也能看出来,在进行i2c通信的时候出现i2c通信异常。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/Ian22l/article/details/106155787

智能推荐

【持续更新,后面带JavaWeb案例】IDEA2023创建JavaWeb项目的方法以及JavaWeb实现购物车案例_idea2023社区版提供web开发吗-程序员宅基地

文章浏览阅读1.1k次,点赞2次,收藏8次。哎呀我的妈妈咪呀,我来讲讲我为啥要写这篇文章。我tm之前用IDEA写Java 电脑项目习惯了,创建项目的时候就直接 新建项目,然后Java > Maven啥的,弄一大堆,然后写好页面和Servlet之后运行tomcat之后,用localhost:8080/项目名称/servlet名直接是找不到资源,搞我心态搞了现在tmb一个月,现在我总算明白了,那个初始模板可能有些插件/模块啥的自己不知道引用哪些,所以先开始总是报红,然后在web.xml里面写也tmd爆红,我真是服了!_idea2023社区版提供web开发吗

黑盒测试简介以及方法简介_一个循环条件为≤时,却错误写成<,用哪种测试方法能够找到这个错误-程序员宅基地

文章浏览阅读2w次,点赞9次,收藏49次。引言:黑盒测试是从软件的外部对软件实施测试,也常形容为闭着眼睛测试。在接下来的学习中将介绍几种常用的黑盒测试方法,其中包括等价类划分、边界值分析、决策表测试等。1. 等价类划分测试等价类划分是一种典型的黑盒测试方法,该方法完全不考虑程序的内部结构,只根据对软件的要求和说明,即需求规格说明,把程序输入域划分成若干个部分,然后从每个部分中选取少数有代表性的数据作_一个循环条件为≤时,却错误写成<,用哪种测试方法能够找到这个错误

对等复制中的冲突检测-程序员宅基地

文章浏览阅读227次。通过对等事务复制可以在拓扑中的任何节点插入、更新或删除数据并将数据更改传播到其他节点。由于可在任何节点上更改数据,因此在不同节点上进行的数据更改可能会相互冲突。如果在多个节点上修改了某一行,则将该行传播给其他节点时可能会导致冲突..._对等冲突检测错误

PHP图片推荐系统python-flask-django-nodejs-程序员宅基地

文章浏览阅读839次,点赞5次,收藏18次。由于互联网技术的快速发展,使得各部门都是以数字化、信息化、无纸化的发展趋势,随着趋势的发展,各种决策系统、辅助系统也应运而生,其中,图片推荐系统就是其中的重要组成部分。

linux执行sh脚本时提示“没有那个文件或目录”_linux系统虚拟机中输入vars.sh显示未找到文件-程序员宅基地

文章浏览阅读1.9w次,点赞2次,收藏6次。在linux上使用bash XXX.sh执行脚本时,提示“for reading(No such file or directory)”。问题原因:因为操作系统是windows,在windows下编辑的脚本,所以有可能有不可见字符。脚本文件是DOS格式的即每一行的行尾以\r\n来标识, 其ASCII码分别是0x0D, 0x0A.解决方法:可以有很多种办法看这个文件是DOS格式的还是U..._linux系统虚拟机中输入vars.sh显示未找到文件

一些无人机航迹规划算法-强化学习法,启发式算法_强化学习无人机航迹点-程序员宅基地

文章浏览阅读556次,点赞11次,收藏8次。7. 更新 Q 值:(, ) = (, ) + [(, ) + (′,) − (, )]step4:如果迭代次数达到地图和指南针算子的迭代上限,则停止当前迭代,转而操作地标算子,否则跳转至Step3;鸽子的位置信息和速度信息进行更新,然后比较所有鸽子的适应度,找到新的最优解;6. 在当前的状态 S 下执行动作 A,得到新的状态′和奖励(, )输入:迭代次数 T,状态集 S,学习率 α,探索率,折扣因子。_强化学习无人机航迹点

随便推点

HT合泰单片机入门教程(第一章 HT单片机环境搭建)-程序员宅基地

文章浏览阅读1.3w次,点赞21次,收藏115次。HT合泰单片机入门教程系列文章目录前言一、合泰单片机的优势二、HT-IDE3000安装1.HT-IDE3000下载2.HT-IDE3000安装总结系列文章目录# 第一章 HT单片机环境搭建目录系列文章目录前言一、合泰单片机的优势二、HT-IDE3000安装1.HT-IDE3000下载2.HT-IDE3000安装总结前言工作已经很长一段时间,虽然还是菜鸟一只。但还是有点心得体会。写合泰单片机系列教程的原因:一、是为了记录自己学习过程和学习经历(ps:当初毕业进公司接触到的第一个就是为一款已_合泰单片机入门教程

Android之如何获取手机中所有的传感器_导出智能手机中的传感器数据-程序员宅基地

文章浏览阅读7.1k次。传感器是第二代智能手机的重要标志之一。可以毫不客气地说,现在市面上的Android手机和平板电脑(TV除外)都内置了传感器。否则很多游戏和应用就无法使用了。Android SDK支持的传感器并不是每一部Android设备都支持所有的传感器。大多数Android设备只支持一部分传感器。例如,方向传感器(电子罗盘)、重力传感器(屏幕翻转、赛车游戏等)。动作(Motion)传感器环境(E_导出智能手机中的传感器数据

基于R的ggplot2包画KEGG富集通路气泡图_KEGGdot_ggplot 高低通路kegg-程序员宅基地

文章浏览阅读6.4k次,点赞2次,收藏16次。背景**基于公司已给出的结果上做出调整(公司只给出了top10),画KEGG富集通路的气泡图,初始文件如下图代码演示> getwd() #显示工作目录> setwd() #如果上述显示不是想要的路径,可以新建一个文件夹然后设置成工作目录,方便一些原始文件以及结果图片的存放> install.packages("ggplot2",destdir="D:/RData/R-win-4.0.2/R-4.0.2/R-packages",lib="D:/RData/R-win-4.0.2_ggplot 高低通路kegg

Vue.js - 学习笔记 (一)_vue innerhtml $-程序员宅基地

文章浏览阅读2.3k次。简介实际上,所有的前台程序都可以分为View以及Model两个层次,Model层使用字面量以及JS特性创建数据模型,并搭载用户数据,View层则以用户友好的方式呈现Model层数据,并提供友好的交互方式。那么如何连接View和Model这两个层次呢?如果写过J2EE程序,那么最经典的连接方式就是MVC模式了,通过控制器进行View以及Model的交互。而另一种异军突起的方式,则是MVVM了,使_vue innerhtml $

深度学习人脸识别:InsightFace-REST 全方位解析-程序员宅基地

文章浏览阅读277次,点赞4次,收藏4次。深度学习人脸识别:InsightFace-REST 全方位解析项目地址:https://gitcode.com/SthPhoenix/InsightFace-RESTInsightFace-REST 是一个基于深度学习的人脸识别 RESTful API 服务,它提供了一个简洁而强大的接口,使得开发者能够轻松地在自己的应用中集成高精度的人脸检测、识别和属性分析功能。项目概述InsightFa...

成为Java顶尖程序员 ,看这11本书就够了_有没有分析java源码的书籍-程序员宅基地

文章浏览阅读517次。转自:http://www.kuqin.com/shuoit/20160107/349896.html“学习的最好途径就是看书“,这是我自己学习并且小有了一定的积累之后的第一体会。个人认为看书有两点好处:1.能出版出来的书一定是经过反复的思考、雕琢和审核的,因此从专业性的角度来说,一本好书的价值远超其他资料2.对着书上的代码自己敲的时候方便“看完书之后再次提升自我的最好途径是看..._有没有分析java源码的书籍