imx6q基于ov5642调试du913/914摄像头,摄像头偏绿调试

硬件

camera senor -> 913 -> 914(parallel并行接口) -> imx6
i2c通信(i2c3)
913的din[x]对应914dout[x]数据脚
pclk像素时钟
vsync场同步时钟
hsync行同步时钟
在这里插入图片描述这里的csi0_data8-19对应的就是914的dout0 - 11对应913的din0-11
示波器测量:
csi0_data8-17能有数据波形
pclk = 66mhz
vsync = 25hz
hsync = 20khz
pclk/hsync = 3300
hsync/vsync = 800
fps = 25fps
按照我的认知,这个摄像头规格是3300x800,帧率是25,数据位是10bit;
实际按照厂家给的,我的913摄像头规格是1280x720,帧率30,数据位8bit(只有高8位有效);
不知道是不是我的理解有问题,分辨率算出来的和实际差得有点离谱,
如果能有大神可以懂得,麻烦在评论解答一下,感谢!

设备树(dts)的配置

&i2c3 {
	clock-frequency = <400000>;
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_i2c3>;
	status = "okay";
	
ds90ub914a: ds90ub914a@6f {
		compatible = "ti,ds90ub914a-q1";
		reg = <0x6f>;
		pinctrl-names = "default";
		pinctrl-0 = <&pinctrl_video>;
		clocks = <&clks IMX6QDL_CLK_CKO>;
		clock-names = "csi_mclk";
		//DOVDD-supply = <&vgen4_reg>; /* 1.8v */
		//AVDD-supply = <&vgen3_reg>;  /* 2.8v, on rev C board is VGEN3,
		//								on rev B board is VGEN5 */
		//DVDD-supply = <&vgen2_reg>;  /* 1.5v*/
		//pwn-gpios = <&gpio1 16 1>;   /* active low: SD1_DAT0 */
		//rst-gpios = <&gpio1 17 0>;   /* active high: SD1_DAT1 */
		csi_id = <0>;
		pdb_gpios = <&gpio4 15 0>;
		mclk = <24000000>;
		mclk_source = <0>;
	};

	pinctrl_video: video { /* parallel camera */
			fsl,pins = <
				MX6QDL_PAD_CSI0_DAT8__IPU1_CSI0_DATA08    0x80000000
				MX6QDL_PAD_CSI0_DAT9__IPU1_CSI0_DATA09    0x80000000
				MX6QDL_PAD_CSI0_DAT10__IPU1_CSI0_DATA10    0x80000000
				MX6QDL_PAD_CSI0_DAT11__IPU1_CSI0_DATA11    0x80000000
				MX6QDL_PAD_CSI0_DAT12__IPU1_CSI0_DATA12    0x80000000
				MX6QDL_PAD_CSI0_DAT13__IPU1_CSI0_DATA13    0x80000000
				MX6QDL_PAD_CSI0_DAT14__IPU1_CSI0_DATA14    0x80000000
				MX6QDL_PAD_CSI0_DAT15__IPU1_CSI0_DATA15    0x80000000
				MX6QDL_PAD_CSI0_DAT16__IPU1_CSI0_DATA16    0x80000000
				MX6QDL_PAD_CSI0_DAT17__IPU1_CSI0_DATA17	   0x80000000
				MX6QDL_PAD_CSI0_DAT18__IPU1_CSI0_DATA18	   0x80000000
				MX6QDL_PAD_CSI0_DAT19__IPU1_CSI0_DATA19    0x80000000
				MX6QDL_PAD_CSI0_VSYNC__IPU1_CSI0_VSYNC     0x80000000
				MX6QDL_PAD_CSI0_PIXCLK__IPU1_CSI0_PIXCLK   0x80000000
				MX6QDL_PAD_CSI0_MCLK__IPU1_CSI0_HSYNC      0x80000000
				MX6QDL_PAD_KEY_ROW4__GPIO4_IO15                  0x80000000
			>;
	};

	v4l2_cap_0 {
		compatible = "fsl,imx6q-v4l2-capture";
		ipu_id = <0>;
		csi_id = <0>;
		mclk_source = <0>;
		status = "okay";
	};

	v4l2_cap_1 {
		compatible = "fsl,imx6q-v4l2-capture";
		ipu_id = <0>;
		csi_id = <1>;
		mclk_source = <0>;
		status = "okay";
	};

i2c 的 clock-frequency = <400000>;这个在914芯片规格书都有提到(不过100kHz也是可以正常使用)
在这里插入图片描述
这支设备树我是基于imx6q开发板的ov564x设备树来修改的,只是注释掉一些没有用上的引脚,添加了一个pdb低功耗(power down)引脚;
这里配置12位的数据脚,这是根据硬件pcb板引脚图来配置的,实际使用的还是data8 - 17;
选择到imx6的通道是ipu0 csi0 ;

ov5642.c代码修改

在这里和大家建议一下,把这部分的驱动都编译成模块(ko),然后再去insmod 去加载驱动,这样能够更加全面的了解这些驱动加载的流程
insmod ipu_bg_overlay_sdc.ko
insmod ipu_csi_enc.ko
insmod ipu_fg_overlay_sdc.ko
insmod ipu_prp_enc.ko
insmod ipu_still.ko
insmod v4l2-int-device.ko
insmod ov5642_camera.ko
insmod mxc_v4l2_capture.ko

insmod ipu_bg_overlay_sdc.ko
insmod ipu_csi_enc.ko
insmod ipu_fg_overlay_sdc.ko
insmod ipu_prp_enc.ko
insmod ipu_still.ko
insmod v4l2-int-device.ko
这几个模块为了驱动提供函数接口的,不加载,直接使用insmod ov5642_camera.ko会报找不到函数的错误
insmod mxc_v4l2_capture.ko
下面会分析

ov5642.c 位于driver/media/platform/mxc/capture
因为我修改了设备树
compatible = “ti,ds90ub914a-q1”;
所以需要在ov5642_i2c_driver结构体下添加.of_match_table = ds90ub914a_of_match
这样就可以匹配我们的设备树了

static const struct of_device_id ds90ub914a_of_match[] = {
	{ .compatible = "ti,ds90ub914a-q1", },
	{ }
};
static struct i2c_driver ov5642_i2c_driver = {
	.driver = {
		  .owner = THIS_MODULE,
		  .name  = "ov5642",
		  .of_match_table = ds90ub914a_of_match,
		  },
	.probe  = ov5642_probe,
	.remove = ov5642_remove,
	.id_table = ov5642_id,
};

下面分析ov5642_probe

static int ov5642_probe(struct i2c_client *client,
			const struct i2c_device_id *id)
{
	struct pinctrl *pinctrl;
	struct device *dev = &client->dev;
	int retval;
	u8 chip_id_high, chip_id_low;
         int RegVal;
         int pdb_gpio;

         pr_info("starting %s\n",__func__);
	/* ov5642 pinctrl */
	pinctrl = devm_pinctrl_get_select_default(dev);//这个是初始化pin脚,设备树里面的pinctrl_video;
	if (IS_ERR(pinctrl)) {
		dev_err(dev, "ov5642 setup pinctrl failed!");
		return PTR_ERR(pinctrl);
	}

	/* request power down pin */
	/*接下来就用#if 0 ...  #endif注释掉了一些没用用的代码*/
         #if 0
	pwn_gpio = of_get_named_gpio(dev->of_node, "pwn-gpios", 0);
	if (!gpio_is_valid(pwn_gpio)) {
		dev_warn(dev, "no sensor pwdn pin available");
		return -EINVAL;
	}
	retval = devm_gpio_request_one(dev, pwn_gpio, GPIOF_OUT_INIT_HIGH,
					"ov5642_pwdn");
	if (retval < 0)
		return retval;

	/* request reset pin */
	rst_gpio = of_get_named_gpio(dev->of_node, "rst-gpios", 0);
	if (!gpio_is_valid(rst_gpio)) {
		dev_warn(dev, "no sensor reset pin available");
		return -EINVAL;
	}
	retval = devm_gpio_request_one(dev, rst_gpio, GPIOF_OUT_INIT_HIGH,
					"ov5642_reset");
	if (retval < 0)
		return retval;
        #endif
        pdb_gpio = of_get_named_gpio(dev->of_node, "pdb_gpios", 0);//初始化pdb引脚
        printk("pdb_gpio_id = %d\n",pdb_gpio);
        if (!gpio_is_valid(pdb_gpio)) {
             dev_err(dev, "no sensor pdb pin available\n");
             return -ENODEV;
         }
        gpio_direction_input(pdb_gpio);//设置pdb引脚为输入

	/* Set initial values for the sensor struct. */
	memset(&ov5642_data, 0, sizeof(ov5642_data));
	ov5642_data.sensor_clk = devm_clk_get(dev, "csi_mclk");//根据设备树ds90ub914a: ds90ub914a@6f来获取
	if (IS_ERR(ov5642_data.sensor_clk)) {
		/* assuming clock enabled by default */
		ov5642_data.sensor_clk = NULL;
		dev_err(dev, "clock-frequency missing or invalid\n");
		return PTR_ERR(ov5642_data.sensor_clk);
	}

	retval = of_property_read_u32(dev->of_node, "mclk",//根据设备树ds90ub914a: ds90ub914a@6f来获取
					(u32 *) &(ov5642_data.mclk));
	if (retval) {
		dev_err(dev, "mclk missing or invalid\n");
		return retval;
	}

	retval = of_property_read_u32(dev->of_node, "mclk_source",//根据设备树ds90ub914a: ds90ub914a@6f来获取
					(u32 *) &(ov5642_data.mclk_source));
	if (retval) {
		dev_err(dev, "mclk_source missing or invalid\n");
		return retval;
	}

	retval = of_property_read_u32(dev->of_node, "csi_id",//根据设备树ds90ub914a: ds90ub914a@6f来获取
					&(ov5642_data.csi));
	if (retval) {
		dev_err(dev, "csi_id missing or invalid\n");
		return retval;
	}

	clk_prepare_enable(ov5642_data.sensor_clk);

/*这里是根据913摄像头的规格来配置的
	//ov5642_data.io_init = ov5642_reset;
	ov5642_data.i2c_client = client;
	ov5642_data.pix.pixelformat = V4L2_PIX_FMT_UYVY;
	ov5642_data.pix.width = 1280;
         ov5642_data.pix.height = 720;
	ov5642_data.streamcap.capability = V4L2_MODE_HIGHQUALITY |
					   V4L2_CAP_TIMEPERFRAME;
	ov5642_data.streamcap.capturemode = 0;
	ov5642_data.streamcap.timeperframe.denominator = DEFAULT_FPS;
	ov5642_data.streamcap.timeperframe.numerator = 1;//帧率 = denominator/numerator, 为什么要做这个除法,而不直街写

	ds90ub914q_device_init();//对914做初始化,下面会分析这个函数
	ds90ub913q_device_init();//对913做初始化,下面会分析这个函数
	//ov5642_power_on(&client->dev);

	//ov5642_reset();

	//ov5642_standby(0);
#if 0
	retval = ov5642_read_reg(OV5642_CHIP_ID_HIGH_BYTE, &chip_id_high);
	if (retval < 0 || chip_id_high != 0x56) {
		pr_warning("camera ov5642 is not found\n");
		clk_disable_unprepare(ov5642_data.sensor_clk);
		return -ENODEV;
	}
	retval = ov5642_read_reg(OV5642_CHIP_ID_LOW_BYTE, &chip_id_low);
	if (retval < 0 || chip_id_low != 0x42) {
		pr_warning("camera ov5642 is not found\n");
		clk_disable_unprepare(ov5642_data.sensor_clk);
		return -ENODEV;
	}

	ov5642_standby(1);
#endif 
	ov5642_int_device.priv = &ov5642_data;
	retval = v4l2_int_device_register(&ov5642_int_device);//注册到链表中,等待注册成设备节点,下面会分析

	clk_disable_unprepare(ov5642_data.sensor_clk);

	pr_info("camera ov5642 is found\n");
	return retval;
}
static int ds90ub914q_write(unsigned char reg_addr,unsigned char value)
{
    int ret;
    unsigned char buf[2];

    buf[0] = reg_addr;
    buf[1] = value;

    ret = i2c_master_send(ov5642_data.i2c_client, buf, 2);
    printk("%s:0x%x = 0x%x\n",__func__,buf[0],buf[1]);
    return ret;
}

static int  ds90ub914q_device_init(void)
{
	int i = 0;

	unsigned char reg_data[] = {
		0x07, 0xB2,//mapped 913 i2c add as 0x59,修改这个寄存器就可以把913映射出来到i2c下,然后操作这个地址来修改913的寄存器
	};

    /*soft reset*/
         if(ds90ub914q_write(DS90UB914Q_REG_Reset,0x07)<0)//DS90UB914Q_REG_Reset = 0x01,修改这寄存器可以让914寄存器重置
               return -1;
         udelay(50);
         
	for(i = 0; i < sizeof(reg_data); i += 2){
		if(ds90ub914q_write(reg_data[i], reg_data[i+1]) < 0){
                                   return -1;
                  }
		udelay(10);
	}

         return 0;
}
static int ds90ub913q_write(unsigned char reg_addr,unsigned char value)
{
         int ret;
         unsigned char buf[2];
         struct i2c_msg msg[1];
         msg[0].addr  = 0x59;
         
         buf[0] = reg_addr;
         buf[1] = value;

         msg[0].addr  = 0x59;
         msg[0].buf   = buf; 
         msg[0].len   = 2;
         msg[0].flags = 0;
         
         ret = i2c_transfer(ov5642_data.i2c_client->adapter, msg, 1);
         printk("%s:0x%x = 0x%x\n",__func__,buf[0],buf[1]);
         return ret;
}

static ds90ub913q_device_init(void){
         int i = 0;
         /*这三个寄存器的设置是摄像头厂家给的,具体为什么要这么做没有说明*/
         unsigned char reg_data[] = {
			0x03, 0xdd,//Pass-Through and Automatically Acknowledge I2C Remote Write
			0x0d, 0x11,//Chip 913 Host RST and Host FrameSync Signal
			0x0d, 0x99,//Chip 913 Host RST and Host FrameSync Signal
		}
         /*soft reset*/
         if(ds90ub913q_write(0x01,0x33)<0)//重置913寄存器
                   return -1;
         udelay(50);
         
         for(i = 0; i < sizeof(reg_data); i += 2){
             if(ds90ub913q_write(reg_data[i], reg_data[i+1]) < 0){
                                        return -1;
                       }
             udelay(10);
         }

         return 0;
}

ds90ub913q_write函数可以实现同一路i2c下不同地址的设备的修改
从同事了解到i2c 中 adapter可以理解为i2c3,然后通过i2c_msg这个结构体来修改地址(默认是0x6f , 914的地址),最后用i2c_transfer函数写913的寄存器。
ds90ub913q_write和ds90ub914q_write是我自己添加的,也可以用ov5642_write。

最后使用
v4l2_int_device_register(&ov5642_int_device);

static struct v4l2_int_slave ov5642_slave = {
	.ioctls = ov5642_ioctl_desc,
	.num_ioctls = ARRAY_SIZE(ov5642_ioctl_desc),
};

static struct v4l2_int_device ov5642_int_device = {
	.module = THIS_MODULE,
	.name = "ov5642",
	.type = v4l2_int_type_slave,
	.u = {
		.slave = &ov5642_slave,
	},
};

这个函数目的是注册到链表当中,等待把函数中的设备名,ioctls等信息注册到设备节点中
这里之所以说是等待,因为还要使用同一目录下mxc_v4l2_capture.c,也就是我们前面说的要加载insmod mxc_v4l2_capture.ko,这样才会真正完成设备的注册。

mxc_v4l2_capture.c是不用修改的,它主要是通过你在ov5642_probe函数中设置的摄像头参数来修改ipu和csi相关的寄存器参数

mxc_v4l2_capture.c分析

static int mxc_v4l2_probe(struct platform_device *pdev)
{
	/* Create cam and initialize it. */
	cam_data *cam = kmalloc(sizeof(cam_data), GFP_KERNEL);
	if (cam == NULL) {
		pr_err("ERROR: v4l2 capture: failed to register camera\n");
		return -1;
	}

	init_camera_struct(cam, pdev);//初始化摄像头参数
	pdev->dev.release = camera_platform_release;

	/* Set up the v4l2 device and register it*/
	cam->self->priv = cam;
	v4l2_int_device_register(cam->self);//这个注册一个master设备,然后和ov5642这个slave设备匹配

	/* register v4l video device */
	if (video_register_device(cam->video_dev, VFL_TYPE_GRABBER, video_nr)//这里才会真正注册一个/dev/videoX设备节点
		< 0) {
		kfree(cam);
		cam = NULL;
		pr_err("ERROR: v4l2 capture: video_register_device failed\n");
		return -1;
	}
	pr_debug("   Video device registered: %s #%d\n",
		 cam->video_dev->name, cam->video_dev->minor);

	if (device_create_file(&cam->video_dev->dev,
			&dev_attr_fsl_v4l2_capture_property))
		dev_err(&pdev->dev, "Error on creating sysfs file"
			" for capture\n");

	if (device_create_file(&cam->video_dev->dev,
			&dev_attr_fsl_v4l2_overlay_property))
		dev_err(&pdev->dev, "Error on creating sysfs file"
			" for overlay\n");

	if (device_create_file(&cam->video_dev->dev,
			&dev_attr_fsl_csi_property))
		dev_err(&pdev->dev, "Error on creating sysfs file"
			" for csi number\n");

	return 0;
}

init_camera_struct(cam, pdev);这个函数虽然是初始化摄像头参数,但是并不是真正把你前面ov5642_probe设置的参数设置进去,只是设置了它默认的参数。

v4l2_int_device_register(cam->self);这个是注册一个master设备到链表中,然后会和之前ov5642中注册到链表中slave设备匹配。我猜想应该每一个slave都要对应一个master,然后slave设备会和最近一个注册的master设备匹配。

video_register_device(cam->video_dev, VFL_TYPE_GRABBER, video_nr)然后利用这个函数才真正注册出一个供应用层使用的设备节点/dev/videoX,一般是video0;

利用应用程序/unit_test/mxc_v4l2_capture.out验证设备

在linux系统中,使用imx6q原生的系统,在/unit_test/目录下会有很多视频的测试工具。
我个人使用的是mxc_v4l2_capture.out,可以保存捕获的视频到本地。
如./mxc_v4l2_capture.out -ow 1024 -oh 768 -f UYVY /tmp/video.yuv,这个命令的意思是生成分辨率是1024x768,采集格式UYVY的/tmp/video.yuv文件。

int v4l_capture_setup(void){
...
	if (ioctl(fd_v4l, VIDIOC_S_PARM, &parm) < 0)
	{
	        printf("VIDIOC_S_PARM failed\n");
	        return -1;
	}
...
}

在mxc_v4l2_capture.out源码中可以看到ioctl(fd_v4l, VIDIOC_S_PARM, &fmt);这个就是真正把ov5642.c的参数设置ipu和csi的寄存器中。

在这里只要你调用ioclt就会调用前面mxc_v4l2_capture.c的mxc_v4l_do_ioctl这个函数,根据case来做相应的操作。

static long mxc_v4l_do_ioctl(struct file *file,
			    unsigned int ioctlnr, void *arg)
{
...
	case VIDIOC_S_PARM:  {
		struct v4l2_streamparm *parm = arg;
		pr_debug("   case VIDIOC_S_PARM\n");
		if (cam->sensor)
			retval = mxc_v4l2_s_param(cam, parm);
		else {
			pr_err("ERROR: v4l2 capture: slave not found!\n");
			retval = -ENODEV;
		}
		break;
	}
...
}	    

这里的mxc_v4l2_s_param就是帮助设置参数的函数

static int mxc_v4l2_s_param(cam_data *cam, struct v4l2_streamparm *parm)
{
	struct v4l2_ifparm ifparm;
	struct v4l2_format cam_fmt;
	struct v4l2_streamparm currentparm;
	ipu_csi_signal_cfg_t csi_param;
	u32 current_fps, parm_fps;
	int err = 0;

	pr_debug("In mxc_v4l2_s_param\n");

	if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
		pr_err(KERN_ERR "mxc_v4l2_s_param invalid type\n");
		return -EINVAL;
	}

	/* Stop the viewfinder */
	if (cam->overlay_on == true)
		stop_preview(cam);

	currentparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

	/* First check that this device can support the changes requested. */
	err = vidioc_int_g_parm(cam->sensor, &currentparm);
	if (err) {
		pr_err("%s: vidioc_int_g_parm returned an error %d\n",
			__func__, err);
		goto exit;
	}

	current_fps = currentparm.parm.capture.timeperframe.denominator
			/ currentparm.parm.capture.timeperframe.numerator;
	parm_fps = parm->parm.capture.timeperframe.denominator
			/ parm->parm.capture.timeperframe.numerator;

	pr_debug("   Current capabilities are %x\n",
			currentparm.parm.capture.capability);
	pr_debug("   Current capturemode is %d  change to %d\n",
			currentparm.parm.capture.capturemode,
			parm->parm.capture.capturemode);
	pr_debug("   Current framerate is %d  change to %d\n",
			current_fps, parm_fps);

	/* This will change any camera settings needed. */
	err = vidioc_int_s_parm(cam->sensor, parm);
	if (err) {
		pr_err("%s: vidioc_int_s_parm returned an error %d\n",
			__func__, err);
		goto exit;
	}

	/* If resolution changed, need to re-program the CSI */
	/* Get new values. */
	vidioc_int_g_ifparm(cam->sensor, &ifparm);

	csi_param.data_width = 0;
	csi_param.clk_mode = 0;
	csi_param.ext_vsync = 0;
	csi_param.Vsync_pol = 0;
	csi_param.Hsync_pol = 0;
	csi_param.pixclk_pol = 0;
	csi_param.data_pol = 0;
	csi_param.sens_clksrc = 0;
	csi_param.pack_tight = 0;
	csi_param.force_eof = 0;
	csi_param.data_en_pol = 0;
	csi_param.data_fmt = 0;
	csi_param.csi = cam->csi;
	csi_param.mclk = 0;

	pr_debug("   clock_curr=mclk=%d\n", ifparm.u.bt656.clock_curr);
	if (ifparm.u.bt656.clock_curr == 0)
		csi_param.clk_mode = IPU_CSI_CLK_MODE_CCIR656_INTERLACED;
	else
		csi_param.clk_mode = IPU_CSI_CLK_MODE_GATED_CLK;

	csi_param.pixclk_pol = ifparm.u.bt656.latch_clk_inv;

	if (ifparm.u.bt656.mode == V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT) {
		csi_param.data_width = IPU_CSI_DATA_WIDTH_8;
	} else if (ifparm.u.bt656.mode
				== V4L2_IF_TYPE_BT656_MODE_NOBT_10BIT) {
		csi_param.data_width = IPU_CSI_DATA_WIDTH_10;
	} else {
		csi_param.data_width = IPU_CSI_DATA_WIDTH_8;
	}

	csi_param.Vsync_pol = ifparm.u.bt656.nobt_vs_inv;
	csi_param.Hsync_pol = ifparm.u.bt656.nobt_hs_inv;
	csi_param.ext_vsync = ifparm.u.bt656.bt_sync_correct;

	/* if the capturemode changed, the size bounds will have changed. */
	cam_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	vidioc_int_g_fmt_cap(cam->sensor, &cam_fmt);
	pr_debug("   g_fmt_cap returns widthxheight of input as %d x %d\n",
			cam_fmt.fmt.pix.width, cam_fmt.fmt.pix.height);

	csi_param.data_fmt = cam_fmt.fmt.pix.pixelformat;

	cam->crop_bounds.top = cam->crop_bounds.left = 0;
	cam->crop_bounds.width = cam_fmt.fmt.pix.width;
	cam->crop_bounds.height = cam_fmt.fmt.pix.height;

	/*
	 * Set the default current cropped resolution to be the same with
	 * the cropping boundary(except for tvin module).
	 */
	if (cam->device_type != 1) {
		cam->crop_current.width = cam->crop_bounds.width;
		cam->crop_current.height = cam->crop_bounds.height;
	}

	/* This essentially loses the data at the left and bottom of the image
	 * giving a digital zoom image, if crop_current is less than the full
	 * size of the image. */
	ipu_csi_set_window_size(cam->ipu, cam->crop_current.width,
				cam->crop_current.height, cam->csi);
	ipu_csi_set_window_pos(cam->ipu, cam->crop_current.left,
			       cam->crop_current.top,
			       cam->csi);
	ipu_csi_init_interface(cam->ipu, cam->crop_bounds.width,
			       cam->crop_bounds.height,
			       cam_fmt.fmt.pix.pixelformat, csi_param);


exit:
	if (cam->overlay_on == true)
		start_preview(cam);

	return err;
}

