页面标题
GitHub Gitee

海思摄像头驱动的移植——以IMX335为例

海思摄像头驱动的移植——以IMX335为例

事情的起源还要追溯到去年十月份的时候——笔者买了块某公司的Hi3519DV500开发板(不是各位所熟知的易百纳的开发板,易百纳还没坑到那地步)。

开发板上的摄像头型号是索尼IMX335,然而,海思原生SDK里并没有他的ISP驱动源码,店家也没提供现成的源码——只提供了一份老款Hi3516的IMX335驱动源码和摄像头的数据手册。

虽然有点犯难,但是网上也有不少的摄像头驱动移植的相关案例,海思官方也提供了一本《Sensor 调试指南》。

那就干脆以这几样东西为蓝本,自己动手,丰衣足食!

1. 确认 Sensor 的基本信息

比如摄像头的寄存器读写(I2C/SPI),主/从模式(Master/Slave),数据通道数(2lane/4lane),工作模式(Linear/Wdr),时钟配置,图像的宽/高,帧率,初始化序列这些。

条件允许的话,在SDK同目录里找一款相近的其他摄像头驱动源码,直接修改试试。

当然,因为我们这里有现成的旧源码,所以可以直接修改。如果没有的话,就只能从摄像头的数据手册里一点一点扒了。

注意,不同型号的 Sensor 驱动源码都在以下目录中:

Hi3519DV500_SDK_V2.0.1.1/smp/a55_linux/source/mpp/cbb/isp/user/sensor

可能具体视SDK版本不同而有一定差异

2. 一些准备工作

在刚才提到的目录下,新建一个文件夹,创建源码文件如下:

其中 Makefile 文件从其他型号驱动文件夹下复制一个现成的就行,注意修改生成库文件名

例如:LIB_NAME := libsns_imx335

然后,修改smp/a55_linux/source/interdrv/sysconfig/sys_cfg.c文件,添加宏定义和时钟配置相关:

#define SENSOR_NAME_IMX335 "imx335"
/*......*/
static unsigned int parse_sensor_clock(const char *name, unsigned int name_len)
{
    unsigned int clock = 0x0;
    /*......*/
	/* get clock from sensor name. */
    if ((strncmp(SENSOR_NAME_OS08A20, name, len) == 0) ||
        (strncmp(SENSOR_NAME_OS04A10, name, len) == 0))
    {
        clock = 0x4001;
    }
	else if (strncmp(SENSOR_NAME_IMX335, name, len) == 0)
    {
        clock = 0x8001;   //
    }
    /*......*/
    return clock;
}

这里时钟频率寄存器值具体查看《SYS_CONFIG 配置指南》

注意:修改此文件需要更新 sys_config.ko

首先 sudo -i 进入管理员模式,然后在管理员模式下进入 a55_linux/source/out/obj 目录。

然后直接 make -j8 即可将板端相关的驱动全部重新编译

然后,修改smp/a55_linux/source/mpp/cbb/isp/include/ot_sns_ctrl.h文件:

extern ot_isp_sns_obj g_sns_imx335_obj;

修改smp/a55_linux/source/mpp/sample/Makefile.param文件:

SENSOR0_TYPE ?= SONY_IMX335_MIPI_5M_30FPS_12BIT
#......
MPI_LIBS += $(REL_LIB)/libsns_imx335.a  #加入静态库文件

修改 smp/a55_linux/source/mpp/sample/common/sample_comm.h 文件:

与前面相对应

修改 smp/a55_linux/source/mpp/sample/common/sample_comm_isp.c 文件:

/*下面这个结构体里各项配置挺重要的,后面用PQ Tools工具的时候还有用*/
static ot_isp_pub_attr g_isp_pub_attr_imx335_mipi_5m_30fps = {
    {0, 0, 2592, 1944},
    {2592, 1944},
    30,
    OT_ISP_BAYER_RGGB,
    OT_WDR_MODE_NONE,
    0,
    0,
    0,
    {
        0,
        {0, 0, 2592, 1944},
    },
};
/*......*/
static td_void sample_comm_isp_get_pub_attr_by_sns_part1(sample_sns_type sns_type, ot_isp_pub_attr *pub_attr)
{
    switch (sns_type) {
        /*......*/
        case SONY_IMX335_MIPI_5M_30FPS_12BIT:
            (td_void)memcpy_s(pub_attr, sizeof(ot_isp_pub_attr),
                &g_isp_pub_attr_imx335_mipi_5m_30fps, sizeof(ot_isp_pub_attr));
            break;
        /*......*/
        default:
            break;
    }
}
/*......*/
td_s32 sample_comm_isp_get_pub_attr_by_sns(sample_sns_type sns_type, ot_isp_pub_attr *pub_attr)
{
    switch (sns_type) {
        /*.....*/
        case SONY_IMX335_MIPI_5M_30FPS_12BIT:
            (td_void)memcpy_s(pub_attr, sizeof(ot_isp_pub_attr),
                &g_isp_pub_attr_imx335_mipi_5m_30fps, sizeof(ot_isp_pub_attr));
            break;

        default:
            (td_void)memcpy_s(pub_attr, sizeof(ot_isp_pub_attr),
                &g_isp_pub_attr_os08a20_mipi_8m_30fps, sizeof(ot_isp_pub_attr));
            break;
    }
    sample_comm_isp_get_pub_attr_by_sns_part1(sns_type, pub_attr);
    return TD_SUCCESS;
}
/*......*/
ot_isp_sns_obj *sample_comm_isp_get_sns_obj(sample_sns_type sns_type)
{
    switch (sns_type) {
        /*......*/
        case SONY_IMX335_MIPI_5M_30FPS_12BIT:
            return &g_sns_imx335_obj;
        /*......*/
        default:
            return TD_NULL;
    }
}

修改 smp/a55_linux/source/mpp/sample/common/sample_comm_vi.c 文件:

/*这些是VI相关配置,后面也要用到的*/
static combo_dev_attr_t g_mipi_4lane_chn0_sensor_imx335_12bit_5m_nowdr_attr = {
    .devno = 0,
    .input_mode = INPUT_MODE_MIPI,
    .data_rate  = MIPI_DATA_RATE_X1,
    .img_rect   = {0, 0, 2592, 1944},
    .mipi_attr = {
        DATA_TYPE_RAW_12BIT,
        OT_MIPI_WDR_MODE_NONE,
        {0, 1, 2, 3, -1, -1, -1, -1}
    }
};

static td_void sample_comm_vi_get_mipi_attr_part1(sample_sns_type sns_type, combo_dev_attr_t *combo_attr,
    td_u32 *ob_height)
{
    switch (sns_type) {
        /*......*/
        case SONY_IMX335_MIPI_5M_30FPS_12BIT:
            *ob_height = 0;
            (td_void)memcpy_s(combo_attr, sizeof(combo_dev_attr_t),
                &g_mipi_4lane_chn0_sensor_imx335_12bit_5m_nowdr_attr, sizeof(combo_dev_attr_t));
            break;
        /*......*/
        default:
            break;
    }
}
static td_void sample_comm_vi_get_mipi_attr(sample_sns_type sns_type, combo_dev_attr_t *combo_attr)
{
    td_u32 ob_height = OB_HEIGHT_START;
    switch (sns_type) {
            /*......*/
        case SONY_IMX335_MIPI_5M_30FPS_12BIT:
            ob_height = OB_HEIGHT_START;
            (td_void)memcpy_s(combo_attr, sizeof(combo_dev_attr_t),
                &g_mipi_4lane_chn0_sensor_imx335_12bit_5m_nowdr_attr, sizeof(combo_dev_attr_t));
            break;
        default:
            (td_void)memcpy_s(combo_attr, sizeof(combo_dev_attr_t),
                &g_mipi_4lane_chn0_sensor_os08a20_12bit_8m_nowdr_attr, sizeof(combo_dev_attr_t));
    }
    sample_comm_vi_get_mipi_attr_part1(sns_type, combo_attr, &ob_height);
    combo_attr->img_rect.height = combo_attr->img_rect.height + ob_height;
}
static td_void sample_comm_vi_get_mipi_attr_by_dev_id_part1(sample_sns_type sns_type, ot_vi_dev vi_dev,
                                                            combo_dev_attr_t *combo_attr, td_u32 *ob_height)
{
    switch (sns_type) {
        /*......*/
        case SONY_IMX335_MIPI_5M_30FPS_12BIT:
            *ob_height = 0;
            if (vi_dev == 0) {
            (td_void)memcpy_s(combo_attr, sizeof(combo_dev_attr_t),
                &g_mipi_4lane_chn0_sensor_imx335_12bit_5m_nowdr_attr, sizeof(combo_dev_attr_t));
            } else if (vi_dev == 2) {
                (td_void)memcpy_s(combo_attr, sizeof(combo_dev_attr_t),
                &g_mipi_4lane_chn0_sensor_imx335_12bit_5m_nowdr_dev2_attr, sizeof(combo_dev_attr_t));
            }
            break;

        default:
            break;
    }
}
static td_void sample_comm_vi_get_mipi_attr_by_dev_id(sample_sns_type sns_type, ot_vi_dev vi_dev,
                                                      combo_dev_attr_t *combo_attr)
{
    td_u32 ob_height = OB_HEIGHT_START;
    switch (sns_type) {
        /*......*/
        case SONY_IMX335_MIPI_5M_30FPS_12BIT:
            ob_height = OB_HEIGHT_START;
            if (vi_dev == 0) {
            (td_void)memcpy_s(combo_attr, sizeof(combo_dev_attr_t),
                &g_mipi_4lane_chn0_sensor_imx335_12bit_5m_nowdr_attr, sizeof(combo_dev_attr_t));
            } else if (vi_dev == 2) {
                (td_void)memcpy_s(combo_attr, sizeof(combo_dev_attr_t),
                &g_mipi_4lane_chn0_sensor_imx335_12bit_5m_nowdr_dev2_attr, sizeof(combo_dev_attr_t));
            }
            break;
        /*......*/
        default:
            (td_void)memcpy_s(combo_attr, sizeof(combo_dev_attr_t),
                &g_mipi_4lane_chn0_sensor_os08a20_12bit_8m_nowdr_attr, sizeof(combo_dev_attr_t));
    }
    sample_comm_vi_get_mipi_attr_by_dev_id_part1(sns_type, vi_dev, combo_attr, &ob_height);
    combo_attr->img_rect.height = combo_attr->img_rect.height + ob_height;
}
/* MIPI相关 */
/* for imx335 only */
static ot_vi_dev_attr g_mipi_raw_dev_attr = {
    .intf_mode = OT_VI_INTF_MODE_MIPI,
    /* Invalid argument */
    .work_mode = OT_VI_WORK_MODE_MULTIPLEX_1,
    /* mask component */
    .component_mask = {0xfff00000, 0x00000000},
    .scan_mode = OT_VI_SCAN_PROGRESSIVE,
    /* Invalid argument */
    .ad_chn_id = {-1, -1, -1, -1},
    /* data seq */
    .data_seq = OT_VI_DATA_SEQ_YUYV,
    /* sync param */
    .sync_cfg = {
        .vsync           = OT_VI_VSYNC_PULSE,
        .vsync_neg       = OT_VI_VSYNC_NEG_LOW,
        .hsync           = OT_VI_HSYNC_VALID_SIG,
        .hsync_neg       = OT_VI_HSYNC_NEG_HIGH,
        .vsync_valid     = OT_VI_VSYNC_VALID_SIG,
        .vsync_valid_neg = OT_VI_VSYNC_VALID_NEG_HIGH,
        .timing_blank    = {
            /* hsync_hfb      hsync_act     hsync_hhb */
            0,                1280,            0,
            /* vsync0_vhb     vsync0_act    vsync0_hhb */
            0,                720,            0,
            /* vsync1_vhb     vsync1_act    vsync1_hhb */
            0,                0,            0
        }
    },
    /* data type */
    .data_type = OT_VI_DATA_TYPE_RAW,
    /* data reverse */
    .data_reverse = TD_FALSE,
    /* input size */
    .in_size = {2592, 1944},
    /* data rate */
    .data_rate = OT_DATA_RATE_X1,
};
/*VI Size*/
td_void sample_comm_vi_get_size_by_sns_type(sample_sns_type sns_type, ot_size *size)
{
    switch (sns_type) {
            /*......*/
        case SONY_IMX335_MIPI_5M_30FPS_12BIT:
            size->width  = 2592;
            size->height = 1944;
            break;
        default:
            size->width  = WIDTH_1920;
            size->height = HEIGHT_1080;
            break;
    }
}
td_u32 sample_comm_vi_get_obheight_by_sns_type(sample_sns_type sns_type)
{
    td_u32 ob_height = OB_HEIGHT_START; //0
    switch (sns_type) {
            /*......*/
        case SONY_IMX335_MIPI_5M_30FPS_12BIT:
            ob_height = OB_HEIGHT_START;
            break;
        /*......*/
        default:
            break;
    }
    return ob_height;
}
static td_u32 sample_comm_vi_get_pipe_num_by_sns_type(sample_sns_type sns_type)
{
    switch (sns_type) {
        /*......*/
        case SONY_IMX335_MIPI_5M_30FPS_12BIT:
            return 1;
        /*......*/
        default:
            return 1;
    }
}
static ot_wdr_mode sample_comm_vi_get_wdr_mode_by_sns_type(sample_sns_type sns_type)
{
    switch (sns_type) {
        /*......*/
        case SONY_IMX335_MIPI_5M_30FPS_12BIT:
            return OT_WDR_MODE_NONE;  //LINEAR模式工作,不使用WDR
		/*......*/
        default:
            return OT_WDR_MODE_NONE;
    }
}
td_void sample_comm_vi_get_default_sns_info(sample_sns_type sns_type, sample_sns_info *sns_info)
{
    sns_info->sns_type    = sns_type;
    sns_info->sns_clk_src = 0;
    sns_info->sns_rst_src = 0;
    sns_info->bus_id      = 3; /* asic i2c4 */       //根据实际开发板配置修改
    sns_info->sns_clk_rst_en = TD_TRUE;
    if (sns_type == GST_412C_SLAVE_THERMO_T3_384_288_30FPS_14BIT) {
        sns_info->bus_id = 6;  /* thermo i2c6 */
    }
}
/*MIPI 数据通道数*/
td_void sample_comm_vi_get_default_mipi_info(sample_sns_type sns_type, sample_mipi_info *mipi_info)
{
    mipi_info->mipi_dev    = 0;
    mipi_info->divide_mode = LANE_DIVIDE_MODE_1; /* 4lane + 4lane */ //4lane模式工作
    sample_comm_vi_get_mipi_attr(sns_type, &mipi_info->combo_dev_attr);
    sample_comm_vi_get_mipi_ext_data_attr(sns_type, &mipi_info->ext_data_type_attr);
}

