Linux ov2640移植

一、设备树的从设备地址的确定

先看摄像头的数据手册:
在这里插入图片描述

而在i2c协议中,从设备地址有(7/10位)一般都是7位(最后一位为读写位),而0x60是8位的,所以整体右移一位变为0x30!

二、设备树gpio节点名称配错

原本是:

        ov2640: camera@0x30 {
                compatible = "ovti,ov2640";
                reg = <0x30>;
                status = "okay";

                pinctrl-names = "default";
                pinctrl-0 = <&pinctrl_csi1 &pinctrl_pwn_rst>;
                resetb = <&gpio1 2 GPIO_ACTIVE_LOW>;
                pwdn = <&gpio1 4 GPIO_ACTIVE_HIGH>;
                clocks = <&clks IMX6UL_CLK_CSI>;
                clock-names = "xvclk";
                port {
                        camera_ep: endpoint {
                                remote-endpoint = <&csi_ep>;
                                bus-width = <8>;
                        };
                };
        };    

于是uboot启动时报错:

ov2640 1-0030: Product ID error fb:fb

说明读取i2c的设备信息出了问题,这不关摄像头部分,于是我们分析ov2640.c就行了。

进入:drivers\media\i2c\soc_camera\ov2640.c

  • probe

    static int ov2640_probe(struct i2c_client *client,
    			const struct i2c_device_id *did)
    {
    	struct ov2640_priv	*priv;
    	struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
    	struct i2c_adapter	*adapter = to_i2c_adapter(client->dev.parent);
    	int			ret;
    
    	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
    		dev_err(&adapter->dev,
    			"OV2640: I2C-Adapter doesn't support SMBUS\n");
    		return -EIO;
    	}
    
    	priv = devm_kzalloc(&client->dev, sizeof(struct ov2640_priv), GFP_KERNEL);
    	if (!priv) {
    		dev_err(&adapter->dev,
    			"Failed to allocate memory for private data!\n");
    		return -ENOMEM;
    	}
    
    	priv->clk = v4l2_clk_get(&client->dev, "xvclk");
    	if (IS_ERR(priv->clk))
    		return -EPROBE_DEFER;
    
    	if (!ssdd && !client->dev.of_node) {
    		dev_err(&client->dev, "Missing platform_data for driver\n");
    		ret = -EINVAL;
    		goto err_clk;
    	}
    
    	if (!ssdd) {
    		ret = ov2640_probe_dt(client, priv);//这里面会设置priv以及soc_camera_subdev_desc的两个函数
    		if (ret)
    			goto err_clk;
    	}
    
    	v4l2_i2c_subdev_init(&priv->subdev, client, &ov2640_subdev_ops);
    	v4l2_ctrl_handler_init(&priv->hdl, 2);
    	v4l2_ctrl_new_std(&priv->hdl, &ov2640_ctrl_ops,
    			V4L2_CID_VFLIP, 0, 1, 1, 0);
    	v4l2_ctrl_new_std(&priv->hdl, &ov2640_ctrl_ops,
    			V4L2_CID_HFLIP, 0, 1, 1, 0);
    	priv->subdev.ctrl_handler = &priv->hdl;
    	if (priv->hdl.error) {
    		ret = priv->hdl.error;
    		goto err_clk;
    	}
    
    	ret = ov2640_video_probe(client);//报错在这里面
    	if (ret < 0)
    		goto err_videoprobe;
    
    	ret = v4l2_async_register_subdev(&priv->subdev);
    	if (ret < 0)
    		goto err_videoprobe;
    
    	dev_info(&adapter->dev, "OV2640 Probed\n");
    
    	return 0;
    
    err_videoprobe:
    	v4l2_ctrl_handler_free(&priv->hdl);
    err_clk:
    	v4l2_clk_put(priv->clk);
    	return ret;
    }
    
  • static int ov2640_video_probe(struct i2c_client *client)
    {
    	struct ov2640_priv *priv = to_ov2640(client);
    	u8 pid, ver, midh, midl;
    	const char *devname;
    	int ret;
    
    	ret = ov2640_s_power(&priv->subdev, 1);//上电
    	if (ret < 0)
    		return ret;
    
    	/*
    	 * check and show product ID and manufacturer ID
    	 */
    	i2c_smbus_write_byte_data(client, BANK_SEL, BANK_SEL_SENS);
    	pid  = i2c_smbus_read_byte_data(client, PID);
    	ver  = i2c_smbus_read_byte_data(client, VER);
    	midh = i2c_smbus_read_byte_data(client, MIDH);
    	midl = i2c_smbus_read_byte_data(client, MIDL);
    
    	switch (VERSION(pid, ver)) {
    	case PID_OV2640:
    		devname     = "ov2640";
    		break;
    	default:
    		dev_err(&client->dev,
    			"Product ID error %x:%x\n", pid, ver);
    		ret = -ENODEV;
    		goto done;
    	}
    
    	dev_info(&client->dev,
    		 "%s Product ID %0x:%0x Manufacturer ID %x:%x\n",
    		 devname, pid, ver, midh, midl);
    
    	ret = v4l2_ctrl_handler_setup(&priv->hdl);
    
    done:
    	ov2640_s_power(&priv->subdev, 0);
    	return ret;
    }
    

    于是我在想,会不会是ov2640_s_power上电没成功导致,读不出来?

  • static int ov2640_s_power(struct v4l2_subdev *sd, int on)
    {
    	struct i2c_client *client = v4l2_get_subdevdata(sd);
    	struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
    	struct ov2640_priv *priv = to_ov2640(client);
    
    	return soc_camera_set_power(&client->dev, ssdd, priv->clk, on);
    }
    
  • static inline int soc_camera_set_power(struct device *dev,
    		struct soc_camera_subdev_desc *ssdd, struct v4l2_clk *clk, bool on)
    {
    	return on ? soc_camera_power_on(dev, ssdd, clk)
    		  : soc_camera_power_off(dev, ssdd, clk);
    }
    
  • int soc_camera_power_on(struct device *dev, struct soc_camera_subdev_desc *ssdd,
    			struct v4l2_clk *clk)
    {
    	int ret;
    	bool clock_toggle;
    
    	if (clk && (!ssdd->unbalanced_power ||
    		    !test_and_set_bit(0, &ssdd->clock_state))) {
    		ret = v4l2_clk_enable(clk);
    		if (ret < 0) {
    			dev_err(dev, "Cannot enable clock: %d\n", ret);
    			return ret;
    		}
    		clock_toggle = true;
    	} else {
    		clock_toggle = false;
    	}
    
    	ret = regulator_bulk_enable(ssdd->sd_pdata.num_regulators,
    				    ssdd->sd_pdata.regulators);
    	if (ret < 0) {
    		dev_err(dev, "Cannot enable regulators\n");
    		goto eregenable;
    	}
    
    	if (ssdd->power) {
    		ret = ssdd->power(dev, 1);//调用前面ssdd注册的power
    		if (ret < 0) {
    			dev_err(dev,
    				"Platform failed to power-on the camera.\n");
    			goto epwron;
    		}
    	}
    
    	return 0;
    
    epwron:
    	regulator_bulk_disable(ssdd->sd_pdata.num_regulators,
    			       ssdd->sd_pdata.regulators);
    eregenable:
    	if (clock_toggle)
    		v4l2_clk_disable(clk);
    
    	return ret;
    }
    
  • /* OF probe functions */
    static int ov2640_hw_power(struct device *dev, int on)
    {
    	struct i2c_client *client = to_i2c_client(dev);
    	struct ov2640_priv *priv = to_ov2640(client);
    
    	dev_dbg(&client->dev, "%s: %s the camera\n",
    			__func__, on ? "ENABLE" : "DISABLE");
    
    	if (priv->pwdn_gpio)//这个没进来所以没有上电成功
    		gpiod_direction_output(priv->pwdn_gpio, !on);
    
    	return 0;
    }
    

    而这个ov2640_hw_power又是在ov2640_probe_dt中设置的

  • ov2640_probe_dt

    static int ov2640_probe_dt(struct i2c_client *client,
    		struct ov2640_priv *priv)
    {
    	/* Request the reset GPIO deasserted */
    	priv->resetb_gpio = devm_gpiod_get_optional(&client->dev, "resetb",
    			GPIOD_OUT_LOW);
    	if (!priv->resetb_gpio){
            dev_dbg(&client->dev, "resetb gpio is not assigned!\n");
            printk("---------------resetb_gpio get failed--------------");
        }
    		dev_dbg(&client->dev, "resetb gpio is not assigned!\n");
    	else if (IS_ERR(priv->resetb_gpio))
    		return PTR_ERR(priv->resetb_gpio);
    
    	/* Request the power down GPIO asserted */
    	priv->pwdn_gpio = devm_gpiod_get_optional(&client->dev, "pwdn",
    			GPIOD_OUT_HIGH);//这个竟然没有获取成功
    	if (!priv->pwdn_gpio){
            dev_dbg(&client->dev, "pwdn gpio is not assigned!\n");
            printk("---------------pwdn_gpio get failed--------------");
        }
    		dev_dbg(&client->dev, "pwdn gpio is not assigned!\n");
    	else if (IS_ERR(priv->pwdn_gpio))
    		return PTR_ERR(priv->pwdn_gpio);
    
    	/* Initialize the soc_camera_subdev_desc */
    	priv->ssdd_dt.power = ov2640_hw_power;
    	priv->ssdd_dt.reset = ov2640_hw_reset;
    	client->dev.platform_data = &priv->ssdd_dt;
    
    	return 0;
    }
    

    dev_dbg是不会打印出来的,所以我们看不到结果,于是我们用printk。

  • 重新编译ov2640.c,加载内核

    ---------------resetb_gpio get failed--------------
    ---------------pwdn_gpio get failed--------------
    

    说明,确实是priv->resetb_gpio与priv->pwdn_gpio这两个节点获取失败造成的ov2640 1-0030: Product ID error fb:fb。

