一,内容概述
本章研究ov13850的驱动,借此了解sensor的相关配置,以及跟isp联动需要的吞吐数据:isp的帧率受sensor clk和分辨率影响;sensor的部分配置需要isp吞吐3a的计算结果比如gain值配置。所以研究sensor的特性有助于了解isp的整体流程以及3a的联动性。
- 品牌:Omnivision
- 型号:CMK-OV13850
- 接口:MIPI
- 像素:1320W
OV13850彩色图像传感器是一款低电压、高性能1/3.06英寸1320万像素CMOS图像传感器,使用OmniBSI+?技术提供了单-1320万像素(4224×3136)摄像头的功能。通过串行摄像头控制总线(SCCB)接口的控制,它提供了全帧、下采样、开窗的10位MIPI图像。
OV13850拥有一个能够在10位1320万像素分辨率下以每秒24帧(fps)的速度运行的图像阵列,用户可以完全控制图像质量、格式和输出数据传输。所有需要的图像处理功能,包括曝光控制、白平衡、缺陷像素消除等,都可以通过SCCB接口进行编程
此外,OmniBSI图像传感器使用专有的传感器技术,通过减少或消除固定图案噪声、污迹等常见的图像污染光源来提高图像质量,从而产生干净、完全稳定的彩色图像。
为了提供定制信息,OV13850包括一个单编程(OPT)存储器。OV13850拥有最多4车道的MIPI接口。
OV13850适用于低功耗相机模块。
ov13850_set_ctrl能配置如曝光模式(Exposure Type),曝光值(Exposure),增益(Gain),白平衡(WHITE_BALANCE),亮度(BRIGHTNESS), 饱和度(SATURATION),对比度(CONTRAST)等信息。可以通过VIDIOC_G_CTRL得到当前值。
二,关键信息描述
理解sensor配置的具体含义对理解整体逻辑尤为重要
Update max exposure while meeting expected vblanking,更新最大曝光,同时满足预期的vblanning
frame_offset最小的dummy_line,是指一帧曝光结束到下次准备好重新开始的时间
最大曝光时间 = VTS - frame_offset,最小曝光时间不是frame_offset,而是datasheet中定义的一个最小曝光行
pclk:控制像素输出的时钟(单位MHz),即pixel采样时钟,一个clk采集一个像素点。表示每个单位时间(1s)内采集的pixel数量。
H_Blank:Horizontal Blanking,行消隐或水平消隐,两行之间的间隔。
V_Blank:Veritical Blanking,场消隐或垂直消隐,从上一帧到下一帧的间隔。
dummy_line:虚拟行,用来填充VTS,在V_Blank中
line_length:HTS行长,包含H_Blank和有效列
frame_length: VTS帧长,包含dummy_line和有效行
frame_offset:最小dummy_line行数,最大曝光行 = VTS - frame_offset
line_time:曝光一行的时间
exposure_time:曝光时间,指曝光一帧的时间,单位用秒、毫秒表示。
integration_time:积分时间,也成为曝光行,指这一帧曝光了多少行。
SHR0:shutter快门释放的时机,可以改变曝光时长
readout time:数据读出时间,从传感器的寄存器中读出数据并传送出去(Readout过程)
Frame Period:一帧图像的时间,Exposure Time < Frame Period <= Exposure Time +Readout Time
有一个 数值显示 叫 “Bits Per Sample”也是一个 BPS,但这个指的其实 就是 Bit depth
mipi D-PHY 的时钟是 DDR 的,什么是 DDR 呢,就是双沿采样,上升沿和下降沿都对数据进行采样,这样的话,数据传输就又快了一倍
pixel rate = link frequency * 2 * lanes / BITS_PER_SAMPLE
OV13850_PIXEL_RATE= (OV13850_LINK_FREQ_300MHZ * 2 * 2 / 10) =120MHZ
三,驱动代码研究
1,分辨率的设置过程
user层通过以下命令从sensor中取一帧数据,通过命令研究sensor的输入参数
v4l2-ctl -d /dev/video0 \
--set-fmt-video=width=720,height=480,pixelformat=NV12 \
--stream-mmap=3 \
--stream-skip=3 \
--stream-to=/tmp/cif.out \
--stream-count=1 \
--stream-poll
# -d:指定摄像头的设备节点
# --set-fmt-video:指定了宽高及pxielformat(用FourCC表示)。NV12即用FourCC表示的pixelformat;
# --Pixelformat:指定文件格式,NV12;
# --stream-mmap:指定 buffer 的类型为 mmap;
# --stream-skip:指定丢弃(不保存到文件)前3帧;
# --stream-to:指定帧数保存的文件路径;
# --stream-count:指定抓取的帧数,不包括--stream-skip丢弃的数量;
# --stream-poll:v4l2-ctl 采用异步 IO。
user层将sensor/mipi/isp当成一个整体,并使用/dev/video0进行交互,由isp1驱动负责传递参数,下图分别向pad传递了set_fmt函数调用和set_selection函数调用,结合前面章节 isp驱动与sensor驱动和mipi驱动的绑定关系研究-CSDN博客 介绍的sensor、mipi、isp绑定关系了解到isp的set_fmt调用mipi的set_fmt,mipi的set_fmt调用sensor的set_fmt将数据传递到ov13850,。
sensor驱动通过find_best_fit得到最佳支持分辨率为2112*1568,然后sensor输出2112*1568分辨率,isp通过set_selection配置从2112*1568裁剪出(0,0,720,480)分辨率的图像通过v4l2上传到user层完成一帧的输出。
static const struct ov13850_mode supported_modes[] = {
/*
* Xclk 24Mhz
* max_framerate 30fps
* mipi_datarate per lane 600Mbps
*/
{
.width = 2112,
.height = 1568,
.max_fps = {
.numerator = 10000,
.denominator = 300000,
},
.exp_def = 0x0600,
.hts_def = 0x12c0,
.vts_def = 0x0680,
.reg_list = ov13850_2112x1568_regs,
},
/*
* Xclk 24Mhz
* max_framerate 7fps
* mipi_datarate per lane 600Mbps
*/
{
.width = 4224,
.height = 3136,
.max_fps = {
.numerator = 20000,
.denominator = 150000,
},
.exp_def = 0x0600,
.hts_def = 0x12c0,
.vts_def = 0x0d00,
.reg_list = ov13850_4224x3136_regs,
},
};
2,sensor的参数设置
sensor驱动中的 exp_def 、 hts_def 、 vts_def ,从datasheet中查找到对应的寄存器配置即可
行消隐(HBlank)与场消隐(VBlank),HBlank是指当行扫描到最右端时需要快速返回到下一行的最左端的过程。而VBlank则是指扫描完一帧,准备开始扫描下一帧,扫描线从右下角返回到左上角的过程。
struct ov13850_mode {
u32 width;/**有效图像宽度**/
u32 height;/**有效图像高度**/
struct v4l2_fract max_fps;/**图像fps**/
u32 hts_def;/**默认hts,为有效图像宽度+hblank**/
u32 vts_def;/**默认vts,为有效图像高度+vblank**/
u32 exp_def;/**默认曝光时间**/
const struct regval *reg_list;/**寄存器列表**/
};
ov13850_set_ctrl
set V4L2_CID_VBLANK:
frame_offset最小的dummy_line,是指一帧曝光结束到下次准备好重新开始的时间
最大曝光时间 = VTS - frame_offset,最小曝光时间不是frame_offset,而是datasheet中定义的一个最小曝光行,此处frame_offset=4
max = ov13850->cur_mode->height + ctrl->val - 4;
__v4l2_ctrl_modify_range(ov13850->exposure,
ov13850->exposure->minimum, max,
ov13850->exposure->step,
ov13850->exposure->default_value);
垂直消隐。每帧之后的空闲时间,在此期间不产生图像数据。垂直消隐的单位是直线。每一行都有图像宽度的长度加上在同一子设备中,由V4L2_CID_PIXEL_RATE速率控制定义的像素速率下的水平消隐
ret = ov13850_write_reg(ov13850->client,
OV13850_REG_VTS,
OV13850_REG_VALUE_16BIT,
ctrl->val + ov13850->cur_mode->height);
set V4L2_CID_EXPOSURE:
确定相机sensor的曝光时间。曝光时间受帧间隔的限制。驱动会将这些值解释为100µs单位,其中值1代表1/10000秒,10000代表1秒,100000代表10秒。这里指行曝光
sensor的输入时钟决定每个像素点的曝光时长
sensor 水平像素点越多,HTS越大,曝光一行的时间line_time越大
一帧的曝光行数,也称为interation time积分时间,单位为行(H)
exposure_time 是曝光一帧的时间 = line_time * interation time,单位为秒或毫秒
曝光亮度与积分时间有关,通常调整曝光指的就是调整积分时间
ret = ov13850_write_reg(ov13850->client,
OV13850_REG_EXPOSURE,
OV13850_REG_VALUE_24BIT,
ctrl->val << 4);
set V4L2_CID_ANALOGUE_GAIN:
模拟增益是影响像素矩阵中所有颜色分量的增益。增益操作在A/D转换之前在模拟域中执行
ret = ov13850_write_reg(ov13850->client,
OV13850_REG_GAIN_H,
OV13850_REG_VALUE_08BIT,
(ctrl->val >> OV13850_GAIN_H_SHIFT) &
OV13850_GAIN_H_MASK);
ret |= ov13850_write_reg(ov13850->client,
OV13850_REG_GAIN_L,
OV13850_REG_VALUE_08BIT,
ctrl->val & OV13850_GAIN_L_MASK);
3,camera启动
camera 状态机跳转,
CAMERA_MODULE_POWER_OFF: camera 掉电状态
CAMERA_MODULE_HW_STANDBY: camera 上电状态
CAMERA_MODULE_SW_STANDBY: cameara PD\RESET 设置完成时的状态
CAMERA_MODULE_STREAMING: camera 正常 run 状态
在这 4 个状态跳转过程中需要一定延时等待
camera启动需要时钟支持
clk_set_rate(ov13850->xvclk, OV13850_XVCLK_FREQ)
clk_prepare_enable(ov13850->xvclk);
reset和pwdn0 gpio
gpiod_set_value_cansleep(ov13850->reset_gpio, 0);
gpiod_set_value_cansleep(ov13850->pwdn_gpio, 1);
从设备驱动的角度看,regulator的控制应该很简单,就是输出的enable/disable、输出电压或电流的大小的控制
各路的 vdd 上电。这里使用了 regulator_bulk ,因为 vdd,vodd,avdd 三者无严格顺序。如果vdd 之间有严格的要求,需要分开处理
regulator_bulk_enable(OV13850_NUM_SUPPLIES, ov13850->supplies);
测试i2c id
ov13850_open
4,sensor对v4l2-ctl的支持
既然涉及到视频输入,就会有很多与 ISP 相关的效果,比如对比度、饱和度、色温、白平衡等等,这些都是通用的、必须的控制项,并且大多数仅需要设置一个整数值即可。V4L2 很贴心地为我们提供了这样一些接口以供使用(可以说是非常贴心的了),在内核里面,这些控制项被抽象为一个个的控制 ID,分别以 V4L2_CID_XXX 来命名的control数量不一定跟这个数字一致。对于video_device或者v4l2_subdev设备来说,需要显式的设置他们的ctrl_handler成员(去内核代码里面看一下就能看到它)指向驱动结构体的 ctrl_handler,否则打开video节点并不能进行相关的控制
4l2_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);
这个函数用来增加整数型菜单的控制变量,max是最大的菜单索引号,def是默认的菜单索引号,qmenu_int是菜单的数组指针。这个函数适用于变量值是不连续的无规则的整数的控制变量
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);
这个函数用来增加非菜单式的控制变量,如声音等。min是最小值,max是最大值,step是步进长度,def是默认值。这个函数适用于在某一范围内均匀变化的控制变量
static const s64 link_freq_menu_items[] = {
OV13850_LINK_FREQ_300MHZ
};
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;
#define OV13850_PIXEL_RATE (OV13850_LINK_FREQ_300MHZ * 2 * 2 / 10)
v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE,
0, OV13850_PIXEL_RATE, 1, OV13850_PIXEL_RATE);/**max:750MHz**/
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);
#define OV13850_EXPOSURE_MIN 4
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);