/* used for two sensor: mipi lane 4 + 4 */
td_void sample_comm_vi_get_mipi_info_by_dev_id(sample_sns_type sns_type, ot_vi_dev vi_dev, sample_mipi_info *mipi_info)
{
    mipi_info->mipi_dev    = vi_dev;
    mipi_info->divide_mode = LANE_DIVIDE_MODE_1;
    sample_comm_vi_get_mipi_attr_by_dev_id(sns_type, vi_dev, &mipi_info->combo_dev_attr);
    sample_comm_vi_get_mipi_ext_data_attr(sns_type, &mipi_info->ext_data_type_attr);
    mipi_info->ext_data_type_attr.devno = vi_dev;
}

至此,准备工作全部完成。

3. 正式开始

首先是 imx335_sensor_ctl.c

这里主要就是初始化启动序列的问题,把启动序列和寄存器地址换成IMX335的,其他的部分可以参考海思提供类似的索尼Sensor驱动代码

/*读写寄存器*/
td_s32 imx335_read_register(ot_vi_pipe vi_pipe, td_u32 addr)
{
    ot_unused(vi_pipe);
    ot_unused(addr);
    return TD_SUCCESS;
}

td_s32 imx335_write_register(ot_vi_pipe vi_pipe, td_u32 addr, td_u32 data)
{
    td_s32 ret;
    if (g_fd[vi_pipe] < 0)
        return TD_SUCCESS;
    td_u32 idx = 0;
    td_u8 buf[I2C_BUF_NUM];

    if (IMX335_ADDR_BYTE == 2)
    {                                  /* 2 byte */
        buf[idx] = (addr >> 8) & 0xff; /* shift 8 */
        idx++;
        buf[idx] = addr & 0xff;
        idx++;
    }
    else {}

    if (IMX335_DATA_BYTE == 2) { /* 2 byte */}
    else
    {
        buf[idx] = data & 0xff;
        idx++;
    }

    ret = write(g_fd[vi_pipe], buf, IMX335_ADDR_BYTE + IMX335_DATA_BYTE);
    if (ret < 0)
    {
        isp_err_trace("I2C_WRITE error!\n");
        return TD_FAILURE;
    }

    return TD_SUCCESS;
}
/*初始化*/
/*寄存器初始化*/
void imx335_default_reg_init(ot_vi_pipe vi_pipe)
{
    td_u32 i;
    td_s32 ret = TD_SUCCESS;
    ot_isp_sns_state *pastimx335 = TD_NULL;
    pastimx335 = imx335_get_ctx(vi_pipe);
    for (i = 0; i < pastimx335->regs_info[0].reg_num; i++)
    {
        ret += imx335_write_register(vi_pipe,
                                     pastimx335->regs_info[0].i2c_data[i].reg_addr,
                                     pastimx335->regs_info[0].i2c_data[i].data);
    }
    if (ret != TD_SUCCESS)
        isp_err_trace("write register failed!\n");
    return;
}

