Chinaunix首页 | 论坛 | 博客
  • 博客访问: 312338
  • 博文数量: 101
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 774
  • 用 户 组: 普通用户
  • 注册时间: 2018-10-15 14:13
个人简介

搭建一个和linux开发者知识共享和学习的平台

文章分类

全部博文(101)

文章存档

2024年(15)

2023年(24)

2022年(27)

2019年(8)

2018年(27)

分类: 嵌入式

2024-08-15 19:33:55

1.摄像头的原理

定时脉冲生成器会生成clock,用于访问image sensor 阵列中的行,预充电,并且按顺序采样像素阵列中的所有行。在一个行的预充电和采样的时间段里,像素的电荷量会随着曝光时间而逐渐减少。这就是快门结构中的曝光时间的概念。
可以通过调整预充电和采样的时间间隔,来调整曝光时间。当一个行的像素数据被采样后,会送至AMP模拟电路,去矫正偏移和提高相应的增益。然后被送往ADC, 阵列中的每个像素都会被转换为10bit的数据

1.1 关于CSI图像采集的相关原理
图像采集的过程的为:
光照反射 -> 镜头汇聚 -> Sensor 光电转换 -> ADC转换 -> RAW RGB

Sensor的感光原理是通过一个一个的感光点对光进行采样和量化,我们将每个感光点叫做像素,所以,通常所说的30万像素或130万像素等,指的是有30万或130万个感光点。
但在Sensor中,每一个像素只能感光RGB中的某一种颜色,即每个像素只能感到R/G/B中的一种颜色。这种颜色通过ADC转换后,就形成了RAW RGB数据。 RAW RGB 数据是未经过处理的数据,表示sensor 接收到R/G/B的光照强度。 我们常说的RAW8, RAW10 就是RAW RGB 8bit/10bit 数据。
如果这个原始数据的排列格式是 RGRG/GBGB排列的,我们叫做 Bayer pattern(这个{BANNED}{BANNED}{BANNED}{BANNED}最佳佳佳佳{BANNED}{BANNED}{BANNED}{BANNED}最佳佳佳佳常见)。所以 Bayer RGB是属于 RGB RAW data的,但是 RGB RAW data不一定是bayer pattern。

要还原一个真正图像,需要每一个像素点都有RGB三种颜色,所以,一般地都需要有一个ISP模块,会将Sensor采集到的RAWRGB数据进行插值和特效处理,例如:如果一个感光点感应的颜色是R,那么,ISP模块就会根据这个感光点周围的G、B感光点的数值来计算出此点的G、B值,那么,这一点的RGB值就被还原了。

2 CSI相关概念&计算公式
2.1 行消隐/场消隐
行消隐和场消隐的概念来源于老的电视视频制式NTSC和PAL,NTSC每秒刷新60次,PAL每秒刷新50次。电子枪从左到右画出像素,每次扫描一条线,画下一条之前要先回到左边并做好画下一条扫描线的准备,这之间有一段时间叫做水平消隐(HBlank)。画完全部扫描线后,又回到屏幕左上交准备画下一帧,这一段时间就是垂直消隐(VBlank)。

对于CMOS Sensor来说,也有VBlank和HBlank的概念,Rolling Sensor在曝光时候一次曝光一行,一般CMOS只有一行ADC用于转换电信号,转换好的数字信号逐像素顺序排列,每一行输出结束和下一行输出开始的间隔我们称为行消隐(HBlank),这一帧结束到下一帧开始这段时间称为场消隐(VBlank)。

2.2 行曝光

何为曝光,就是光透入到sensor到采样这一段过程,叫做曝光。为了好理解,你可以假想成采样是逐个pixel进行的,采集一行所需的时间,就是一行曝光的时间。采集一帧的时间,就是曝光时间。逐行曝光的sensor只有一行ADC。

2.3 相关计算公式
在计算相关公式前,我们先给给出一些概念

项目 描述
pclk(pixelclock) 曝光一个像素点需要的clock
width 有效图像宽度,也就是一行有效像素的个数 , 详见于CIS 的datasheet
height 有效图像宽度, 也就是有多少个像素行, 详见于CIS 的datasheet
fps 帧速率,单位HZ
HTS HTS=width + HBLANK
VTS VTS = height + VBLANK
link_freq 链路的速率,即是MIPI-CSI 的clock
关于pixelclock 有如下关系:
pclk = (HTS*VTS)*fps = (width + HBLANK)*(height + VBLANK)*fps

关于曝光时间,计算如下:
exposure_line_time = HTS/pclk;
exposure_time = exposure_line_time * exposure_line

曝光时间就是开启一次快门,能曝光多少行。一帧图像需要多次曝光来完成。
exposure_line 是曝光的行数,一般是一个寄存器,用来控制曝光时间是多少倍的行曝光时间(exposure_line_time)。曝光行数{BANNED}{BANNED}{BANNED}{BANNED}最佳佳佳佳大就是VTS。

MIPI-CSI 链路速率计算公式如下:
link_freq = (pclk * bits_per_sample) /(2*nr_of_lanes)

变量 描述
nr_of_lanes Number of data lanes used on the CSI-2 link. This can be obtained from the OF endpoint configuration.
2 Two bits are transferred per clock cycle per lane.
bits_per_sample Number of bits per sample
要将CIS 产生的数据能够及时搬完,就至少需要link_freq 的频率。

3. CSI驱动移植相关
3.1 上电时序
这个简单,按照摄像头的datasheet 上的PowerOn/Off 时序做就可以。

一般的sensor 基本都需要AVDD,DOVDD 这两个电源,还有PWDN, RST两个控制pin,一般地按照datasheet 的时序拉起来就可以了。另外还有一个,容易被忽略的信号是Mclk,它主要给sensor 提供采集的pixelclock,有些sensor 也提供系统时钟,所以这个clk 出不来,sensor 就无法进行I2C 通信。

3.2 I2C 识别sensor
基本上完全按照datasheet的PowerOn/Off sequence 设定后,基本都能正常的识别sensor,如果不能识别,可以参照以下几个方面去排除问题:

I2C bus 能不能发送信号
如果不能发送信号请排除bus 的配置,时钟速率等。

检查上电时序是否正常
AVDD, DOVDD 是不是起来了,时序正常么? PWDN/RST 的状态正常么等等。

MCLK 是否正常输出
有些sensor 需要MCLK 才能正常工作,否则无法识别。

我曾经就碰到过这个问题,发现RK3568 的 CLK_CAM0_OUT 作为MCLK 就是出不来,驱动里使能时钟也没有clock输出,

ret = clk_prepare_enable(ov426->xclk);
if (ret < 0) {
dev_err(&client->dev, "Failed to enable xvclk\n");
return ret;
}
clk_set_rate(ov426->xclk, 24000000);

{BANNED}{BANNED}{BANNED}{BANNED}最佳佳佳佳后把该pin 的pinmux 和Clock的相关寄存器打印出来都确认了一遍都没有问题。{BANNED}{BANNED}{BANNED}{BANNED}最佳佳佳佳后问RK 说是该clock 的电源域没有打开,所以有在设备树中sensor 节点加了电源域的配置,

ov426: ov426@36 {
......
clocks = <&cru CLK_CAM0_OUT>;
clock-names = "xclk";
power-domains = <&power RK3568_PD_VI>;  /* 电源域配置*/
.....
};

然后在CSI driver 中使能clock前,加了以下代码

/* Enable power domain and own power*/
pm_runtime_enable(dev);
pm_runtime_get_sync(dev);
// Enable Xclk code

然后clock 才出来了。所以推荐大家在CSI driver 中使用pm_runtime_xxx 相关的API 来控制上电时序,因为它可以确保它依赖的电源/电源域全部被打开。

如果以上条件都排除了,那么很可能是sensor的硬件那里有问题,是不是smt没有接著好,sensor坏掉等原因。

3.3 CSI 驱动的核心实现
CSI 驱动{BANNED}{BANNED}最佳佳佳核心的部分是要实现下面的结构体

