Android Camera驱动分析

Android Camera驱动分析文章目录一、Camera的硬件接口二、代码路径三、Camera代码分析1、硬件接口设置2、Camera设备驱动3、模组驱动代码一、Camera的硬件接口引脚名称及作用VCAMA就是AVDD,模拟供电,主要给感光区和ADC部分供电,2.8VVCAMD就是DVDD,数字供电,主要给 ISP 供电,1.2VVCAM_IO就是VDDIO,数字IO供电,主要给 I2C 部分供电,1.8VVCAM_AF自动对焦马达供电RESET复位引脚PDN工作状态控


一、Camera的硬件接口

引脚 名称及作用
VCAMA 就是AVDD,模拟供电,主要给感光区和ADC部分供电,2.8V
VCAMD 就是DVDD,数字供电,主要给 ISP 供电,1.2V
VCAM_IO 就是VDDIO,数字IO供电,主要给 I2C 部分供电,1.8V
VCAM_AF 自动对焦马达供电
RESET 复位引脚
PDN 工作状态控制引脚
CMMCLK 时钟引脚
SCL、SDA I2C接口
RCN、RCP MIPI时钟接口
RDN0、RDP0 MIPI数据接口通道0

注:MIPI数据接口可能有多通道。

二、代码路径

描述 路径 文件
系统设置 device\top\top6737t_36_a_m0
kernel-3.18\arch\arm\configs
ProjectConfig.mk
user版本和userdebug版本对应:
top6737t_36_a_m0_defconfig
debug版本对应:
top6737t_36_a_m0_debug_defconfig
接口设置 kernel-3.18\arch\arm\boot\dts top6737t_36_a_m0.dts
cust_i2c.dtsi
Kernel代码 kernel-3.18\drivers\misc\mediatek\imgsensor
HAL代码 vendor\mediatek\proprietary\custom\mt6735\hal\D2\imgsensor
镜头、闪光灯等相关代码 kernel-3.18\drivers\misc\mediatek\lens
kernel-3.18\drivers\misc\mediatek\flashlight
vendor\mediatek\proprietary\custom\mt6735\hal\D2\lens
vendor\mediatek\proprietary\custom\mt6735\hal\D2\flashlight

三、Camera代码分析

1、硬件接口设置

​ 在dts文件中,对照硬件接口,修改对应GPIO设置:

// top6737t_36_a_m0.dts
/* CAMERA GPIO standardization */
&pio {
	camera_pins_cam0_rst0: cam0@0 {
		pins_cmd_dat {
			pins = <PINMUX_GPIO44__FUNC_GPIO44>;/*GPIO_CAMERA_CMRST_PIN*/
			slew-rate = <1>; /*direction 0:in, 1:out*/
			output-low;/*direction out used only. output_low or high*/
		};
	};
	camera_pins_cam0_rst1: cam0@1 {
		pins_cmd_dat {
			pins = <PINMUX_GPIO44__FUNC_GPIO44>;/*GPIO_CAMERA_CMRST_PIN*/
			slew-rate = <1>;
			output-high;
		};
	};
	camera_pins_cam0_pnd0: cam0@2 {
		pins_cmd_dat {
			pins = <PINMUX_GPIO7__FUNC_GPIO7>;/*GPIO_CAMERA_CMPDN_PIN*/
			slew-rate = <1>;
			output-low;
		};
	};
	camera_pins_cam0_pnd1: cam0@3 {
		pins_cmd_dat {
			pins = <PINMUX_GPIO7__FUNC_GPIO7>;/*GPIO_CAMERA_CMPDN_PIN*/
			slew-rate = <1>;
			output-high;
		};
	};
	camera_pins_cam1_rst0: cam1@0 {
		pins_cmd_dat {
			pins = <PINMUX_GPIO11__FUNC_GPIO11>;/*GPIO_CAMERA_CMRST1_PIN*/
			slew-rate = <1>; /*direction 0:in, 1:out*/
			output-low;/*direction out used only. output_low or high*/
		};
	};
	camera_pins_cam1_rst1: cam1@1 {
		pins_cmd_dat {
			pins = <PINMUX_GPIO11__FUNC_GPIO11>;/*GPIO_CAMERA_CMRST1_PIN*/
			slew-rate = <1>;
			output-high;
		};
	};
	camera_pins_cam1_pnd0: cam1@2 {
		pins_cmd_dat {
			pins = <PINMUX_GPIO12__FUNC_GPIO12>;/*GPIO_CAMERA_CMPDN1_PIN*/
			slew-rate = <1>;
			output-low;
		};
	};
	camera_pins_cam1_pnd1: cam1@3 {
		pins_cmd_dat {
			pins = <PINMUX_GPIO12__FUNC_GPIO12>;/*GPIO_CAMERA_CMPDN1_PIN*/
			slew-rate = <1>;
			output-high;
		};
	};
	camera_pins_cam_ldo0_0: cam@0 {
		pins_cmd_dat {
			pins = <PINMUX_GPIO68__FUNC_GPIO68>;
			slew-rate = <1>;
			output-low;
		};
	};
	camera_pins_cam_ldo0_1: cam@1 {
		pins_cmd_dat {
			pins = <PINMUX_GPIO68__FUNC_GPIO68>;
			slew-rate = <1>;
			output-high;
		};
	};
	camera_pins_default: camdefault {
	};
};
&kd_camera_hw1 {
	pinctrl-names = "default", "cam0_rst0", "cam0_rst1", "cam0_pnd0", "cam0_pnd1",
	"cam1_rst0", "cam1_rst1", "cam1_pnd0", "cam1_pnd1",
	"cam_ldo0_0", "cam_ldo0_1";
	pinctrl-0 = <&camera_pins_default>;
	pinctrl-1 = <&camera_pins_cam0_rst0>;
	pinctrl-2 = <&camera_pins_cam0_rst1>;
	pinctrl-3 = <&camera_pins_cam0_pnd0>;
	pinctrl-4 = <&camera_pins_cam0_pnd1>;
	pinctrl-5 = <&camera_pins_cam1_rst0>;
	pinctrl-6 = <&camera_pins_cam1_rst1>;
	pinctrl-7 = <&camera_pins_cam1_pnd0>;
	pinctrl-8 = <&camera_pins_cam1_pnd1>;
	pinctrl-9 = <&camera_pins_cam_ldo0_0>;
	pinctrl-10 = <&camera_pins_cam_ldo0_1>;
	status = "okay";
};
/* CAMERA GPIO end */

还有对应的I2C设置:

/* cust_i2c.dtsi */
&i2c0 {
	camera_main@10 {
		compatible = "mediatek,camera_main";
		reg = <0x10>;
	};
	camera_main_af@0c {
		compatible = "mediatek,camera_main_af";
		reg = <0x0c>;
	};
	camera_sub@3c {
		compatible = "mediatek,camera_sub";
		reg = <0x3c>;
	};
};

2、Camera设备驱动

​ 文件kd_sensorlist.c是摄像头作为字符型设备驱动的入口,可以通过分析本文件简单捋一下摄像头驱动的代码结构。

​ 首先,是一个I2C设备的驱动:

//kd_sensorlist.c
#ifdef CONFIG_OF
static const struct of_device_id CAMERA_HW_i2c_of_ids[] = { 
   
    { 
    .compatible = "mediatek,camera_main", }, //在文件cust_i2c.dtsi中找对应的描述,设置用哪组I2C,以及地址
    { 
   }
};
#endif
//实现I2C设备驱动的几个标准接口
struct i2c_driver CAMERA_HW_i2c_driver = { 
   
	.probe = CAMERA_HW_i2c_probe,
	.remove = CAMERA_HW_i2c_remove,
	.driver = { 
   
		.name = CAMERA_HW_DRVNAME1,
		.owner = THIS_MODULE,
	#ifdef CONFIG_OF
		.of_match_table = CAMERA_HW_i2c_of_ids,
	#endif
	},
	.id_table = CAMERA_HW_i2c_id,
};

在I2C的设备驱动probe函数中,注册摄像头模组的字符型设备:

//kd_sensorlist.c
static int CAMERA_HW_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)
{ 
   
	......
	//注册摄像头模组的字符型设备
	i4RetValue = RegisterCAMERA_HWCharDrv();
	......
	PK_DBG("[CAMERA_HW] Attached!!\n");
	return 0;
}

在注册摄像头字符型设备函数RegisterCAMERA_HWCharDrv();中,可以看到这个字符型设备的操作函数:

//kd_sensorlist.c
static inline int RegisterCAMERA_HWCharDrv(void)
{ 
   
	......
	//操作函数
	cdev_init(g_pCAMERA_HW_CharDrv, &g_stCAMERA_HW_fops);
	......
	sensor_device = device_create(sensor_class, NULL, g_CAMERA_HWdevno, NULL, CAMERA_HW_DRVNAME1);
	return 0;
}
//操作函数结构体
static const struct file_operations g_stCAMERA_HW_fops = { 
   
	.owner = THIS_MODULE,
	.open = CAMERA_HW_Open,
	.release = CAMERA_HW_Release,
	.unlocked_ioctl = CAMERA_HW_Ioctl,
#ifdef CONFIG_COMPAT
	.compat_ioctl = CAMERA_HW_Ioctl_Compat,
#endif
};

​ 然后,我们看下摄像头字符型设备操作函数CAMERA_HW_Ioctl中,具体的一些操作:

//kd_sensorlist.c
static long CAMERA_HW_Ioctl(struct file *a_pstFile,unsigned int a_u4Command,unsigned long a_u4Param)
{ 
   
	......
	switch (a_u4Command) 
    { 
       
     //找到控制摄像头的驱动函数,以便获取摄像头的SensorOpen、SensorGetInfo等接口; 
	case KDIMGSENSORIOC_X_SET_DRIVER:
		i4RetValue = kdSetDriver((unsigned int *)pBuff);
		break;
    //调用接口SensorOpen,给摄像头上电,通过I2C读取摄像头ID,以及设置寄存器相关参数; 
	case KDIMGSENSORIOC_T_OPEN:
		i4RetValue = adopt_CAMERA_HW_Open();
		break;
    //调用接口SensorGetInfo,获取摄像头MIPI、CLOCK等参数; 
	case KDIMGSENSORIOC_X_GETINFO:
		i4RetValue = adopt_CAMERA_HW_GetInfo(pBuff);
		break;
    //调用接口SensorGetResolution,获取摄像头分辨率等参数; 
	case KDIMGSENSORIOC_X_GETRESOLUTION2:
		i4RetValue = adopt_CAMERA_HW_GetResolution(pBuff);
		break;       
	case KDIMGSENSORIOC_X_GETINFO2:
		i4RetValue = adopt_CAMERA_HW_GetInfo2(pBuff);
		break;
    //调用接口SensorFeatureControl,获取或设置摄像头增益、进入不同模式等操作; 
	case KDIMGSENSORIOC_X_FEATURECONCTROL:
		i4RetValue = adopt_CAMERA_HW_FeatureControl(pBuff);
		break;
    //调用接口SensorControl,控制摄像头预览、拍照等操作; 
	case KDIMGSENSORIOC_X_CONTROL:
		i4RetValue = adopt_CAMERA_HW_Control(pBuff);
		break;
    //调用接口SensorClose,关闭摄像头; 
	case KDIMGSENSORIOC_T_CLOSE:
		i4RetValue = adopt_CAMERA_HW_Close();
		break;
    //通过读ID方式检查摄像头是否存在; 
    case KDIMGSENSORIOC_T_CHECK_IS_ALIVE:
		i4RetValue = adopt_CAMERA_HW_CheckIsAlive();
		break;        
	......
	default:
		PK_DBG("No such command\n");
		i4RetValue = -EPERM;
		break;
	}
	......
}

查找并加载摄像头驱动功能函数的过程如下:

//kd_sensorlist.c
int kdSetDriver(unsigned int *pDrvIndex)
{ 
   
	ACDK_KD_SENSOR_INIT_FUNCTION_STRUCT *pSensorList = NULL;
	u32 drvIdx[KDIMGSENSOR_MAX_INVOKE_DRIVERS] = { 
   0, 0};
	u32 i;
    //KDIMGSENSOR_INVOKE_DRIVER_0和KDIMGSENSOR_INVOKE_DRIVER_1表示摄像头位置,目前支持两个位置:前摄和后摄
    //传进来的int型参数,高16位表示main还是sub,低16位表示摄像头Index
	PK_INF("pDrvIndex:0x%08x/0x%08x\n", pDrvIndex[KDIMGSENSOR_INVOKE_DRIVER_0], pDrvIndex[KDIMGSENSOR_INVOKE_DRIVER_1]);
	gDrvIndex = pDrvIndex[KDIMGSENSOR_INVOKE_DRIVER_0];
    //获取摄像头列表
	if (0 != kdGetSensorInitFuncList(&pSensorList)) { 
   
		PK_ERR("ERROR:kdGetSensorInitFuncList()\n");
		return -EIO;
	}
    //分别查找前摄和后摄驱动
	for (i = KDIMGSENSOR_INVOKE_DRIVER_0; i < KDIMGSENSOR_MAX_INVOKE_DRIVERS; i++) { 
   
		spin_lock(&kdsensor_drv_lock);
		//未找到前,不支持该驱动
		g_bEnableDriver[i] = FALSE;
		//根据传进来的参数,赋值main还是sub
		g_invokeSocketIdx[i] = (CAMERA_DUAL_CAMERA_SENSOR_ENUM)((pDrvIndex[i] & KDIMGSENSOR_DUAL_MASK_MSB) >> KDIMGSENSOR_DUAL_SHIFT);
		spin_unlock(&kdsensor_drv_lock);
		drvIdx[i] = (pDrvIndex[i] & KDIMGSENSOR_DUAL_MASK_LSB);
		//若传进来的参数是NONE,表示当前位置没有摄像头
		if (DUAL_CAMERA_NONE_SENSOR == g_invokeSocketIdx[i]) { 
   
				continue;
		}
        //根据摄像头位置,确定使用那一组I2C
		if (DUAL_CAMERA_SUB_SENSOR == g_invokeSocketIdx[i]) { 
   
			spin_lock(&kdsensor_drv_lock);
			gI2CBusNum = SUPPORT_I2C_BUS_NUM2;
			spin_unlock(&kdsensor_drv_lock);
			/* PK_XLOG_INFO("kdSetDriver: switch I2C BUS2\n"); */
		} else { 
   
			spin_lock(&kdsensor_drv_lock);
			gI2CBusNum = SUPPORT_I2C_BUS_NUM1;
			spin_unlock(&kdsensor_drv_lock);
			/* PK_XLOG_INFO("kdSetDriver: switch I2C BUS1\n"); */
		}
		PK_INF("g_invokeSocketIdx[%d]=%d,drvIdx[%d]=%d\n", i, g_invokeSocketIdx[i], i, drvIdx[i]);
		//Index不能超过定义的最大指:MAX_NUM_OF_SUPPORT_SENSOR
		if (MAX_NUM_OF_SUPPORT_SENSOR > drvIdx[i]) { 
   
		    //利用SensorInit函数将摄像头的具体函数放到g_pInvokeSensorFunc中
			if (NULL == pSensorList[drvIdx[i]].SensorInit) { 
   
				PK_ERR("ERROR:kdSetDriver()\n");
				return -EIO;
			}
			pSensorList[drvIdx[i]].SensorInit(&g_pInvokeSensorFunc[i]);
			if (NULL == g_pInvokeSensorFunc[i]) { 
   
				PK_ERR("ERROR:NULL g_pSensorFunc[%d]\n", i);
				return -EIO;
			}
			spin_lock(&kdsensor_drv_lock);
			//设置标识,支持这颗摄像头
			g_bEnableDriver[i] = TRUE;
			spin_unlock(&kdsensor_drv_lock);
			//把摄像头的名字copy到g_invokeSensorNameStr
			memcpy((char *)g_invokeSensorNameStr[i], (char *)pSensorList[drvIdx[i]].drvname, sizeof(pSensorList[drvIdx[i]].drvname));
			PK_INF("[%d][%d][%d][%s]\n", i, g_bEnableDriver[i], g_invokeSocketIdx[i], g_invokeSensorNameStr[i]);
		}
	}
	return 0;
}

打开摄像头的操作,包括上电、读ID等操作,是在kd_MultiSensorOpen函数中实现的,具体如下:

//kd_sensorlist.c
MUINT32 kd_MultiSensorOpen(void)
{ 
   
	......
	for (i = (KDIMGSENSOR_MAX_INVOKE_DRIVERS - 1); i >= KDIMGSENSOR_INVOKE_DRIVER_0; i--) 
	{ 
   
		if (g_bEnableDriver[i] && g_pInvokeSensorFunc[i]) 
		{ 
   
			if (0 != (g_CurrentSensorIdx & g_invokeSocketIdx[i])) 
			{ 
   
			#ifndef CONFIG_FPGA_EARLY_PORTING
				//给摄像头上电,kdCISModulePowerOn函数在tb_kd_camera_hw.c文件中实现;
				ret = kdCISModulePowerOn((CAMERA_DUAL_CAMERA_SENSOR_ENUM)g_invokeSocketIdx[i], (char *)g_invokeSensorNameStr[i], true,CAMERA_HW_DRVNAME1);
			#endif
				if (ERROR_NONE != ret) 
				{ 
   
					PK_ERR("[%s]", __func__);
					return ret;
				}
				//调用这颗摄像头的open函数,里边有读ID等操作
				ret = g_pInvokeSensorFunc[i]->SensorOpen();
				if (ERROR_NONE != ret) 
				{ 
   
				#ifndef CONFIG_FPGA_EARLY_PORTING
                      //如果open失败,还要下电;
					kdCISModulePowerOn((CAMERA_DUAL_CAMERA_SENSOR_ENUM)g_invokeSocketIdx[i], (char *)g_invokeSensorNameStr[i], false, CAMERA_HW_DRVNAME1);
				#endif
					PK_ERR("SensorOpen");
					return ret;
				}
			}
		}
	}
	......
	KD_MULTI_FUNCTION_EXIT();
	return ERROR_NONE;
}

​ 函数kd_MultiSensorOpen中调用的给摄像头上下电的函数,是在文件tb_kd_camera_hw.c中实现。不同模组,打开和关闭时,硬件接口的操作上会有差异,比如:先给哪路上电或下电?复位操作对应高电平还是低电平?上电后需要多长时间模组才Ready?具体的操作时序,可以参考模组的规格书。下面的代码就是上电和下电的具体操作:

// kd_camera_hw.c
int kdCISModulePowerOn(CAMERA_DUAL_CAMERA_SENSOR_ENUM SensorIdx, char *currSensorName, bool On, char *mode_name)
{ 
   
    ......
	//上电操作
	if (On) 
	{ 
   
        ......
		//判断要操作的模组,SensorIdx用来判断前摄还是后摄,currSensorName用来判断模组名字
		if (SensorIdx == DUAL_CAMERA_MAIN_SENSOR && currSensorName && (0 == strcmp(SENSOR_DRVNAME_GC030A_MIPI_RAW, currSensorName)))
		{ 
   
			//第1步:使能CMMCLK
			ISP_MCLK1_EN(1); 
			// 第2步:操作PDN脚和RESET脚,为芯片正常工作做准备;
			if (GPIO_CAMERA_INVALID != pinSet[pinSetIdx][IDX_PS_CMPDN]) 
			{ 
   
				mtkcam_gpio_set(pinSetIdx, CAMPDN,pinSet[pinSetIdx][IDX_PS_CMPDN + IDX_PS_OFF]);
			}
			if (GPIO_CAMERA_INVALID != pinSet[pinSetIdx][IDX_PS_CMRST]) 
			{ 
   
				mtkcam_gpio_set(pinSetIdx, CAMRST,pinSet[pinSetIdx][IDX_PS_CMRST + IDX_PS_OFF]);
			}
			mdelay(5);		
			//第3步:分别给VDDIO、AVDD、DVDD上电,如果自动对焦马达需要供电,也需要给VCAM_AF上电
			if (TRUE != _hwPowerOnCnt(VCAMIO, VOL_1800, mode_name)) 
			{ 
   
				PK_DBG("[CAMERA SENSOR] Fail to enable IO power (VCAM_IO),power id = %d\n",VCAMIO);
				goto _kdCISModulePowerOn_exit_;
			}	
			mdelay(1);	
			if (TRUE != _hwPowerOnCnt(VCAMA, VOL_2800, mode_name)) 
			{ 
   
				PK_DBG("[CAMERA SENSOR] Fail to enable analog power (VCAM_A),power id = %d\n",VCAMA);
				goto _kdCISModulePowerOn_exit_;
			}
			mdelay(5);
			if (TRUE != _hwPowerOnCnt(VCAMD, VOL_1200, mode_name)) 
			{ 
   
					PK_DBG ("[CAMERA SENSOR] Fail to enable digital power (VCAM_D),power id = %d\n", VCAMD);
					goto _kdCISModulePowerOn_exit_;
			 }
			if (TRUE != _hwPowerOnCnt(VCAMAF, VOL_2800, mode_name)) 
			{ 
   
					PK_DBG ("[CAMERA SENSOR] Fail to enable analog power (VCAM_AF),power id = %d\n", VCAMAF);
					goto _kdCISModulePowerOn_exit_;
			 }
			//第4步:使模组进入Nomal模式
			 if (GPIO_CAMERA_INVALID != pinSet[pinSetIdx][IDX_PS_CMPDN]) 
			{ 
   
				mtkcam_gpio_set(pinSetIdx, CAMPDN,pinSet[pinSetIdx][IDX_PS_CMPDN + IDX_PS_ON]);
			}
			if (GPIO_CAMERA_INVALID != pinSet[pinSetIdx][IDX_PS_CMRST]) 
			{ 
   
				mtkcam_gpio_set(pinSetIdx, CAMRST,pinSet[pinSetIdx][IDX_PS_CMRST + IDX_PS_ON]);
			}
			mdelay(5);			
		}
        ......
	}
	else //下电操作
	{ 
   
        ......
		//判断要操作的模组,SensorIdx用来判断前摄还是后摄,currSensorName用来判断模组名字
		if (SensorIdx == DUAL_CAMERA_MAIN_SENSOR && currSensorName && (0 == strcmp(SENSOR_DRVNAME_GC030A_MIPI_RAW, currSensorName))) 
		{ 
   
			//第1步:操作PDN脚和RESET脚,停止模组工作
			if (GPIO_CAMERA_INVALID != pinSet[pinSetIdx][IDX_PS_CMPDN]) 
			{ 
   
				mtkcam_gpio_set(pinSetIdx, CAMPDN,pinSet[pinSetIdx][IDX_PS_CMPDN + IDX_PS_OFF]);
			}
			if (GPIO_CAMERA_INVALID != pinSet[pinSetIdx][IDX_PS_CMRST]) 
			{ 
   
				mtkcam_gpio_set(pinSetIdx, CAMRST,pinSet[pinSetIdx][IDX_PS_CMRST + IDX_PS_OFF]);
			}
			//第2步:分别关闭VDDIO、AVDD、DVDD、VCAM_AF的电
			if (TRUE != _hwPowerDownCnt(VCAMA, mode_name)) 
			{ 
   
				PK_DBG("[CAMERA SENSOR] Fail to OFF analog power (VCAM_A), power id= (%d)\n",VCAMA);
				goto _kdCISModulePowerOn_exit_;
			}
			if (TRUE != _hwPowerDownCnt(VCAMIO, mode_name)) 
			{ 
   
				PK_DBG("[CAMERA SENSOR] Fail to OFF digital power (VCAM_IO), power id = %d\n",VCAMIO);
				goto _kdCISModulePowerOn_exit_;
			}
			if (TRUE != _hwPowerDownCnt(VCAMD, mode_name)) 
			{ 
   
				PK_DBG("[CAMERA SENSOR] Fail to OFF analog power (VCAMD), power id= (%d)\n",VCAMD);
				goto _kdCISModulePowerOn_exit_;
			}
			if (TRUE != _hwPowerDownCnt(VCAMAF, mode_name)) 
			{ 
   
				PK_DBG("[CAMERA SENSOR] Fail to OFF digital power (VCAMAF), power id = %d\n",VCAMAF);
				goto _kdCISModulePowerOn_exit_;
			}
			mdelay(5);
			// 第3步:关闭CMMCLK
			ISP_MCLK1_EN(0);
		}
        ......
	}
	......
	return 0;

	_kdCISModulePowerOn_exit_:
	return -EIO;
}

​ 至此,Kernel中给摄像头上电、打开摄像头、控制摄像头、关闭摄像头等实现,就比较清晰了。

3、模组驱动代码

​ 分析设备驱动代码时,已经知晓在kdSetDriver操作中会根据不同模组找到对应的驱动代码,根据不同的模组有不同的上下电操作。不同模组的不同驱动代码都在下面这个文件中:

//kd_sensorlist.h
......
UINT32 GC030AMIPI_RAW_SensorInit(PSENSOR_FUNCTION_STRUCT *pfFunc);
......
ACDK_KD_SENSOR_INIT_FUNCTION_STRUCT kdSensorList[MAX_NUM_OF_SUPPORT_SENSOR+1] =
{ 
   
......
#if defined(GC030A_MIPI_RAW)
    { 
   GC030AMIPI_SENSOR_ID, SENSOR_DRVNAME_GC030A_MIPI_RAW,GC030AMIPI_RAW_SensorInit},
#endif
......
}