void imx335_init(ot_vi_pipe vi_pipe)
{
    ot_wdr_mode wdr_mode;
    td_bool init;
    td_s32 ret;
    ot_isp_sns_state *pastimx335 = TD_NULL;
    pastimx335 = imx335_get_ctx(vi_pipe);
    init = pastimx335->init;
    wdr_mode = pastimx335->wdr_mode;

    if (init == TD_FALSE)
    {
        ret = imx335_i2c_init(vi_pipe);
        if (ret != TD_SUCCESS)
        {
            isp_err_trace("i2c init failed!\n");
            return;
        }
    }
    /* When sensor first init, config all registers */

    if (OT_WDR_MODE_2To1_LINE == wdr_mode) {}
    else if (OT_WDR_MODE_NONE == wdr_mode)
    {
        imx335_linear_5m_init(vi_pipe);   //LINEAR模式 启动序列
    }
    else
    {
        isp_err_trace("IMX335_init Not support this mode\n");
        return;
    }
    pastimx335->init = TD_TRUE;
    return;
}

/*初始化启动序列*/
void imx335_linear_5m_init(ot_vi_pipe vi_pipe)
{
    td_s32 ret = TD_SUCCESS;

    imx335_write_register(vi_pipe, 0x3000, 0x01); // standby
    imx335_write_register(vi_pipe, 0x3001, 0x00);
    imx335_write_register(vi_pipe, 0x3002, 0x01);
    imx335_write_register(vi_pipe, 0x3003, 0x00);

    imx335_write_register(vi_pipe, 0x300C, 0x5B);
    imx335_write_register(vi_pipe, 0x300D, 0x40);
    imx335_write_register(vi_pipe, 0x3030, 0x94); // VMAX_LOW
    imx335_write_register(vi_pipe, 0x3031, 0x11); // VMAX_MIDDLE
    imx335_write_register(vi_pipe, 0x3032, 0x00); // VMAX_HIGH

    imx335_write_register(vi_pipe, 0x3034, 0x26); // HMAX
    imx335_write_register(vi_pipe, 0x3035, 0x02);

    imx335_write_register(vi_pipe, 0x3058, 0x09); // SHR0
    imx335_write_register(vi_pipe, 0x3059, 0x00);
    imx335_write_register(vi_pipe, 0x305A, 0x00);

    imx335_write_register(vi_pipe, 0x30E8, 0x00); // Gain
    imx335_write_register(vi_pipe, 0x30E9, 0x00);

    imx335_write_register(vi_pipe, 0x315A, 0x02);
    imx335_write_register(vi_pipe, 0x316A, 0x7E);

    imx335_write_register(vi_pipe, 0x319D, 0x01);
    imx335_write_register(vi_pipe, 0x319E, 0x01);

    imx335_write_register(vi_pipe, 0x3288, 0x21);
    imx335_write_register(vi_pipe, 0x328A, 0x02);

    imx335_write_register(vi_pipe, 0x3414, 0x05);
    imx335_write_register(vi_pipe, 0x3416, 0x18);

    imx335_write_register(vi_pipe, 0x341C, 0x47);
    imx335_write_register(vi_pipe, 0x341D, 0x00);

    imx335_write_register(vi_pipe, 0x3648, 0x01);
    imx335_write_register(vi_pipe, 0x364A, 0x04);
    imx335_write_register(vi_pipe, 0x364C, 0x04);

    imx335_write_register(vi_pipe, 0x3678, 0x01);
    imx335_write_register(vi_pipe, 0x367C, 0x31);
    imx335_write_register(vi_pipe, 0x367E, 0x31);

    imx335_write_register(vi_pipe, 0x3706, 0x10);
    imx335_write_register(vi_pipe, 0x3708, 0x03);

    imx335_write_register(vi_pipe, 0x3714, 0x02);
    imx335_write_register(vi_pipe, 0x3715, 0x02);
    imx335_write_register(vi_pipe, 0x3716, 0x01);
    imx335_write_register(vi_pipe, 0x3717, 0x03);
    imx335_write_register(vi_pipe, 0x371C, 0x3D);
    imx335_write_register(vi_pipe, 0x371D, 0x3F);

    imx335_write_register(vi_pipe, 0x372C, 0x00);
    imx335_write_register(vi_pipe, 0x372D, 0x00);
    imx335_write_register(vi_pipe, 0x372E, 0x46);
    imx335_write_register(vi_pipe, 0x372F, 0x00);
    imx335_write_register(vi_pipe, 0x3730, 0x89);
    imx335_write_register(vi_pipe, 0x3731, 0x00);
    imx335_write_register(vi_pipe, 0x3732, 0x08);
    imx335_write_register(vi_pipe, 0x3733, 0x01);
    imx335_write_register(vi_pipe, 0x3734, 0xFE);
    imx335_write_register(vi_pipe, 0x3735, 0x05);

    imx335_write_register(vi_pipe, 0x3740, 0x02);

    imx335_write_register(vi_pipe, 0x375D, 0x00);
    imx335_write_register(vi_pipe, 0x375E, 0x00);
    imx335_write_register(vi_pipe, 0x375F, 0x11);
    imx335_write_register(vi_pipe, 0x3760, 0x01);

    imx335_write_register(vi_pipe, 0x3768, 0x1B);
    imx335_write_register(vi_pipe, 0x3769, 0x1B);
    imx335_write_register(vi_pipe, 0x376A, 0x1B);
    imx335_write_register(vi_pipe, 0x376B, 0x1B);
    imx335_write_register(vi_pipe, 0x376C, 0x1A);
    imx335_write_register(vi_pipe, 0x376D, 0x17);
    imx335_write_register(vi_pipe, 0x376E, 0x0F);

    imx335_write_register(vi_pipe, 0x3776, 0x00);
    imx335_write_register(vi_pipe, 0x3777, 0x00);
    imx335_write_register(vi_pipe, 0x3778, 0x46);
    imx335_write_register(vi_pipe, 0x3779, 0x00);
    imx335_write_register(vi_pipe, 0x377A, 0x89);
    imx335_write_register(vi_pipe, 0x377B, 0x00);
    imx335_write_register(vi_pipe, 0x377C, 0x08);
    imx335_write_register(vi_pipe, 0x377D, 0x01);
    imx335_write_register(vi_pipe, 0x377E, 0x23);
    imx335_write_register(vi_pipe, 0x377F, 0x02);
    imx335_write_register(vi_pipe, 0x3780, 0xD9);
    imx335_write_register(vi_pipe, 0x3781, 0x03);
    imx335_write_register(vi_pipe, 0x3782, 0xF5);
    imx335_write_register(vi_pipe, 0x3783, 0x06);
    imx335_write_register(vi_pipe, 0x3784, 0xA5);
    imx335_write_register(vi_pipe, 0x3788, 0x0F);
    imx335_write_register(vi_pipe, 0x378A, 0xD9);
    imx335_write_register(vi_pipe, 0x378B, 0x03);
    imx335_write_register(vi_pipe, 0x378C, 0xEB);
    imx335_write_register(vi_pipe, 0x378D, 0x05);
    imx335_write_register(vi_pipe, 0x378E, 0x87);
    imx335_write_register(vi_pipe, 0x378F, 0x06);
    imx335_write_register(vi_pipe, 0x3790, 0xF5);
    imx335_write_register(vi_pipe, 0x3792, 0x43);
    imx335_write_register(vi_pipe, 0x3794, 0x7A);
    imx335_write_register(vi_pipe, 0x3796, 0xA1);

    imx335_default_reg_init(vi_pipe);

    ret += imx335_write_register(vi_pipe, 0x3000, 0x00); /* standby */
    delay_ms(18);                                        /* 18ms */
    ret += imx335_write_register(vi_pipe, 0x3002, 0x00); /* master mode start */
    //if (ret != TD_SUCCESS)
    //{
    //    isp_err_trace("imx335 write register failed!\n");
    //    return;
    //}
    printf("===Sony IMX335_init_5M_2592x1944_12bit_linear30 Initial OK!===\n");
    return;
}