vidioc_int_s_parm,vidioc_int_s_parm和vidioc_int_g_ifparm,vidioc_int_g_fmt_cap都是可以在ov5642.c中找到相应的位置的。

ipu_csi_set_window_size,ipu_csi_set_window_pos,ipu_csi_init_interface这三函数就是设置ipu和csi相关寄存器的函数
ipu_csi_set_window_size会根据应用层给的分辨率来设置视频的分辨率
ipu_csi_set_window_pos会把视频显示位置上下左右移动
ipu_csi_init_interface代码

int32_t
ipu_csi_init_interface(struct ipu_soc *ipu, uint16_t width, uint16_t height,
	uint32_t pixel_fmt, ipu_csi_signal_cfg_t cfg_param)
{
	uint32_t data = 0;
	uint32_t csi = cfg_param.csi;
    void __iomem        *plx_membase; 

         //printk("%s:%s pixelfmt = %d\n",__FILE__,__func__,pixel_fmt);
	/* Set SENS_DATA_FORMAT bits (8, 9 and 10)
	   RGB or YUV444 is 0 which is current value in data so not set
	   explicitly
	   This is also the default value if attempts are made to set it to
	   something invalid. */
	switch (pixel_fmt) {
	case IPU_PIX_FMT_YUYV:
		cfg_param.data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_YUYV;
		break;
	case IPU_PIX_FMT_UYVY:
		cfg_param.data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_UYVY;
		break;
	case IPU_PIX_FMT_RGB24:
	case IPU_PIX_FMT_BGR24:
         case IPU_PIX_FMT_YUV444:
		cfg_param.data_fmt = CSI_SENS_CONF_DATA_FMT_RGB_YUV444;
		break;
	case IPU_PIX_FMT_GENERIC:
	case IPU_PIX_FMT_GENERIC_16:
		cfg_param.data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER;
		break;
	case IPU_PIX_FMT_RGB565:
		cfg_param.data_fmt = CSI_SENS_CONF_DATA_FMT_RGB565;
		break;
	case IPU_PIX_FMT_RGB555:
		cfg_param.data_fmt = CSI_SENS_CONF_DATA_FMT_RGB555;
		break;
	default:
		return -EINVAL;
	}

	/* Set the CSI_SENS_CONF register remaining fields */
	data |= cfg_param.data_width << CSI_SENS_CONF_DATA_WIDTH_SHIFT |
		cfg_param.data_fmt << CSI_SENS_CONF_DATA_FMT_SHIFT |
		cfg_param.data_pol << CSI_SENS_CONF_DATA_POL_SHIFT |
		cfg_param.Vsync_pol << CSI_SENS_CONF_VSYNC_POL_SHIFT |
		cfg_param.Hsync_pol << CSI_SENS_CONF_HSYNC_POL_SHIFT |
		cfg_param.pixclk_pol << CSI_SENS_CONF_PIX_CLK_POL_SHIFT |
		cfg_param.ext_vsync << CSI_SENS_CONF_EXT_VSYNC_SHIFT |
		cfg_param.clk_mode << CSI_SENS_CONF_SENS_PRTCL_SHIFT |
		cfg_param.pack_tight << CSI_SENS_CONF_PACK_TIGHT_SHIFT |
		cfg_param.force_eof << CSI_SENS_CONF_FORCE_EOF_SHIFT |
		cfg_param.data_en_pol << CSI_SENS_CONF_DATA_EN_POL_SHIFT;

	_ipu_get(ipu);

	mutex_lock(&ipu->mutex_lock);
	ipu_csi_write(ipu, csi, data, CSI_SENS_CONF);

	/* Setup sensor frame size */
	ipu_csi_write(ipu, csi, (width - 1) | (height - 1) << 16, CSI_SENS_FRM_SIZE);

	/* Set CCIR registers */
	if (cfg_param.clk_mode == IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE) {
		ipu_csi_write(ipu, csi, 0x40030, CSI_CCIR_CODE_1);
		ipu_csi_write(ipu, csi, 0xFF0000, CSI_CCIR_CODE_3);
	} else if (cfg_param.clk_mode == IPU_CSI_CLK_MODE_CCIR656_INTERLACED) {
		if (width == 720 && height == 625) {
			/* PAL case */
			/*
			 * Field0BlankEnd = 0x6, Field0BlankStart = 0x2,
			 * Field0ActiveEnd = 0x4, Field0ActiveStart = 0
			 */
			ipu_csi_write(ipu, csi, 0x40596, CSI_CCIR_CODE_1);
			/*
			 * Field1BlankEnd = 0x7, Field1BlankStart = 0x3,
			 * Field1ActiveEnd = 0x5, Field1ActiveStart = 0x1
			 */
			ipu_csi_write(ipu, csi, 0xD07DF, CSI_CCIR_CODE_2);

			ipu_csi_write(ipu, csi, 0xFF0000, CSI_CCIR_CODE_3);

		} else if (width == 720 && height == 525) {
			/* NTSC case */
			/*
			 * Field0BlankEnd = 0x7, Field0BlankStart = 0x3,
			 * Field0ActiveEnd = 0x5, Field0ActiveStart = 0x1
			 */
			ipu_csi_write(ipu, csi, 0xD07DF, CSI_CCIR_CODE_1);
			/*
			 * Field1BlankEnd = 0x6, Field1BlankStart = 0x2,
			 * Field1ActiveEnd = 0x4, Field1ActiveStart = 0
			 */
			ipu_csi_write(ipu, csi, 0x40596, CSI_CCIR_CODE_2);
			ipu_csi_write(ipu, csi, 0xFF0000, CSI_CCIR_CODE_3);
		} else {
			dev_err(ipu->dev, "2.Unsupported CCIR656 interlaced "
					"video mode\n");
			mutex_unlock(&ipu->mutex_lock);
			_ipu_put(ipu);
			return -EINVAL;
		}
		_ipu_csi_ccir_err_detection_enable(ipu, csi);
	} else if ((cfg_param.clk_mode ==
			IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR) ||
		(cfg_param.clk_mode ==
			IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR) ||
		(cfg_param.clk_mode ==
			IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_DDR) ||
		(cfg_param.clk_mode ==
			IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR)) {
		ipu_csi_write(ipu, csi, 0x40030, CSI_CCIR_CODE_1);
		ipu_csi_write(ipu, csi, 0xFF0000, CSI_CCIR_CODE_3);
		_ipu_csi_ccir_err_detection_enable(ipu, csi);
	} else if ((cfg_param.clk_mode == IPU_CSI_CLK_MODE_GATED_CLK) ||
		   (cfg_param.clk_mode == IPU_CSI_CLK_MODE_NONGATED_CLK)) {
		_ipu_csi_ccir_err_detection_disable(ipu, csi);
	}

	dev_info(ipu->dev, "CSI_SENS_CONF = 0x%08X\n",
		ipu_csi_read(ipu, csi, CSI_SENS_CONF));
         //plx_membase = ioremap(0x2600000, 0x30000); 
         //printk("BEN:CSI_CONF:addr = %lx\n",(u_long)plx_membase); 
         //printk("CSI_SENS_CONF =  0x%08X\n",readl((u_long)plx_membase));
	mutex_unlock(&ipu->mutex_lock);

	_ipu_put(ipu);

	return 0;
}