static const struct v4l2_subdev_ops ovxxx_subdev_ops = {
.core = &ovxxx_core_ops,
.video = &ovxxx_video_ops,
.pad = &ovxxx_pad_ops,
};

在RK Android 平台,v4l2_subdev_core_ops 必须实现以下三个函数:

static const struct v4l2_subdev_core_ops ovxxx_core_ops = {
.s_power = ovxxx_s_power,
.ioctl = ovxxx_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl32 = ovxxx_compat_ioctl32,
#endif
}

s_power 再/dev/video0 被open 时调用。该函数主要作用是控制sensor 的上电/关闭。一般都可以通过pm_runtime_xxx 相关函数来实现。如下demo 所示:

static int ovxxx_s_power(struct v4l2_subdev *sd, int on)
{
....
mutex_lock(&ovxxx->mutex);
on = !!on;

if (on){
ret = pm_runtime_get_sync(&client->dev);
if (ret < 0)
pm_runtime_put_noidle(&client->dev);
}else{
pm_runtime_put(&client->dev);
}

mutex_unlock(&ovxxx->mutex);

return ret;
}

ioctl 会被v4l2 core 的ioctl ,主要让上层读取摄像头的基本信息,和快速开启/关闭摄像头数据流。

在RK Android 平台,v4l2_subdev_video_ops必须实现以下三个函数:

static const struct v4l2_subdev_video_ops ovxxx_video_ops = {
.s_stream = ovxxx_set_stream,
.g_mbus_config = ovxxx_g_mbus_config,
.g_frame_interval = ovxxx_g_frame_interval,
};

s_stream 是用户层通过ioctl时(/dev/video0),调用VIDIOC_STREAMON/OFF时,该函数被调用。
g_mbus_config是驱动必须要实现的接口,该接口用于获取支持的总线配置,总线类型存在DVP/MIPI/LVDS等几种类型,并口里面存在BT601/BT656/BT1120,MIPI存在DPHY/CPHY协议,控制器通过这个接口获取当前sensor使用的总线参数,来确认控制器的采集方式。
我们先来看一个DVP的g_mbus_config的实现:

static int ovxxx_g_mbus_config(struct v4l2_subdev *sd,
     struct v4l2_mbus_config *cfg){
cfg->type = V4L2_MBUS_PARALLEL;
cfg->flags = V4L2_MBUS_HSYNC_ACTIVE_HIGH |
V4L2_MBUS_VSYNC_ACTIVE_LOW |
V4L2_MBUS_PCLK_SAMPLE_RISING;

return 0;
}

对于DVP类型,必须配置总线的类型,hsync, vsync, 和pixelclock的极性。上层通过该接口来获取该摄像头的基础bus 配置信息。

比如使用mipi时,当Sensor支持多种MIPI传输模式时,可以根据Sensor当前使用的MIPI模式上传参数。 例子如下:

static int ovxx_g_mbus_config(struct v4l2_subdev *sd,
     struct v4l2_mbus_config *cfg){
cfg->type = V4L2_MBUS_CSI2;
cfg->flags = V4L2_MBUS_CSI2_1_LANE |
V4L2_MBUS_CSI2_CHANNEL_0 |
V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;


return 0;
}

对于MIPI-CSI 类型的摄像头,必须配置mipi bus 的type,使用mipi的几个lane, 使用拿一个lane,CSI clock的类型等。在这里需要注意的是,MIPI-CSI 和MIPI-DSI 一样,它的Clock 电压大概在200mV左右,用示波器测量时,一定得注意。
g_frame_interval 一般实现的是max fps,可以参考其他driver 来实现。

v4l2-subdev PAD 级别的操作函数,其必须实现以下函数:
static const struct v4l2_subdev_pad_ops ovxx_pad_ops = {
.get_fmt = ovxx_get_format,
.set_fmt = ovxx_set_format,
.enum_mbus_code = ovxx_enum_mbus_code,
.enum_frame_size = ovxx_enum_frame_size,
.enum_frame_interval = ovxx_enum_frame_interval,
};

