1. MTK平台相机开发基础与常见配置问题

在MTK平台进行相机开发时,首先需要了解相机模组的基本配置和硬件交互原理。MTK平台通常使用MIPI接口连接相机传感器,通过I2C总线进行寄存器配置,而GPIO则用于控制电源、复位信号和外围设备(如闪光灯和马达)。在实际开发中,配置错误或硬件兼容性问题经常导致相机无法正常工作。

1.1 相机模组配置与定焦设置

相机模组的配置通常位于/vendor/mediatek/proprietary/custom/[项目名]/hal/imgsensor_metadata/目录下。以定焦镜头配置为例,需要修改config_static_metadata.lens.[传感器名].h文件中的对焦参数:

CONFIG_METADATA_BEGIN(MTK_LENS_INFO_MINIMUM_FOCUS_DISTANCE)
CONFIG_ENTRY_VALUE(0, MFLOAT)  // 将最小值设为0表示定焦
CONFIG_METADATA_END()

CONFIG_METADATA_BEGIN(MTK_LENS_INFO_HYPERFOCAL_DISTANCE)
CONFIG_ENTRY_VALUE(0, MFLOAT)  // 将超焦距设为0
CONFIG_METADATA_END()

修改后,可以通过dumpsys media.camera命令验证配置是否生效:

android.lens.info.minimumFocusDistance (90005): float[1] [0.00000000]

如果值从非零变为0,说明定焦配置成功。我在实际项目中遇到过配置宏定义缺少下划线导致相机无法点亮的问题,因此建议仔细检查传感器名称的拼写。

1.2 前后摄上电顺序调整

在某些项目中,前后相机模组之间存在硬件干扰,可能导致其中一个相机无法正常打开。这时可以通过调整上电顺序来解决:

// 文件路径:/vendor/mediatek/proprietary/hardware/mtkcam/drv/src/sensor/common/v1/HalSensorList.enumList.cpp
MUINT HalSensorList::enumerateSensor_Locked() {
    // 先搜索副摄再搜索主摄,改变上电顺序
    ret_sub = pSensorDrv->searchSensor((IMGSENSOR_SENSOR_IDX)1);
    ret_main = pSensorDrv->searchSensor((IMGSENSOR_SENSOR_IDX)0);
    
    for(MUINT i = 0; i < 2; i++) {
        // 初始化传感器元数据
        querySensorDrvInfo((IMGSENSOR_SENSOR_IDX)i);
        buildSensorMetadata((IMGSENSOR_SENSOR_IDX)i);
        // ... 其他初始化代码
    }
}

这种调整只改变上电顺序,不影响初始化和ID识别流程。我在一个项目中遇到约5%的设备后摄无法打开的问题,通过交换上电顺序后问题得到解决。

2. I2C通信问题排查与解决方案

I2C总线是相机传感器与主芯片通信的关键接口,配置不当会导致通信失败或数据错误。

2.1 I2C输出模式配置

MTK平台的I2C控制器支持推挽(Push-Pull)和开漏(Open-Drain)两种输出模式。可以通过以下方式检查当前配置:

# 查看I2C节点属性
cat /sys/firmware/devicetree/base/soc/i2c@11000000/mediatek,use-push-pull

如果返回1表示推挽模式,返回0或不存在表示开漏模式。在代码中的配置位置:

// 文件路径:/kernel-4.19/drivers/i2c/busses/i2c-mt65xx.c
static int mtk_i2c_parse_dt(struct device_node *np, struct mtk_i2c *i2c) {
    // ... 其他解析代码
    i2c->use_push_pull = of_property_read_bool(np, "mediatek,use-push-pull");
}

推挽模式输出能力强,但可能产生信号反射;开漏模式需要上拉电阻,但抗干扰能力更好。根据实际硬件设计选择合适的模式。

2.2 I2C电压异常问题排查

在实际项目中,我曾遇到I2C总线电压无法拉低到0V的问题,表现为低电平停留在280mV左右。这通常与硬件设计和驱动配置有关:

// 文件路径:/vendor/mediatek/proprietary/bootable/bootloader/preloader/platform/mt6765/src/drivers/i2c.c
// 修改I2C2的驱动能力配置
I2C_SET_REG32((0x10002800 + 0x20), 0x0FFF, 0x0BEF);

修改后低电平可以降到150mV,符合I2C规范要求。如果还需要进一步降低,可以调整上拉电阻的配置:

// 修改上拉电阻值
I2C_SET_REG32(0x10002800 + 0xA0, 0xFF, 0xFF);

上拉电阻的取值对应关系:0x0-75kΩ, 0x1-5kΩ, 0x2-15kΩ, 0x3-1kΩ。根据实际硬件特性选择合适的阻值。

3. GPIO控制实战技巧

GPIO控制是相机开发中的重要环节,涉及电源管理、复位控制和外围设备驱动。

3.1 GPIO配置与上拉设置

在设备树中配置GPIO节点:

// 文件路径:/kernel-4.19/arch/arm64/boot/dts/mediatek/tb8797p2_64_k419_wifi.dts
&odm {
    hdmi_gpio: hdmi_gpio {
        compatible = "mediatek,hdmi_gpio";
        hdmi,gpio_power_ena = <&pio 129 0x0>;
        hdmi,gpio_reset = <&pio 13 0x0>;
    };
};

在驱动代码中使用GPIO:

// 文件路径:/kernel-4.19/drivers/misc/mediatek/imgsensor/src/mt6885/camera_project/tb8797p2_64_k419_wifi/hd1080_mipi_yuv/hd1080_mipi_Sensor.c
void hdmi_parse_dt(void) {
    struct device_node *np;
    np = of_find_compatible_node(NULL, NULL, "mediatek,hdmi_gpio");
    if (np) {
        hdmi_power_pinA[0] = of_get_named_gpio_flags(np, "hdmi,gpio_power_ena", 0, NULL);
        gpio_request(hdmi_power_pinA[0], "hdmi,gpio_power_ena");
        // ... 其他GPIO初始化
    }
}

// 设置GPIO值
gpio_set_value(hdmi_power_pinA[1], GPIO_OUT_ZERO);

需要注意的是,GPIO编号需要加上基地址偏移量(如29),可以在/sys/devices/platform/pinctrl/mt_gpio中查看具体的偏移值。

3.2 RST信号无法拉高问题排查

在某项目中,遇到HDMI芯片的RST信号无法拉高的问题。通过添加调试信息定位问题:

// 文件路径:/kernel-4.19/drivers/pinctrl/mediatek/pinctrl-mtk-common-v2.c
int mtk_hw_set_value(struct mtk_pinctrl *hw, const struct mtk_pin_desc *desc, int field, int value) {
    pr_err("%s :zgy %d, field=%d, value=%d", __func__, desc->number, field, value);
    if (desc->number == 129 || desc->number == 13){
        pr_err("[debug48]%s :zgy %d, field=%d, value=%d", __func__, desc->number, field, value);
        dump_stack();
    }
    // ... 原有逻辑
}

通过日志发现RST引脚没有配置模式的过程,最终发现是上电时序文件中的配置冲突。修复方法是将imgsensor_hw_power_sequence.c中除了sensor = "hd1080_mipi_yuv"之外的其他配置恢复默认值。

3.3 上电时序问题解决

相机上电时序对正常启动至关重要。在MTK平台中,需要关注MCLK和RST信号在开机前的状态:

// 文件路径:/kernel-4.19/arch/arm64/boot/dts/mediatek/cust_mt8768_camera.dtsi
camera_pins_cam0_mclk_on: camera_pins_cam0_mclk_on {
    pins_cmd_dat {
        pinmux = <PINMUX_GPIO99__FUNC_CMMCLK0>;
    };
};

camera_pins_cam0_rst0: cam0@0 {
    pins_cmd_dat {
        pinmux = <PINMUX_GPIO101__FUNC_GPIO101>;
        slew-rate = <1>;
        output-low;
    };
};

修改DWS文件中的配置,防止MCLK提前运行:

<!-- 文件路径:/vendor/mediatek/proprietary/bootable/bootloader/preloader/custom/tb8768p1_64_bsp/dct/dct/tb8768p1_64_bsp.dws -->
<gpio99>
    <eint_mode>false</eint_mode>
    <def_mode>0</def_mode>
    <inpull_en>true</inpull_en>
    <inpull_selhigh>false</inpull_selhigh>
    <def_dir>OUT</def_dir>
    <out_high>false</out_high>
</gpio99>

<gpio101>
    <eint_mode>false</eint_mode>
    <def_mode>0</def_mode>
    <inpull_en>true</inpull_en>
    <inpull_selhigh>false</inpull_selhigh>
    <def_dir>OUT</def_dir>
    <out_high>false</out_high>
</gpio101>

关键参数说明:

  • def_mode:GPIO默认模式,0表示普通GPIO
  • inpull_en:使能上下拉电阻
  • inpull_selhigh:true为上拉,false为下拉
  • def_dir:默认方向,IN或OUT
  • out_high:输出电平高低

4. 闪光灯控制与调试

闪光灯是相机系统的重要组成部分,合理的配置可以提升拍摄效果。

4.1 自动模式触发阈值调整

在某些场景下,闪光灯在明亮环境中也会触发,需要调整触发阈值:

// 文件路径:/vendor/mediatek/proprietary/hardware/mtkcam/aaa/source/isp_40/ae_mgr/ae_mgr.cpp
MBOOL AeMgr::IsStrobeBVTrigger() {
    MINT32 i4Bv = 0;
    // ... BV值计算逻辑
    bStrobeBVTrigger = (i4Bv < m_CurrentCaptureTable.i4StrobeTrigerBV)?MTRUE:MFALSE;
    
    // 添加日志观察亮度值
    AE_LOG("[%s()] -40 i4SensorDev:%d bStrobeBVTrigger:%d BV:%d %d/n", 
           __FUNCTION__, m_eSensorDev, bStrobeBVTrigger, i4Bv, m_CurrentCaptureTable.i4StrobeTrigerBV);
    return bStrobeBVTrigger;
}

通过日志观察到亮度值在-10到11之间变化,修改tuning文件中的触发阈值:

// 文件路径:/vendor/mediatek/proprietary/custom/mt6765/hal/imgsensor/ver1/gc13a0_mipi_raw/camera_AE_PLineTable_gc13a0mipiraw.h
static strAETable g_AE_CaptureAutoTable = {
    AETABLE_CAPTURE_AUTO, //eAETableID
    167, //u4TotalIndex
    -10, //i4StrobeTrigerBV  // 将阈值从20改为-10
    // ... 其他参数
};

修改后,闪光灯在明亮环境下不再自动开启。需要注意的是,视频模式也有独立的闪光灯配置,需要一并修改。

4.2 闪光灯电流调节

闪光灯电流分为手电筒模式和闪光模式,分别对应不同的电流值:

// 文件路径:/kernel-4.19/drivers/misc/mediatek/flashlight/flashlights-mt6370.c
static const int mt6370_current[MT6370_LEVEL_NUM] = {
    25, 50, 75, 100, 125, 150, 175, 200, 225, 250,
    275, 300, 325, 350, 375, 400, 450, 500, 550, 600,
    650, 700, 750, 800, 850, 900, 950, 1000, 1050, 1100,
    1150, 1200
};

static const unsigned char mt6370_strobe_level[MT6370_LEVEL_FLASH] = {
    0x80, 0x00, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x0E, 0x10,
    0x12, 0x14, 0x16, 0x18, 0x1A, 0x1C, 0x20, 0x24, 0x28, 0x2C,
    0x30, 0x34, 0x38, 0x3C, 0x40, 0x44, 0x48, 0x4C, 0x50, 0x54,
    0x58, 0x5C
};

通过IOCTL接口设置电流等级:

static int mt6370_ioctl(unsigned int cmd, unsigned long arg) {
    switch (cmd) {
    case FLASH_IOC_SET_DUTY:
        mt6370_set_level(channel, fl_arg->arg);
        break;
    case FLASH_IOC_GET_DUTY_CURRENT:
        fl_arg->arg = mt6370_current[fl_arg->arg];
        break;
    }
}