然后是 imx335_cmos.h

/*部分寄存器配置*/
#define IMX335_I2C_ADDR    0x34
#define IMX335_ADDR_BYTE   2
#define IMX335_DATA_BYTE   1
#define imx335_sensor_get_ctx(pipe, ctx)   ((ctx) = imx335_get_ctx(pipe))

#define IMX335_FULL_LINES_MAX 0xFFFFF

#define IMX335_INCREASE_LINES  0  /* make real fps less than stand fps because NVR require */ 
//1?
#define IMX335_VMAX_4K2K_LINEAR    (0x1194 + IMX335_INCREASE_LINES) //3300?

typedef enum {
    IMX335_SENSOR_5M_30FPS_LINEAR_MODE = 0,
    IMX335_MODE_BUTT
} imx335_res_mode;

然后是imx335_cmos.c

这里主要关注一些相关增益参数,寄存器地址,还有曝光时间,AE的相关配置及实现等

这里的代码有些是按照旧代码移植过来的

另外,Sensor工作模式全部都是按照LINEAR配置的,如果需要WDR模式,请自行修改

/*一些宏定义*/
#define IMX335_ID 335
/*图像的宽高*/
#define SENSOR_IMX335_WIDTH 2592  
#define SENSOR_IMX335_HEIGHT 1944
const imx335_video_mode_tbl g_imx335_mode_tbl[IMX335_MODE_BUTT] = {
    {IMX335_VMAX_4K2K_LINEAR, IMX335_FULL_LINES_MAX, 30, 0.07,
     2592, 1944, 0, OT_WDR_MODE_NONE, "IMX335_SENSOR_5M_30FPS_LINEAR_MODE"},
};
/*IMX335相关参数*/
/* Imx335 Register Address */
#define IMX335_VMAX_ADDR_L 0x3030
#define IMX335_VMAX_ADDR_M 0x3031
#define IMX335_VMAX_ADDR_H 0x3032

#define IMX335_SHR0_L 0x3058
#define IMX335_SHR0_M 0x3059
#define IMX335_SHR0_H 0x305A
#define IMX335_GAIN_PGC_L 0x30E8
#define IMX335_GAIN_PGC_H 0x30E9

// sensor gain
#define IMX335_AGAIN_MIN 1024
#define IMX335_AGAIN_MAX 32381

#define IMX335_DGAIN_MIN 1024
#define IMX335_DGAIN_MAX 128914

#define IMX335_AD_GAIN_TBL_RANGE 241
#define IMX335_AGAIN_TBL_RANGE 100
#define IMX335_DGAIN_TBL_RANGE 140

