fuzidage
专注嵌入式、linux驱动 、arm裸机研究

导航

 

0 前言-camera sensor基础

0.1 摄像头组成

一个常见的监控用的摄像头包括:外壳、感光芯片电路、镜头座、镜头、红外灯板。
image

摄像头模组(CameraCompact Module),简称CCM。包含四大件: 镜头(lens)、传感器(sensor)、软板(FPC)、图像处理芯片(DSP)。
image

0.2 摄像头原理

image

  1. 通过镜头(LENS)生成的光学图像投射到图像传感器(Sensor)表面上
  2. 感光传感器把光信号转为电信号,经过A/D(模数转换)转换后变为数字图像信号
  3. 再送到数字信号处理芯片(DSP)中加工处理,转换成标准的RGB、YUV等格式图像信号,如raw,Yuv格式

0.3 sensor相关术语

名称 含义
3A算法 AEC, AWB, AF算法
AEC Auto Exposure Control, 自动曝光控制
AWB Auto White Balance, 自动白平衡
AF Auto Focus, 自动对焦

AEC对sensor内部来说,除了曝光控制,还有增益控制。

下面这些术语是camera驱动中经常用到的缩略语。

# 图像处理算法
AE(Auto Exposure):自动曝光。
AF(Auto Focus) :自动对焦。
AWB(Auto White Balance):自动白平衡。
3A :指自动曝光(AE)、自动对焦(AF)和自动白平衡(AWB)算法。

IQ(Image Quality) :指为Bayer Raw Camera调试的IQ xml,用于3A tunning。
Bayer Raw(或Raw Bayer) :Bayer是相机内部的原始图片,一般后缀为.raw。.raw格式内部的存储方
式有:RGGB、BGGR、GRBG等。

# 图像处理单元
ISP(Image Signal Processing) :图像信号处理单元。
CIF :soc vendor定义的camera interface framework,用来接收各类型接口的camera。
MIPI-DPHY :MIPI-DPHY协议的控制器。

#图像控制信号
Frame :帧。
DVP(Digital Video Port) :一种并行数据传输接口。
HSYNC :行同步信号,HSYNC有效时,接收到的信号属于同一行。
VSYNC :场同步信号,VSYNC有效时,接收到的信号属于同一帧
PCLK(Pixel Clock) :指Sensor输出的Pixel Clock。

#v4l2术语
Media Controller :Linux内核中的一种媒体框架,用于拓扑结构的管理。
Entity :Media Controller架构下的各节点。
Pipeline :Media Controller架构的各Entity之间相互连接形成的链路。
V4L2(Video4Linux2) :指Linux内核的视频处理模块。
Async Sub Device:在Media Controller结构下注册的V4L2异步子设备,例:Sensor、MIPI DPHY。

0.3.0 色彩空间

颜色空间-RGB:
image

颜色空间-YUV:
yuv444,yuv422,yuv420格式的合成,yuv又会分为planned和packed格式区分。
image
image

细节详见:https://fuzidage.github.io/2024/04/02/mipi-csi软件篇/#4-2-yuv-ge-shi

0.3.1 图像噪音

指的是图像中的杂点干扰,表现为图像中有固定的彩色杂点。

0.3.2 颜色深度-BPP

对camera来说,有raw8 ,raw10, raw12,raw16色彩深度。就是用多少位的二进制数字来记录三种原色。实际就是

A/D转换器的量化精度,是指将信号分成多少个等级,常用色彩位数(bit)表示。彩色深度越高,获得

的影像色彩就越艳丽动人。

mipi-rx之软件篇 - fuzidage - 博客园 (cnblogs.com)

mipi-csi软件篇 | Hexo (fuzidage.github.io)

8位彩色,有256种深度。

16位彩色:65,536种颜色。

24位彩色:每种原色都有256个层次,它们的组合便有256256256种颜色。

32位彩色:除了24位彩色的颜色外,额外的8位是储存重叠图层的图形资料(alpha透明度)。
image

0.3.3 图像格式

mipi-rx之软件篇 - fuzidage - 博客园 (cnblogs.com)

mipi-csi软件篇 | Hexo (fuzidage.github.io)

0.3.4 分辨率(Resolution)

mipi-csi软件篇 | Hexo (fuzidage.github.io)

mipi-rx之软件篇 - fuzidage - 博客园 (cnblogs.com)

0.3.5 曝光(Exposure)

曝光就是图像的明暗程度 ,照片太暗称为曝光不足 ,照片太亮称为曝光过度。曝光由光圈、曝光时间、ISO三者共同决定。

光圈:控制进光量。

曝光时间:光到达的时间长度。

ISO:增益,或称为感光度。

0.4 摄像头接口

USB

我们常用的电脑摄像头接口是USB接口,这种摄像头比较常见,需要支持UVC(USB Video Class)协议。

DVP

DVP(Digital Video Port)摄像头数据并口传输协议。

DVP是并口,提供8-bit或10-bit并行传输数据线、HSYNC(Horizontal sync)行同步线、VSYNC(Vertical sync)帧同步线和PCLK(Pixel Clock)时钟同步线。

缺点:接收无需csi2, mipi接收器,硬件设计简单。

