作者简介: 一个平凡而乐于分享的小比特,中南民族大学通信工程专业研究生在读,研究方向无线联邦学习
擅长领域:驱动开发,嵌入式软件开发,BSP开发
作者主页:一个平凡而乐于分享的小比特的个人主页
文章收录专栏:IMX8MP,本专栏记录imx8mp开发板,学习开发过程中的问题及解决方法记录
欢迎大家点赞 👍 收藏 ⭐ 加关注哦!💖💖
Camera开发-OV5640(MIPI)
如果需要OV5640产品规格书和mipi摄像头引脚定义,请自行下载
1.OV5640摄像头介绍
OV5640_V5( V5 是版本号,下面均以 OV5640 表示该产品)是芯路恒科技推出的一款高性能 500W 像素高清摄像头模块。该模块采用 OmniVision 公司生产的一颗 1/4 英寸CMOS QSXGA( 25921944)图像传感器 OV5640,配合高质量的光学镜头及为实现更高性能而精心设计的 PCBA,使该模块拥有了尽可能高的成像质量。OV5640 模块的特点如下:
- 1.4μm1.4μm 像素大小,并且使用 OmniBSI 技术以达到更高性能(高灵敏度、低串扰和低噪声)
- 自动图像控制功能:自动曝光( AEC)、自动白平衡( AWB)、自动消除灯光条纹、自动黑电平校准( ABLC)和自动带通滤波器( ABF)等。
- 支持图像质量控制:色饱和度调节、色调调节、 gamma 校准、锐度和镜头校准等
- 标准的 SCCB 接口,兼容 IIC 接口
- 支持 RawRGB、 RGB(RGB565/RGB555/RGB444)、 CCIR656、 YUV(422/420)、 YCbCr( 422)和压缩图像( JPEG)输出格式
- 支持 QSXGA( 500W)图像尺寸输出,以及按比例缩小到其他任何尺寸
- 支持图像缩放、平移和窗口设置
- 支持图像压缩,即可输出 JPEG 图像数据
- 支持数字视频接口( DVP/MIPI)
- 自带嵌入式微处理器
- 集成 LDO, 仅需提供 3.3V 电源即可正常工作
2.CMOS 图像传感器成像原理
OV5640 是一个典型的 CMOS 图像传感器,作为一个图像传感器,其主要作用就是将现实中的各种光线转换为数字系统能够识别的数字信号,光线中三元色各个颜色的强度本身是模拟信号,所以图像传感器的最基本的原理就是进行模数转换,将光线这个模拟量转换为数字信号。
仅仅有模数转换功能还不够,我们还得先搞清楚另一个问题——光线是一种怎样的模拟量。
光线是一种怎样的模拟量呢,这个在初中物理中已经有过详细的介绍。我们都知道,自然界中的光,实际上是三种基本单色光的组合,这三种基本单色光为红(RED)、绿(GREEN)、蓝(BLUE),我们称之为三原色。通过将这三种基本颜色按照不同的比例混合,就可以得到其他的任意颜色。例如纯黄色是由红色和绿色按照一比一的比例混合得到的,蓝色量为 0。下图为三种颜色混合得到几种常见颜色的示意图。
既然已经知道,每一束光线都可以理解为三原色按照不同比例混合得到的效果,那么我们只要想办法知道该束光线的三原色的比例,然后用颜色加比例的表示方法,就能唯一确定这束光线的最终颜色了。所以图像传感器里面所谓的模数转换,实质就是对一束光线的三原色的强度进行转换,将三原色中每一种颜色的强度转化为数字信号。再用颜色加数字的方式来表示该束光线的真实颜色。
这里,假如我们对三原色中的每一种基本颜色的强度都分为 256 级,那么每一种基本颜色的强度就可以用一个 8 位的数字来表示,0 表示该颜色强度最弱,或者说无该颜色分量,255 表示该颜色强度最强。这样一来,我就可以用一个 24 位的数字来唯一表示该光线的颜色了。下表为上图中几种颜色的对应三原色的数值。
这种表示颜色的方法就是最常见的 RGB888 格式。所谓 RGB888 就是使用 3 个 8 位的数据表示一种颜色,其中高八位表示红色分量,中八位表示绿色分量,低八位表示蓝色分量,上述 8 种颜色用 RGB888 格式表示就如下表所示:
行转换得到数字信号,就能唯一表示该颜色了,但是接下来,另一个问题又出现了。如何才能对一束自然光中的三原色的强度分别进行模数转换呢?这就涉及到对一束光的三原色分离。
所谓对光的三原色分离就是通过某种手段,将该束光线中的三种颜色分别独立提取出来,当三种颜色都独立的提取到之后,就能使用模数转换器对该颜色的强度进行转换了。三原色分离的原理其实非常简单,就是使用单色滤光片,
如上图,通过加入滤光片,就能让对应颜色的光线通过滤光片到达感光元件,通过这种方式,只需要三个不同颜色的滤光片,就能对一束入射的复合光线进行分离,得到三原色,然后使用模数转换器对感光元件感应到的单色光的光照强度进行转换,就能得到该单色光的强度数字值了。
但是,通过上述分析我们也发现一个事实,那就是一个感光元件只能对一种颜色的光进行感应,如果要对一束光线中的三种颜色分别感应,就需要三个感光元件。如果对应到CMOS 图像传感器里面的概念来说,这每一个滤光片加感光元件都是一个像素。注意这个概念,每一个滤光片加感光元件都是一个像素,那么从反面来说就是,每个像素都只能感应一种颜色的光线。所以,下次看到某某图像传感器宣传其有多少多少万像素大小时,作为半专业人士的我们,要在心里默念,这只是物理像素个数,实际上每个像素只能感应一种颜色。
既然每个像素只能感应一种颜色,那么新一个问题又来了——如何才能得到某束光线的三种颜色分量的值呢?答案就是使用至少 3 个像素,每个像素感应该束光的一种颜色,然后三个像素就能把该束光的三种颜色分量全提取出来了。但是,实际上使用 3 个像素来提取一束光的三种颜色分量,理论上可行,但是实际上却存在 3 个像素间摆放位置的物理限制,首先是如何让该束光线能够均匀的照射到感应三种颜色分量光线的像素上的问题,其次还要知道,一个图像传感器,并不是只感应一束光,是要感应成千上万束细小的光束,每一束光线都需要有对应的像素组来提取其三种颜色分量。因此,像素和像素之间的物理摆放问题也是需要认真考虑的问题。同时,还得兼顾考虑这种摆放模式下图像传感器的可生产性问题。
正是为了解决这些问题,诞生了著名的拜尔(以下简称 Bayer)矩阵。Bayer 矩阵定义,感光像素矩阵中,奇数行间隔放置绿色和红色感应像素,偶数行间隔放置绿色和蓝色感应像素,奇数列间隔放置绿色和蓝色感应像素,偶数列间隔放置红色和绿色感应像素,其输出数据格式如下图所示。
根据这种分布规律,在 Bayer 矩阵中,以相邻的四个像素作为一组,在该组中,有两个感应绿色分量的像素呈对角分布,另外两个像素则分别对应感应红色分量和感应蓝色分量。
通过这样一种方式,我们可以发现,任取一个像素,其与相邻的 3 个像素组成的矩阵中,总符合这样的规律。不同的只是三种颜色的像素所在的位置的差异。下图为图像传感器手册中给出的像素物理分布图。
而且通过对上述 8*8 的矩阵进行分析发现,在整个矩阵中,像素的位置关系有且只有下面四种情况:
所以,在实际颜色提取时,需要通过数据转换,将相邻四个像素的数据通过插值算法合并为一个 RGB 像素颜色,此种转换算法名为 RAW2RGB,这里取左上角四个像素点的数据为例,具体颜色转换算法如下所示:
保留相邻四个像素中的红色和蓝色分量,而对两个绿色分量求平均,得到新的绿色分量,此三种颜色分量组成一个新的 RGB 格式的像素,新的像素色彩组成如下所示:
按照这样的思路,整个图像传感器中的每一个像素都可以以不同的角色参与 4 次运算,并最终得到 4 个 RGB 颜色值,所以理论来说,还是可以认为一个图像传感器有多少个物理像素,就能得到多少个 RGB 格式的像素值,虽然每个物理像素都只能感应一种颜色。但是进过插值运算后,其能输出 RGB 的数据格式的像素数量还是等于其物理像素个数的。有了 Bayer 像素矩阵后,只需要使用一个模数转换器,依次对每一个像素的感光元件感应到的模拟量进行转换并输出,就能得到一幅完整图像的原始像素信息了。下图为OV5640 图像传感器的功能框图。
3.OV5640内部结构
通过该图可以看到,该图像传感有一个基本的像素矩阵,在图中名为 image array,该像素矩阵共有 25921944 个物理像素,2592*1944=5,038,848,这也就是我们常说的 500 万像素的由来。在像素矩阵外围,有一个 row select 功能模块来选择当前输出哪一行的像素,和一个 column sample/hold 电路来依次采样每行像素中的每一个像素的感光元件感应结果(模拟信号)并输出到信号放大器(AMP),经由 AMP 对该信号放大之后,送给 10 位的模数转换器(10-bit ADC)进行模数转换。
4.SCCB协议
4.1 SCCB协议简介
SCCB (Serial Camera Control Bus)是 OmniVision公司公布的串行摄像机控制总线协议,相当于一个简易的I2C 协议。SCCB有三线和两线之分,三线的是一主机多从机,两线的是一主机一从机。
- SCCB_E:使能信号,低电平有效。
- SIO_D:双向数据总线,可由主设备或从设备驱动,空闲时拉高。
- SIO_C:单向时钟总线,只能由主设备驱动,高电平数据有效,空闲时拉高。
- PWDN:输出/输入关闭。
4.2 SCCB时序分析
起始信号
当SCCB_E拉低之后,SIO_D在SIO_C高电平期间拉低,表示一次数据传输开始。
停止信号
当SCCB_E拉低之后,SIO_D在SIO_C为高电平期间拉高,表示一次数据传输结束。
4.3 数据传输
在 SCCB协议中,一个基本传输单元称作一个相(phase),一个相包含总共9比特,前8比特为数据,它的响应信号ACK被称为一个传输单元的第9位,分为Don’t care和 NA(No ACK)。Don’t care位由从机产生;NA位由主机产生,由于SCCB不支持多字节的读写,NA位必须为高电平。SCCB没有重复起始的概念,因此在 SCCB的读周期中,当主机发送完片内寄存器地址后,必须发送总线停止条件。不然在发送读命令时,从机将不能生Don’t care响应信号。
写数据到从机被定义为写传输,从机中读数据被定义为读传输,每个传输都要有开始位(start)和结束位(sotp);完整的数据传输包括两个或三个phase,每一个phase包含9位数据,其中高8位为所要传输的数据,最低位根据器件读写情况有不同的取值:
每一个阶段组成:8位数据+don’t care/NA
- 如果是主机发送数据,即进行写操作,第9位就为don’t care;
- 如果是从机发送数据,即为读操作,第九位就为NA。
三相写传输
ID Addr :表示从机的器件地址以及读/写控制位;0:写寄存器,1:读寄存器
Sub Addr:写控制字节,表示从机的寄存器地址;
Write Data :表示写入的1字节数据;
3个相后面的的最后1位X都是 Don’t-Care bits。
两相写传输:读数据第一阶段
ID Addr :表示从机的器件地址以及写控制位;
Sub Addr :表示从机的寄存器地址;
2个相后面的的最后1位X都是 Don’t-Care bits。
两相读传输:读数据第二阶段
ID Addr :表示从机的器件地址以及读控制位;读控制字节
Read Data:表示接收从机发送的数据;
X都是 Don’t-Care bits;NA必须为1。
4.4 sccb与i2c的区别
- SCCB的应答位称为X,表示“Don’t care”,而IIC应答位称为ACK。
- SCCB只能单次读,而IIC除了单次读还支持连续读。
- SCCB读操作中间有stop,而IIC读操作中间可以有stop也可以不需要stop,具体表现如下
SCCB读:start_1 + phase_1 + phase_2 + stop_1 + start_2 + phase_1+ phase_2 + stop_2
I2C读:start_1 + phase_1 + phase_2 + + start_2 + phase_1 + phase_2+ stop_2
除去上面三点,SCCB和IIC再无区别,因此如果只需要配置寄存器(只用到写),可以直接拿IIC的时序来当做SCCB用,如果需要读,读操作中间必须有一个stop。
5.原理图引脚定义
6.设备数添加及内核使能
ov5640_0: ov5640_mipi@3c {
compatible = "ovti,ov5640";
reg = <0x3c>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_csi0_pwn>, <&pinctrl_csi0_rst>, <&pinctrl_csi_mclk>;
clocks = <&clk IMX8MP_CLK_IPP_DO_CLKO2>;
clock-names = "xclk";
assigned-clocks = <&clk IMX8MP_CLK_IPP_DO_CLKO2>;
assigned-clock-parents = <&clk IMX8MP_CLK_24M>;
assigned-clock-rates = <24000000>;
csi_id = <0>;
powerdown-gpios = <&gpio2 11 GPIO_ACTIVE_HIGH>;
reset-gpios = <&gpio1 6 GPIO_ACTIVE_LOW>;
mclk = <24000000>;
mclk_source = <0>;
mipi_csi;
status = "okay";
port {
ov5640_mipi_0_ep: endpoint {
remote-endpoint = <&mipi_csi0_ep>;
data-lanes = <1 2>;
clock-lanes = <0>;
};
};
};
&mipi_csi_0 {
#address-cells = <1>;
#size-cells = <0>;
status = "okay";
port@0 {
reg = <0>;
mipi_csi0_ep: endpoint {
remote-endpoint = <&ov5640_mipi_0_ep>;
data-lanes = <2>;
csis-hs-settle = <13>;
csis-clk-settle = <2>;
csis-wclk;
};
};
};
&vpu_g1 {
status = "okay";
};
&vpu_g2 {
status = "okay";
};
&vpu_vc8000e {
status = "okay";
};
&vpu_v4l2 {
status = "okay";
};
&cameradev {
status = "okay";
};
&isi_0 {
status = "okay";
cap_device {
status = "okay";
};
m2m_device {
status = "okay";
};
};
pinctrl_csi0_pwn: csi0_pwn_grp {
fsl,pins = <
MX8MP_IOMUXC_SD1_STROBE__GPIO2_IO11 0x10
>;
};
pinctrl_csi0_rst: csi0_rst_grp {
fsl,pins = <
/*MX8MP_IOMUXC_GPIO1_IO05__GPIO1_IO05 0x10*/
MX8MP_IOMUXC_GPIO1_IO06__GPIO1_IO06 0x10
>;
};
pinctrl_csi_mclk: csi_mclk_grp {
fsl,pins = <
MX8MP_IOMUXC_GPIO1_IO15__CCM_CLKO2 0x50
>;
};
7. 功能测试
利用i2cdetect -y 1
命令查看0V5640设备在位
查看到dev路径下相应设备
利用v4l2-ctl --device=/dev/video3 --list-formats-ext
查看支持格式
(此处只是截取了一部分)
用v4l2-ctl命令对不同分别率录制视屏,举个例子:(仅需改动width=640,height=480)
v4l2-ctl --device=/dev/video3 --set-fmt-video=width=640,height=480,pixelformat=NV12 --stream-mmap --stream-to=video640x480.yuv --stream-count=60
利用ffplay -video_size 640x480 -pixel_format nv12 -framerate 10 -i video640x480.yuv
视频播放