于是我们把这两个节点改一下名字:(其实gpio子系统的节点后缀都要加-gpios)

        ov2640: camera@0x30 {
                compatible = "ovti,ov2640";
                reg = <0x30>;
                status = "okay";

                pinctrl-names = "default";
                pinctrl-0 = <&pinctrl_csi1 &pinctrl_pwn_rst>;
                resetb-gpios = <&gpio1 2 GPIO_ACTIVE_LOW>;
                pwdn-gpios = <&gpio1 4 GPIO_ACTIVE_HIGH>;
                clocks = <&clks IMX6UL_CLK_CSI>;
                clock-names = "xvclk";
                port {
                        camera_ep: endpoint {
                                remote-endpoint = <&csi_ep>;
                                bus-width = <8>;
                        };
                };
        };    

于是匹配成功:

ov2640 1-0030: ov2640 Product ID 26:42 Manufacturer ID 7f:a2

三、没有出现videox节点

在v4l2架构中,如果是uvc摄像头驱动,那么它由于只有一个subdev,所以它的video_device一定是在这个subdev的驱动注册的,而对应coms摄像头驱动,至少有两个subdev,它们之间通过v4l2_device连接起来,而一般是数据传输的subdev比如mipi/csi子设备的subdev驱动方注册的video_device,而i2c控制subdev则只是注册subdev进入v4l2中用于日后的控制(mipi的subdev中注册的video_device有效ioctl是调用到i2c subdev的ioctl)。