#define IMX335_EXP_TIME_LMT 8
/*AE相关*/
static td_void cmos_get_ae_comm_default(ot_vi_pipe vi_pipe, ot_isp_ae_sensor_default *ae_sns_dft,
                                        const ot_isp_sns_state *sns_state)
{
    td_u32 U32MaxFps = 30;
    ae_sns_dft->full_lines_std = sns_state->fl_std;
    ae_sns_dft->flicker_freq = 50 * 256; /* light flicker freq: 50Hz, accuracy: 256 */
    ae_sns_dft->full_lines_max = IMX335_FULL_LINES_MAX;
    ae_sns_dft->hmax_times = (1000000000) / (sns_state->fl_std * 30); /* 1000000000ns, 30fps */

    ae_sns_dft->int_time_accu.accu_type = OT_ISP_AE_ACCURACY_LINEAR;
    ae_sns_dft->int_time_accu.accuracy = 1;
    ae_sns_dft->int_time_accu.offset = 0;

    ae_sns_dft->again_accu.accu_type = OT_ISP_AE_ACCURACY_TABLE;
    ae_sns_dft->again_accu.accuracy = 1;

    ae_sns_dft->dgain_accu.accu_type = OT_ISP_AE_ACCURACY_TABLE;
    ae_sns_dft->dgain_accu.accuracy = 1;

    ae_sns_dft->isp_dgain_shift = 8; /* accuracy: 8 */
    ae_sns_dft->min_isp_dgain_target = 1 << ae_sns_dft->isp_dgain_shift;
    ae_sns_dft->max_isp_dgain_target = 2 << ae_sns_dft->isp_dgain_shift; /* max 2 */
    if (g_lines_per500ms[vi_pipe] == 0)
        ae_sns_dft->lines_per500ms = (sns_state->fl_std * U32MaxFps) >> 1; /* 30fps, div 2 */
    else
        ae_sns_dft->lines_per500ms = g_lines_per500ms[vi_pipe];
    
    (td_void)memcpy_s(&ae_sns_dft->piris_attr, sizeof(ot_isp_piris_attr), &g_piris, sizeof(ot_isp_piris_attr));
    ae_sns_dft->max_iris_fno = OT_ISP_IRIS_F_NO_1_4;
    // OT_ISP_IRIS_F_NO_1_0?
    ae_sns_dft->min_iris_fno = OT_ISP_IRIS_F_NO_5_6;
    // OT_ISP_IRIS_F_NO_32_0?
    ae_sns_dft->ae_route_ex_valid = TD_FALSE;
    ae_sns_dft->ae_route_attr.total_num = 0;
    ae_sns_dft->ae_route_attr_ex.total_num = 0;
    ae_sns_dft->quick_start.quick_start_enable = g_quick_start_en[vi_pipe];
    ae_sns_dft->quick_start.black_frame_num = 0;
    ae_sns_dft->ae_stat_pos = g_ae_stat_pos[vi_pipe]; /* 1 use be stat to AE */
    return;
}

static td_void cmos_get_ae_linear_default(ot_vi_pipe vi_pipe, ot_isp_ae_sensor_default *ae_sns_dft,
                                          const ot_isp_sns_state *sns_state)
{
    ae_sns_dft->max_again = IMX335_AGAIN_MAX;
    ae_sns_dft->min_again = IMX335_AGAIN_MIN;
    ae_sns_dft->max_again_target = ae_sns_dft->max_again;
    ae_sns_dft->min_again_target = ae_sns_dft->min_again;

    ae_sns_dft->max_dgain = IMX335_DGAIN_MAX;
    ae_sns_dft->min_dgain = IMX335_DGAIN_MIN;
    ae_sns_dft->max_dgain_target = ae_sns_dft->max_dgain;
    ae_sns_dft->min_dgain_target = ae_sns_dft->min_dgain;

    ae_sns_dft->ae_compensation = 0x38;
    ae_sns_dft->ae_exp_mode = OT_ISP_AE_EXP_HIGHLIGHT_PRIOR;
    ae_sns_dft->init_exposure = g_init_exposure[vi_pipe] ? g_init_exposure[vi_pipe] : 130000; 
    /* init 76151 */
    // 76151?

    ae_sns_dft->max_int_time = sns_state->fl_std - IMX335_EXP_TIME_LMT; /* sub 2 */
    ae_sns_dft->min_int_time = 2;                    /* min 2 */
    // 9? 27?
    ae_sns_dft->max_int_time_target = 65535;                    /* max 65535 */
    ae_sns_dft->min_int_time_target = ae_sns_dft->min_int_time; /* min 2 */
    ae_sns_dft->int_time_accu.offset = -0.198;
    ae_sns_dft->ae_route_ex_valid = g_ae_route_ex_valid[vi_pipe];
    (td_void) memcpy_s(&ae_sns_dft->ae_route_attr, sizeof(ot_isp_ae_route),
                       &g_init_ae_route[vi_pipe], sizeof(ot_isp_ae_route));
    (td_void) memcpy_s(&ae_sns_dft->ae_route_attr_ex, sizeof(ot_isp_ae_route_ex),
                       &g_init_ae_route_ex[vi_pipe], sizeof(ot_isp_ae_route_ex));
    return;
}

