24.学习Camera之——高通-Camera多模组兼容方案

一、需求
工作中,经常遇到这样的需求:

sensor相同
模组厂不同(意味着走线、fpc、镜头、组装等不相同)
这时候,效果的同事需要做针对不同模组厂同一颗sensor进行效果调试。效果参数是2份,即做兼容!

如果你只兼容2个模组厂生产的同一颗sensor,那么直接让2家模组厂区分sensor_id就可以了!
然后驱动代码搞2份,sensor_id和效果文件区别一下就行了!

但是如果有3家甚至更多的模组厂呢,就无法根据sensor_id来区分了,因为sensor_id的地址只能是2个!
(ps:为啥sensor_id只有2个地址:
你看一下sensor规格书就知道了。sensor_id的地址是由硬件上某根线接高或者接地决定的。
以s5k4h7为例子,如果接地了,sensor_id为0x20,接电源,sensor_id为0x5A。)

怎么办呢?

还记得OTP的知识吗?

OTP烧录的数据类型
一般包括:

AF:自动对焦校准数据
AWB:白平衡校准数据
LSC:镜头阴影校准 (Lens Shading Calibration)
Moudle Info:模组信息,包含模组的生产年日月,模组ID等
模组ID等信息是模组厂烧录进去的,我们可以让模组厂烧录不同的模组ID进行区分。
兼容方案小结

确定只兼容2家模组厂
可以根据sensor_id进行区分
模组厂有3家及其以上
根据module_id进行区分,这种方案最实用!
二、兼容方案的实现
我们让模组厂把sensor_id弄成相同,这样方便otp数据读取!
module_id不同,方便进行驱动的兼容!

1.原理分析
kernel/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c

int msm_sensor_match_id(struct msm_sensor_ctrl_t *s_ctrl)
{
    int rc = 0;
    uint16_t chipid = 0;
    struct msm_camera_i2c_client *sensor_i2c_client;
    struct msm_camera_slave_info *slave_info;
    const char *sensor_name;

    if (!s_ctrl) {
        pr_err("%s:%d failed: %pK\n",
            __func__, __LINE__, s_ctrl);
        return -EINVAL;
    }    
    sensor_i2c_client = s_ctrl->sensor_i2c_client;
    slave_info = s_ctrl->sensordata->slave_info;
    sensor_name = s_ctrl->sensordata->sensor_name;

    if (!sensor_i2c_client || !slave_info || !sensor_name) {
        pr_err("%s:%d failed: %pK %pK %pK\n",
            __func__, __LINE__, sensor_i2c_client, slave_info,
            sensor_name);
        return -EINVAL;
    }    

    rc = sensor_i2c_client->i2c_func_tbl->i2c_read(
        sensor_i2c_client, slave_info->sensor_id_reg_addr,
        &chipid, MSM_CAMERA_I2C_WORD_DATA);
    if (rc < 0) { 
        pr_err("%s: %s: read id failed\n", __func__, sensor_name);
        return rc;
    }

    pr_err("%s: read id: 0x%x expected id 0x%x:\n",
            __func__, chipid, slave_info->sensor_id);
    if (msm_sensor_id_by_mask(s_ctrl, chipid) != slave_info->sensor_id) {
        pr_err("%s chip id %x does not match %x\n",
                __func__, chipid, slave_info->sensor_id);
        return -ENODEV;
    }
    return rc;
}

我们来看msm_sensor_match_id这个函数,逻辑很简单。

1.获取的驱动代码里面的slave_info->sensor_id
2.通过i2c_read把sensor_id读取到变量chipid 中
3.比较这两个sensor_id,相同则匹配驱动成功,否则失败!
思考:
既然这个函数,通过比较sensor_id是否相同,来匹配驱动!
那么我们一样可以在这里比较module_id是否相同,来匹配驱动!

需要注意的地方:
sensor_id的读取,可以直接通过一个i2c_read函数就轻松读到!
module_id的读取,稍微复杂些,要遵守OTP数据的读写规范!

2.具体实现
sensor:s5k4h7
模组厂:丘钛、信利
模组id:丘钛烧录值为0x1、信利烧录值为0x2

2.1 OTP读写规则

2.2 module_id的地址

2.3 函数的封装与实现