缺点:

  1. 接线太多,R(0-7), G(8-16), B(17-23),8位的并口sensor,光数据线就要24根。还要控制线。

  2. 抗干扰能力弱,由于不像csi mipi,sublvds协议串行差分信号,数据传入易发生bit反转,出错,因此走线不能太长。

  3. 速率低,功耗高,无法使用高分辨率

image

mipi-rx之硬件篇 - fuzidage - 博客园 (cnblogs.com)

mipi-csi硬件篇 | Hexo (fuzidage.github.io)

mipi

MIPI 传输只需要clk lane和data lane. 对比MIPI 接口比 DVP 的接口信号线少,由于是低压差分信号,产生的干扰小,抗干扰能力也强。

image

mipi-rx之硬件篇 - fuzidage - 博客园 (cnblogs.com)

mipi-csi硬件篇 | Hexo (fuzidage.github.io)

举个mipi控制器特点:

• 可同時支持 2 路 sensor 輸入(2组D-PHY, 每组5对差分线(1C4D))
• sensor 0 最大支持 4K2K @60fps HDR or @30fps 線性輸入
• sensor 1 最大支持 3M(2304x1296) @60fps HDR or linear 輸入
• 單路最多支持 4-Lane MIPI D-PHY 接口,最大支持 2.5Gbps/Lane
• 單路最多支持 4-Lane sub-LVDS/ HiSPi 接口,最大支持 1.5Gbps/Lane
• 支持 RAW8/ RAW10/ RAW12 數據類型的解析
• 支持 YUV422 8-bit/ YUV422 10-bit 數據類型的解析
• 最多支持 2 幀 WDR,支持多種 WDR 時序
• 支持 sub-LVDS/ HiSPi 模式像素/同步碼大小端配置
• 支持 Lane 數和 Lane 順序可配置
MIPI Rx 的帶寬有兩部分限制: PHY 的接口數據率和內部處理速度。
輸入接口最大支持 2.5Gbps/Lane,內部處理速度最大為 600M*1pixels/s(MAC clk)。

0.5 mipi-rx软硬件篇介绍

mipi-csi硬件篇 | Hexo (fuzidage.github.io)

mipi-csi软件篇 | Hexo (fuzidage.github.io)

mipi-rx之硬件篇 - fuzidage - 博客园 (cnblogs.com)

mipi-rx之软件篇 - fuzidage - 博客园 (cnblogs.com)

1 ov8856 sensor介绍

1.1 spec

image

image

功能:支持帧率控制,flip/mirror功能,裁剪

尺寸性能规格:

最大支持8MP 30fps和720p 90fps

走mipi协议,支持2lane, 4 lane数据传输

控制走i2c协议

10 bit raw RGB数据格式

1.2 框图和引脚描述

image

1.3 连接soc

image

1.4 lane id和P/N swap线序

image

一般硬件设计上,sensor的输出和csi 接受输入的data laneclk lane一一对应,如果不对应,就需要配置csi controllerlane idpn swap线序。

1.5 上电时序

mipi-rx之硬件篇 - fuzidage - 博客园 (cnblogs.com)

mipi-csi硬件篇 | Hexo (fuzidage.github.io)

1.6 初始化序列

soc厂商发邮件给cmos模组厂申请初始化序列(initial Sequence)。比如要配置的的分辨率,mclk,lane线序(1C4D, 1C2D, 1C1D)等等。

1.7 帧率控制

image

可以通过设置VTS的大小来控制fps,当帧间隔越长,fps越低。

1.8 flip mirror

image

配置0x3820和0x3821寄存器就能进行水平或者垂直翻转。

1.9 window crop

image

可以配置上述寄存器控制裁剪的位置和大小。

1.9 曝光增益控制

image

控制源码见曝光增益控制

2 v4l2-utils套件包

2.0 下载移植

V4l-utils - LinuxTVWiki

v4l-utils-1.28.1 (linuxfromscratch.org)

v4l-utils工具是由Linux维护的V4L2开发工具包。

它提供了一套用于配置V4L2子设备属性的V4L2和媒体框架相关工具,测试V4L2设备,并提供开发库,如libv4l2等等。

2.1 media-ctl工具

media-ctlv4l2-utils包中的一个工具,主要用来查看、配置Media Framework的各Entity的信息,如格

式、裁剪、链接使能等,针对/dev/mediaX

V4l2-ctl 工具则是针对/dev/video0,/dev/video1 等 video设备,它在 video 设备上进行 set_fmt

reqbuf(申请buf),qbuf(送buf回队列),dqbuf(从队列取出buf),stream_onstream_off 等一系列操

作。

/sys/class/video4linux/下可以找到v4l2相关的设备节点:

# ls sys/class/video4linux
v4l-subdev0 v4l-subdev2 video1 video3 video5 video7
v4l-subdev1 video0 video2 video4 video6 video8
#cat sys/class/video4linux/video0/dev
81:0

2.1.1 查看媒体拓扑结构

media-ctl -d /dev/media0 -p  # 查看媒体拓扑结构

image

entity70信息中可以看到:

Entity名称是:m00_b_ov13850 4-0010

它是一个V4L2 subdev(Sub-Device) Sensor

它对应的节点是/dev/v4l-subdev3,应用程序(如v4l2-ctl)可以打开它,并进行配置

