一、设备树的从设备地址的确定
先看摄像头的数据手册:
而在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)