static int read_module_id( uint_16 *module_id ,const char *sensor_name ,struct msm_camera_i2c_client *sensor_i2c_client;)
{ 
    int rc = 0;
    //如果sensor是s5k4h7,模组厂是丘钛或者信利
    if(!strcmp(sensor_name, "s5k4h7_qiutai") || !strcmp(sensor_name, "s5k4h7_truly")){
       /*set the page21 of otp*/
        sensor_i2c_client->i2c_func_tbl->i2c_write(sensor_i2c_client, 0x0A02,
        0x15, MSM_CAMERA_I2C_BYTE_DATA);
        if (rc < 0) {
            pr_err("%s: %s: write 0x0A02  failed\n", __func__, sensor_name);
            return rc;
        }
        /*OTP enable and read start*/
        sensor_i2c_client->i2c_func_tbl->i2c_write(sensor_i2c_client, 0x0A00,
        0x0100, MSM_CAMERA_I2C_WORD_DATA);
        if (rc < 0) {
            pr_err("%s: %s: write 0x0A00failed\n", __func__, sensor_name);
            return rc;
        }
        /*读取0x0A05寄存器的值并保持到变量module_id 中*/
        sensor_i2c_client->i2c_func_tbl->i2c_read(sensor_i2c_client, 0x0A05,
                module_id , MSM_CAMERA_I2C_BYTE_DATA);
        if (rc < 0) {
            pr_err("%s: %s: read 0x0A05failed\n", __func__, sensor_name);
            return rc;
        }
    }

    //如果你想兼容其他sensor,可以继续添加

    return rc;
}

2.4 调用read_module_id

kernel/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c

int msm_sensor_match_id(struct msm_sensor_ctrl_t *s_ctrl)
{
···
++  int module_id = -1;
···
    sensor_i2c_client = s_ctrl->sensor_i2c_client;
    slave_info = s_ctrl->sensordata->slave_info;
    sensor_name = s_ctrl->sensordata->sensor_name;

    if (!sensor_i2c_client || !slave_info || !sensor_name) {
        pr_err("%s:%d failed: %pK %pK %pK\n",
            __func__, __LINE__, sensor_i2c_client, slave_info,
            sensor_name);
        return -EINVAL;
    }    

    rc = sensor_i2c_client->i2c_func_tbl->i2c_read(
        sensor_i2c_client, slave_info->sensor_id_reg_addr,
        &chipid, MSM_CAMERA_I2C_WORD_DATA);
    if (rc < 0) { 
        pr_err("%s: %s: read id failed\n", __func__, sensor_name);
        return rc;
    }

    pr_err("%s: read id: 0x%x expected id 0x%x:\n",
            __func__, chipid, slave_info->sensor_id);
    if (msm_sensor_id_by_mask(s_ctrl, chipid) != slave_info->sensor_id) {
        pr_err("%s chip id %x does not match %x\n",
                __func__, chipid, slave_info->sensor_id);
        return -ENODEV;
    }

//read id成之后,继续比较module_id
++++++++++++++++++++
    rc = read_module_id(&module_id ,sensor_name ,sensor_i2c_client );
    if (rc < 0) { 
        pr_err("%s: %s: read module_id failed\n", __func__, sensor_name);
        return rc;
    }

    //如果加载到的是 丘钛 的驱动代码
    if(!strcmp(sensor_name, "s5k4h7_qiutai")) {
        if(module_id == 0x1) {//判断module_id 是否为0x1
            //丘钛 sensor 匹配上了丘钛 的驱动代码
            pr_err("%s: %s: module_id = 0x1,it is qiutai sensor\n", __func__, sensor_name);
            return rc;//直接返回成功
        }else {
             pr_err("%s: %s: module_id != 0x1,it not't qiutai sensor\n", __func__, sensor_name);
            return -ENODEV;//模组id不匹配,直接返回失败
      }
    }
    //如果加载到的是 信利 的驱动代码
    if(!strcmp(sensor_name, "s5k4h7_truly")) {
        if(module_id == 0x2) {//判断module_id 是否为0x2
            //信利sensor 匹配上了信利的驱动代码
            pr_err("%s: %s: module_id = 0x1,it is truly sensor\n", __func__, sensor_name);
            return rc;//直接返回成功
        }else {
             pr_err("%s: %s: module_id != 0x1,it not't truly sensor\n", __func__, sensor_name);
            return -ENODEV;//模组id不匹配,直接返回失败
      }
    }
++++++++++++++++++++
    return rc;
}

