1.项目背景:
使用海思芯片,接收FPGA发送的MIPI数据,不需要ISP处理,YUV图像格式为YUV422。
2.移植MIPI驱动
修改IMX347的驱动远吗,将I2C读写的部分注释,其他的不用再做修改。
int imx347_slave_i2c_init(ot_vi_pipe vi_pipe)
{
return 0;
if (g_fd[vi_pipe] >= 0) {
return TD_SUCCESS;
}
#ifdef OT_GPIO_I2C
g_fd[vi_pipe] = open("/dev/gpioi2c_ex", O_RDONLY, S_IRUSR);
if (g_fd[vi_pipe] < 0) {
isp_err_trace("Open gpioi2c_ex error!\n");
return TD_FAILURE;
}
#else
int ret;
char dev_file[I2C_DEV_FILE_NUM] = {0};
td_u8 dev_num;
ot_isp_sns_commbus *imx347slavebusinfo = TD_NULL;
imx347slavebusinfo = imx347_slave_get_bus_info(vi_pipe);
dev_num = imx347slavebusinfo->i2c_dev;
if (snprintf_s(dev_file, sizeof(dev_file), sizeof(dev_file) - 1, "/dev/i2c-%u", dev_num) < 0) {
isp_err_trace("snprintf_s error!\n");
return TD_FAILURE;
}
g_fd[vi_pipe] = open(dev_file, O_RDWR, S_IRUSR | S_IWUSR);
if (g_fd[vi_pipe] < 0) {
isp_err_trace("Open /dev/ot_i2c_drv-%u error!\n", dev_num);
return TD_FAILURE;
}
ret = ioctl(g_fd[vi_pipe], OT_I2C_SLAVE_FORCE, (IMX347_SLAVE_I2C_ADDR >> 1));
if (ret < 0) {
isp_err_trace("I2C_SLAVE_FORCE error!\n");
close(g_fd[vi_pipe]);
g_fd[vi_pipe] = -1;
return ret;
}
#endif
return TD_SUCCESS;
}
td_s32 imx347_slave_write_register(ot_vi_pipe vi_pipe, td_u32 addr, td_u32 data)
{
return TD_SUCCESS;
td_s32 ret;
if (g_fd[vi_pipe] < 0) {
return TD_SUCCESS;
}
#ifdef OT_GPIO_I2C
i2c_data.dev_addr = IMX347_SLAVE_I2C_ADDR;
i2c_data.reg_addr = addr;
i2c_data.addr_byte_num = IMX347_SLAVE_ADDR_BYTE;
i2c_data.data = data;
i2c_data.data_byte_num = IMX347_SLAVE_DATA_BYTE;
ret = ioctl(g_fd[vi_pipe], GPIO_I2C_WRITE, &i2c_data);
if (ret) {
isp_err_trace("GPIO-I2C write failed!\n");
return ret;
}
#else
td_u32 idx = 0;
td_u8 buf[I2C_BUF_NUM];
if (IMX347_SLAVE_ADDR_BYTE == 2) { /* 2 byte */
buf[idx] = (addr >> 8) & 0xff; /* shift 8 */
idx++;
buf[idx] = addr & 0xff;
idx++;
} else {
}
if (IMX347_SLAVE_DATA_BYTE == 2) { /* 2 byte */
} else {
buf[idx] = data & 0xff;
idx++;
}
ret = write(g_fd[vi_pipe], buf, IMX347_SLAVE_ADDR_BYTE + IMX347_SLAVE_DATA_BYTE);
if (ret < 0) {
isp_err_trace("I2C_WRITE error!\n");
return TD_FAILURE;
}
#endif
return TD_SUCCESS;
}
3.修改VI配置信息
3.1配置MIPI输入参数
3519DV500 mipi接口输入配置参考如下:
static combo_dev_attr_t g_mipi_4lane_chn0_sensor_imx347_sdi_16bit_2lan_nowdr_attr = {
.devno = 0,
.input_mode = INPUT_MODE_MIPI,
.data_rate = MIPI_DATA_RATE_X1,
.img_rect = {0, 0, 1920, 1080},
.mipi_attr = {
DATA_TYPE_YUV422_8BIT,
OT_MIPI_WDR_MODE_NONE,
{0, 1, -1, -1, -1, -1, -1, -1}
}
};
static combo_dev_attr_t g_mipi_4lane_chn0_sensor_imx347_pal_16bit_2lan_nowdr_attr = {
.devno = 2,
.input_mode = INPUT_MODE_MIPI,
.data_rate = MIPI_DATA_RATE_X1,
.img_rect = {0, 0, 720, 576},
.mipi_attr = {
DATA_TYPE_YUV422_8BIT,
OT_MIPI_WDR_MODE_NONE,
{4, 6, -1, -1, -1, -1, -1, -1}
}
};
值得注意的是,当接口模式为OT_VI_INTF_MODE_MIPI_YUV420_NORM, OT_VI_INTF_MODE_MIPI_YUV420_LEGACY,OT_VI_INTF_MODE_MIPI_YUV422 时data_reverse必须为TD_FALSE,且掩码的设置必须为component_mask[0] = 0xFF000000,component_mask[1] = 0x00FF0000,即高8bit输入Y数据,低8bit 输入C数据,否则会导致图像异常或无图像等现像。
static ot_vi_dev_attr g_mipi_yuv422_dev_attr = {
.intf_mode = OT_VI_INTF_MODE_MIPI_YUV422,
/* Invalid argument */
.work_mode = OT_VI_WORK_MODE_MULTIPLEX_1,
/* mask component */
.component_mask = {0xFF000000, 0x00FF0000},
.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_FIELD,
.vsync_neg = OT_VI_VSYNC_NEG_HIGH,
.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, 0, 0,
/* vsync0_vhb vsync0_act vsync0_hhb */
0, 0, 0,
/* vsync1_vhb vsync1_act vsync1_hhb */
0, 0, 0
}
},
/* data type */
.data_type = OT_VI_DATA_TYPE_YUV,
/* data reverse */
.data_reverse = TD_FALSE,
/* input size */
.in_size = {1920, 1080},
/* data rate */
.data_rate = OT_DATA_RATE_X1,
};
3.2 配置VI设备通道参数
td_void sample_comm_vi_get_default_pipe_info(sample_sns_type sns_type, ot_vi_bind_pipe *bind_pipe,
sample_vi_pipe_info pipe_info[])
{
td_u32 i;
ot_size size;
sample_comm_vi_get_size_by_sns_type(sns_type, &size);
for (i = 0; i < bind_pipe->pipe_num; i++) {
/* pipe attr */
pipe_info[i].pipe_attr.pipe_bypass_mode = OT_VI_PIPE_BYPASS_NONE;
pipe_info[i].pipe_attr.isp_bypass = TD_TRUE;
pipe_info[i].pipe_attr.size.width = size.width;
pipe_info[i].pipe_attr.size.height = size.height;
pipe_info[i].pipe_attr.pixel_format = OT_PIXEL_FORMAT_YVU_SEMIPLANAR_422;
pipe_info[i].pipe_attr.compress_mode = OT_COMPRESS_MODE_NONE;
pipe_info[i].pipe_attr.frame_rate_ctrl.src_frame_rate = -1;
pipe_info[i].pipe_attr.frame_rate_ctrl.dst_frame_rate = -1;
if (sns_type == GST_412C_SLAVE_THERMO_T3_384_288_30FPS_14BIT) {
pipe_info[i].pipe_attr.compress_mode = OT_COMPRESS_MODE_NONE;
pipe_info[i].pipe_attr.pixel_format = OT_PIXEL_FORMAT_RGB_BAYER_14BPP;
}
pipe_info[i].pipe_need_start = TD_TRUE;
pipe_info[i].isp_need_run = TD_TRUE;
pipe_info[i].isp_quick_start = TD_FALSE;
if (i == 0) {
pipe_info[i].is_master_pipe = TD_TRUE;
}
/* pub attr */
sample_comm_isp_get_pub_attr_by_sns(sns_type, &pipe_info[i].isp_info.isp_pub_attr);
pipe_info[i].nr_attr.enable = TD_TRUE;
pipe_info[i].nr_attr.compress_mode = OT_COMPRESS_MODE_FRAME;
pipe_info[i].nr_attr.nr_type = OT_NR_TYPE_VIDEO_NORM;
pipe_info[i].nr_attr.nr_motion_mode = OT_NR_MOTION_MODE_NORM;
/* chn info */
pipe_info[i].chn_num = 1;
pipe_info[i].chn_info[0].vi_chn = 0;
pipe_info[i].chn_info[0].fmu_mode = OT_FMU_MODE_OFF;
pipe_info[i].chn_info[0].chn_attr.size.width = size.width;
pipe_info[i].chn_info[0].chn_attr.size.height = size.height;
pipe_info[i].chn_info[0].chn_attr.pixel_format = OT_PIXEL_FORMAT_YVU_SEMIPLANAR_422;
pipe_info[i].chn_info[0].chn_attr.dynamic_range = OT_DYNAMIC_RANGE_SDR8;
pipe_info[i].chn_info[0].chn_attr.video_format = OT_VIDEO_FORMAT_LINEAR;
pipe_info[i].chn_info[0].chn_attr.compress_mode = OT_COMPRESS_MODE_NONE;
pipe_info[i].chn_info[0].chn_attr.mirror_en = TD_FALSE;
pipe_info[i].chn_info[0].chn_attr.flip_en = TD_FALSE;
pipe_info[i].chn_info[0].chn_attr.depth = 1;
pipe_info[i].chn_info[0].chn_attr.frame_rate_ctrl.src_frame_rate = -1;
pipe_info[i].chn_info[0].chn_attr.frame_rate_ctrl.dst_frame_rate = -1;
if (sns_type == GST_412C_SLAVE_THERMO_T3_384_288_30FPS_14BIT) {
pipe_info[i].chn_info[0].chn_attr.compress_mode = OT_COMPRESS_MODE_NONE;
}
}
}
重要参数说明:
不使用ISP图像调整: pipe_info[i].pipe_attr.isp_bypass = TD_TRUE;
VI组通道使用YUV422格式:
pipe_info[i].pipe_attr.pixel_format = OT_PIXEL_FORMAT_YVU_SEMIPLANAR_422;
pipe_info[i].chn_info[0].chn_attr.pixel_format = OT_PIXEL_FORMAT_YVU_SEMIPLANAR_422;
4.修改VPSS配置信息
td_void sample_comm_vpss_get_default_grp_attr(ot_vpss_grp_attr *grp_attr)
{
grp_attr->ie_en = TD_FALSE;
grp_attr->dci_en = TD_FALSE;
grp_attr->buf_share_en = TD_FALSE;
grp_attr->mcf_en = TD_FALSE;
grp_attr->max_width = VPSS_DEFAULT_WIDTH;
grp_attr->max_height = VPSS_DEFAULT_HEIGHT;
grp_attr->max_dei_width = 0;
grp_attr->max_dei_height = 0;
grp_attr->dynamic_range = OT_DYNAMIC_RANGE_SDR8;
grp_attr->pixel_format = OT_PIXEL_FORMAT_YVU_SEMIPLANAR_422;
grp_attr->dei_mode = OT_VPSS_DEI_MODE_OFF;
grp_attr->buf_share_chn = OT_VPSS_CHN0;
grp_attr->frame_rate.src_frame_rate = -1;
grp_attr->frame_rate.dst_frame_rate = -1;
}
5.遇到问题
5.1图像为绿色
原因:YUV422数据位错误
解决办法:和FPGA工程师沟通一下,明确输入的MIPI数据YUV格式是,YUYV、YVYU等。
5.2 图像出现错位
原因:MIPI时钟和FPGA时钟不匹配
解决办法:检查syscfg的时钟配置。
5.2 其他错误
原因:就是硬件不行
解决办法:甩锅给硬件。