所以,没有出现videox节点的原因应该就是另一个csi的subdev驱动没有编译进内核。

通过寻找:我们找到drivers\media\platform\mxc\subdev\mx6s_capture.c

它的of_match_table:

static const struct of_device_id mx6s_csi_dt_ids[] = {
	{ .compatible = "fsl,imx6s-csi", },
	{ /* sentinel */ }
};

在我们设备树中也有:

                        csi: csi@021c4000 {
                                compatible = "fsl,imx6ul-csi", "fsl,imx6s-csi";
                                reg = <0x021c4000 0x4000>;
                                interrupts = <GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>;
                                clocks = <&clks IMX6UL_CLK_DUMMY>,
                                        <&clks IMX6UL_CLK_CSI>,
                                        <&clks IMX6UL_CLK_DUMMY>;
                                clock-names = "disp-axi", "csi_mclk", "disp_dcic";
                                status = "disabled";
                        };

于是确定,是这个驱动没错。

platform目录对应的Kconfig

config VIDEO_MXC_CAPTURE
	tristate "MXC Video For Linux Video Capture"
	depends on VIDEO_V4L2
	---help---
	  This is the video4linux2 capture driver based on i.MX video-in module.

打开mx6s_capture.c对应内核目录的Kconfig

if VIDEO_MXC_CAPTURE

config VIDEO_MXC_CSI_CAMERA
	tristate "CSI camera support"
	depends on VIDEO_MXC_CAPTURE && VIDEO_V4L2
	---help---
	  This is the video4linux2 capture driver based on CSI module.

config MXC_CAMERA_OV5640
        tristate "OmniVision ov5640 camera support"
        depends on VIDEO_MXC_CAPTURE && I2C
        ---help---
          If you plan to use the ov5640 Camera with your MXC system, say Y here.

config MXC_VADC
	tristate "mxc VADC support"
	depends on VIDEO_MXC_CAPTURE && VIDEO_V4L2
	---help---
		If you plan to use the VADC with your MXC system, say Y here.

config MXC_MIPI_CSI
	tristate "mxc mipi csi driver"
	depends on VIDEO_MXC_CAPTURE && VIDEO_V4L2
	---help---
	  This is a V4L2 driver for i.MX7D SoC MIPI-CSI2 receiver devices.

config MXC_CAMERA_OV5640_MIPI
	tristate "OmniVision ov5640 camera support using mipi"
	depends on MXC_MIPI_CSI && I2C
	---help---
	  If you plan to use the ov5640 Camera with mipi interface in your MXC system, say Y here.

config MXC_CAMERA_OV5647_MIPI
	tristate "OmniVision ov5647 camera support using mipi"
	depends on MXC_MIPI_CSI && I2C
	---help---
	  If you plan to use the ov5647 Camera with mipi interface in your MXC system, say Y here.
endif

所以我们应该配置VIDEO_MXC_CAPTURE与VIDEO_MXC_CSI_CAMERA

到内核的make menuconfig中去寻找:

在这里插入图片描述

于是配置好,这两个就行了。

之后加载内核成功看见video1。

四、去掉video0

之前我我一直很奇怪,为什么还没接任何摄像头就会有一个video0,于是这里打算搞清楚来源!

我们都知道,video的注册是在video_register_device里面

int __video_register_device(struct video_device *vdev, int type, int nr,
		int warn_if_nr_in_use, struct module *owner)
{
	.......
	.......
	vdev->minor = i + minor_offset;
	vdev->num = nr;
	devnode_set(vdev);

	/* Should not happen since we thought this minor was free */
	WARN_ON(video_device[vdev->minor] != NULL);
	vdev->index = get_index(vdev);
	video_device[vdev->minor] = vdev;//这里将video_device放入video_device数组里是很有必要的,因为后面的file要获取video_device的数据那么就可以通过inode获得次设备号来从数组中获得了
	mutex_unlock(&videodev_lock);

	if (vdev->ioctl_ops)
		determine_valid_ioctls(vdev);

	/* Part 3: Initialize the character device */
	vdev->cdev = cdev_alloc();
	if (vdev->cdev == NULL) {
		ret = -ENOMEM;
		goto cleanup;
	}
	vdev->cdev->ops = &v4l2_fops;
	vdev->cdev->owner = owner;
	ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1);//原来这个cdev是video_device的,之前以为是v4l2的,只不过它的ops使用的是v4l2的罢了
	if (ret < 0) {
		printk(KERN_ERR "%s: cdev_add failed\n", __func__);
		kfree(vdev->cdev);
		vdev->cdev = NULL;
		goto cleanup;
	}
	
	printk("--------------video_name = %s%d-------------\n",name_base, vdev->num);
	dump_stack();
	/* Part 4: register the device with sysfs */
	vdev->dev.class = &video_class;//video_class是在v4l2_dev.c中videodev_init创建的,这个函数是内核启动时调用的
	vdev->dev.devt = MKDEV(VIDEO_MAJOR, vdev->minor);
	vdev->dev.parent = vdev->dev_parent;
	dev_set_name(&vdev->dev, "%s%d", name_base, vdev->num);//确定设备节点名字是videox
	ret = device_register(&vdev->dev);//注册设备节点
	.....
	.....
}