它仅有一个输出(Source)节点,记为pad0

它的输出格式是[fmt:SBGGR10/4224x3136],其中SBGGR10是一种mbus-code的简称

它的Source pad0 链接到"rockchip-csi2-dphy0"pad0,并且当前的状态是 ENABLED。

2.1.1.0 rk3568的media 拓扑结构

image

2.1.2 找到video设备

拓扑结构中有多个的Entity,一些是sub device,一些是video device。根据名字能找到对应的video设备

$ media-ctl -d /dev/media0 -e "rkisp_selfpath"
/dev/video1
$ media-ctl -d /dev/media0 -e "rkisp_mainpath"
/dev/video0

2.13 修改Entity的format、size

media-ctl -d/dev/media0 --set-v4l2' "m00_b_ov13850 4-0010":0[fmt:SBGGR10//640x480]'

修改sensor 格式为SBGGR10,分辨率为640x480.

同时修改后级isp subdev的参数:

$ media-ctl -d/dev/media0 --set-v4l2 ' "rkisp-isp-subdev":0[fmt:SBGGR10/640x480]'
$ media-ctl -d/dev/media0 --set-v4l2 ' "rkisp-isp-subdev":0[crop: (0,0)/640x480]'
$ media-ctl -d/dev/media0 --set-v4l2 ' "rkisp-isp-subdev":2[crop: (0,0)/640x480]'
$ v4l2-ctl -d/dev/video0 --set-selection=target=crop, top=0, left=0, width=640, height=480

2.2 v4l2-ctl工具

v4l2-ctl使用手册

https://www.mankier.com/1/v4l2-ctl

列出所有设备

# v4l2-ctl --list-devices
v4l2-ctl --list-devices
rkisp-statistics (platform: rkisp):
    /dev/video7
    /dev/video8
rkisp_mainpath (platform:rkisp-vir0):
    /dev/video0
    /dev/video1
    /dev/video2
    /dev/video3
    /dev/video4
    /dev/video5

获取指定设备的所有信息

v4l2-ctl --all --device /dev/video0
v4l2-ctl --all --device /dev/video0
Driver Info:
    Driver name : rkisp_v5
    Card type : rkisp_mainpath
    Bus info : platform:rkisp-vir0
    Driver version : 1.8.0
    Capabilities : 0x84201000
        Video Capture Multiplanar
        Streaming
        Extended Pix Format
        Device Capabilities
Device Caps : 0x04201000
    Video Capture Multiplanar
    Streaming
    Extended Pix Format
....

列出支持的格式和分辨率

v4l2-ctl --list-formats-ext --device /dev/video0
v4l2-ctl --list-formats-ext --device /dev/video1

支持的格式如下:

v4l2-ctl --list-formats-ext --device /dev/video0
ioctl: VIDIOC_ENUM_FMT
Index : 0
Type : Video Capture Multiplanar
Pixel Format: 'UYVY'
Name : UYVY 4:2:2
Size: Stepwise 32x16 - 2112x1568 with step 8/8
Index : 1
Type : Video Capture Multiplanar
Pixel Format: '422P'
Name : Planar YUV 4:2:2
Size: Stepwise 32x16 - 2112x1568 with step 8/8
Index : 2
Type : Video Capture Multiplanar
Pixel Format: 'NV16'
Name : Y/CbCr 4:2:2
Size: Stepwise 32x16 - 2112x1568 with step 8/8
Index : 3
Type : Video Capture Multiplanar
Pixel Format: 'NV61'
Name : Y/CrCb 4:2:2
Size: Stepwise 32x16 - 2112x1568 with step 8/8
Index : 4
Type : Video Capture Multiplanar
Pixel Format: 'YM16'
Name : Planar YUV 4:2:2 (N-C)
Size: Stepwise 32x16 - 2112x1568 with step 8/8
Index : 5
Type : Video Capture Multiplanar
Pixel Format: 'NV21'
Name : Y/CrCb 4:2:0
Size: Stepwise 32x16 - 2112x1568 with step 8/8

2.2.1 抓帧

v4l2-ctl -d /dev/video0 --set-fmt-video=width=800,height=600,pixelformat=NV12 --stream-mmap=3 --stream-to=/sdcard/out.yuv --stream-skip=9 --stream-count=1
m-mmap=3 --stream-to=/sdcard/out.yuv --stream-skip=9 --stream-count=1

产看out.yuv,或者用windows yuv工具

ffplay out.yuv -f rawvideo -pixel_format nv12 -video_size 800x600

2.2.2 显示摄像头参数

rk3568_r:/ # v4l2-ctl -d /dev/video0 -l
v4l2-ctl -d /dev/video0 -l

User Controls
		exposure 0x00980911 (int) : min=4 max=3324 step=1 default=1536 value=1536
Image Source Controls
		vertical_blanking 0x009e0901 (int) : 
			min=192 max=29631 step=1 default=192 value=192
		horizontal_blanking 0x009e0902 (int) : 
			min=576 max=576 step=1 default=576 value=576 flags=read-only
		analogue_gain 0x009e0903 (int) : 
			min=16 max=248 step=1 default=16 value=16
		Image Processing Controls link_frequency 0x009f0901 (intmenu): 
			min=0 max=0 default=0
			value=0 flags=read-only
		pixel_rate 0x009f0902 (int64) : 
			min=0 max=120000000 step=1
			default=120000000 value=120000000 flags=read-only
		test_pattern 0x009f0903 (menu) : min=0 max=4 default=0 value=0

