Linux ARM平台开发系列讲解(摄像头V4L2子系统) 2.12.5 V4L2 control的原理和实现

1. 概述

既然涉及到视频输入,就会有很多与 ISP 相关的效果,比如对比度、饱和度、色温、白平衡等等,这些都是通用的、必须的控制项,并且大多数仅需要设置一个整数值即可。Linux 内核中V4L2已经为我们提供了这个控制类型的API,我们只需要明确自己需要添加什么控制类型,然后将其添加进内核中即可。该部分摄像头代码一半厂家会提供驱动或者相对应的参数,否则没有图像处理知识很难开发出来。

2. v4l2_ctrl 注册流程框图

  • 一个v4l2设备可以对应多个v4l2_ctrl_handler,一个v4l2_ctrl_handler下可以对应多个控制单元,这些控制单元统一交给v4l2_ctrl_ref链表统一管理,具体如下:
    Linux ARM平台开发系列讲解(摄像头V4L2子系统) 2.12.5 V4L2 control的原理和实现_第1张图片
  • 控制单元类型定义:
控制类型 说明
V4L2_CID_VBLANK 垂直消隐。 每帧之后的空闲时间段,在此期间不生成图像数据。垂直消隐的单位是行。 每条线的长度为图像宽度加上行消隐,像素速率由同一子设备中的 V4L2_CID_PIXEL_RATE 控件定义。
V4L2_CID_HBLANK 水平消隐。每一行图像数据后不产生图像数据的空闲时间。水平消隐的单位为像素
V4L2_CID_EXPOSURE 确定相机传感器的曝光时间。曝光时间受帧间隔的限制。驱动程序应该将这些值解释为100µs单位,其中值1代表1/ 10000s, 10000代表1秒,100000代表10
V4L2_CID_ANALOGUE_GAIN 模拟增益是影响像素矩阵中所有颜色分量的增益。在A/D转换前,在模拟域进行增益运算
V4L2_CID_DIGITAL_GAIN 数字增益是所有颜色分量相乘的值。通常应用的数字增益是控制值除以例如0x100,这意味着要获得无数字增益,控制值需要为0x100。无增益配置通常也是默认配置
V4L2_CID_PIXEL_RATE dev的源pad中的像素率。该控件是只读的,其单位是像素/秒
V4L2_CID_LINK_FREQ 数据总线频率。 与媒体总线像素代码、总线类型(每个样本的时钟周期)一起,数据总线频率定义了像素阵列中的像素速率(V4L2_CID_PIXEL_RATE)(如果设备不是图像传感器,则可能在其他地方)。 可以从像素时钟、图像宽度和高度以及水平和垂直消隐计算帧速率。 虽然像素速率控制可以在包含像素阵列的 subdev 之外的其他地方定义,但无法从该信息中获得帧速率。 这是因为只有在像素阵列上才能假定垂直和水平消隐信息是准确的:在像素阵列中不允许其他消隐。 通过选择所需的水平和垂直消隐来执行帧速率的选择。 此控制的单位是 Hz

3. OV13850 中v4l2_ctrl注册

  • ov13850中注册v4l2_ctrl 步骤如下:
  • 源码路径: drivers\media\i2c\ov13850.c
static int ov13850_initialize_controls(struct ov13850 *ov13850)
{
	const struct ov13850_mode *mode;
	struct v4l2_ctrl_handler *handler;
	struct v4l2_ctrl *ctrl;
	s64 exposure_max, vblank_def;
	u32 h_blank;
	int ret;

	handler = &ov13850->ctrl_handler;
	mode = ov13850->cur_mode;
	ret = v4l2_ctrl_handler_init(handler, 8);
	if (ret)
		return ret;
	handler->lock = &ov13850->mutex;
	/* 注册添加一个int控制菜单在handler下,用于控制总线的频率 */
	ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ,
				      0, 0, link_freq_menu_items);
	if (ctrl)
		ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
	/* 注册添加一个标准控制在handler下,设置子设备的像素,该控制是只读的,用于读取pad下的子设备像素 */
	v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE,
			  0, OV13850_PIXEL_RATE, 1, OV13850_PIXEL_RATE);

	h_blank = mode->hts_def - mode->width;
	/* 添加水平消隐的时间间隔控制 */
	ov13850->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK,
				h_blank, h_blank, 1, h_blank);
	if (ov13850->hblank)
		ov13850->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;

	vblank_def = mode->vts_def - mode->height;
	/* 配置垂直消隐控制 */
	ov13850->vblank = v4l2_ctrl_new_std(handler, &ov13850_ctrl_ops,
				V4L2_CID_VBLANK, vblank_def,
				OV13850_VTS_MAX - mode->height,
				1, vblank_def);

	exposure_max = mode->vts_def - 4;
	/* 控制相机的曝光时间 */
	ov13850->exposure = v4l2_ctrl_new_std(handler, &ov13850_ctrl_ops,
				V4L2_CID_EXPOSURE, OV13850_EXPOSURE_MIN,
				exposure_max, OV13850_EXPOSURE_STEP,
				mode->exp_def);
	/* 模拟增益控制 */
	ov13850->anal_gain = v4l2_ctrl_new_std(handler, &ov13850_ctrl_ops,
				V4L2_CID_ANALOGUE_GAIN, OV13850_GAIN_MIN,
				OV13850_GAIN_MAX, OV13850_GAIN_STEP,
				OV13850_GAIN_DEFAULT);
	/* ISP测试ctrl */
	ov13850->test_pattern = v4l2_ctrl_new_std_menu_items(handler,
				&ov13850_ctrl_ops, V4L2_CID_TEST_PATTERN,
				ARRAY_SIZE(ov13850_test_pattern_menu) - 1,
				0, 0, ov13850_test_pattern_menu);

	if (handler->error) {
		ret = handler->error;
		dev_err(&ov13850->client->dev,
			"Failed to init controls(%d)\n", ret);
		goto err_free_handler;
	}

	ov13850->subdev.ctrl_handler = handler;

	return 0;

err_free_handler:
	v4l2_ctrl_handler_free(handler);

	return ret;
}

返回总目录

你可能感兴趣的:(Linux,ARM平台从入门到精通,arm,linux,驱动开发,摄像头驱动,嵌入式)