在实际项目中,需要根据闪光灯模组的规格书调整电流值,避免过流损坏器件。

5. 马达控制与对焦优化

马达控制直接影响相机对焦性能,不当的配置会导致对焦不准或产生异响。

5.1 强制使用PDAF对焦

在某些应用场景下,需要强制使用PDAF(相位检测自动对焦)代替CAF(连续自动对焦):

// 文件路径:/vendor/mediatek/proprietary/hardware/mtkcam/aaa/source/mt6765/af_mgr/af_mgr.cpp
MBOOL AfMgr::Start() {
    /* Begin of add temp for CAF can't focus in close distance 2023/11/14*/
    if (m_eLIB3A_AFMode == LIB3A_AF_MODE_AFS) {
        CAM_LOGI("AF-%-15s: AFS -> AFC", __FUNCTION__);
        m_eLIB3A_AFMode = LIB3A_AF_MODE_AFC;
    }
    /* End of add temp for CAF can't focus in close distance 2023/11/14*/
    m_pIAfAlgo->setAFMode(m_eLIB3A_AFMode);
}

MRESULT AfMgr::setAFMode(MINT32 eAFMode, MUINT32 u4Caller) {
    /* Begin of add temp for CAF can't focus in close distance 2023/11/14*/
    if (eAFMode == MTK_CONTROL_AF_MODE_AUTO)
        eAFMode = MTK_CONTROL_AF_MODE_CONTINUOUS_PICTURE;
    /* End of add temp for CAF can't focus in close distance 2023/11/14*/
}

这种修改可以在近距离拍摄时获得更准确的对焦效果,但需要注意可能会增加功耗。

5.2 关闭相机时马达异响问题

在关闭相机时,马达长距离移动可能产生撞击声,需要通过修改驱动代码解决:

// 文件路径:/kernel-4.19/drivers/misc/mediatek/lens/mtk/main/common/aw8601x/AW8601xAF.c
int AW8601xAF_Release(struct inode *a_pInode, struct file *a_pstFile) {
    if (*g_pAF_Opened == 2) {
        unsigned long pos;
        if (g_motor_type == AW_MID_MOUNTED_MOTOR)
            pos = (unsigned long)((AW_MID_INIT_POS_H << 8) | AW_MID_INIT_POS_L);
        if (g_motor_type == AW_BOTTOM_MOTOR)
            pos = (unsigned long)((AW_BOTTOM_INIT_POS_H << 8) | AW_BOTTOM_INIT_POS_L);
        
        // 注释掉移动马达的代码,避免关闭时产生异响
        // if (pos != 0)
        //    ret = AW8601xAF_SetPos(pos);
    }
}

修改后,关闭相机时马达不再移动,消除了异响问题。但需要注意,这可能会影响下一次打开相机时的初始位置。

6. 传感器方向与功能配置

传感器方向的正确配置关系到图像数据的准确性,而某些传感器可能需要禁用特定功能。

6.1 G-Sensor方向校准

G-Sensor方向错误会导致自动旋转屏幕功能异常。首先通过dumpsys sensorservice命令查看传感器数据:

正常的G-Sensor数据:0 9 0 , -9 0 0 , 9 0 0
异常的数据:9 0 0 , 0 9 0 , 0 -9 0

数据显示x轴和y轴反了,且x轴坐标符号错误。修改传感器方向配置:

// 文件路径:/vendor/mediatek/proprietary/tinysys/scp/project/RV33_A/mt6885/tb8797p2_64_k419_wifi/cust/accGyro/cust_accGyro.c
#ifdef CFG_STK83XX_SUPPORT
{
    .name = "stk83xx",
    .i2c_num = 0,
    .direction = 2,  // 从3改为2
    .i2c_addr = {0x18, 0},
    .eint_num = 3,
},
#endif

方向配置对应关系:

  • 0: {1, 1, 1}
  • 1: {-1, 1, 1}
  • 2: {-1, -1, 1}
  • 3: {1, -1, 1}

修改后需要重新校准传感器数据。

6.2 关闭PSensor功能

在某些项目中,需要关闭距离传感器(PSensor)而保留光线传感器(ALS)。可以通过修改驱动代码实现:

// 文件路径:/vendor/mediatek/proprietary/tinysys/freertos/source/middleware/contexthub/MEMS_Driver/alsps/stk3acx.h
// 注释掉PSensor相关的宏定义
// #define STK3ACX_PS_ENABLE
// #define STK3ACX_PS_OC_REPORT

修改后重新编译,PSensor功能将被禁用,而ALS功能保持正常。也可以通过修改ProjectConfig.mk文件全局关闭PSensor:

# 文件路径:/device/mediateksample/tb8786p1_64_k419/ProjectConfig.mk
CUSTOM_KERNEL_ALSPS = no
CUSTOM_KERNEL_ALS = yes

这种方法更为彻底,但需要重新编译整个系统。

7. 系统集成与故障排除

相机功能的完整实现需要系统各部分的协调配合,包括版本管理、权限设置和异常处理。

7.1 相机版本号管理

为相机添加版本信息有助于问题追踪和版本管理:

# 创建版本信息文件
echo "Rear Version : 84.C.01" > /vendor/mediatek/proprietary/custom/tb8768p1_64_bsp/camera_info/camera_version.info
echo "Front Version : 84.C.01" >> /vendor/mediatek/proprietary/custom/tb8768p1_64_bsp/camera_info/camera_version.info

在设备配置文件中添加复制规则:

# 文件路径:/device/mediateksample/tb8768p1_64_bsp/device.mk
PRODUCT_COPY_FILES += \
    vendor/mediatek/proprietary/custom/$(MTK_TARGET_PROJECT)/camera_info/camera_version.info:vendor/etc/camera_version.info

这样版本信息文件会被打包到系统镜像中,便于应用程序读取和显示。

7.2 前摄录像时无法拍照问题

在某些设备上,前摄录像时无法拍照,这是因为相机能力集配置不正确:

// 文件路径:/packages/apps/Camera2/src/com/android/camera/VideoModule.java
private void takeASnapshot() {
    // 检查是否支持视频截图
    if(!mCameraCapabilities.supports(CameraCapabilities.Feature.VIDEO_SNAPSHOT)) {
        Log.w(TAG, "Cannot take a video snapshot - not supported by hardware");
        return;
    }
    // ... 其他逻辑
}

问题根源在于相机能力集中未正确设置VIDEO_SNAPSHOT支持标志。需要检查相机HAL层的实现,确保正确报告能力集。

7.3 录制视频达到上限时的异常处理

当录制视频达到存储上限时,相机应用可能发生崩溃,需要添加适当的异常处理:

// 文件路径:/packages/apps/Camera2/src/com/android/camera/VideoModule.java
private void saveVideo() {
    long duration = SystemClock.uptimeMillis() - mRecordingStartTime;
    if (duration > 0) {
        // 添加空指针检查
        if(mCurrentVideoValues != null) {
            mCurrentVideoValues.put(Video.Media.SIZE, mVideoFileDescriptor.getStatSize());
            mCurrentVideoValues.put(Video.Media.DURATION, duration);
            mCurrentVideoValues.put(Video.Media.IS_PENDING, 0);
            getServices().getMediaSaver().addVideo(mCurrentVideoUri, mCurrentVideoValues, mOnVideoSavedListener);
            mCurrentVideoValues = null;
        }
    }
}

添加空指针检查后,即使在某些异常情况下,应用也不会崩溃,而是优雅地处理错误。

在实际开发过程中,我还遇到过WiFi设置中扫码导致相机崩溃的问题,这类问题通常与相机资源竞争有关。解决方法是在使用相机前添加适当的资源检查和管理机制,确保多个应用不会同时访问相机硬件。

通过以上实战经验的分享,希望能帮助开发者更好地理解MTK平台相机开发的细节和常见问题的解决方法。在实际项目中,耐心调试和系统性的问题分析往往比技术本身更为重要。

更多推荐