ipu_csi_write(ipu, csi, data, CSI_SENS_CONF);
这个写函数,就会把设置的参数写到给CSI_SENS_CONF这个寄存器中
在这里插入图片描述通过读这个寄存器就可以知道你设置的参数生效没。

这时候可以把/tmp/video.yuv拷出来,在电脑上播放,可以使用pyuv播放
在这里插入图片描述这个时候就到我最绝望奔溃的时候,图像出来了,可是全绿的,一点彩色点都没有,把imx6,914,913三份规格书图像显示的都看了一遍都没有发现。后来就开始漫长的调试过程。

修复绿色图像

首先排除913摄像头自身的问题,因为在其他平台是可以正常使用的。
可以排除是格式设置问题,但是这个图像很清晰,如果是分辨率,yuv格式设置不对是会有花屏的现象,不过你还是看到彩色点。
数据丢失问题,测量913的input data脚,914的output data脚的频率,发现两端是一样的,所以数据是没有丢失的。
这时候就去考虑imx6q处理的问题
这个是ov5642原生代码的接口配置,使用了CSI0_DATA12 - 19来接收数据

		pinctrl_ipu1_2: ipu1grp-2 { /* parallel camera */
			fsl,pins = <
			/*
				MX6QDL_PAD_CSI0_DAT12__IPU1_CSI0_DATA12    0x80000000
				MX6QDL_PAD_CSI0_DAT13__IPU1_CSI0_DATA13    0x80000000
				MX6QDL_PAD_CSI0_DAT14__IPU1_CSI0_DATA14    0x80000000
				MX6QDL_PAD_CSI0_DAT15__IPU1_CSI0_DATA15    0x80000000
				MX6QDL_PAD_CSI0_DAT16__IPU1_CSI0_DATA16    0x80000000
				MX6QDL_PAD_CSI0_DAT17__IPU1_CSI0_DATA17    0x80000000
				MX6QDL_PAD_CSI0_DAT18__IPU1_CSI0_DATA18    0x80000000
				MX6QDL_PAD_CSI0_DAT19__IPU1_CSI0_DATA19    0x80000000
				MX6QDL_PAD_CSI0_DATA_EN__IPU1_CSI0_DATA_EN 0x80000000
				MX6QDL_PAD_CSI0_PIXCLK__IPU1_CSI0_PIXCLK   0x80000000
				MX6QDL_PAD_CSI0_MCLK__IPU1_CSI0_HSYNC      0x80000000
				MX6QDL_PAD_CSI0_VSYNC__IPU1_CSI0_VSYNC     0x80000000
				MX6QDL_PAD_SD1_DAT1__GPIO1_IO17            0x80000000
				MX6QDL_PAD_SD1_DAT0__GPIO1_IO16            0x80000000
				*/
			>;
		};