于是我加了:printk(“--------------video_name = %s%d-------------\n”,name_base, vdev->num);dump_stack();

内核打印:

----------------------mx6s_csi_probe--------------------
----------------------__video_register_device-----------------------
--------------video_name = video0-------------
CPU: 0 PID: 1 Comm: swapper/0 Not tainted 4.1.15 #36
Hardware name: Freescale i.MX6 Ultralite (Device Tree)
[<80015dbc>] (unwind_backtrace) from [<80012748>] (show_stack+0x10/0x14)
[<80012748>] (show_stack) from [<808b875c>] (dump_stack+0x84/0xc4)
[<808b875c>] (dump_stack) from [<805b8d74>] (__video_register_device+0x1028/0x1130)
[<805b8d74>] (__video_register_device) from [<805f072c>] (mx6s_csi_probe+0x200/0x4ac)
[<805f072c>] (mx6s_csi_probe) from [<80384b18>] (platform_drv_probe+0x44/0xa4)
[<80384b18>] (platform_drv_probe) from [<803833d8>] (driver_probe_device+0x174/0x2b4)
[<803833d8>] (driver_probe_device) from [<803835e8>] (__driver_attach+0x8c/0x90)
[<803835e8>] (__driver_attach) from [<8038193c>] (bus_for_each_dev+0x68/0x9c)
[<8038193c>] (bus_for_each_dev) from [<80382b74>] (bus_add_driver+0x148/0x1f0)
[<80382b74>] (bus_add_driver) from [<80383be4>] (driver_register+0x78/0xf8)
[<80383be4>] (driver_register) from [<80009730>] (do_one_initcall+0x8c/0x1d8)
[<80009730>] (do_one_initcall) from [<80c01dac>] (kernel_init_freeable+0x144/0x1e4)
[<80c01dac>] (kernel_init_freeable) from [<808b4790>] (kernel_init+0xc/0xe8)
[<808b4790>] (kernel_init) from [<8000f528>] (ret_from_fork+0x14/0x2c)
CSI: Registered sensor subdevice: ov2640 1-0030
-------mxc_pxp_v4l2.c----------pxp_probe-----------------
----------------------__video_register_device-----------------------
--------------video_name = video1-------------
CPU: 0 PID: 1 Comm: swapper/0 Not tainted 4.1.15 #36
Hardware name: Freescale i.MX6 Ultralite (Device Tree)
[<80015dbc>] (unwind_backtrace) from [<80012748>] (show_stack+0x10/0x14)
[<80012748>] (show_stack) from [<808b875c>] (dump_stack+0x84/0xc4)
[<808b875c>] (dump_stack) from [<805b8d74>] (__video_register_device+0x1028/0x1130)
[<805b8d74>] (__video_register_device) from [<805fc284>] (pxp_probe+0x114/0x1b4)
[<805fc284>] (pxp_probe) from [<80384b18>] (platform_drv_probe+0x44/0xa4)
[<80384b18>] (platform_drv_probe) from [<803833d8>] (driver_probe_device+0x174/0x2b4)
[<803833d8>] (driver_probe_device) from [<803835e8>] (__driver_attach+0x8c/0x90)
[<803835e8>] (__driver_attach) from [<8038193c>] (bus_for_each_dev+0x68/0x9c)
[<8038193c>] (bus_for_each_dev) from [<80382b74>] (bus_add_driver+0x148/0x1f0)
[<80382b74>] (bus_add_driver) from [<80383be4>] (driver_register+0x78/0xf8)
[<80383be4>] (driver_register) from [<80009730>] (do_one_initcall+0x8c/0x1d8)
[<80009730>] (do_one_initcall) from [<80c01dac>] (kernel_init_freeable+0x144/0x1e4)
[<80c01dac>] (kernel_init_freeable) from [<808b4790>] (kernel_init+0xc/0xe8)
[<808b4790>] (kernel_init) from [<8000f528>] (ret_from_fork+0x14/0x2c)

可以看到,我们的csi摄像头匹配到了video0,而这个video1可以看到是pxp_probe注册到的。

在内核根目录下搜索:pxp_probe路径为:drivers/media/platform/mxc/output/mxc_pxp_v4l2.c