2.2.3 set exposure

exposure值区间为: 4-3324

命令实例:

v4l2-ctl -d /dev/video0 --set-ctrl exposure=3324

2.2.4 模拟增益-analogue_gain

analogue_gain值区间:16-248

命令实例:

v4l2-ctl -d /dev/video0 --set-ctrl analogue_gain=240

2.2.5 开启 test_pattern

image

video0表示sensor, test_pattern等于多少参考sensor datasheet支持多少中pattern

v4l2-ctl -d /dev/video0 --set-ctrl test_pattern=0

3 OV5640 camera驱动分析

由于我手上只有ov5640的datasheet,因此用这颗camera驱动进行分析。camera一般作为/dev/v4l2_subdevi2c_client设备。

源代码位于:linux_5.10\drivers\media\i2c\ov5640.c

image

老规矩从入口开始,当dts和驱动中compatible配对后,调用probe.

3.1 ov5640_probe

3.1.1 ov5640_dev

struct ov5640_dev {
	struct i2c_client *i2c_client;
	struct v4l2_subdev sd;
	struct media_pad pad;
	struct v4l2_fwnode_endpoint ep; /* the parsed DT endpoint info */
	struct clk *xclk; /* system clock to OV5640 */
	u32 xclk_freq;

	struct regulator_bulk_data supplies[OV5640_NUM_SUPPLIES];
	struct gpio_desc *reset_gpio;
	struct gpio_desc *pwdn_gpio;
	bool   upside_down;

	/* lock to protect all members below */
	struct mutex lock;
	int power_count;
	struct v4l2_mbus_framefmt fmt;
	bool pending_fmt_change;

	const struct ov5640_mode_info *current_mode;
	const struct ov5640_mode_info *last_mode;
	enum ov5640_frame_rate current_fr;
	struct v4l2_fract frame_interval;

	struct ov5640_ctrls ctrls;

	u32 prev_sysclk, prev_hts;
	u32 ae_low, ae_high, ae_target;
	bool pending_mode_change;
	bool streaming;
};

image

初始化ov5640_dev结构,填充v4l2_mbus_framefmt,该结构体参考:

4.15.3.4.1. Media Bus Formats — The Linux Kernel documentation

MEDIA_BUS_FMT_UYVY8_2X8格式:

是一种8-bit YUV values downsampled to 4:2:2,yuv422 pack的一种格式,采用uy,uv的编排方式。设置当前模式位30fps OV5640_MODE_VGA_640_480模式。

image

该模式对应的i2c初始化序列:

image

3.1.2 dts资源提取

提取dts节点描述:

image

3.1.2.1 endpoint setting

of_fwnode_handle(pdev->dev.of_node);和dev_fwnode(&client->dev);等价都是把dev转成fwnode_handle

提取endpopint有2个api:
fwnode_graph_get_next_endpoint或者fwnode_graph_get_endpoint_by_id

可以看到这颗sensor驱动适配了3套协议,1.prallel并口, 2. mpi-csi2,3 BT656. 其他协议表示这颗sensor不支持。endpoint的bus_type表示支持的协议类型v4l2_mbus_type:

/**
 * enum v4l2_mbus_type - media bus type
 * @V4L2_MBUS_UNKNOWN:	unknown bus type, no V4L2 mediabus configuration
 * @V4L2_MBUS_PARALLEL:	parallel interface with hsync and vsync
 * @V4L2_MBUS_BT656:	parallel interface with embedded synchronisation, can
 *			also be used for BT.1120
 * @V4L2_MBUS_CSI1:	MIPI CSI-1 serial interface
 * @V4L2_MBUS_CCP2:	CCP2 (Compact Camera Port 2)
 * @V4L2_MBUS_CSI2_DPHY: MIPI CSI-2 serial interface, with D-PHY
 * @V4L2_MBUS_CSI2_CPHY: MIPI CSI-2 serial interface, with C-PHY
 * @V4L2_MBUS_INVALID:	invalid bus type (keep as last)
 */
enum v4l2_mbus_type {
	V4L2_MBUS_UNKNOWN,
	V4L2_MBUS_PARALLEL,
	V4L2_MBUS_BT656,
	V4L2_MBUS_CSI1,
	V4L2_MBUS_CCP2,
	V4L2_MBUS_CSI2_DPHY,
	V4L2_MBUS_CSI2_CPHY,
	V4L2_MBUS_INVALID,
};

获取解析endpoint,然后设置endpoint。

然后dts获取mclk, 复位引脚,power引脚。

3.1.3 v4l2_subdev 异步初始化

image

最后注册v4l2_sub_dev。调用的函数有:

v4l2_i2c_subdev_init(&sensor->sd, client, &ov5640_subdev_ops);
v4l2_async_register_subdev_sensor_common(&sensor->sd);

3.1.3.1 v4l2_async_register_subdev_sensor_common

image

