海思摄像头驱动的移植——以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.a 和 libsns_imx335.so库文件。
4. PQ Tools 工具出图
首先,修改系统驱动加载脚本,关闭内存保护隔离设置:

然后,在挂载的nfs文件夹下解压Hi3519DV500_PQ_V2.0.1.1.tgz文件
将编译出来的两个库文件,以及libot_mpi_isp.a和libot_mpi_isp.so两个文件放在解压后的 Hi3519DV500_PQ_V2.0.1.1/libs 目录内,在configs文件夹下新建imx335文件夹,
文件夹下新建 config_entry 和 imx335_5M30 两个配置文件。
其中config_entry按照如下内容修改:
[video_mode]
ModeNum = 1
UseMode = 0
[mode.0]
ModeName = 4M30
IniFilename = imx335_5M30.ini
imx335_4M30则根据之前修改的 sample_comm_vi.c 和 sample_comm_isp.c 两个文件修改配置。
注意修改
SensorLibFile = ./libs/libsns_imx335.so
板端执行 ./PQTools.sh -a imx335,根据提示在 PQTools 电脑端界面输入IP地址,若出现图象,则成功。
5. 在 sample 例程里使用

这里实在懒得写了,具体按照笔者Gitee仓库里修改sample_vio,然后编译、运行就可以了。
需要 PQ Tools 配置文件的可以去看 Gitee 仓库了,笔者这边最近才看见有人提 issue,顺手更新了。
话说这篇文章居然这么多人看......

浙公网安备 33010602011771号