static int pxp_probe(struct platform_device *pdev)
{
	struct pxps *pxp;
	struct v4l2_device *v4l2_dev;
	int err = 0;

	pxp = kzalloc(sizeof(*pxp), GFP_KERNEL);
	if (!pxp) {
		dev_err(&pdev->dev, "failed to allocate control object\n");
		err = -ENOMEM;
		goto exit;
	}

	dev_set_drvdata(&pdev->dev, pxp);

	v4l2_dev = kzalloc(sizeof(*v4l2_dev), GFP_KERNEL);
	if (!v4l2_dev) {
		dev_err(&pdev->dev, "failed to allocate v4l2_dev structure\n");
		err = -ENOMEM;
		goto freeirq;
	}

	err = v4l2_device_register(&pdev->dev, v4l2_dev);
	if (err) {
		dev_err(&pdev->dev, "register v4l2 device failed\n");
		goto freev4l2;
	}

	INIT_LIST_HEAD(&pxp->outq);
	spin_lock_init(&pxp->lock);
	mutex_init(&pxp->mutex);

	pxp->pdev = pdev;

	pxp->vdev = video_device_alloc();
	if (!pxp->vdev) {
		dev_err(&pdev->dev, "video_device_alloc() failed\n");
		err = -ENOMEM;
		goto relv4l2;
	}

	memcpy(pxp->vdev, &pxp_template, sizeof(pxp_template));
	pxp->vdev->v4l2_dev = v4l2_dev;
	video_set_drvdata(pxp->vdev, pxp);

	err = video_register_device(pxp->vdev, VFL_TYPE_GRABBER, video_nr);//这里也注册video_device????
	if (err) {
		dev_err(&pdev->dev, "failed to register video device\n");
		goto freevdev;
	}

	dev_info(&pdev->dev, "initialized\n");

exit:
	return err;

freevdev:
	video_device_release(pxp->vdev);
relv4l2:
	v4l2_device_unregister(v4l2_dev);
freev4l2:
	kfree(v4l2_dev);
freeirq:
	kfree(pxp);

	return err;
}
static const struct of_device_id imx_pxpv4l2_dt_ids[] = {
	{ .compatible = "fsl,imx6sl-pxp-v4l2", },
	{ /* sentinel */ }
};

我们不需要注册它,于是在设备树中找到fsl,imx6sl-pxp-v4l2

 68         pxp_v4l2 {
 69               compatible = "fsl,imx6ul-pxp-v4l2", "fsl,imx6sx-pxp-v4l2", "fsl,imx6sl-pxp-v4l2";
 70               status = "enable";
 71               this node must set diabled or it works
 72         };

这竟然不是.dtsi文件中的,这个节点信息那么少,应该这个去掉也没什么实际作用。

我们把整个节点注释掉,注意:如果它不是在根设备树中的那么把它改为disable它还是能匹配上,只有把整个节点注释才彻底消失

最后,终于发现只剩下我们的video0,也就是ov2640的节点了!

五、使用opencv3.4发现ov2640驱动缺少某个ioctl的实现!

其实在opencv3.1版本并不会出现这样的情况。

一旦打开摄像头:不断报错…

VIDIOC_QUERYCTRL: Inappropriate ioctl for device

应该是内核中缺少VIDIOC_QUERYCTRL这个ioctl了。

于是在内核中添加上,(由于这个VIDIOC_QUERYCTRL的作用是用于获取摄像头的属性,而不是设置,所以不是太重要,我们可以去其它的摄像头驱动中拿过来使用)

static const struct v4l2_ioctl_ops mx6s_csi_ioctl_ops = {
	.vidioc_querycap          = mx6s_vidioc_querycap,
	.vidioc_enum_fmt_vid_cap  = mx6s_vidioc_enum_fmt_vid_cap,
	.vidioc_try_fmt_vid_cap   = mx6s_vidioc_try_fmt_vid_cap,
	.vidioc_g_fmt_vid_cap     = mx6s_vidioc_g_fmt_vid_cap,
	.vidioc_s_fmt_vid_cap     = mx6s_vidioc_s_fmt_vid_cap,
	.vidioc_cropcap       = mx6s_vidioc_cropcap,
	.vidioc_s_crop        = mx6s_vidioc_s_crop,
	.vidioc_g_crop        = mx6s_vidioc_g_crop,
	.vidioc_reqbufs       = mx6s_vidioc_reqbufs,
	.vidioc_querybuf      = mx6s_vidioc_querybuf,
	.vidioc_qbuf          = mx6s_vidioc_qbuf,
	.vidioc_dqbuf         = mx6s_vidioc_dqbuf,
	.vidioc_g_std         = mx6s_vidioc_g_std,
	.vidioc_s_std         = mx6s_vidioc_s_std,
	.vidioc_querystd      = mx6s_vidioc_querystd,
	.vidioc_enum_input    = mx6s_vidioc_enum_input,
	.vidioc_g_input       = mx6s_vidioc_g_input,
	.vidioc_s_input       = mx6s_vidioc_s_input,
	.vidioc_streamon      = mx6s_vidioc_streamon,
	.vidioc_streamoff     = mx6s_vidioc_streamoff,
	.vidioc_g_parm        = mx6s_vidioc_g_parm,
	.vidioc_s_parm        = mx6s_vidioc_s_parm,
	.vidioc_enum_framesizes = mx6s_vidioc_enum_framesizes,
	.vidioc_enum_frameintervals = mx6s_vidioc_enum_frameintervals,

	.vidioc_queryctrl = mx6s_vidioc_queryctrl,
	//为什么要加这个呢?因为opencv3.4版本需要有这个ioctrl(3.1版本好像不需要),但是这个query的ioctrl很随意,
	//可以直接搬移其它驱动文件的,我这里般的是mxc\output\mxc_pxp_v4l2.c
};
struct v4l2_queryctrl ov2640_qctrl[] = {
	{
		.id 		= V4L2_CID_HFLIP,
		.type		= V4L2_CTRL_TYPE_BOOLEAN,
		.name		= "Horizontal Flip",
		.minimum	= 0,
		.maximum	= 1,
		.step		= 1,
		.default_value	= 0,
		.flags		= 0,
	}, {
		.id 	= V4L2_CID_VFLIP,
		.type		= V4L2_CTRL_TYPE_BOOLEAN,
		.name		= "Vertical Flip",
		.minimum	= 0,
		.maximum	= 1,
		.step		= 1,
		.default_value	= 0,
		.flags		= 0,
	}, {
		.id 	= V4L2_CID_PRIVATE_BASE,
		.type		= V4L2_CTRL_TYPE_INTEGER,
		.name		= "Rotation",
		.minimum	= 0,
		.maximum	= 270,
		.step		= 90,
		.default_value	= 0,
		.flags		= 0,
	}, {
		.id 	= V4L2_CID_PRIVATE_BASE + 1,
		.name		= "Background Color",
		.minimum	= 0,
		.maximum	= 0xFFFFFF,
		.step		= 1,
		.default_value	= 0,
		.flags		= 0,
		.type		= V4L2_CTRL_TYPE_INTEGER,
	}, {
		.id 	= V4L2_CID_PRIVATE_BASE + 2,
		.name		= "Set S0 Chromakey",
		.minimum	= -1,
		.maximum	= 0xFFFFFF,
		.step		= 1,
		.default_value	= -1,
		.flags		= 0,
		.type		= V4L2_CTRL_TYPE_INTEGER,
	}, {
		.id 	= V4L2_CID_PRIVATE_BASE + 3,
		.name		= "YUV Colorspace",
		.minimum	= 0,
		.maximum	= 1,
		.step		= 1,
		.default_value	= 0,
		.flags		= 0,
		.type		= V4L2_CTRL_TYPE_BOOLEAN,
	},
};