调用v4l2_async_subdev_notifier_register注册异步通知器。

v4l2_async_register_subdev注册子设备。

3.1.4 media_entity和media_pad初始化

同时还初始化media_entitymedia_pad

image

pad.flags设置为:MEDIA_PAD_FL_SOURCE表示pad为source pad表示发送端。还有一种pad是sink pad,表示接收端。

entity.function = MEDIA_ENT_F_CAM_SENSOR;,表示摄像头功能:

image

然后调用:

media_entity_pads_init(&sensor->sd.entity, 1, &sensor->pad);

3.1.5 regulator 初始化

参考:Linux Regulator Framework(1)_概述 (wowotech.net)

image

image

然后获取“电压稳定器”资源。

3.1.5 i2c通信校验chip id

然后获取几个VDD电压:

image

image

还要用i2c校验chip_id是否成功,这时需要执行上电时序。i2c通信才能成功,chip_id读出来才等于0x5640:
image

ov5640_set_power_on函数:

image

image

首先开启mclk,设置reset复位时序。设置pwdn_gpio

power on 上电时序见:

mipi-rx之硬件篇 - fuzidage - 博客园 (cnblogs.com)

mipi-csi硬件篇 | Hexo (fuzidage.github.io)

最后进行i2c传输,读出chip_id。i2c传输原理过程见:

s3c2440裸机-I2c编程-1.i2c协议 - fuzidage - 博客园 (cnblogs.com) s3c2440裸机编程-I2C | Hexo (fuzidage.github.io)

Linux I2C子系统驱动 - fuzidage - 博客园 (cnblogs.com) s3c2440裸机编程-I2C | Hexo (fuzidage.github.io)

3.1.6 设置v4l2_ctrl_ops和v4l2_ctrl_handler

image

设置v4l2_ctrl_opsov5640_ctrl_ops。初始化ov5640_dev的sensor属性参数,这些后面具体的ioctl会具体展开:

struct ov5640_ctrls {
	struct v4l2_ctrl_handler handler;
	struct v4l2_ctrl *pixel_rate;
	struct {
		struct v4l2_ctrl *auto_exp;
		struct v4l2_ctrl *exposure;
	};
	struct {
		struct v4l2_ctrl *auto_wb;
		struct v4l2_ctrl *blue_balance;
		struct v4l2_ctrl *red_balance;
	};
	struct {
		struct v4l2_ctrl *auto_gain;
		struct v4l2_ctrl *gain;
	};
	struct v4l2_ctrl *brightness;
	struct v4l2_ctrl *light_freq;
	struct v4l2_ctrl *saturation;
	struct v4l2_ctrl *contrast;
	struct v4l2_ctrl *hue;
	struct v4l2_ctrl *test_pattern;
	struct v4l2_ctrl *hflip;
	struct v4l2_ctrl *vflip;
};

3.1.6.0 引入V4L2 控件

常用:

  1. 如何添加控件?
  2. 如何设置控件的值?(即 s_ctrl

偶尔:

  1. 如何获取控件的值?(即 g_volatile_ctrl

  2. 如何验证用户建议的控制值?(即 try_ctrl

3.1.6.0.1 控件操作函数v4l2_ctrl_ops
struct v4l2_ctrl_ops {
	int (*g_volatile_ctrl)(struct v4l2_ctrl *ctrl);
	int (*try_ctrl)(struct v4l2_ctrl *ctrl);
	int (*s_ctrl)(struct v4l2_ctrl *ctrl);
};

通常是需要实现s_ctrl:

static int foo_s_ctrl(struct v4l2_ctrl *ctrl){
        struct foo *state = container_of(ctrl->handler, struct foo, ctrl_handler);
        switch (ctrl->id) {
        case V4L2_CID_BRIGHTNESS:
                write_reg(0x123, ctrl->val);
                break;
        case V4L2_CID_CONTRAST:
                write_reg(0x456, ctrl->val);
                break;
        }
        return 0;
}

1.12. V4L2 Controls — The Linux Kernel documentation

3.1.6.1 v4l2_ctrl和v4l2_ctrl_handler的关系

3.1.6.1.1struct v4l2_ctrl
s64 min 控件的最小值。
s64 max 控件的最大值。
u64 step 控件的步长值
s64 def 控件的默认值。
3.1.6.1.2 v4l2_ctrl_handler

v4l2_ctrl_handler用来管理所有的v4l2_ctrl

v4l2_ctrl_handler:控制器控件管理者。

v4l2_ctrl:具体的控件。

ov5640_ctrls里面有一个v4l2_ctrl_handler和大量的v4l2_ctrl控件。

对于 V4L2 驱动程序,请执行以下操作:

struct foo_dev {
        ...
        struct v4l2_device v4l2_dev;
        ...
        struct v4l2_ctrl_handler ctrl_handler;
        ...
};

foo->v4l2_dev.ctrl_handler = &foo->ctrl_handler;

对于子设备驱动程序,请执行以下操作:

struct foo_dev {
        ...
        struct v4l2_subdev sd;
        ...
        struct v4l2_ctrl_handler ctrl_handler;
        ...
};

foo->sd.ctrl_handler = &foo->ctrl_handler;
3.1.6.1.3 控件操作API
初始化和删除控件控制器
v4l2_ctrl_handler_init(&foo->ctrl_handler, nr_of_controls);
v4l2_ctrl_handler_free(&foo->ctrl_handler);
添加控件
struct v4l2_ctrl *v4l2_ctrl_new_std(struct v4l2_ctrl_handler *hdl,
                const struct v4l2_ctrl_ops *ops,
                u32 id, s32 min, s32 max, u32 step, s32 def);//非菜单控件
/*参数:
struct v4l2_ctrl_handler * hdl: 控制处理程序。
const struct v4l2_ctrl_ops * ops: 控制行动。
u32 id :控件 ID。
u8 max :控件的最大值。
u64 mask: 控件的菜单控件的跳过掩码。
u8 def :控件的默认值。
*/

菜单和整数菜单控件是通过调用 v4l2_ctrl_new_std_menu 添加的:

struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl,
                const struct v4l2_ctrl_ops *ops,
                u32 id, s32 max, s32 skip_mask, s32 def);
truct v4l2_ctrl *v4l2_ctrl_new_std_menu_items(
                struct v4l2_ctrl_handler *hdl,
                const struct v4l2_ctrl_ops *ops, u32 id, s32 max,
                s32 skip_mask, s32 def, const char * const *qmenu);//菜单控件

具有驱动程序特定菜单的整数菜单控件可以通过调用 v4l2_ctrl_new_int_menu

struct v4l2_ctrl *v4l2_ctrl_new_int_menu(struct v4l2_ctrl_handler *hdl,
                const struct v4l2_ctrl_ops *ops,
                u32 id, s32 max, s32 def, const s64 *qmenu_int);//整数菜单
控件集群

默认情况下,所有控件都独立于其他控件。但在 复杂方案,你可以获取从一个控件到另一个控件的依赖关系。在这种情况下,您需要对它们进行 'cluster' :

例如:

struct foo {
        struct v4l2_ctrl_handler ctrl_handler;
#define AUDIO_CL_VOLUME (0)
#define AUDIO_CL_MUTE   (1)
        struct v4l2_ctrl *audio_cluster[2];
        ...
};

state->audio_cluster[AUDIO_CL_VOLUME] =
        v4l2_ctrl_new_std(&state->ctrl_handler, ...);
state->audio_cluster[AUDIO_CL_MUTE] =
        v4l2_ctrl_new_std(&state->ctrl_handler, ...);

v4l2_ctrl_cluster(ARRAY_SIZE(state->audio_cluster), state->audio_cluster);//把AUDIO_CL_VOLUME和AUDIO_CL_MUTE控件集群
使用 Auto Clusters 处理 autogain/gain 类型的控件

autogain/gain、autoexposure/exposure、 自动白平衡/红平衡/蓝平衡。

要将控件标记为 volatile,您必须设置 V4L2_CTRL_FLAG_VOLATILE

ctrl = v4l2_ctrl_new_std(&sd->ctrl_handler, ...);
if (ctrl)
        ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;

void v4l2_ctrl_auto_cluster(unsigned ncontrols, struct v4l2_ctrl **controls,
			    u8 manual_val, bool set_volatile);
/*参数 
unsigned int ncontrols :此群集中的控件数。
struct v4l2_ctrl ** controls
u8 manual_val: 集群中第一个控件的值等于 手动设置。
bool set_volatile:如果为 true,则除第一个 auto 控件之外的所有控件都将不稳定。
*/
查找控件
struct v4l2_ctrl *volume;
volume = v4l2_ctrl_find(sd->ctrl_handler, V4L2_CID_AUDIO_VOLUME);

3.1.7 设置控件管理

所有控件参数配置好后添加如下,把控件管理器添加到v4l2_subdev.

sensor->sd.ctrl_handler = hdl;

3.2 v4l2_subdev_ops实例

v4l2_subdev_ops是如何被调用的:

  1. 直接调用 ops 函数:

    err = sd->ops->core->s_power(sd, &norm);//例如调用core的s_power函数
    
  2. 使用v4l2_subdev_call

     v4l2_subdev_call(sd, core, s_power, &norm);
    
    //也可以调用所有子设备或子设备的子集:
    v4l2_device_call_all(v4l2_dev, 0, core, g_std, &norm);
    //任何不支持此操作的 subdev 都会被跳过,错误结果是 忽视。如果您想检查错误,请使用以下内容:
    err = v4l2_device_call_until_err(v4l2_dev, 0, core, g_std, &norm);
    

image

更多的做法是开ioctl出来给用户态去调用:

参考[v4l2架构.md的3.5.1 v4l2_subdev_ops]
https://www.cnblogs.com/fuzidage/p/18462450#v4l2_subdev_ops

3.2.1 ov5640_core_ops

struct v4l2_subdev_core_ops {
    // IO引脚复用配置
    int (*s_io_pin_config)(struct v4l2_subdev *sd, size_t n,
    struct v4l2_subdev_io_pin_config *pincfg);
    // 初始化从设备的某些寄存器,使其恢复默认
    int (*init)(struct v4l2_subdev *sd, u32 val);
    // 加载固件
    int (*load_fw)(struct v4l2_subdev *sd);
    // 复位
    int (*reset)(struct v4l2_subdev *sd, u32 val);
    // 设置GPIO引脚输出值
    int (*s_gpio)(struct v4l2_subdev *sd, u32 val);
    // 设置从设备的电源状态,0-省电模式,1-正常操作模式
    int (*s_power)(struct v4l2_subdev *sd, int on);
    // 中断函数,被主设备的中断函数调用
    int (*interrupt_service_routine)(struct v4l2_subdev *sd,
    u32 status, bool *handled);
......
};

3.2.1.1 ov5640_set_power

image

1.ov5640_set_power_on就是开启sensor mclk,然后拉起regulator supplies,也就是sensor的的3个电压:

	"DOVDD", /* Digital I/O (1.8V) supply */
	"AVDD",  /* Analog (2.8V) supply */
	"DVDD",  /* Digital Core (1.5V) supply */

然后进行上电时序power on。前面3.1.5 介绍过了上电时序见3.1.5 i2c校验chipid.

2.ov5640_restore_mode就是设置初始化序列,设置成last capture模式.设置图像大小和像素模式。

image

设置hact,vact,htot,vtol尺寸信息。

image

设置像素格式:

image

3.初始化序列通过i2c写好后,初始化成不同接口类型,支持DVPmipi-csi接口类型。dts要根据硬件走线确认好是用的mipi还是DVP接口。

image

同理如果要做休眠低功耗,就会调用ov5640_set_power_off:

image

剩下的几个成员函数都是用的v4l2自带的内置函数:

3.2.1.2 v4l2_ctrl_subdev_log_status

记录 subdev 的控件处理程序拥有的所有控件.

3.2.1.3 v4l2_ctrl_subdev_subscribe_event

订阅控制事件的函数。

3.2.2 ov5640_video_ops

3.2.2.1 g_frame_interval

对应cmd VIDIOC_SUBDEV_G_FRAME_INTERVAL,获取帧间隔。

static int ov5640_g_frame_interval(struct v4l2_subdev *sd,
				   struct v4l2_subdev_frame_interval *fi)
{
	struct ov5640_dev *sensor = to_ov5640_dev(sd);
	mutex_lock(&sensor->lock);
	fi->interval = sensor->frame_interval;
	mutex_unlock(&sensor->lock);
	return 0;
}
  • v4l2_subdev_frame_interval
__u32 pad 媒体控制器 API 报告的填充码。
结构体 v4l2_fract interval 连续视频帧之间的时间段(以秒为单位)。
__u32 reserved[9] 保留供将来扩展使用。应用程序和驱动程序必须设置 数组为零。

v4l2_fract:

__u32 numerator 分子
__u32 denominator 分母

3.2.2.2 s_frame_interval

同理,对应cmd VIDIOC_SUBDEV_S_FRAME_INTERVAL, 设置子开发板上的帧间隔。也就是等效设置fps。

image

根据sensor->current_mode当前模式提取帧率和帧间隔fi->interval

调用__v4l2_ctrl_s_ctrl_int64通过控件安全地设置控件的新值sensor->ctrls.pixel_rate, 允许从 v4l2_ctrl_ops 函数中使用它。从而设置好帧间隔frame_interval和fps。

ov5640_calc_pixel_rate是根据帧率*img size=pixel_rate

image

回到前面添加的控件:

image

进入到控件处理函数:

image

但是源码中实际上并没有对添加的V4L2_CID_PIXEL_RATE控件id进行处理:

image

设置帧间隔控制帧率其实很简单,只需要控制sensor的VBLANK寄存器即可。VBLANK行数增加,帧间隔增大,fps就会减少。

3.2.2.3 s_stream

image

这里最底下的通过I2c进行sensor 寄存器的配置写入就不展开介绍,可以去查询ov5640 datasheet或者询问vendor支持。配置好模式帧属性后就调用ov5640_set_stream_mipi或者ov5640_set_stream_dvp使能sensor。

3.2.3 ov5640_pad_ops

3.2.3.1 enum_mbus_code

对应cmd VIDIOC_SUBDEV_ENUM_MBUS_CODE.枚举媒体总线格式

结构体v4l2_subdev_mbus_code_enum:

__u32 pad 媒体控制器 API 报告的填充码。
__u32 index 枚举中的格式编号,由应用程序设置。
__u32 code 媒体总线格式代码,如 Media Bus 格式中所定义。
__u32 which 要从枚举的媒体总线格式代码,从 enum v4l2_subdev_format_whence
__u32 reserved[8] 保留供将来扩展使用。应用程序和驱动程序必须设置 数组为零。

image

3.2.3.2 get_fmt和set_fmt

对应cmd VIDIOC_SUBDEV_G_FMT VIDIOC_SUBDEV_S_FMT. 获取设置v4l2_mbus_framefmt格式(包括图像尺寸,格式)。

v4l2_mbus_framefmt:

__u32 width 图像宽度,以像素为单位。
__u32 height 图像高度(以像素为单位)。
__u32 code 格式化代码,从 enum v4l2_mbus_pixelcode
__u32 field Field order,来自 enum v4l2_field。有关详细信息,请参阅 Field Order
__u32 colorspace 图像色彩空间,来自枚举v4l2_colorspace。有关详细信息,请参阅色彩空间
enum v4l2_ycbcr_encoding ycbcr_enc 此信息是对 的补充,必须由 捕获流的驱动程序和应用程序的输出驱动程序 流中,请参阅 色彩空间.colorspace
enum v4l2_quantization quantization 此信息是对 的补充,必须由 捕获流的驱动程序和应用程序的输出驱动程序 流中,请参阅 色彩空间.colorspace
enum v4l2_xfer_func xfer_func 此信息是对 的补充,必须由 捕获流的驱动程序和应用程序的输出驱动程序 流中,请参阅 色彩空间.colorspace
__u16 reserved[11] 保留供将来扩展使用。应用程序和驱动程序必须设置 数组为零。

image

ov5640_try_fmt_internal是判断ioctl下来的v4l2_mbus_framefmt是否支持。支持就赋值给sensor。

3.2.3.3 enum_frame_size

对应cmd VIDIOC_SUBDEV_ENUM_FRAME_SIZE. 获取帧尺寸。

v4l2_subdev_frame_size_enum:

/**
 * struct v4l2_subdev_frame_size_enum - Media bus format enumeration
 * @pad: pad number, as reported by the media API
 * @index: format index during enumeration
 * @code: format code (MEDIA_BUS_FMT_ definitions)
 * @which: format type (from enum v4l2_subdev_format_whence)
 */
struct v4l2_subdev_frame_size_enum {
	__u32 index;
	__u32 pad;
	__u32 code;
	__u32 min_width;
	__u32 max_width;
	__u32 min_height;
	__u32 max_height;
	__u32 which;
	__u32 reserved[8];
};

image

3.2.3.3 enum_frame_interval

对应cmd VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL.枚举帧间隔帧率。

image

3.3 OV5640 控件功能分析

3.3.1 v4l2_ctrl_op实例ov5640_ctrl_ops

回到控件添加的地方:

image

3.3.1.1 V4L2_CID_AUTOGAIN-增益控件设置

image

image

对寄存器0x350a,0x350b寄存器进行写入gain值,这里ov5640_write_reg16是一次性写入16bit数据,也就是同时会对地址0x350a,0x350b进行写入,刚好对应datasheet的增益寄存器。可以看到这里对0x3507是写8bit数据,对0x3508只写入2bit数据。

3.3.1.2 V4L2_CID_EXPOSURE_AUTO-曝光控件设置

image

0x3503寄存器,bit[0]控制是否自动曝光, bit[1]控制是否自动增益。ov5640_mod_reg是修改寄存器的:

static int ov5640_mod_reg(struct ov5640_dev *sensor, u16 reg, u8 mask, u8 val){
	u8 readval;
	int ret;
	ret = ov5640_read_reg(sensor, reg, &readval);
	if (ret)
		return ret;
	readval &= ~mask;//例如mask为BIT(0),那么readval就是bit(0) clear,其他位保持
	val &= mask;
	val |= readval;
	return ov5640_write_reg(sensor, reg, val);
}

0x3500到0x3502寄存器,用来设置曝光值。ov5640_get_exposure读取曝光值。

3.3.1.3 V4L2_CID_AUTO_WHITE_BALANCE-自动白平衡

image

image

加入awb为true表示自动开启白平衡。寄存器0x3406。否则手动写入red gain,blue gain数据。

3.3.1.4 V4L2_CID_HUE-色调控件

image

image

5580寄存器是一个特殊数字效果(SDE)功能包括色调/饱和度控制、亮度、对比度等。SDE还支持 
负片、黑白、棕褐色、绿色、蓝色、红色、曝光和其他图像效果。

image

value等于1表示使能色调调节。可以看到对应5580寄存器的bit[0]

3.3.1.5 V4L2_CID_CONTRAST-对比度

image

同理,对比度开关也是5580寄存器的bit[2],对对比度进行设置。

3.3.1.6 V4L2_CID_SATURATION-饱和度

image

同理,饱和度开关是5580寄存器的bit[1],对饱和度进行设置。

3.3.1.7 V4L2_CID_TEST_PATTERN-固定图案

image

image

设置固图案。OV5640_TEST_ENABLEBIT[7],对应使能。可以看到支持4种pattern

00: Color bar
01: Random data
10: Square data
11: Black imag

3.3.1.8 V4L2_CID_POWER_LINE_FREQUENCY-自适应灯光频率

一般我们家用灯光用的是50HZ,即闪烁频率是1秒50下。所以OV5640可以检测光闪烁频率,既能支持50hz光源,也能支持60hz的光源。像有些sensor可能不知持此功能,因此为什么我们一般建议设置sensor 帧率为25的整数倍,否则可能再做AEC或者AGC会遇到flicker问题。

OV5640可以检测光闪烁频率。启用此功能后,传感器可以检测到光线 
频率并选择相应的带通滤波器值。要消除条带,应打开条带滤波器 
并且带状滤波器值应设置为适当的值.

image

image

3.3.1.9 V4L2_CID_HFLIP/V4L2_CID_VFLIP-水平竖直翻转

回到1.8 filp_mirror的地方:

image

posted on 2024-10-13 17:20  fuzidage  阅读(589)  评论(0编辑  收藏  举报