Android 相机启动流程笔记

和你一起终身学 习,这里是程序员Android

经典好文推荐,通过阅读本文,您将收获以下知识点:

一、Camera 框架介绍:

Camera的框架分为Kernel部分和hal部分,其中kernel部分主要有两块:

  • image sensor driver,负责具体型号的sensorid检测,上电,以及在previewcapture初始化3A等等功能设定时的寄存器配置;

  • isp driver,通过DMAsensor数据流上传;

HAL层部分主要有三部分组成:

  • imageio,主要负责数据buffer上传的pipe

  • drv,包含imgsensorisphal层控制;

  • feature io,包含各种3A等性能配置;

这篇内容主要介绍开机过程中search sensor以及上电流程等内容。

二、Camera 启动流程

1、CameraService是在开机时启动的,启动后进行searchSensor的操作,会search系统有多少camera,开机时的search操作,只进行camera支持数量的遍历,以及sensor ID的读取操作,如下是hal部分的ASTAH绘制调用流程图,对应的接口的文件路径:

  • HalSensorList:
    vendor/mediatek/proprietary/hardware/mtkcam/drv/src/sensor/common/v1/HalSensorList.enumList.cpp
    vendor/mediatek/proprietary/hardware/mtkcam/drv/src/sensor/common/v1/HalSensorList.cpp

  • SeninfDrv:
    vendor/mediatek/proprietary/hardware/mtkcam/drv/src/sensor/mt6765/seninf_drv.cpp

  • SensorDrv:
    vendor/mediatek/proprietary/hardware/mtkcam/drv/src/sensor/common/v1/imgsensor_drv.cpp

(1) 这里先看enumerateSensor_Locked完成的工作,直接看代码:

go 复制代码
MUINT HalSensorList::searchSensors()
{
    Mutex::Autolock _l(mEnumSensorMutex);
    MY_LOGD("searchSensors");
    return  enumerateSensor_Locked();
}

MUINT HalSensorList::enumerateSensor_Locked()
{
    SensorDrv *const pSensorDrv = SensorDrv::get();
    SeninfDrv *const pSeninfDrv = SeninfDrv::createInstance();
    //初始化seninf,配置ISP相关内容
    pSeninfDrv->init();

    //将所有的clk全部打开
    pSeninfDrv->setAllMclkOnOff(ISP_DRIVING_8MA, TRUE);

    pSensorDrv->init();
    for (MUINT i = IMGSENSOR_SENSOR_IDX_MIN_NUM; i <= max_index_of_camera; i++) {
        if((ret = pSensorDrv->searchSensor((IMGSENSOR_SENSOR_IDX)i)) == SENSOR_NO_ERROR){
            //query sensorinfo
           querySensorDrvInfo((IMGSENSOR_SENSOR_IDX)i);
           //fill in metadata
           buildSensorMetadata((IMGSENSOR_SENSOR_IDX)i);
           pSensorInfo = pSensorDrv->getSensorInfo((IMGSENSOR_SENSOR_IDX)i);
           addAndInitSensorEnumInfo_Locked(
                (IMGSENSOR_SENSOR_IDX)i,
                mapToSensorType(pSensorInfo->GetType()),
                pSensorInfo->getDrvMacroName());
        }
    }     
}

(2) 下面主要看下searchSensor的流程,这里有去获取sensorList的内容:

go 复制代码
MINT32 ImgSensorDrv::searchSensor(IMGSENSOR_SENSOR_IDX sensorIdx)
{
    GetSensorInitFuncList(&pSensorInitFunc);

    featureControl(sensorIdx, SENSOR_FEATURE_SET_DRIVER, (MUINT8 *)&idx, &featureParaLen);

    NSFeature::SensorInfoBase* pSensorInfo = pSensorInitFunc[idx].pSensorInfo;
}

GetSensorInitFuncList是获取到配置的sensorList的内容,此sensorList需要与kernel层配置的一致,不一致的话在打开camera时会出现异常:

文件位置:
vendor/mediatek/proprietary/custom/mt6765/hal/imgsensor_src/sensorlist.cpp

go 复制代码
MSDK_SENSOR_INIT_FUNCTION_STRUCT SensorList[] =
{
#if defined(IMX486_MIPI_RAW)
    RAW_INFO(IMX486_SENSOR_ID, SENSOR_DRVNAME_IMX486_MIPI_RAW, CAM_CALGetCalData),
#endif
//.....
}

UINT32 GetSensorInitFuncList(MSDK_SENSOR_INIT_FUNCTION_STRUCT **ppSensorList)
{
    if (NULL == ppSensorList) {
        ALOGE("ERROR: NULL pSensorList\n");
        return MHAL_UNKNOWN_ERROR;
    }
    *ppSensorList = &SensorList[0];
    return MHAL_NO_ERROR;
}