static int mx6s_vidioc_queryctrl(struct file *file, void *priv,
			 struct v4l2_queryctrl *qc)
{
	int i;

	for (i = 0; i < ARRAY_SIZE(ov2640_qctrl); i++)
		if (qc->id && qc->id == ov2640_qctrl[i].id) {
			memcpy(qc, &(ov2640_qctrl[i]), sizeof(*qc));
			return 0;
		}

	return -EINVAL;
}

加上之后能够正常使用opencv3.4打开摄像头了。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yrbzNCDZ-1689577134771)(C:\Users\cww\AppData\Roaming\Typora\typora-user-images\image-20230717141253077.png)]

.name = “Set S0 Chromakey”,
.minimum = -1,
.maximum = 0xFFFFFF,
.step = 1,
.default_value = -1,
.flags = 0,
.type = V4L2_CTRL_TYPE_INTEGER,
}, {
.id = V4L2_CID_PRIVATE_BASE + 3,
.name = “YUV Colorspace”,
.minimum = 0,
.maximum = 1,
.step = 1,
.default_value = 0,
.flags = 0,
.type = V4L2_CTRL_TYPE_BOOLEAN,
},
};

static int mx6s_vidioc_queryctrl(struct file *file, void *priv,
struct v4l2_queryctrl *qc)
{
int i;

for (i = 0; i < ARRAY_SIZE(ov2640_qctrl); i++)
	if (qc->id && qc->id == ov2640_qctrl[i].id) {
		memcpy(qc, &(ov2640_qctrl[i]), sizeof(*qc));
		return 0;
	}

return -EINVAL;

}