我使用的是IPU1_CSI0_DATA10 - 17来接收数据

		pinctrl_video: video { /* parallel camera */
			fsl,pins = <
				MX6QDL_PAD_CSI0_DAT8__IPU1_CSI0_DATA08    0x80000000
				MX6QDL_PAD_CSI0_DAT9__IPU1_CSI0_DATA09    0x80000000
				MX6QDL_PAD_CSI0_DAT10__IPU1_CSI0_DATA10    0x80000000
				MX6QDL_PAD_CSI0_DAT11__IPU1_CSI0_DATA11    0x80000000
				MX6QDL_PAD_CSI0_DAT12__IPU1_CSI0_DATA12    0x80000000
				MX6QDL_PAD_CSI0_DAT13__IPU1_CSI0_DATA13    0x80000000
				MX6QDL_PAD_CSI0_DAT14__IPU1_CSI0_DATA14    0x80000000
				MX6QDL_PAD_CSI0_DAT15__IPU1_CSI0_DATA15    0x80000000
				MX6QDL_PAD_CSI0_DAT16__IPU1_CSI0_DATA16    0x80000000
				MX6QDL_PAD_CSI0_DAT17__IPU1_CSI0_DATA17	   0x80000000
				MX6QDL_PAD_CSI0_DAT18__IPU1_CSI0_DATA18	   0x80000000
				MX6QDL_PAD_CSI0_DAT19__IPU1_CSI0_DATA19    0x80000000
				MX6QDL_PAD_CSI0_VSYNC__IPU1_CSI0_VSYNC     0x80000000
				MX6QDL_PAD_CSI0_PIXCLK__IPU1_CSI0_PIXCLK   0x80000000
				MX6QDL_PAD_CSI0_MCLK__IPU1_CSI0_HSYNC      0x80000000
				MX6QDL_PAD_KEY_ROW4__GPIO4_IO15                  0x80000000
			>;
		};