对应的MSDK_SENSOR_INIT_FUNCTION_STRUCT的结构体如下:

go 复制代码
typedef struct
{
    MUINT32 sensorType;
    MUINT32 SensorId;
    MUINT8  drvname[32];
    NSFeature::SensorInfoBase* pSensorInfo;
    MUINT32 (*getCameraIndexMgr)(CAMERA_DATA_TYPE_ENUM CameraDataType, MVOID *pDataBuf, MUINT32 size);
    MUINT32 (*getCameraCalData)(UINT32* pGetSensorCalData);
} MSDK_SENSOR_INIT_FUNCTION_STRUCT, *PMSDK_SENSOR_INIT_FUNCTION_STRUCT;

(3) featureControlsetDriver流程:

文件位置:
vendor/mediatek/proprietary/hardware/mtkcam/drv/src/sensor/common/v1/imgsensor_drv.cpp

go 复制代码
MINT32  ImgSensorDrv::featureControl(
    IMGSENSOR_SENSOR_IDX sensorIdx,
    ACDK_SENSOR_FEATURE_ENUM FeatureId,
    MUINT8 *pFeaturePara,
    MUINT32 *pFeatureParaLen
)
{
    //结构ACDK_SENSOR_FEATURECONTROL_STRUCT和kernel中一致
    featureCtrl.InvokeCamera = sensorIdx;
    featureCtrl.FeatureId = FeatureId;//SENSOR_FEATURE_SET_DRIVER
    featureCtrl.pFeaturePara = pFeaturePara;
    featureCtrl.pFeatureParaLen = pFeatureParaLen;

    if (ioctl(m_fdSensor, KDIMGSENSORIOC_X_FEATURECONCTROL , &featureCtrl) < 0) {
        LOG_ERR("[featureControl] Err-ctrlCode (%s)", strerror(errno));
        return -errno;
    }

    return SENSOR_NO_ERROR;
}

三、kernel 启动流程

先来看整体的框架图如下:

1、set clock 设置时钟

go 复制代码
static long imgsensor_ioctl(
    struct file *a_pstFile,
    unsigned int a_u4Command,
    unsigned long a_u4Param)
{
    case KDIMGSENSORIOC_X_SET_MCLK_PLL:
        i4RetValue = imgsensor_clk_set(
            &pgimgsensor->clk,
            (struct ACDK_SENSOR_MCLK_STRUCT *)pBuff);
        break;
        //......
}

int imgsensor_clk_set(
    struct IMGSENSOR_CLK *pclk, struct ACDK_SENSOR_MCLK_STRUCT *pmclk)
{
    if (pmclk->on) {
        clk_prepare_enable(pclk->imgsensor_ccf[mclk_index])
        ret = clk_set_parent(
            pclk->imgsensor_ccf[pmclk->TG],
            pclk->imgsensor_ccf[mclk_index]);
    } else {
        clk_disable_unprepare(pclk->imgsensor_ccf[mclk_index]);
    }
}

2、set driver

go 复制代码
static long imgsensor_ioctl(
    struct file *a_pstFile,
    unsigned int a_u4Command,
    unsigned long a_u4Param) 
{
    case KDIMGSENSORIOC_X_FEATURECONCTROL:
        i4RetValue = adopt_CAMERA_HW_FeatureControl(pBuff);
        break;
        //......
}

static inline int adopt_CAMERA_HW_FeatureControl(void *pBuf)
{
    /* copy from user */
    switch (pFeatureCtrl->FeatureId) {
        case SENSOR_FEATURE_SET_DRIVER:
        {
            MINT32 drv_idx;

            psensor->inst.sensor_idx = pFeatureCtrl->InvokeCamera;
            drv_idx = imgsensor_set_driver(psensor);

            memcpy(pFeaturePara, &drv_idx, FeatureParaLen);
            break;
        }
    }
}

遍历CONFIG_CUSTOM_KERNEL_IMGSENSOR的内容,然后看sensorList是否对应,并获取对应的下标,调用imgsensor_check_is_alive进行上下电并读取ID 的操作:

go 复制代码
struct IMGSENSOR_INIT_FUNC_LIST kdSensorList[MAX_NUM_OF_SUPPORT_SENSOR] = {
#if defined(XXXXXX_MIPI_RAW)
        {XXXXXX_SENSOR_ID,
        SENSOR_DRVNAME_XXXXXX_MIPI_RAW,
        XXXXXX_MIPI_RAW_SensorInit},
#endif
    //......
}