加上之后能够正常使用opencv3.4打开摄像头了。
![在这里插入图片描述](https://img-blog.csdnimg.cn/02937af5f9d54439aa3521e98725df5a.png)


  • 4
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
索智R16点亮ov2640.txt 开发板:深圳市索智科技有限公司SC3817R开发板,全志R16(CPU:Quad-Core Cortex-A7 CPU) 全志官网R16的简介 android4.4.2/linux3.4.39 2016/7/18 16:38 1、配置ov2640在android层配置的分辨率为vga(预览/拍照/录像) 注意:系统自带的快拍APP就在这里查找分辨率参数发送到camera的HAL层。 这里得分辨率设置为:1280x720 驱动里面搜索可以设置为:1600x1200, 1280x960, 1024x768, 1280x720, 800x600(出错), 640x480 R:\wyb\test_ov2640_r16\android\device\softwinner\astar-evb20\configs\camera.cfg ;------------------------------------------------------------------------------- ; 用于camera的配置 ; ; 采用格式: ; key = key_value ; 注意: 每个key需要顶格写; ; key_value紧跟着key后面的等号后面, 位于同一行中; ; key_value限制大小为256字节以内; ; ;------------------------------------------------------------------------------- ;------------------------------------------------------------------------------- ; exif information of "make" and "model" ;------------------------------------------------------------------------------- key_camera_exif_make = MAKE_A31S key_camera_exif_model = MODEL_A31ST ;------------------------------------------------------------------------------- ; 1 for single camera, 2 for double camera ;------------------------------------------------------------------------------- number_of_camera = 1 ;------------------------------------------------------------------------------- ; CAMERA_FACING_BACK ; gc0307 ov2640 ;------------------------------------------------------------------------------- camera_id = 0 ;------------------------------------------------------------------------------- ; 1 for CAMERA_FACING_FRONT ; 0 for CAMERA_FACING_BACK ;------------------------------------------------------------------------------- camera_facing = 0 ;------------------------------------------------------------------------------- ; 1 for camera without isp(using built-in isp of Axx) ; 0 for camera with isp ;------------------------------------------------------------------------------- use_builtin_isp = 0 ;------------------------------------------------------------------------------- ; camera orientation (0, 90, 180, 270) ;------------------------------------------------------------------------------- camera_orientation = 90 ;------------------------------------------------------------------------------- ; driver device name ;------------------------------------------------------------------------------- camera_device = /dev/video0 ;------------------------------------------------------------------------------- ; device id ; for two camera devices with one CSI ;------------------------------------------------------------------------------- device_id = 0 used_preview_size = 1 key_support_preview_size = 1600x1200, 1280x960, 1024x768, 1280x720, 800x600, 640x480 key_default_preview_size = 1280x720 used_picture_size = 1 key_support_picture_size = 1600x1200, 1280x960, 1024x768, 1280x720, 800x600, 640x480 key_default_picture_size = 1600x1200 used_flash_mode = 0 key_support_flash_mode = on,off,auto key_default_flash_mode = on used_color_effect=0 key_support_color_effect = none,mono,negative,sepia,aqua key_default_color_effect = none used_frame_rate = 1 key_support_frame_rate = 10 key_default_frame_rate = 10 used_focus_mode = 0 key_support_focus_mode = auto,infinity,macro,fixed,continuous-video,continuous-picture key_default_focus_mode = auto used_scene_mode = 0 key_support_scene_mode = auto,portrait,landscape,night,night-portrait,theatre,beach,snow,sunset,steadyphoto,fireworks,sports,party,candlelight,barcode key_default_scene_mode = auto used_white_balance = 0 key_support_white_balance = auto,incandescent,fluorescent,warm-fluorescent,daylight,cloudy-daylight key_default_white_balance = auto used_exposure_compensation = 1 key_max_exposure_compensation = 3 key_min_exposure_compensation = -3 key_step_exposure_compensation = 1 key_default_exposure_compensation = 0 2、没有/dev/video1,只有/dev/video0 R:\wyb\test_ov2640_r16\android\device\softwinner\astar-evb20\ueventd.sun8i.rc /dev/video0 0666 media media #/dev/video1 0666 media media 3、在init.rc(init.sun8i.rc)中加载ov2640.ko这个驱动模块。 R:\wyb\test_ov2640_r16\android\device\softwinner\astar-evb20\init.sun8i.rc #csi module insmod /system/vendor/modules/videobuf-core.ko insmod /system/vendor/modules/videobuf-dma-contig.ko insmod /system/vendor/modules/cam_detect.ko #insmod /system/vendor/modules/actuator.ko #insmod /system/vendor/modules/ad5820_act.ko insmod /system/vendor/modules/cci.ko insmod /system/vendor/modules/vfe_os.ko insmod /system/vendor/modules/vfe_subdev.ko #insmod /system/vendor/modules/gc0307.ko #insmod /system/vendor/modules/ov2035.ko insmod /system/vendor/modules/ov2640.ko insmod /system/vendor/modules/vfe_v4l2.ko 启动之后实际加载的摄像头的驱动模块: shell@astar-evb20:/ $ lsmod gt82x 9849 0 - Live 0x00000000 bma250 7848 0 - Live 0x00000000 sunxi_schw 12559 0 - Live 0x00000000 (O) rtl8150 9023 0 - Live 0x00000000 sunxi_keyboard 3021 0 - Live 0x00000000 sw_device 13916 0 - Live 0x00000000 vfe_v4l2 445444 0 - Live 0x00000000 ov2640 11637 0 - Live 0x00000000 vfe_subdev 4523 2 vfe_v4l2,ov2640, Live 0x00000000 vfe_os 3951 2 vfe_v4l2,vfe_subdev, Live 0x00000000 cci 21775 2 vfe_v4l2,ov2640, Live 0x00000000 videobuf_dma_contig 5567 1 vfe_v4l2, Live 0x00000000 videobuf_core 16520 2 vfe_v4l2,videobuf_dma_contig, Live 0x00000000 bcm_btlpm 7442 0 - Live 0x00000000 bcmdhd 629907 0 - Live 0x00000000 mali 209490 25 - Live 0x00000000 (O) lcd 41263 0 - Live 0x00000000 disp 992816 8 mali,lcd, Live 0x00000000 nand 282774 0 - Live 0x00000000 (O) shell@astar-evb20:/ $ 4、驱动程序:ov2640.c及其V4L2的适配层。 配置ov2640.c的编译选项,只需要修改device目录中的Makefile打开ov2640即可: R:\wyb\test_ov2640_r16\lichee\linux-3.4\drivers\media\video\sunxi-vfe\device\ov2640.c R:\wyb\test_ov2640_r16\lichee\linux-3.4\drivers\media\video\sunxi-vfe\device\Makefile obj-m += ov2640.o R:\wyb\test_ov2640_r16\lichee\linux-3.4\drivers\media\video\sunxi-vfe\Kconfig R:\wyb\test_ov2640_r16\lichee\linux-3.4\drivers\media\video\sunxi-vfe\Makefile 5、 R:\wyb\test_ov2640_r16\lichee\tools\pack\chips\sun8iw5p1\configs\evb-20\sys_config.fex ;-------------------------------------------------------------------------------- ;vip (video input port) configuration ;vip_used: 0:disable 1:enable ;vip_mode: 0:sample one interface to one buffer 1:sample two interface to one buffer ;vip_dev_qty: The quantity of devices linked to capture bus ; ;vip_define_sensor_list: If you want use sensor detect function, please set vip_define_sensor_list = 1, and ; verify that file /system/etc/hawkview/sensor_list_cfg.ini is properly configured! ; ;vip_dev(x)_pos: sensor position, "rear" or "front", if vip_define_sensor_list = 1,vip_dev(x)_pos must be configured! ; ;vip_dev(x)_isp_used 0:not use isp 1:use isp ;vip_dev(x)_fmt: 0:yuv 1:bayer raw rgb ;vip_dev(x)_stby_mode: 0:not shut down power at standby 1:shut down power at standby ;vip_dev(x)_vflip: flip in vertical direction 0:disable 1:enable ;vip_dev(x)_hflip: flip in horizontal direction 0:disable 1:enable ;vip_dev(x)_iovdd: camera module io power handle string, pmu power supply ;vip_dev(x)_iovdd_vol: camera module io power voltage, pmu power supply ;vip_dev(x)_avdd: camera module analog power handle string, pmu power supply ;vip_dev(x)_avdd_vol: camera module analog power voltage, pmu power supply ;vip_dev(x)_dvdd: camera module core power handle string, pmu power supply ;vip_dev(x)_dvdd_vol: camera module core power voltage, pmu power supply ;vip_dev(x)_afvdd: camera module vcm power handle string, pmu power supply ;vip_dev(x)_afvdd_vol: camera module vcm power voltage, pmu power supply ;x indicates the index of the devices which are linked to the same capture bus ;fill voltage in uV, e.g. iovdd = 2.8V, vip_devx_iovdd_vol = 2800000 ;fill handle string as below: ;axp22_eldo3 ;axp22_dldo4 ;axp22_eldo2 ;fill handle string "" when not using any pmu power supply ;-------------------------------------------------------------------------------- [csi0] vip_used = 1 vip_mode = 0 vip_dev_qty = 1 vip_define_sensor_list = 0 vip_csi_pck = port:PE00 vip_csi_mck = port:PE01 vip_csi_hsync = port:PE02 vip_csi_vsync = port:PE03 vip_csi_d0 = port:PE04 vip_csi_d1 = port:PE05 vip_csi_d2 = port:PE06 vip_csi_d3 = port:PE07 vip_csi_d4 = port:PE08 vip_csi_d5 = port:PE09 vip_csi_d6 = port:PE10 vip_csi_d7 = port:PE11 vip_csi_sck = port:PE12 vip_csi_sda = port:PE13 ;vip_dev0_mname = "ov5640" vip_dev0_mname = "ov2640" vip_dev0_pos = "rear" vip_dev0_lane = 1 vip_dev0_twi_id = 2 ;vip_dev0_twi_addr = 0x78 vip_dev0_twi_addr = 0x60 vip_dev0_isp_used = 0 vip_dev0_fmt = 0 vip_dev0_stby_mode = 0 vip_dev0_vflip = 0 vip_dev0_hflip = 0 vip_dev0_iovdd = "axp22_dldo3" vip_dev0_iovdd_vol = 3300000 vip_dev0_avdd = "" vip_dev0_avdd_vol = 3300000 vip_dev0_dvdd = "" vip_dev0_dvdd_vol = 1800000 vip_dev0_afvdd = "" vip_dev0_afvdd_vol = 3300000 vip_dev0_power_en = vip_dev0_reset = port:PE14 vip_dev0_pwdn = port:PE15 vip_dev0_flash_en = vip_dev0_flash_mode = vip_dev0_af_pwdn = 6、开发板上摄像头的I2C挂载在TWI2上面了,逻辑是需要打开的(但是不打开也能够用,很奇怪!): ;---------------------------------------------------------------------------------- ;i2c configuration ; twi_used = twix enable ;---------------------------------------------------------------------------------- [twi0] twi_used = 1 twi_scl = port:PH02 twi_sda = port:PH03 [twi1] twi_used = 1 twi_scl = port:PH04 twi_sda = port:PH05 [twi2] twi_used = 0 twi_scl = port:PE12 twi_sda = port:PE13 直接配置摄像头的I2C了: vip_csi_sck = port:PE12 vip_csi_sda = port:PE13 不过奇怪的是,摄像头并没有向其它平台那样生成这个设备节点: /sys/class/i2c-adapter/i2c-2/2-0030 shell@octopus-f1:/ $ shell@octopus-f1:/ $ cd /sys/class/i2c-adapter/i2c-2/ shell@octopus-f1:/sys/class/i2c-adapter/i2c-2 $ shell@octopus-f1:/sys/class/i2c-adapter/i2c-2 $ ll drwxr-xr-x root root 1970-01-02 08:06 2-003b --w------- root root 4096 1970-01-02 08:06 delete_device lrwxrwxrwx root root 1970-01-02 08:06 device -> ../../twi.2 -r--r--r-- root root 4096 1970-01-02 08:06 name --w------- root root 4096 1970-01-02 08:06 new_device drwxr-xr-x root root 1970-01-02 08:06 power lrwxrwxrwx root root 1970-01-02 08:06 subsystem -> ../../../../bus/i2c -rw-r--r-- root root 4096 1970-01-02 08:06 uevent shell@octopus-f1:/sys/class/i2c-adapter/i2c-2 $ 可能是ov2640驱动程序不完整,没有通过i2c_detect来注册2-0030这个节点。 直接在驱动程序中读取I2C,还是对的。device ID = 0x2642。 7、奇葩的问题: 摄像头使用的是: http://www.waveshare.net/shop/OV2640-Camera-Board.htm OV2640 Camera Board vsync=15HZ,XCLK=24MHZ。href/pclk都有 camera打开摄像头是绿色屏幕(VGA分辨率),但是右上角貌似出来一点点图像。 后来使用示波器的探头挂在vsync之后,图像正常。 由于开发板和摄像头模组ov2640是飞线链接,硬件另外接了一个地,然后用纸胶布将地线和vsync捆绑在在一起,解决问题。 刚开始摄像头的3.3V电源使用的是g-sensor的(VCC-3V0),看见图像出现竖条纹(电源纹波干扰),接回来(VCC-3V0-CSI)就正常了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值