static td_s32 cmos_get_ae_default(ot_vi_pipe vi_pipe, ot_isp_ae_sensor_default *ae_sns_dft)
{
    ot_isp_sns_state *sns_state = TD_NULL;

    sns_check_pointer_return(ae_sns_dft);
    imx335_sensor_get_ctx(vi_pipe, sns_state);
    sns_check_pointer_return(sns_state);

    (td_void) memset_s(&ae_sns_dft->ae_route_attr, sizeof(ot_isp_ae_route), 0, sizeof(ot_isp_ae_route));

    cmos_get_ae_comm_default(vi_pipe, ae_sns_dft, sns_state);

    switch (sns_state->wdr_mode)
    {
    case OT_WDR_MODE_NONE: /* linear mode */
        cmos_get_ae_linear_default(vi_pipe, ae_sns_dft, sns_state);
        break;
    default:
        break;
    }

    return TD_SUCCESS;
}
/* the function of sensor set fps */
static td_void cmos_fps_set(ot_vi_pipe vi_pipe, td_float fps, ot_isp_ae_sensor_default *ae_sns_dft)
{
    /*......*/
    sns_state->fl_std = vmax;
    ae_sns_dft->lines_per500ms = (td_u32)(lines * max_fps / 2); /* div 2 */

    ae_sns_dft->fps = fps;
    ae_sns_dft->full_lines_std = sns_state->fl_std;
    ae_sns_dft->max_int_time = sns_state->fl_std - IMX335_EXP_TIME_LMT;
    sns_state->fl[0] = sns_state->fl_std;
    ae_sns_dft->full_lines = sns_state->fl[0];
    ae_sns_dft->hmax_times =
        (td_u32)((1000000000) / (sns_state->fl_std * div_0_to_1_float(fps))); /* 1000000000ns */

    return;
}
static td_void cmos_slow_framerate_set(ot_vi_pipe vi_pipe, td_u32 full_lines, ot_isp_ae_sensor_default *ae_sns_dft)
{
    /*......*/
    lines_max = g_imx335_mode_tbl[sns_state->img_mode].max_ver_lines;
    if (sns_state->wdr_mode == OT_WDR_MODE_NONE)
    {
        vmax = full_lines;
        vmax = (vmax > lines_max) ? lines_max : vmax;
        sns_state->fl[0] = vmax;
    }
    switch (sns_state->wdr_mode)
    {
    case OT_WDR_MODE_NONE:
        sns_state->regs_info[0].i2c_data[5].data = low_8bits(vmax);    /* index 5 */
        sns_state->regs_info[0].i2c_data[6].data = high_8bits(vmax);   /* index 6 */
        sns_state->regs_info[0].i2c_data[7].data = higher_4bits(vmax); /* index 7 */
        break;
    default:
        break;
    }
    ae_sns_dft->full_lines = sns_state->fl[0];
    ae_sns_dft->max_int_time = sns_state->fl[0] - IMX335_EXP_TIME_LMT;

    return;
}
static td_void cmos_inttime_update_linear(ot_vi_pipe vi_pipe, td_u32 int_time)
{
    /*......*/
    value = sns_state->fl[0] - int_time;
    value = min(value, IMX335_FULL_LINES_MAX);
    value = min(max(value, 9), (sns_state->fl[0] - 1));
	/*......*/
    return;
}
/*曝光时间相关*/
static td_void cmos_get_inttime_max(ot_vi_pipe vi_pipe, td_u16 man_ratio_enable, td_u32 *ratio,
                                    ot_isp_ae_int_time_range *int_time, td_u32 *lf_max_int_time)
{
    td_u32 u32IntTimeMaxTmp = 0;
    td_u32 u32RatioTmp = 0x40;
    td_u32 u32ShortTimeMinLimit = 4;
    ot_isp_sns_state *sns_state = TD_NULL;
    /*......*/
    if (u32IntTimeMaxTmp >= u32ShortTimeMinLimit) {} 
    else {
        if (man_ratio_enable == 1) {
            printf("Manaul ExpRatio is too large!\n");
            return;
        } else {
            u32IntTimeMaxTmp = u32ShortTimeMinLimit;

            if (sns_state->wdr_mode == OT_WDR_MODE_2To1_LINE) {
                u32RatioTmp = 0xFFF;
                int_time->int_time_max[0] = u32IntTimeMaxTmp;
                int_time->int_time_max[1] = int_time->int_time_max[0] * u32RatioTmp >> 6;
            } else {}
            int_time->int_time_min[0] = int_time->int_time_max[0];
            int_time->int_time_min[1] = int_time->int_time_max[1];
            int_time->int_time_min[2] = int_time->int_time_max[2];
            int_time->int_time_min[3] = int_time->int_time_max[3];
        }
    }

    return;
}
/*白平衡相关*/
/* awb static param for Fuji Lens New IR_Cut */
#define CALIBRATE_STATIC_TEMP 4900       // 4950
#define CALIBRATE_STATIC_WB_R_GAIN 0x1E3 // 450
#define CALIBRATE_STATIC_WB_GR_GAIN 0x100
#define CALIBRATE_STATIC_WB_GB_GAIN 0x100
#define CALIBRATE_STATIC_WB_B_GAIN 0x1d1 // 447

/* Calibration results for Auto WB Planck */
#define CALIBRATE_AWB_P1 -0x0012 // 18
#define CALIBRATE_AWB_P2 0x010b  // 238
#define CALIBRATE_AWB_Q1 -0x0007 // 0
#define CALIBRATE_AWB_A1 0x2711F // 212714
#define CALIBRATE_AWB_B1 0x0080
#define CALIBRATE_AWB_C1 (-0x1A5C1) //(-163748)
static ot_isp_cmos_dng_color_param g_dng_color_param = {{378, 256, 430}, {439, 256, 439}, {2810, {0x01AC, 0x8093, 0x8019, 0x8070, 0x01EA, 0x807A, 0x802A, 0x80F3, 0x021D}}, {4940, {0x01D7, 0x8084, 0x8053, 0x8053, 0x01D9, 0x8086, 0x8010, 0x80B3, 0x01C3}}};
static td_void cmos_get_isp_dng_default(const ot_isp_sns_state *sns_state, ot_isp_cmos_default *isp_def)
{
    /*......*/
    switch (sns_state->img_mode)
    {
    case IMX335_SENSOR_5M_30FPS_LINEAR_MODE:
        isp_def->sns_mode.dng_raw_format.bits_per_sample = 12; /* 12bit */
        isp_def->sns_mode.dng_raw_format.white_level = 2592;   /* max 4095 */
        break;
    default:
        break;
    }
    /*......*/
    return;
}
/*黑电平相关*/
static td_s32 cmos_get_isp_black_level(ot_vi_pipe vi_pipe, ot_isp_cmos_black_level *black_level)
{
    /*......*/
    /* Don't need to update black level when iso change */
    black_level->auto_attr.update = TD_FALSE;
    if (black_level->auto_attr.update == TD_TRUE){}
    /* black level of linear mode */
    if (sns_state->wdr_mode == OT_WDR_MODE_NONE)
    {
        for (i = 0; i < OT_ISP_BAYER_CHN_NUM; i++)
            black_level->auto_attr.black_level[0][i] = 800;    //
    }
    return TD_SUCCESS;
}
/*ISP相关配置(是否启用),比如锐化,DRC之类的,根据情况自行调整*/
static void cmos_get_isp_linear_default(ot_isp_cmos_default *isp_def)
{
    /*...配置选项略...*/  
    (td_void) memcpy_s(&isp_def->noise_calibration, sizeof(ot_isp_noise_calibration),
                       &g_cmos_noise_calibration, sizeof(ot_isp_noise_calibration));
    return;
}