想要和ov5642一样,就是使用CSI0_DATA12 - 19来接收数据,这样只能修改硬件
如下飞线
在这里插入图片描述这样接收到的图像就是正常的了

后来nxp论坛上看到
使用I.MX6Q CSI并行接口时,如果是YUV或者BT.656格式 8bit模式,确实是需要接在D12~D19上的。
也可以参考这位大哥的文章
https://blog.csdn.net/yanbixing123/article/list/7?

有什么问题大家可以在评论留言,谢谢

  • 7
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
你好!对于imx6ull摄像头驱动ov2640,您可以按照以下步骤进行配置和驱动: 1. 首先,确保您的imx6ull开发板已经正确连接了ov2640摄像头模块。 2. 在Linux系统中,您需要检查并确保i2c总线已经启用。可以通过命令`ls /dev/i2c*`来查看是否存在i2c设备。 3. 安装v4l2工具包,可以通过以下命令安装: ``` sudo apt-get install v4l-utils ``` 4. 下载并编译ov2640驱动源码。您可以从官方网站或GitHub上找到适用于imx6ull的ov2640驱动源码。 5. 在编译驱动之前,您需要为imx6ull配置适当的内核选项。打开内核配置文件(位于`/path/to/linux/source/.config`),确保以下选项已启用: ``` CONFIG_VIDEO_DEV=y CONFIG_VIDEO_V4L2_SUBDEV_API=y CONFIG_VIDEO_V4L2=y CONFIG_V4L2_MEM2MEM_DEV=y CONFIG_VIDEOBUF2_DMA_CONTIG=y CONFIG_VIDEOBUF2_DMA_SG=y ``` 6. 编译驱动代码并生成ko文件。根据驱动源码提供的说明进行编译,并生成ov2640.ko文件。 7. 将生成的ov2640.ko文件复制到imx6ull开发板上。 8. 使用`insmod`命令加载驱动: ``` sudo insmod ov2640.ko ``` 9. 确认驱动已成功加载并工作。您可以使用v4l2-ctl命令来测试摄像头: ``` v4l2-ctl -d /dev/video0 --list-formats-ext ``` 以上是一般的步骤,具体的驱动配置和编译过程可能因您使用的Linux发行版和驱动源码而有所不同。请确保在操作之前参考相关文档和指南。祝您成功驱动imx6ull摄像头ov2640!如有更多问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值