到这里,我们的兼容就ok了,剩下的就是驱动代码和效果代码提供2份了!

2.5 驱动代码的修改

丘钛的驱动
vendor/qcom/proprietary/mm-camera/mm-camera2/media-controller/modules/sensors/sensor_libs/s5k4h7_qiutai
s5k4h7_qiutai.c

#define SENSOR_MODEL_NO_S5K4H7 "s5k4h7_qiutai"
//区分效果文件
#define S5K4H7_LOAD_CHROMATIX(n) \
  "libchromatix_"SENSOR_MODEL_NO_S5K4H7"_"#n".so"

//这里只有sensor_name 不同,slave_addr 都是0x5A
static struct msm_camera_sensor_slave_info sensor_slave_info = {
  .i2c_freq_mode = I2C_FAST_MODE,
  .sensor_name = "s5k4h7_qiutai",
  /* Camera slot where this camera is mounted */
  .camera_id = CAMERA_0,
  /* sensor slave address */
  .slave_addr = 0x5A, //0x20
···
}

信利的驱动
vendor/qcom/proprietary/mm-camera/mm-camera2/media-controller/modules/sensors/sensor_libs/s5k4h7_truly

s5k4h7_truly.c


#define SENSOR_MODEL_NO_S5K4H7 "s5k4h7_truly"
//区分效果文件
#define S5K4H7_LOAD_CHROMATIX(n) \
  "libchromatix_"SENSOR_MODEL_NO_S5K4H7"_"#n".so"

//这里只有sensor_name 不同,slave_addr 都是0x5A
static struct msm_camera_sensor_slave_info sensor_slave_info = {
  .i2c_freq_mode = I2C_FAST_MODE,
  .sensor_name = "s5k4h7_truly",
  /* Camera slot where this camera is mounted */
  .camera_id = CAMERA_0,
  /* sensor slave address */
  .slave_addr = 0x5A, //0x20
···
}

3.结语
之前问过高通的工程师,他们居然不知道怎么兼容!!!
其实也不复杂,只能说做的工作不一样吧!
兼容方案虽然简单,贵在思考!

继续当一名咸鱼(* ̄︶ ̄)!

在ROS2中,需要对代码进行一些修改和调整来适应新的API和数据类型。以下是将代码移植到ROS2的示例: ```cpp #include "rclcpp/rclcpp.hpp" #include "sensor_msgs/msg/camera_info.hpp" class CheckpointDetector : public rclcpp::Node { public: CheckpointDetector() : Node("checkpoint_detector") { camera_info_subscription_ = create_subscription<sensor_msgs::msg::CameraInfo>( "camera_info", 10, std::bind(&CheckpointDetector::cameraInfoCallback, this, std::placeholders::_1)); } private: void cameraInfoCallback(const sensor_msgs::msg::CameraInfo::SharedPtr camera_info) { sensor_msgs::msg::CameraInfo my_camera_info; my_camera_info.header = camera_info->header; my_camera_info.distortion_model = camera_info->distortion_model; my_camera_info.binning_x = camera_info->binning_x; my_camera_info.binning_y = camera_info->binning_y; my_camera_info.width = camera_info->width; my_camera_info.height = camera_info->height; my_camera_info.d = camera_info->d; my_camera_info.k = camera_info->k; my_camera_info.p = camera_info->p; my_camera_info.r = camera_info->r; } rclcpp::Subscription<sensor_msgs::msg::CameraInfo>::SharedPtr camera_info_subscription_; }; int main(int argc, char** argv) { rclcpp::init(argc, argv); rclcpp::spin(std::make_shared<CheckpointDetector>()); rclcpp::shutdown(); return 0; } ``` 请注意,ROS2中使用`rclcpp`库代替了ROS中的`ros::NodeHandle`和`ros::Subscriber`等。另外,`sensor_msgs::CameraInfo`的成员变量名称也有所不同。在ROS2中,它们被改为小写字母。 你可以将这个代码段放入你的ROS2工程中,并进行构建和运行。这样,当订阅到`camera_info`主题时,`cameraInfoCallback`函数将被调用,并将`camera_info`消息的内容赋值给`my_camera_info`变量。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值