static td_s32 cmos_get_isp_default(ot_vi_pipe vi_pipe, ot_isp_cmos_default *isp_def)
{
	/*...配置选项略...*/
    switch (sns_state->wdr_mode)
    {
    case OT_WDR_MODE_NONE:
        cmos_get_isp_linear_default(isp_def);
        break;
    default:
        break;
    }

    isp_def->wdr_switch_attr.exp_ratio[0] = 0x40;
    isp_def->sns_mode.sns_id = IMX335_ID;
    isp_def->sns_mode.sns_mode = sns_state->img_mode;
    cmos_get_isp_dng_default(sns_state, isp_def);

    return TD_SUCCESS;
}
static td_void cmos_set_pixel_detect(ot_vi_pipe vi_pipe, td_bool enable)
{
    /*......*/
    if (sns_state->wdr_mode == OT_WDR_MODE_2To1_LINE)
        return;
    else
    {
        if (sns_state->img_mode == IMX335_SENSOR_5M_30FPS_LINEAR_MODE)
            full_lines_5fps = (IMX335_VMAX_4K2K_LINEAR * 30) / 5; /* 30fps, 5fps */
        else
            return;
    }
    max_int_time_5fps = full_lines_5fps - IMX335_EXP_TIME_LMT;

    /*......*/
    return;
}
static td_void cmos_config_image_mode_param(ot_vi_pipe vi_pipe, td_u8 sns_image_mode,
                                            ot_isp_sns_state *sns_state)
{
    switch (sns_image_mode)
    {
    case IMX335_SENSOR_5M_30FPS_LINEAR_MODE:
        sns_state->fl_std = IMX335_VMAX_4K2K_LINEAR;  //
        break;
    default:
        break;
    }
}

这里列出了大部分需要自行修改的部分,其他部分可参考海思提供索尼其他Sensor的驱动代码

最后是 imx335_cmos_ex.h

/*这里仅列出部分ISP配置参数,其他的可参考海思提供索尼其他Sensor的驱动代码,也可以自行通过PQ Tools工具调试*/
/* Piris attr */
static ot_isp_piris_attr g_piris = {
    0,   // bStepFNOTableChange
    1,   // bZeroIsMax
    94,  // u16TotalStep
    62,  // u16StepCount
    /* Step-F number mapping table. Must be from small to large. F1.0 is 1024 and F32.0 is 1 */
    {30, 35, 40, 45, 50, 56, 61, 67, 73, 79, 85, 92, 98, 105, 112, 120, 127, 135, 143, 150, 
     158, 166, 174, 183, 191, 200, 208, 217, 225, 234, 243, 252, 261, 270, 279, 289, 298, 
     307, 316, 325, 335, 344, 353, 362, 372, 381, 390, 399, 408, 417, 426, 435, 444, 453, 
     462, 470, 478, 486, 493, 500, 506, 512},
    OT_ISP_IRIS_F_NO_1_4,  // enMaxIrisFNOTarget
    OT_ISP_IRIS_F_NO_5_6,   // enMinIrisFNOTarget
    1,
    512,
    32
};

static const ot_isp_cmos_black_level g_cmos_blc = {
    TD_TRUE, /* user_black_level_en */
    {
        {800, 800, 800, 800},
        {800, 800, 800, 800},
        {800, 800, 800, 800},
        {800, 800, 800, 800},
    },   /* user_black_level */
    OT_ISP_BLACK_LEVEL_MODE_MANUAL, /* ot_isp_black_level_mode */
    {
        {
            {800, 800, 800, 800},
            {800, 800, 800, 800},
            {800, 800, 800, 800},
            {800, 800, 800, 800},
        },
    }, /* manual_attr */
    {
        OT_ISP_DYNAMIC_PATTERN_RGB, /* ot_isp_black_level_dynamic_pattern */
        {0, 0, 0, 0}, /* ob_area */
        10,    /* low_threshold */
        4095,  /* high_threshold */
        {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},    /* dynablc offset */
        10,    /* dynablc tolerance */
        16,    /* filter_strength */
        0x0,    /* separate_mode */
        {800, 800, 800, 800, 800, 800, 800, 800, 800, 800, 800, 800, 800, 800, 800, 800}, /* calibration black level */
        0,
    }, /* dynamic_attr */
    {
        0,                            /* update mode 0 */
        {
            {800, 800, 800, 800},
            {800, 800, 800, 800},
            {800, 800, 800, 800},
            {800, 800, 800, 800}, /* black level */
        },
    },
    {
        {2592, 1944},
    },
};

之后,进入Hi3519DV500_SDK_V2.0.1.1/smp/a55_linux/source/mpp/cbb/isp/user目录,

make clean 
make -j8

重新编译即可,在mpp/cbb/isp/user/lib下将生成libsns_imx335.alibsns_imx335.so库文件。

4. PQ Tools 工具出图

首先,修改系统驱动加载脚本,关闭内存保护隔离设置:

然后,在挂载的nfs文件夹下解压Hi3519DV500_PQ_V2.0.1.1.tgz文件

将编译出来的两个库文件,以及libot_mpi_isp.alibot_mpi_isp.so两个文件放在解压后的 Hi3519DV500_PQ_V2.0.1.1/libs 目录内,在configs文件夹下新建imx335文件夹,

文件夹下新建 config_entryimx335_5M30 两个配置文件。

其中config_entry按照如下内容修改:

[video_mode]
ModeNum   = 1
UseMode  = 0

[mode.0]
ModeName        = 4M30
IniFilename     = imx335_5M30.ini

imx335_4M30则根据之前修改的 sample_comm_vi.csample_comm_isp.c 两个文件修改配置。

注意修改 SensorLibFile = ./libs/libsns_imx335.so

板端执行 ./PQTools.sh -a imx335,根据提示在 PQTools 电脑端界面输入IP地址,若出现图象,则成功。

5. 在 sample 例程里使用

这里实在懒得写了,具体按照笔者Gitee仓库里修改sample_vio,然后编译、运行就可以了。

https://gitee.com/Mr_tsura/imx335_drv_hi3519dv500

需要 PQ Tools 配置文件的可以去看 Gitee 仓库了,笔者这边最近才看见有人提 issue,顺手更新了。
话说这篇文章居然这么多人看......

posted @ 2025-03-12 15:18  Wintoki  阅读(1116)  评论(0)    收藏  举报