不同模组列表kdSensorList,其实是不同的模组以结构体ACDK_KD_SENSOR_INIT_FUNCTION_STRUCT格式进行填充,结构体定义如下:

//kd_imgsensor_define.h
typedef struct { 
   
	MUINT32 SensorId; //模组ID
	MUINT8 drvname[32];//模组名字
	MUINT32(*SensorInit)(PSENSOR_FUNCTION_STRUCT *pfFunc);//模组初始化函数
} ACDK_KD_SENSOR_INIT_FUNCTION_STRUCT, *PACDK_KD_SENSOR_INIT_FUNCTION_STRUCT;

模组的初始化函数是用来获取模组驱动函数接口的,模组驱动函数接口定义在:

//kd_imgsensor_define.h
typedef struct { 
   
    MUINT32(*SensorOpen)(void);
    MUINT32(*SensorGetInfo)(MSDK_SCENARIO_ID_ENUM ScenarioId, MSDK_SENSOR_INFO_STRUCT *pSensorInfo,MSDK_SENSOR_CONFIG_STRUCT *pSensorConfigData);
    MUINT32(*SensorGetResolution)(MSDK_SENSOR_RESOLUTION_INFO_STRUCT *pSensorResolution);
    MUINT32(*SensorFeatureControl)(MSDK_SENSOR_FEATURE_ENUM FeatureId, MUINT8 *pFeaturePara, MUINT32 *pFeatureParaLen);
    MUINT32(*SensorControl)(MSDK_SCENARIO_ID_ENUM ScenarioId, MSDK_SENSOR_EXPOSURE_WINDOW_STRUCT *pImageWindow, MSDK_SENSOR_CONFIG_STRUCT *pSensorConfigData);
    MUINT32(*SensorClose)(void);
#if 1 /* isp suspend resume patch */
    MSDK_SCENARIO_ID_ENUM ScenarioId;
    MSDK_SENSOR_EXPOSURE_WINDOW_STRUCT imageWindow;
    MSDK_SENSOR_CONFIG_STRUCT sensorConfigData;
#endif
} SENSOR_FUNCTION_STRUCT, *PSENSOR_FUNCTION_STRUCT;

分析不同模组驱动代码,就是分析不同模组实现SENSOR_FUNCTION_STRUCT的具体函数,比如 gc030a_mipi_raw :

//gc030amipi_Sensor.c
static SENSOR_FUNCTION_STRUCT sensor_func = { 
   
    open,
    get_info,
    get_resolution,
    feature_control,
    control,
    close
};

首先,看open函数:

//gc030amipi_Sensor.c
static kal_uint32 open(void)
{ 
   
	......
	while (imgsensor_info.i2c_addr_table[i] != 0xff) 
	{ 
   
		spin_lock(&imgsensor_drv_lock);
		imgsensor.i2c_write_id = imgsensor_info.i2c_addr_table[i];
		spin_unlock(&imgsensor_drv_lock);
		do 
		{ 
   
			//通过I2C读取模组ID
			sensor_id = return_sensor_id();
			if (sensor_id == imgsensor_info.sensor_id) 
			{ 
   
				LOG_INF("GC030A open i2c write id: 0x%x, sensor id: 0x%x\n", imgsensor.i2c_write_id,sensor_id);
				break;
			}
			LOG_INF("GC030A open Read sensor id fail, write id: 0x%x, id: 0x%x\n", imgsensor.i2c_write_id,sensor_id);
			retry--;
		} while(retry > 0);
		i++;
		if (sensor_id == imgsensor_info.sensor_id) break;
		
		retry = 2;
	}
	//ID不匹配则直接退出
	if (imgsensor_info.sensor_id != sensor_id)
	{ 
   
		return ERROR_SENSOR_CONNECT_FAIL;
	}
	//通过I2C把摄像头Sensor的寄存器初始化参数写进去,包括镜像、增益、MIPI等基础参数
	sensor_init();
	......
	return ERROR_NONE;
} 

open函数中调用的sensor_init函数,是通过I2C写摄像头模组的初始化参数,调试过程中可能会遇到成像镜像等问题,可以通过调试这些参数来修正:

//gc030amipi_Sensor.c
static void sensor_init(void)
{ 
   
	/*SYS*/
	write_cmos_sensor(0xfe, 0x80);
	write_cmos_sensor(0xfe, 0x80);
	write_cmos_sensor(0xfe, 0x80);
	write_cmos_sensor(0xf7, 0x01);
	write_cmos_sensor(0xf8, 0x05);
	write_cmos_sensor(0xf9, 0x0f);
	write_cmos_sensor(0xfa, 0x00);
	write_cmos_sensor(0xfc, 0x0f);
	write_cmos_sensor(0xfe, 0x00);
	/*ANALOG & CISCTL*/
	write_cmos_sensor(0x03, 0x01);
	write_cmos_sensor(0x04, 0xc8);
	write_cmos_sensor(0x05, 0x03);
	write_cmos_sensor(0x06, 0x7b);
	write_cmos_sensor(0x07, 0x00);
	write_cmos_sensor(0x08, 0x06);
	write_cmos_sensor(0x0a, 0x00);
	write_cmos_sensor(0x0c, 0x08);
	write_cmos_sensor(0x0d, 0x01);
	write_cmos_sensor(0x0e, 0xe8);
	write_cmos_sensor(0x0f, 0x02);
	write_cmos_sensor(0x10, 0x88);
	write_cmos_sensor(0x12, 0x28);//23 add 20170110 
	write_cmos_sensor(0x17, MIRROR);//Don't Change Here!!!
	write_cmos_sensor(0x18, 0x12);
	write_cmos_sensor(0x19, 0x07);
	write_cmos_sensor(0x1a, 0x1b);
	write_cmos_sensor(0x1d, 0x48);//40 travis20160318
	write_cmos_sensor(0x1e, 0x50);
	write_cmos_sensor(0x1f, 0x80);
	write_cmos_sensor(0x23, 0x01);
	write_cmos_sensor(0x24, 0xc8);
    write_cmos_sensor(0x27, 0xaf);
    write_cmos_sensor(0x28, 0x24);
	write_cmos_sensor(0x29, 0x1a);
	write_cmos_sensor(0x2f, 0x14);
	write_cmos_sensor(0x30, 0x00);
	write_cmos_sensor(0x31, 0x04);
	write_cmos_sensor(0x32, 0x08);
	write_cmos_sensor(0x33, 0x0c);
	write_cmos_sensor(0x34, 0x0d);
	write_cmos_sensor(0x35, 0x0e);
    write_cmos_sensor(0x36, 0x0f);
	write_cmos_sensor(0x72, 0x98);
	write_cmos_sensor(0x73, 0x9a);
	write_cmos_sensor(0x74, 0x47);
	write_cmos_sensor(0x76, 0x82);
	write_cmos_sensor(0x7a, 0xcb);
	write_cmos_sensor(0xc2, 0x0c);
	write_cmos_sensor(0xce, 0x03);	
	write_cmos_sensor(0xcf, 0x48);
	write_cmos_sensor(0xd0, 0x10);
	write_cmos_sensor(0xdc, 0x75);
	write_cmos_sensor(0xeb, 0x78);
	/*ISP*/
	write_cmos_sensor(0x90, 0x01);
	write_cmos_sensor(0x92, STARTY);//Don't Change Here!!!
	write_cmos_sensor(0x94, STARTX);//Don't Change Here!!!
	write_cmos_sensor(0x95, 0x01);
	write_cmos_sensor(0x96, 0xe0);
	write_cmos_sensor(0x97, 0x02);
	write_cmos_sensor(0x98, 0x80);
	/*Gain*/
	write_cmos_sensor(0xb0, 0x46);
	write_cmos_sensor(0xb1, 0x01);
	write_cmos_sensor(0xb2, 0x00);
	write_cmos_sensor(0xb3, 0x40);
	write_cmos_sensor(0xb4, 0x40);
	write_cmos_sensor(0xb5, 0x40);
	write_cmos_sensor(0xb6, 0x00);
	/*BLK*/
	write_cmos_sensor(0x40, 0x26); 
	write_cmos_sensor(0x4e, 0x00);
	write_cmos_sensor(0x4f, 0x3c);
	/*Dark Sun*/
	write_cmos_sensor(0xe0, 0x9f);
	write_cmos_sensor(0xe1, 0x90);
	write_cmos_sensor(0xe4, 0x0f);
	write_cmos_sensor(0xe5, 0xff);
	/*MIPI*/
	write_cmos_sensor(0xfe, 0x03);
	write_cmos_sensor(0x10, 0x00);	
	write_cmos_sensor(0x01, 0x03);
	write_cmos_sensor(0x02, 0x33);
	write_cmos_sensor(0x03, 0x96);
	write_cmos_sensor(0x04, 0x01);
	write_cmos_sensor(0x05, 0x00);
	write_cmos_sensor(0x06, 0x80);	
	write_cmos_sensor(0x11, 0x2b);
	write_cmos_sensor(0x12, 0x20);
	write_cmos_sensor(0x13, 0x03);
	write_cmos_sensor(0x15, 0x00);
	write_cmos_sensor(0x21, 0x10);
	write_cmos_sensor(0x22, 0x00);
	write_cmos_sensor(0x23, 0x30);
	write_cmos_sensor(0x24, 0x02);
	write_cmos_sensor(0x25, 0x12);
	write_cmos_sensor(0x26, 0x02);
	write_cmos_sensor(0x29, 0x01);
	write_cmos_sensor(0x2a, 0x0a);
	write_cmos_sensor(0x2b, 0x03);
	write_cmos_sensor(0xfe, 0x00);
	write_cmos_sensor(0xf9, 0x0e);
	write_cmos_sensor(0xfc, 0x0e);
	write_cmos_sensor(0xfe, 0x00);
	write_cmos_sensor(0x25, 0xa2);
	write_cmos_sensor(0x3f, 0x1a);
	Sleep(100);
	write_cmos_sensor(0x25,0xe2);
} 

接下来,再看get_info、get_resolution函数,主要是获取摄像头模组的工作参数,参考如下:

//gc030amipi_Sensor.c
static kal_uint32 get_info(MSDK_SCENARIO_ID_ENUM scenario_id,MSDK_SENSOR_INFO_STRUCT *sensor_info,MSDK_SENSOR_CONFIG_STRUCT *sensor_config_data)
{ 
   
    LOG_INF("scenario_id = %d\n", scenario_id);
	
    sensor_info->SensorClockPolarity = SENSOR_CLOCK_POLARITY_LOW;
    sensor_info->SensorClockFallingPolarity = SENSOR_CLOCK_POLARITY_LOW; /* not use */
    sensor_info->SensorHsyncPolarity = SENSOR_CLOCK_POLARITY_LOW; // inverse with datasheet
    sensor_info->SensorVsyncPolarity = SENSOR_CLOCK_POLARITY_LOW;
    sensor_info->SensorInterruptDelayLines = 4; /* not use */
    sensor_info->SensorResetActiveHigh = FALSE; /* not use */
    sensor_info->SensorResetDelayCount = 5; /* not use */

    sensor_info->SensroInterfaceType = imgsensor_info.sensor_interface_type;
    sensor_info->MIPIsensorType = imgsensor_info.mipi_sensor_type;
    sensor_info->SettleDelayMode = imgsensor_info.mipi_settle_delay_mode;
    sensor_info->SensorOutputDataFormat = imgsensor_info.sensor_output_dataformat;

    sensor_info->CaptureDelayFrame = imgsensor_info.cap_delay_frame;
    sensor_info->PreviewDelayFrame = imgsensor_info.pre_delay_frame;
    sensor_info->VideoDelayFrame = imgsensor_info.video_delay_frame;
    sensor_info->HighSpeedVideoDelayFrame = imgsensor_info.hs_video_delay_frame;
    sensor_info->SlimVideoDelayFrame = imgsensor_info.slim_video_delay_frame;

    sensor_info->SensorMasterClockSwitch = 0; /* not use */
    sensor_info->SensorDrivingCurrent = imgsensor_info.isp_driving_current;

    sensor_info->AEShutDelayFrame = imgsensor_info.ae_shut_delay_frame;          /* The frame of setting shutter default 0 for TG int */
    sensor_info->AESensorGainDelayFrame = imgsensor_info.ae_sensor_gain_delay_frame;    /* The frame of setting sensor gain */
    sensor_info->AEISPGainDelayFrame = imgsensor_info.ae_ispGain_delay_frame;
    sensor_info->IHDR_Support = imgsensor_info.ihdr_support;
    sensor_info->IHDR_LE_FirstLine = imgsensor_info.ihdr_le_firstline;
    sensor_info->SensorModeNum = imgsensor_info.sensor_mode_num;

    sensor_info->SensorMIPILaneNumber = imgsensor_info.mipi_lane_num;
    sensor_info->SensorClockFreq = imgsensor_info.mclk;
    sensor_info->SensorClockDividCount = 3; /* not use */
    sensor_info->SensorClockRisingCount = 0;
    sensor_info->SensorClockFallingCount = 2; /* not use */
    sensor_info->SensorPixelClockCount = 3; /* not use */
    sensor_info->SensorDataLatchCount = 2; /* not use */

    sensor_info->MIPIDataLowPwr2HighSpeedTermDelayCount = 0;
    sensor_info->MIPICLKLowPwr2HighSpeedTermDelayCount = 0;
    sensor_info->SensorWidthSampling = 0;  // 0 is default 1x
    sensor_info->SensorHightSampling = 0;    // 0 is default 1x
    sensor_info->SensorPacketECCOrder = 1;

    switch (scenario_id) { 
   
        case MSDK_SCENARIO_ID_CAMERA_PREVIEW:
            sensor_info->SensorGrabStartX = imgsensor_info.pre.startx;
            sensor_info->SensorGrabStartY = imgsensor_info.pre.starty;
            sensor_info->MIPIDataLowPwr2HighSpeedSettleDelayCount = imgsensor_info.pre.mipi_data_lp2hs_settle_dc;
            break;
        case MSDK_SCENARIO_ID_CAMERA_CAPTURE_JPEG:
            sensor_info->SensorGrabStartX = imgsensor_info.cap.startx;
            sensor_info->SensorGrabStartY = imgsensor_info.cap.starty;
            sensor_info->MIPIDataLowPwr2HighSpeedSettleDelayCount = imgsensor_info.cap.mipi_data_lp2hs_settle_dc;
            break;
        case MSDK_SCENARIO_ID_VIDEO_PREVIEW:
            sensor_info->SensorGrabStartX = imgsensor_info.normal_video.startx;
            sensor_info->SensorGrabStartY = imgsensor_info.normal_video.starty;
            sensor_info->MIPIDataLowPwr2HighSpeedSettleDelayCount = imgsensor_info.normal_video.mipi_data_lp2hs_settle_dc;
            break;
        case MSDK_SCENARIO_ID_HIGH_SPEED_VIDEO:
            sensor_info->SensorGrabStartX = imgsensor_info.hs_video.startx;
            sensor_info->SensorGrabStartY = imgsensor_info.hs_video.starty;
            sensor_info->MIPIDataLowPwr2HighSpeedSettleDelayCount = imgsensor_info.hs_video.mipi_data_lp2hs_settle_dc;
            break;
        case MSDK_SCENARIO_ID_SLIM_VIDEO:
            sensor_info->SensorGrabStartX = imgsensor_info.slim_video.startx;
            sensor_info->SensorGrabStartY = imgsensor_info.slim_video.starty;
            sensor_info->MIPIDataLowPwr2HighSpeedSettleDelayCount = imgsensor_info.slim_video.mipi_data_lp2hs_settle_dc;
            break;
        default:
            sensor_info->SensorGrabStartX = imgsensor_info.pre.startx;
            sensor_info->SensorGrabStartY = imgsensor_info.pre.starty;
            sensor_info->MIPIDataLowPwr2HighSpeedSettleDelayCount = imgsensor_info.pre.mipi_data_lp2hs_settle_dc;
            break;
    }

    return ERROR_NONE;
}   
static kal_uint32 get_resolution(MSDK_SENSOR_RESOLUTION_INFO_STRUCT *sensor_resolution)
{ 
   
    LOG_INF("E\n");
    sensor_resolution->SensorFullWidth = imgsensor_info.cap.grabwindow_width;
    sensor_resolution->SensorFullHeight = imgsensor_info.cap.grabwindow_height;
    sensor_resolution->SensorPreviewWidth = imgsensor_info.pre.grabwindow_width;
    sensor_resolution->SensorPreviewHeight = imgsensor_info.pre.grabwindow_height;
    sensor_resolution->SensorVideoWidth = imgsensor_info.normal_video.grabwindow_width;
    sensor_resolution->SensorVideoHeight = imgsensor_info.normal_video.grabwindow_height;
    sensor_resolution->SensorHighSpeedVideoWidth     = imgsensor_info.hs_video.grabwindow_width;
    sensor_resolution->SensorHighSpeedVideoHeight     = imgsensor_info.hs_video.grabwindow_height;
    sensor_resolution->SensorSlimVideoWidth     = imgsensor_info.slim_video.grabwindow_width;
    sensor_resolution->SensorSlimVideoHeight     = imgsensor_info.slim_video.grabwindow_height;
    return ERROR_NONE;
}

然后,我们看下feature_control和control函数,feature_control主要用来获取或设置模组的一些特征参数,contral主要用来控制模组拍照、预览、录像等,参考如下:

//gc030amipi_Sensor.c
static kal_uint32 feature_control(MSDK_SENSOR_FEATURE_ENUM feature_id,UINT8 *feature_para,UINT32 *feature_para_len)
{ 
   
    UINT16 *feature_return_para_16=(UINT16 *) feature_para;
    UINT16 *feature_data_16=(UINT16 *) feature_para;
    UINT32 *feature_return_para_32=(UINT32 *) feature_para;
    UINT32 *feature_data_32=(UINT32 *) feature_para;
    unsigned long long *feature_data=(unsigned long long *) feature_para;

    SENSOR_WINSIZE_INFO_STRUCT *wininfo;
    MSDK_SENSOR_REG_INFO_STRUCT *sensor_reg_data=(MSDK_SENSOR_REG_INFO_STRUCT *) feature_para;

    LOG_INF("feature_id = %d\n", feature_id);
    switch (feature_id) { 
   
        case SENSOR_FEATURE_GET_PERIOD:
            *feature_return_para_16++ = imgsensor.line_length;
            *feature_return_para_16 = imgsensor.frame_length;
            *feature_para_len=4;
            break;
        case SENSOR_FEATURE_GET_PIXEL_CLOCK_FREQ:
            *feature_return_para_32 = imgsensor.pclk;
            *feature_para_len=4;
            break;
        case SENSOR_FEATURE_SET_ESHUTTER:
            set_shutter(*feature_data);
            break;
        case SENSOR_FEATURE_SET_NIGHTMODE:
            night_mode((BOOL) *feature_data);
            break;
        case SENSOR_FEATURE_SET_GAIN:
            set_gain((UINT16) *feature_data);
            break;
        case SENSOR_FEATURE_SET_FLASHLIGHT:
            break;
        case SENSOR_FEATURE_SET_ISP_MASTER_CLOCK_FREQ:
            break;
        case SENSOR_FEATURE_SET_REGISTER:
            write_cmos_sensor(sensor_reg_data->RegAddr, sensor_reg_data->RegData);
            break;
        case SENSOR_FEATURE_GET_REGISTER:
            sensor_reg_data->RegData = read_cmos_sensor(sensor_reg_data->RegAddr);
            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.
            *feature_return_para_32=LENS_DRIVER_ID_DO_NOT_CARE;
            *feature_para_len=4;
            break;
        case SENSOR_FEATURE_SET_VIDEO_MODE:
            set_video_mode(*feature_data);
            break;
        case SENSOR_FEATURE_CHECK_SENSOR_ID:
            get_imgsensor_id(feature_return_para_32);
            break;
        case SENSOR_FEATURE_SET_AUTO_FLICKER_MODE:
            set_auto_flicker_mode((BOOL)*feature_data_16,*(feature_data_16+1));
            break;
        case SENSOR_FEATURE_SET_MAX_FRAME_RATE_BY_SCENARIO:
            set_max_framerate_by_scenario((MSDK_SCENARIO_ID_ENUM)*feature_data, *(feature_data+1));
            break;
        case SENSOR_FEATURE_GET_DEFAULT_FRAME_RATE_BY_SCENARIO:
            get_default_framerate_by_scenario((MSDK_SCENARIO_ID_ENUM)*(feature_data), (MUINT32 *)(uintptr_t)(*(feature_data+1)));
            break;
        case SENSOR_FEATURE_SET_TEST_PATTERN:
            set_test_pattern_mode((BOOL)*feature_data);
            break;
        case SENSOR_FEATURE_GET_TEST_PATTERN_CHECKSUM_VALUE: //for factory mode auto testing
            *feature_return_para_32 = imgsensor_info.checksum_value;
            *feature_para_len=4;
            break;
        case SENSOR_FEATURE_SET_FRAMERATE:
            LOG_INF("current fps :%d\n", (UINT32)*feature_data);
            spin_lock(&imgsensor_drv_lock);
            imgsensor.current_fps = *feature_data;
            spin_unlock(&imgsensor_drv_lock);
            break;
        case SENSOR_FEATURE_SET_HDR:
            LOG_INF("ihdr enable :%d\n", (BOOL)*feature_data);
            spin_lock(&imgsensor_drv_lock);
            imgsensor.ihdr_en = (BOOL)*feature_data;
            spin_unlock(&imgsensor_drv_lock);
            break;
        case SENSOR_FEATURE_GET_CROP_INFO:
            LOG_INF("SENSOR_FEATURE_GET_CROP_INFO scenarioId:%d\n", (UINT32)*feature_data);

            wininfo = (SENSOR_WINSIZE_INFO_STRUCT *)(uintptr_t)(*(feature_data+1));

            switch (*feature_data_32) { 
   
                case MSDK_SCENARIO_ID_CAMERA_CAPTURE_JPEG:
                    memcpy((void *)wininfo,(void *)&imgsensor_winsize_info[1],sizeof(SENSOR_WINSIZE_INFO_STRUCT));
                    break;
                case MSDK_SCENARIO_ID_VIDEO_PREVIEW:
                    memcpy((void *)wininfo,(void *)&imgsensor_winsize_info[2],sizeof(SENSOR_WINSIZE_INFO_STRUCT));
                    break;
                case MSDK_SCENARIO_ID_HIGH_SPEED_VIDEO:
                    memcpy((void *)wininfo,(void *)&imgsensor_winsize_info[3],sizeof(SENSOR_WINSIZE_INFO_STRUCT));
                    break;
                case MSDK_SCENARIO_ID_SLIM_VIDEO:
                    memcpy((void *)wininfo,(void *)&imgsensor_winsize_info[4],sizeof(SENSOR_WINSIZE_INFO_STRUCT));
                    break;
                case MSDK_SCENARIO_ID_CAMERA_PREVIEW:
                default:
                    memcpy((void *)wininfo,(void *)&imgsensor_winsize_info[0],sizeof(SENSOR_WINSIZE_INFO_STRUCT));
                    break;
            }
            break;
        case SENSOR_FEATURE_SET_IHDR_SHUTTER_GAIN:
            LOG_INF("SENSOR_SET_SENSOR_IHDR LE=%d, SE=%d, Gain=%d\n",(UINT16)*feature_data,(UINT16)*(feature_data+1),(UINT16)*(feature_data+2));
            ihdr_write_shutter_gain((UINT16)*feature_data,(UINT16)*(feature_data+1),(UINT16)*(feature_data+2));
            break;
        default:
            break;
    }

    return ERROR_NONE;
}    

static kal_uint32 control(MSDK_SCENARIO_ID_ENUM scenario_id, MSDK_SENSOR_EXPOSURE_WINDOW_STRUCT *image_window,MSDK_SENSOR_CONFIG_STRUCT *sensor_config_data)
{ 
   
    LOG_INF("scenario_id = %d\n", scenario_id);
    spin_lock(&imgsensor_drv_lock);
    imgsensor.current_scenario_id = scenario_id;
    spin_unlock(&imgsensor_drv_lock);
    switch (scenario_id) { 
   
        case MSDK_SCENARIO_ID_CAMERA_PREVIEW:
            preview(image_window, sensor_config_data);
            break;
        case MSDK_SCENARIO_ID_CAMERA_CAPTURE_JPEG:
            capture(image_window, sensor_config_data);
            break;
        case MSDK_SCENARIO_ID_VIDEO_PREVIEW:
            normal_video(image_window, sensor_config_data);
            break;
        case MSDK_SCENARIO_ID_HIGH_SPEED_VIDEO:
            hs_video(image_window, sensor_config_data);
            break;
        case MSDK_SCENARIO_ID_SLIM_VIDEO:
            slim_video(image_window, sensor_config_data);
            break;
        default:
            LOG_INF("Error ScenarioId setting");
            preview(image_window, sensor_config_data);
            return ERROR_INVALID_SCENARIO_ID;
    }
    return ERROR_NONE;
}    

最后,是close函数,用来关闭模组,如果不需要特殊操作,在设备驱动的Close操作中,对模组进行下电等操作即可,模组的close函数只是一个空函数:

//gc030amipi_Sensor.c
static kal_uint32 close(void)
{ 
   
    LOG_INF("E\n");
    /*No Need to implement this function*/
    return ERROR_NONE;
} 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:http://bianchenghao.cn/38091.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注