int imgsensor_set_driver(struct IMGSENSOR_SENSOR *psensor)
{
    struct IMGSENSOR_SENSOR_INST    *psensor_inst = &psensor->inst;
    struct IMGSENSOR_INIT_FUNC_LIST *pSensorList  = kdSensorList;
    //获取config的size
    char *sensor_configs = STRINGIZE(CONFIG_CUSTOM_KERNEL_IMGSENSOR);
    imgsensor_i2c_init(&psensor_inst->i2c_cfg,
    imgsensor_custom_config[psensor->inst.sensor_idx].i2c_dev);

    memcpy(psensor_list_config, sensor_configs+1, strlen(sensor_configs)-2);
    //对应config字符串进行按空格进行拆解
    driver_name = strsep(&psensor_list_config, " \0");

    while (driver_name != NULL) {
        for (j = 0; j < MAX_NUM_OF_SUPPORT_SENSOR; j++) {
            //判断对应的init函数是否存在
            if (pSensorList[j].init == NULL)
                break;
            else if (!strcmp(driver_name, pSensorList[j].name)) {
                //如果在config中和sensorlist中同时有定义进行赋值
                orderedSearchList[i++] = j;
                break;
            }
        }
        driver_name = strsep(&psensor_list_config, " \0");
    }

    for (i = 0; i < MAX_NUM_OF_SUPPORT_SENSOR; i++) {
        //上面获取到的sensorlist的下标
        drv_idx = orderedSearchList[i];
        if (pSensorList[drv_idx].init) {
            //调用对应驱动的init函数
            pSensorList[drv_idx].init(&psensor->pfunc);
            if (psensor->pfunc) {
                psensor_inst->psensor_name =
                    (char *)pSensorList[drv_idx].name;
                    //到这里是重点,进行上电读取ID的操作
                if (!imgsensor_check_is_alive(psensor)) {
                    ret = drv_idx;
                }
            }
        }
    }
}

下面看对应的上下电以及读取ID的操作:

go 复制代码
static inline int imgsensor_check_is_alive(struct IMGSENSOR_SENSOR *psensor)
{
    struct IMGSENSOR_SENSOR_INST  *psensor_inst = &psensor->inst;
    //上电
    err = imgsensor_hw_power(&pgimgsensor->hw,
                psensor,
                psensor_inst->psensor_name,
                IMGSENSOR_HW_POWER_STATUS_ON);
    //读取ID
    imgsensor_sensor_feature_control(
            psensor,
            SENSOR_FEATURE_CHECK_SENSOR_ID,
            (MUINT8 *)&sensorID,
            &retLen);

    if (sensorID == 0 || sensorID == 0xFFFFFFFF) {
        pr_info("Fail to get sensor ID %x\n", sensorID);
        err = ERROR_SENSOR_CONNECT_FAIL;
    } else {
        pr_info(" Sensor found ID = 0x%x\n", sensorID);
        err = ERROR_NONE;
    }
    //下电
    imgsensor_hw_power(&pgimgsensor->hw,
        psensor,
        psensor_inst->psensor_name,
        IMGSENSOR_HW_POWER_STATUS_OFF);

    return err ? -EIO:err;
}

3、上电相关

上电时序配置:

go 复制代码
struct IMGSENSOR_HW_POWER_INFO {
    enum IMGSENSOR_HW_PIN       pin;
    enum IMGSENSOR_HW_PIN_STATE pin_state_on;
    u32  pin_on_delay;
    enum IMGSENSOR_HW_PIN_STATE pin_state_off;
    u32  pin_off_delay;
};

struct IMGSENSOR_HW_POWER_SEQ sensor_power_sequence[] = {
    //......
    #if defined(XXXXXX_MIPI_RAW)
        {
            SENSOR_DRVNAME_XXXXXX_MIPI_RAW,
            {
                {RST, Vol_Low, 0},
                {DVDD, Vol_1100, 1},
                {AVDD, Vol_2800, 1},
                {DOVDD, Vol_1800, 1},
                {RST, Vol_High, 1},
                {SensorMCLK, Vol_High, 0},
            },
        },
    #endif
}

对应的控制的流程如下:

go 复制代码
static enum IMGSENSOR_RETURN imgsensor_hw_power_sequence(
    struct IMGSENSOR_HW             *phw,
    enum   IMGSENSOR_SENSOR_IDX      sensor_idx,
    enum   IMGSENSOR_HW_POWER_STATUS pwr_status,
    struct IMGSENSOR_HW_POWER_SEQ   *ppower_sequence,
    char *pcurr_idx)
{
    ppwr_info = ppwr_seq->pwr_info;
    // 上电
    while (ppwr_info->pin != IMGSENSOR_HW_PIN_NONE &&
        ppwr_info < ppwr_seq->pwr_info + IMGSENSOR_HW_POWER_INFO_MAX) {

        if (pwr_status == IMGSENSOR_HW_POWER_STATUS_ON &&
           ppwr_info->pin != IMGSENSOR_HW_PIN_UNDEF) {
            pdev = phw->pdev[psensor_pwr->id[ppwr_info->pin]];
            if (pdev->set != NULL)
                //调用GPIO或者regulator的set 电压操作,这里的pdev在imgsensor_probe中已经设置
                pdev->set( 
                    pdev->pinstance,
                    sensor_idx,
                    ppwr_info->pin,
                    ppwr_info->pin_state_on);

            mdelay(ppwr_info->pin_on_delay);
        }
        // 从上到下依次上电
        ppwr_info++;
        pin_cnt++;
    }

    // 下电操作
    if (pwr_status == IMGSENSOR_HW_POWER_STATUS_OFF) {
        while (pin_cnt) {
            //从下到上依次下电
            ppwr_info--;
            pin_cnt--;

            if (ppwr_info->pin != IMGSENSOR_HW_PIN_UNDEF) {
                pdev =
                    phw->pdev[psensor_pwr->id[ppwr_info->pin]];
                mdelay(ppwr_info->pin_on_delay);

                if (pdev->set != NULL)
                    pdev->set(
                        pdev->pinstance,
                        sensor_idx,
                        ppwr_info->pin,
                        ppwr_info->pin_state_off);
            }
        }
    }

    /* wait for power stable */
    if (pwr_status == IMGSENSOR_HW_POWER_STATUS_ON)
        mdelay(5);
    return IMGSENSOR_RETURN_SUCCESS;
}

四、总结

通过上面的代码流程,可以知道上开机时,camera模块先会将所有的MCLK打开,然后对依次对对应的sensor进行上电,读取ID(判断I2C是否正常通讯)。这部分调试过程中遇到的问题总结如下:

1、ID读取不到,I2C不通

  • 检查上电时序,3项电压(AVDD/DVDD/IOVDD)是否正确;

  • I2C地址及通道设置是否正确;

  • 检查cfg_setting_imgsensor.cppMCLKHW链接配置是否正确;

2、Camera 启动时间过长

  • 检查Sensor上电时序要求的延时,是否有偏长的情况;

  • 去掉多余的I2C地址,因为大部分驱动会多添加一些地址;

  • OTP的加载调整到每次开机时第一次打开加载,之后不加载;

  • sensorInit如果时间过长,可以调节I2C speed(400->1000)

3、preview 阶段耗时

  • 检查streamOn/Off的耗时;

  • preview_init是否有较长时间的耗时

  • 以及延时操作使用mdelay代替msleep

  • pre_delay_frame/cap_delay_frame丢帧操作是否合适;

4、低电流、功耗相关问题

  • 检查电压是否都有下电成功,防止漏电;

  • 对于共pinsensor,在操作时是否有做好workaround

  • I2C寄存器单个读写,调整为连续读写的方式也有一定优化;

  • sensorPIN是否有被其他模块占用,异常操作的行为;

友情推荐:

Android 开发干货集锦

至此,本篇已结束。转载网络的文章,小编觉得很优秀,欢迎点击阅读原文,支持原创作者,如有侵权,恳请联系小编删除,欢迎您的建议与指正。同时期待您的关注,感谢您的阅读,谢谢!

点个在看,方便您使用时快速查找!

相关推荐
文弱_书生3 小时前
关于对鱼眼相机图片进行畸变校正的两种思路
数码相机·鱼眼畸变校正·传统几何方法·深度学习方法
汇能感知3 小时前
摄像头模块在运动相机中的特殊应用
经验分享·笔记·科技
疾风铸境3 小时前
qt+halcon开发相机拍照软件步骤
数码相机·qt·halcon·拍照
阿巴Jun3 小时前
【数学】线性代数知识点总结
笔记·线性代数·矩阵
茯苓gao3 小时前
STM32G4 速度环开环,电流环闭环 IF模式建模
笔记·stm32·单片机·嵌入式硬件·学习
是誰萆微了承諾3 小时前
【golang学习笔记 gin 】1.2 redis 的使用
笔记·学习·golang
DKPT4 小时前
Java内存区域与内存溢出
java·开发语言·jvm·笔记·学习
ST.J4 小时前
前端笔记2025
前端·javascript·css·vue.js·笔记
Suckerbin5 小时前
LAMPSecurity: CTF5靶场渗透
笔记·安全·web安全·网络安全
一笑的小酒馆5 小时前
Android性能优化之截屏时黑屏卡顿问题
android