get_fmt 和set_fmt 是ioctl 的VIDIOC_SUBDEV_G_FMT 和VIDIOC_SUBDEV_S_FMT 的{BANNED}{BANNED}最佳佳佳终调用函数。
enum_mbus_code 是应用层调用ioctl 的VIDIOC_SUBDEV_ENUM_MBUS_CODE的{BANNED}{BANNED}最佳佳佳终调用函数。
enum_frame_size 是应用层调用ioctl 的VIDIOC_SUBDEV_ENUM_FRAME_SIZE的{BANNED}{BANNED}最佳佳佳终调用函数。
enum_frame_interval 是应用层调用ioctl 的VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL的{BANNED}{BANNED}最佳佳佳终调用函数。
大概CSI 驱动都是围绕以上的接口来实现的,实现了以上接口,CSI 的driver 工作基本就完成了。接下来就是CSI 的driver 调试了。

3.4 media-ctl 生成拓扑和数据流图
(1)media-ctl 工具
media-ctl是v41-utils软件包的其中一个工具,用于配置pipeline的应用程序,主要参数如下:

–device (默认为/ dev / media0)
–entity <名称>打印与给定实体关联的设备名称
–set-v4l2 逗号分隔要设置的格式列表
–get-v4l2 在给定的pad上打印活动格式
–set-dv 在给定的对象上配置DV时序
–interactive交互式修改链接
–links 逗号分隔要设置的链接描述符列表
–known-mbus-fmts列出已知格式及其数值
–print-topology打印设备拓扑
–reset将所有链接重置为非活动状态

(2)将拓扑生成dot文件
可以利用media-ctl工具将pipeline拓扑生成dot文件:
media-ctl -p  -d /dev/mediaX

这个需要和设备树中的isp 相对应,/dev/mediaX 从0~N,直到找到对应的ISP即可。
media-ctl --print-dot> media0.dot
media-ctl -d /dev/media1 --print-dot> media1.dot

(3)graphviz安装
下载地址:
终端敲入:dot -version

(4)将dot转换为png图像
命令如下:
dot -Tpng InputFile.dot -o OutputFile.png
一般通过main path/self path 来抓raw图。如果设备树所配置的逻辑和该图片的sink/source逻辑基本一致,说明设备树配置没有问题。

3.5 v4l2-ctl 抓原图
v4l2-ctl 抓图
$ ./v4l2-ctl -d /dev/video0 --get-crop  ## 默认采集窗口大小
Crop: Left 0, Top 0, Width 1920, Height 1080

## 设置采集窗口大小400x400
$ v4l2-ctl -d /dev/video0 --set-crop top=0,left=0,width=400,heigth=400  
## 以上两步一般除了很特殊的摄像头(如ov426)之外不需要操作
## 抓图
$ ./v4l2-ctl -d /dev/video0 --set-fmt-video=width=600,height=600,pixelformat='BG10' --stream-mmap=3 --stream-skip=20 --stream-to=/data/400p30.raw --stream-count=5 --stream-poll
<<<<<<<<<< 30fps  ## 如果成功就会打印该消息,并将数据写进文件中

如果抓不到就会报错,或者目标文件中没有内容。 否则有上面的输出结果就表示抓图成功。
之前还遇到过./v4l2-ctl 抓图时,只报select timeout 的错误,后来确认了MIPI-CSI 的总线配置,lane配置,和上电时序后,就okay 了。

使用V4L2工具抓图没有报错,有正常的数据输出,且使用V4L2的命令可以实现曝光增益等的控制,即可认为驱动基本没问题了。

4. Android 层的移植工作
如果用v4l-ctl 可以正常抓图,基本CSI 驱动就没有问题了,接下来就是安卓层的适配了。

确认HAL层注册sensor 是否成功?
dumpsys media.camera
1
如果发现sensor 的数量是0,就表示没有成功,需要确认以下几个方面:

CSI driver 中,subdev 的名字格式:
例如 m00_b_ov426
m00 : moduleId
b: back,后置摄像头
ov426: 驱动名
检查name和moduleID这两个属性 是否 和 CSI 的subdev 中的name 和moduleID 一致,否则,就无法匹配成功。
如上所述的那些驱动接口,是不是正确
如enum_mbus_code, enum_frame_interval, enum_frame_size, g_mbus_config 等。
查看修改是否生效:

cat /vendor/camera/camera3_profiles.xml //查看该文件是否是修改过后的文件
1
如果驱动okay, android的所有问题均可通过logcat 查看是否有致命错误

logcat|grep "E RkCamera"
# Or
logcat|grep "XCORE"
## enable debug message
setprop persist.vendor.camera.hal.debug 5

查看是否是致命错误,以定位分析问题所在。

5. 摄像头中的概念
摄像头的3A 指的是AE/AF/AWB。下面我们详细描述。

5.1 自动曝光(Auto Exposure)-AE
曝光补偿是指拍摄者根据需要对相机所计算的亮度进行调整。 曝光是测量被摄体反射的光量得到的。 在画面内有较多白色的被摄体时,相机会判断画面过亮而降低曝光;画面中较多黑色被摄体时,相机会判断画面过暗而增强曝光(白加黑减原理)。

自动曝光算法可以理解为一个伺服系统,它不断监控ISP生成的每一帧图像的曝光状态,如果发现采集到的状态与理想目标发生偏离(超过容许范围)则立即进行干预响应,使系统尽快回到容许的工作范围内。

在一个响应周期内,AE算法需要处理的具体事项是:

根据ISP 硬件生成的图像曝光统计数据评估当前图像的曝光质量
如果曝光质量需要调整,则根据当前的工作参数和理想曝光目标生成下一帧图像的工作参数
将新的工作参数写入各硬件设备,驱动光圈、sensor 快门及增益到达新的位置
AE 算法的主要调控对象一般是光圈、sensor积分时间、sensor增益(包含模拟增益和数字增益)、ISP数字增益这四个参数。

5.2 自动对焦(Auto Focus) -AF
简单来说,就是当物体在分别在远景和近景的时候,对应的成像位置是不同的,需要调整镜头和感光芯片的距离,使得感光芯片上始终可以获得清洗的成像效果。对于手机摄像头模组而言,镜头位置不动,主要是靠马达来带动镜头移动实现。
自动对焦就是相机根据成相的远近,自动对焦。
AF需要摄像头的镜头支持才可以,也就是镜头支持自动变焦的装置才行。

5.3 自动白平衡(Auto White Balance) -AWB
“不管在任何光源下,都能将白色物体还原为白色”,对在特定光源下拍摄时出现的偏色现象,通过加强对应的补色来进行补偿。
手动白平衡是指摄影师把摄像机对准白纸拍摄,这时,白纸充当标准白色,摄像机 需要通过内部自动调整 ,即估计出 红、绿、蓝 色偏色的比例 并做相应的调 整,使拍摄出的画面呈现纯白色。
自动白平衡就是ISP在采集图像帧后,采集关于白平衡的统计信息,进而通过ISP的AWB算法库的处理,使得到的图像和肉眼更为接近。AWB一般由ISP模块处理。自动白平衡(auto white balance,AWB),颜色校正(colorcorrection,CC),3维查找表(three dimension look up table,3dlut)等颜色调整模块 一般都属于ISP的功能范畴。
自动白平衡算法能自动的计算WB gain (R G B通道的白平衡增益),并将其与RGB通道分别相乘后,使受环境光影响的白色还原成纯白色,保证在各个光线条件下,相机成像色彩跟物体真实的色彩保持一致。当场景存在白点时基于自动检测的白点计算WB gain,当场景不存在白点时通过单纯色方法得到WBgain。色适应模块,对白平衡校正的目标进行调节,使白平衡校正后的图像尽可能与人眼感知的外貌一致。色调调整模块,根据喜好调整整体色调。由硬件的统计和软件的策略构成自动白平衡。

阅读(351) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~