0 前言-camera sensor基础
0.1 摄像头组成
一个常见的监控用的摄像头包括:外壳、感光芯片电路、镜头座、镜头、红外灯板。
摄像头模组(CameraCompact Module),简称CCM。包含四大件: 镜头(lens)、传感器(sensor)、软板(FPC)、图像处理芯片(DSP)。
0.2 摄像头原理
- 通过镜头(LENS)生成的光学图像投射到图像传感器(Sensor)表面上
- 感光传感器把光信号转为电信号,经过A/D(模数转换)转换后变为数字图像信号
- 再送到数字信号处理芯片(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:
颜色空间-YUV:
yuv444,yuv422,yuv420格式的合成,yuv又会分为planned和packed格式区分。
细节详见: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透明度)。
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接收器,硬件设计简单。
缺点:
-
接线太多,R(0-7), G(8-16), B(17-23),8位的并口sensor,光数据线就要24根。还要控制线。
-
抗干扰能力弱,由于不像csi mipi,sublvds协议串行差分信号,数据传入易发生bit反转,出错,因此走线不能太长。
-
速率低,功耗高,无法使用高分辨率
mipi-rx之硬件篇 - fuzidage - 博客园 (cnblogs.com)
mipi-csi硬件篇 | Hexo (fuzidage.github.io)
mipi
MIPI 传输只需要clk lane和data lane. 对比MIPI 接口比 DVP 的接口信号线少,由于是低压差分信号,产生的干扰小,抗干扰能力也强。
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
功能:支持帧率控制,flip/mirror功能,裁剪
尺寸性能规格:
最大支持8MP 30fps和720p 90fps
走mipi协议,支持2lane, 4 lane数据传输
控制走i2c协议
10 bit raw RGB数据格式
1.2 框图和引脚描述
1.3 连接soc
1.4 lane id和P/N swap线序
一般硬件设计上,sensor的输出和csi 接受输入的data lane
和clk lane
一一对应,如果不对应,就需要配置csi controller
的lane id
和pn 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 帧率控制
可以通过设置VTS的大小来控制fps,当帧间隔越长,fps越低。
1.8 flip mirror
配置0x3820和0x3821寄存器就能进行水平或者垂直翻转。
1.9 window crop
可以配置上述寄存器控制裁剪的位置和大小。
1.9 曝光增益控制
控制源码见曝光增益控制
2 v4l2-utils套件包
2.0 下载移植
v4l-utils-1.28.1 (linuxfromscratch.org)
v4l-utils工具是由Linux维护的V4L2开发工具包。
它提供了一套用于配置V4L2子设备属性的V4L2和媒体框架相关工具,测试V4L2设备,并提供开发库,如libv4l2
等等。
2.1 media-ctl工具
media-ctl
是v4l2-utils
包中的一个工具,主要用来查看、配置Media Framework
的各Entity的信息,如格
式、裁剪、链接使能等,针对/dev/mediaX
。
V4l2-ctl
工具则是针对/dev/video0,/dev/video1
等 video设备,它在 video 设备上进行 set_fmt
,
reqbuf
(申请buf),qbuf
(送buf回队列),dqbuf
(从队列取出buf),stream_on
,stream_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 # 查看媒体拓扑结构
从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 拓扑结构
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
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_subdev
和i2c_client
设备。
源代码位于:linux_5.10\drivers\media\i2c\ov5640.c
老规矩从入口开始,当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;
};
初始化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
模式。
该模式对应的i2c初始化序列:
3.1.2 dts资源提取
提取dts节点描述:
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 异步初始化
最后注册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
调用v4l2_async_subdev_notifier_register
注册异步通知器。
v4l2_async_register_subdev
注册子设备。
3.1.4 media_entity和media_pad初始化
同时还初始化media_entity
和media_pad
:
pad.flags
设置为:MEDIA_PAD_FL_SOURCE
表示pad为source pad表示发送端。还有一种pad是sink pad,表示接收端。
entity.function = MEDIA_ENT_F_CAM_SENSOR;
,表示摄像头功能:
然后调用:
media_entity_pads_init(&sensor->sd.entity, 1, &sensor->pad);
3.1.5 regulator 初始化
参考:Linux Regulator Framework(1)_概述 (wowotech.net)
然后获取“电压稳定器”
资源。
3.1.5 i2c通信校验chip id
然后获取几个VDD电压:
还要用i2c校验chip_id是否成功,这时需要执行上电时序。i2c通信才能成功,chip_id读出来才等于0x5640:
ov5640_set_power_on函数:
首先开启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
设置v4l2_ctrl_ops
为ov5640_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 控件
常用:
- 如何添加控件?
- 如何设置控件的值?(即
s_ctrl
)
偶尔:
-
如何获取控件的值?(即
g_volatile_ctrl
) -
如何验证用户建议的控制值?(即
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
是如何被调用的:
-
直接调用 ops 函数:
err = sd->ops->core->s_power(sd, &norm);//例如调用core的s_power函数
-
使用
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);
更多的做法是开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
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
模式.设置图像大小和像素模式。
设置hact,vact,htot,vtol
尺寸信息。
设置像素格式:
3.初始化序列通过i2c写好后,初始化成不同接口类型,支持DVP
和mipi-csi
接口类型。dts要根据硬件走线确认好是用的mipi还是DVP接口。
同理如果要做休眠低功耗,就会调用ov5640_set_power_off
:
剩下的几个成员函数都是用的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。
根据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
:
回到前面添加的控件:
进入到控件处理函数:
但是源码中实际上并没有对添加的V4L2_CID_PIXEL_RATE
控件id进行处理:
设置帧间隔控制帧率其实很简单,只需要控制sensor的VBLANK寄存器即可。VBLANK行数增加,帧间隔增大,fps就会减少。
3.2.2.3 s_stream
这里最底下的通过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] |
保留供将来扩展使用。应用程序和驱动程序必须设置 数组为零。 |
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] |
保留供将来扩展使用。应用程序和驱动程序必须设置 数组为零。 |
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];
};
3.2.3.3 enum_frame_interval
对应cmd VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL
.枚举帧间隔帧率。
3.3 OV5640 控件功能分析
3.3.1 v4l2_ctrl_op实例ov5640_ctrl_ops
回到控件添加的地方:
3.3.1.1 V4L2_CID_AUTOGAIN-增益控件设置
对寄存器0x350a,0x350b
寄存器进行写入gain值,这里ov5640_write_reg16
是一次性写入16bit数据,也就是同时会对地址0x350a,0x350b
进行写入,刚好对应datasheet的增益寄存器。可以看到这里对0x3507是写8bit数据,对0x3508只写入2bit数据。
3.3.1.2 V4L2_CID_EXPOSURE_AUTO-曝光控件设置
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-自动白平衡
加入awb为true表示自动开启白平衡。寄存器0x3406。否则手动写入red gain,blue gain
数据。
3.3.1.4 V4L2_CID_HUE-色调控件
5580寄存器是一个特殊数字效果(SDE)功能包括色调/饱和度控制、亮度、对比度等。SDE还支持
负片、黑白、棕褐色、绿色、蓝色、红色、曝光和其他图像效果。
value等于1表示使能色调调节。可以看到对应5580寄存器的bit[0]
。
3.3.1.5 V4L2_CID_CONTRAST-对比度
同理,对比度开关也是5580寄存器的bit[2]
,对对比度进行设置。
3.3.1.6 V4L2_CID_SATURATION-饱和度
同理,饱和度开关是5580寄存器的bit[1]
,对饱和度进行设置。
3.3.1.7 V4L2_CID_TEST_PATTERN-固定图案
设置固图案。OV5640_TEST_ENABLE
是BIT[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可以检测光闪烁频率。启用此功能后,传感器可以检测到光线
频率并选择相应的带通滤波器值。要消除条带,应打开条带滤波器
并且带状滤波器值应设置为适当的值.
3.3.1.9 V4L2_CID_HFLIP/V4L2_CID_VFLIP-水平竖直翻转
回到1.8 filp_mirror的地方: