|
一、 手机 Camera 的物理结构:
, S3 \; M* D. v* W. C' o , E& x% y- Z$ C9 @9 G
% ~0 F+ b: t0 q- z$ F/ ?1 Z5 Q b, s
二、 Camera 的成像原理:
7 s# Q8 }0 p/ \
5 m* n" g! W: p5 Z) X 景物通过镜头(LENS)生成的光学图像投射到图像传感器(Sensor)表面上,然后转为模拟的电信号,经过 A/D(模数转换)转换后变为数字图像信号,再送到数字信号处理芯片(DSP)中加工处理,再通过 IO 接口传输到 CPU 中处理,通过 LCD 就可以看到图像了。8 a" M% z' d3 O/ u+ w
' \+ j- X4 p! k2 ]$ B
& w& u$ {; o1 ]7 b" @ 图像传感器(SENSOR)是一种半导体芯片,其表面包含有几十万到几百万的光电二极管。光电二极管受到光照射时,就会产生电荷。目前的 SENSOR 类型有两种:+ ~1 @& {6 A) n. a0 Q8 ]
CCD(Charge Couple Device),电荷耦合器件,它是目前高像素类 sensor 中比较成熟的成像器件,是以一行为单位的电流信号。0 c# T% e* E4 s( G5 l k
CMOS(Complementary Metal Oxide Semiconductor),互补金属氧化物半导体。CMOS的信号是以点为单位的电荷信号,更为敏感,速度也更快,更为省电。7 i: N0 H$ c3 v+ w
ISP 的性能是决定影像流畅的关键,JPEG encoder 的性能也是关键指标之一。而 JPEG encoder 又分为硬件 JPEG 压缩方式,和软件 RGB 压缩方式。
U, D+ w0 o, ?* u- W$ y: f DSP 控制芯片的作用是:将感光芯片获取的数据及时快速地传到 baseband 中并刷新感光芯片,因此控制芯片的好坏,直接决定画面品质(比如色彩饱和度、清晰度)与流畅度。
4 u8 v, @8 M0 k# V/ M. e) k
2 K5 h8 h1 T3 f" s三、 Camera 常见的数据输出格式:
3 J i9 v& K# M6 P+ s/ J: g6 k 常见的数据输出格式有:Rawdata 格式、YUV 格式、RGB 格式。2 S6 v2 z+ z, l& H5 w+ D2 @3 n/ e. Z5 Z; U
RGB 格式:采用这种编码方法,每种颜色都可用三个变量来表示红色、绿色以及蓝色的强度。每一个像素有三原色 R 红色、G 绿色、B 蓝色组成。' W7 J* J$ |& J$ G+ r+ X' ?
YUV 格式:其中“Y”表示明亮度(Luminance 或 Luma),就是灰阶值;而“U”和“V”表示色度(Chrominance 或 Chroma),是描述影像色彩及饱和度,用于指定像素的颜色。
8 o9 v+ h0 Z9 b& }5 Q RAW DATA 格式:是 CCD 或 CMOS 在将光信号转换为电信号时的电平高低的原始记录,单纯地将没有进行任何处理的图像数据,即摄像元件直接得到的电信号进行数字化处理而得到的。0 [& c8 L* G( `3 @% n
支持 YUV/RGB 格式的模组,一般会在模组上集成 ISP(Image Single Processor),经过A/D 转换过的原始数据经过 ISP 处理生成 YUV 标准格式传到 BB。一般来说,这种设计适用于低像素 Camera 的要求,会在主板上省去一个 DSP,可降低成本。在调试过程中,YUV/RGB 格式的摄像头,其所有参数都可在 kernel 层通过寄存器来控制。调试一般由 sensor的原厂支持。* k( k2 A1 g9 \# Q1 G9 `
支持 RawData 格式的模组,由于感光区域的需求,不会再模组内集成 ISP 以最大程度的增大感光区域的面积,提高照片质量。模组把原始的数字信号传给 BB 上的 DSP 进行处理,MTK 自带的 DSP 一般包含 ISP、JPEG encoder、和 DSP 控制芯片。在调试的时候图像的效果需要 MTK 在 HAL 层的参数进行支持。( ^8 X1 F2 g. C1 W6 L
8 L8 y3 Q- Y1 t% `+ R: r. f2 h
四、 阅读 Camera 的规格书(以 Truly 模组 OV5647_Raw 为例):! Z( l( m1 H" ?, n4 L+ b
2 o5 B+ c' p( j" W ( G2 i g+ h( v) R* }9 l& g
9 e* g2 ^9 F& c& |" ^五、 Camera 的硬件原理图及引脚:) Y9 g" K* X% d
) V5 g# ?8 p+ R. R. s7 m
从上面可看出,连接 Camera 的 30 根 Pin 脚可大致分为以下几类:
% A" [+ w& l$ h% Y2 o 1、电源部分:
$ X8 L1 I5 T1 z1 b) { a)VCAMD 就是 DVDD 数字供电,主要给 ISP 供电,由于 RAWDATA格式的sensor其ISP是在 BB 端,所以将其引脚将其 NC。从上面的规格书上可以看出 DVDD 是内部 BB 端供电。模组已将其 NC 掉了;& m5 e" x2 V$ |: j- X4 f
b) VCAM_IO 就是 VDDIO 数字 IO 电源主要给 I2C 部分供电;
! ]/ ]# {4 z( P+ t7 s# P) a c) VCAMA 就是 AVDD 模拟供电,主要给感光区和 ADC 部分供电;
, _) m5 [% X, R. M d) VCAM_AF 是对 Camera 自动对焦马达的供电。
+ Y& G4 }2 t4 [- H) p" i' q& L 2、Sensor Input 部分:
: P) J0 @* ~" o. m, s/ B) B5 S a) Reset 信号,用于复位、初始化。0 Q$ r) N. f: O2 S
b) Standby/PowerDown 信号,用于进入待机模式,降低功耗。
+ _: Z# p9 f% ~ c) Mclk,即 MasterClock 信号,是由 BB 端提供。$ ^* \+ E+ k2 T4 h7 E# a
3、Sensor OutPut 部分:) Z: J" A3 @8 c* L- u
a)Pclk,即 PixelClock 信号,由 MCLK 分频得到,作为外部时钟控制图像传输帧率
& ^) B" _/ m7 x b) HSYNC,行同步信号,其上升沿表示新一列行图像数据的开始。. h& J$ f# R1 v4 n; o. U
c) VSYNC,帧同步信号,其下降沿表示新的一帧图片的开始。- O8 x5 k( X; V3 `7 Y$ U
d) D0-D9 一共 10 根数据线(8/10 根等);
, l% a# w# B9 k& {% i& K 4、I2C 部分:SCL,I2C 时钟信号线和 SDA,I2C 数据信号线。
0 d. @5 P4 }% n4 W ?; t# j B. A& R2 e' N
$ ` k/ u8 H4 G/ H- _/ [/ T六、 MTK 平台 Camera 相关代码文件(以下代码均为 MTK6575 平台)
Q& W& D& x3 q" f1 A 1、 CameraSensor 驱动相关文件
4 g6 w& _9 `2 a 6 W: r3 M- `8 o4 a) k
2、 Sensor ID 和一些枚举类型的定义
R0 V1 H1 e5 y6 m8 i2 n- p" ? ( \$ |6 ?( `. t6 r, C
3、 Sensor 供电8 k" V( r. B% J( \# d) m
' U6 _9 z s3 q+ ^+ F0 g , [+ E0 x f5 h* ~6 r6 h
4、 Kernel Space 的 SensorList,imgsensor 模块注册
$ Q% v9 d3 k3 g- { " x1 M" e8 ~4 t0 [
5、 User Space 的 SensorList,向用户空间提供支持的 SensorList
2 d, o/ ~! s7 W) G* Q: c2 d
* V5 ^" G$ ~( @- z( R% u' o" v: \% P! X" i: t5 q0 J& L
6、 Sensor 效果调整的接口
. K0 M, ^7 ?* N2 d% p- Y
. l6 t1 p. w) D% v6 N7 }; r1 }/ l 1 Y! k1 p% o# t
: ]% G2 t+ ?5 T" }" _" n% f
七、 Camera 模块驱动、设备与总线结构:- n! w# s6 k# P9 C; x
一般在 Linux 设备驱动模型中,我们只需要关心总线、设备、驱动这三个实体。总线会充当红娘对加载于其上的设备与驱动进行配对,对于 Camera 模块也不例外,下面从总线、设备、驱动的角度来分析 Camera 模块驱动的注册、匹配与加载过程。3 f/ L/ A8 J ~1 P' j7 \
a) 驱动的注册:
0 E; Z' x% ?) D 在(\custom\common\kernel\imgsensor\src\Kd_sensorlist.c)CAMERA_HW_i2C_init 这个函数里通过 Platform_driver_register(&g_stCAMERA_HW_Driver)把 Camera 模块驱动注册
# S$ N" g1 L0 y- E: {. r) V到 Platform 总线上。而 g_stCAMERA_HW_Driver 是对结构体 Platform_driver 这个结构体的填充。
$ a, ^4 G% O# S9 y% x& v6 m 0 s+ o3 }+ i M- V8 @% ]$ }
(Kernel\include\linux\Platform_device.h)
- D! F9 S: [/ t. w+ r% I 1 z9 S1 W8 V9 D2 R+ B$ w
b) 设备的注册:
: I# c4 S- `) Y, P 对 platform_device 的定义通常在 BSP 的板级文件:- Z3 I8 j9 y' K3 p
(kernel\arch\sh\boards\mach-ap325rxa\Setup.c)中实现,在板级文件中,将 platform_device归纳为一个数组,最终通过 platform_add_device()函数统一注册:" c0 e* P ~! f1 b' k2 E
% |, D' t. K7 l1 E' \9 O ( Z) O1 p8 D% w2 v3 \$ [
- k; \/ ]+ \, n- I0 ^9 i c) 总线的匹配:
5 q9 |. m& d/ [1 \. p* O 既 然 是 驱 动 Platform_device 那 对 应 的 设 备 必 然 是 挂 载 Platform 总 线 上 的Platform_device,Platform 总线是 Linux 系统提供的一种机制,不同于 I2C、I2S 等总线,它2 d% i7 T: S4 j" P
是一种虚拟的总线。Linux 系统为 Platform 总线定义了一个 bus_type 的实例 Platform_bus_type:# U2 y& i; w! v0 K$ ~# a4 y
(Kernel\drivers\base\platform.c)
I, c/ s$ ]+ ^* I3 D$ N* U+ V
, D. o" F. G( C, X' Y Platform 总线通过 platform_match 这个成员函数来确定 platform_device 与 platform_driver 如何进行匹配:
$ C. {1 O1 v9 W
' P+ `1 {% u P( U' u0 K1 a8 T' v. S# q" }; d4 Y" ?
八、 Camera 驱动工作流程:/ I% t$ F1 G7 {. i) ~3 N
: A( d6 ~2 S2 S6 ]: _5 m+ U; T
; K+ O0 a6 f2 u( |7 x' a) g% I6 T 从上图可以清晰的了解到 Camera 的一个工作流程主要分为这么七步:/ S& o( t4 r% Y' V6 i
1. 打开 Camera Power LDO,让 Camera 有能量保证。 q/ z# V; w* X" ]0 z" u
2. 打开 IIC,设置 PDN 引脚,使 Camera 退出 Standby 模式,按照要求让 Reset 脚做一个复位动作。
( E4 M- ~5 V; r: `% t9 z 3. 读一下 sensor 的版本 ID,这样可以让你确认是否连接上你想要的 sensor。
! @+ s4 g' d" f4 a* L d; X 4. 对 Sensor 进行初始化下载最基本的参数让 Sensor 工作起来,可能包括软复位。0 K, e3 L% Z4 \* v1 C1 o
5. 下载 preview 的参数,为预览动作准备。- m) C; Z2 [& w! g1 `
6. 下载 Capture 的参数,为拍照动作准备。
% o$ w1 d& h5 ?/ k3 \: m3 k 7. 设置 PDN 引脚,使 Sensor 进入 Standby 模式, 或者关掉 LDO 等动作,退出 Camera。
: M t6 u$ E- @; q m7 [ 我们都知道,Linux 内核是通过模块的机制来加载设备驱动的,那么接下来我们就从设备模块加载的角度来看下 Camera 工作流程的驱动代码是如何工作的。
6 ^6 T- ^* y8 i' M 在-alps\mediatek\custom\common\kernel\imgsensor\src\kd_sensorlist.c 中可以看到:! r7 `0 L* Y, H# e& X
module_init(CAMERA_HW_i2C_init);/ p& a6 b1 E3 ~# j
module_exit(CAMERA_HW_i2C_exit); P: Z4 u! e& x7 D2 B6 [
在这里 Linux 内核加载和卸载 Camera 模块。
; A& I% e6 @* \, d: J6 ^9 A0 C3 F static struct platform_driver g_stCAMERA_HW_Driver = {
2 g' W3 K+ a3 w+ Z .probe = CAMERA_HW_probe,
5 `$ D- R3 R2 h' d7 M1 |$ \' X .remove = CAMERA_HW_remove,
, _' i/ q( \2 D2 ?3 v+ h+ F( d& M .suspend = CAMERA_HW_suspend,
! h9 G1 y0 t" ^1 N" F y# M .resume = CAMERA_HW_resume,1 I/ d: ~" z) \# O
.driver ={
- n4 {& \; j1 C7 G .name = "image_sensor",
' `' ^4 j& h) Z9 z3 N+ S9 O .owner = THIS_MODULE,0 g! J: r7 b8 f
}
" N: e* t3 p: J' F) } };
$ W, F% h4 s! r% } Camera 模块初始化开始向总线注册驱动,在 Platform_driver 的成员函数.probe()中,通过 i2c_add_driver(&CAMERA_HW_i2c_driver)向 I2C 申请,而 CAMERA_HW_i2c_driver 这个结构体里填充的是将 Camera 作为一个字符设备在 I2C 上进行注册:
' `. Q$ v5 ~5 W+ P7 B9 ~' ^ ' m) ^& y3 V& ?, g# v1 ?+ q
) [. \& C- b! c/ J 在 RegisterCAMERA_HWCharDrv()中cdev_init(g_pCAMERA_HW_CharDrv, &g_stCAMERA_HW_fops);对设备进行初始化,并将g_stCAMERA_HW_fops 这个文件操作函数作为上层对 Camera 设备操作的接口留给上层进行调用:
) J- T: ]+ U# n+ a& j; i2 O
2 h# Q) J3 n" _ 其中成员函数 open()只是初始化一个原子变量留给系统调用。ioctl()才是整个 Camera驱动的入口:
- D. [8 z: o) f1 O1 [# Q( M 7 ~! R9 \2 |. R! h8 L9 j/ I
CAMERA_HW_Ioctl()是上层文件操作系统操作底层硬件的方法,它先对 Camera 需要的Buffer 做一个初始化,然后建立对 Cameraopen、getinfo 等操作的接口:- F: X# }( f2 l7 i: v4 H
$ _& v+ j3 S: D' K 通过判断 Sensor 状态的逻辑值来进行具体的操作,对于这个值的定义在:5 u: B/ Q8 E$ g
Mediatek\custom\common\kernel\imgsensor\inc\Kd_imgsensor.h 中! S8 \4 n5 r5 d D
6 I" y1 W% p' W$ j6 G 在 KdSetDriver()中通过判断 name 和 ID 匹配具体型号的 sensor 的驱动,判断它是主摄还是次摄,并对它进行初始化:* `! A6 b: c$ X
" t3 c0 A9 K( b 5 n- A* a5 G/ y) J5 Q# K
通过 NAME 和 ID 匹配完成后会将 PSENSOR_FUNCTION_STRUCT *pfFunc 这个结构体匹配到具体型号的驱动代码中:" f5 o% G0 G, L8 t L5 I! E
. y6 [7 Z2 j4 U. } 到这里,整个 Camera 驱动从总线注册到完成具体 sensor 的初始化的流程就完成了,CAMERA_HW_Ioctl()中其他的 ioctl 操作函数最后都会在$sensor$_sensor.c 中实现。
/ |1 X) B6 k6 i0 [
% ? P- O* u6 j: m. ~* R九、 Camera 驱动添加、调试流程:
$ L2 J; |; q% u$ q 1、 修改系统配置文件 ProjectConfig.mk:: h3 j% ^5 Q/ o( g& l
-alps\mediatek\config\$project$\ProjectConfig.mk; J. D) ]" V7 O/ F) L9 H. l+ r
: I+ H$ Q c/ N* K* V, P
' A! S- `) N4 g2 G) H9 Q
2、 检查、配置供电文件:+ S) F% }( P1 I6 T- V
-alps\mediatek\custom\$project$\Kernel\Camera\Camera\kd_camera_hw.c
- V, Y7 n4 D( E: E. }9 D4 o Camera 供电流程(以 3M 前摄 MT9V114+5M 后摄 OV5647 为例):+ Z$ L5 G6 Q \) [6 ~
: m+ V. n. t# r. C 其实在 kd_camera_hw.c 中只有一个函数 kdCISModulePowerOn(),在这个函数中需要注意的是通过 GPIO 口控制 PDN 和 RST 引脚的时候,对于其相关的定义,由其在切换平台的时候。例如 MT6573 和 MT6575 的定义顺序就不同2 L# R' q) i4 X* A
; N6 L7 @0 S; M% [" }3 |
3、 添加 Camera 驱动(以 ov5647 为例):
# U, j a) Q8 D1 w7 A, b1 H: b" M 创建 SensorFuncOV5647 这样一个数据结构
/ X; n! G4 K6 [+ {) l$ ]2 b' V) | SENSOR_FUNCTION_STRUCT SensorFuncOV5647={
+ q( U! b- ~3 s/ o8 f: B0 A OV5647Open,
3 z" k( L+ Y* A9 W/ U OV5647GetInfo,
+ z& c7 K: m( w: P0 s, o OV5647GetResolution,, q) w8 v# r0 j5 d# ?/ h5 ^( I
OV5647FeatureControl,
5 \. ^4 V7 _ B/ ~9 u OV5647Control,
4 ~% ~& D! v2 e+ c OV5647Close: H# C5 ^4 X/ B/ h; `7 ?
};
7 h2 ~; s% u8 h9 g1 H! _ a)OV5647Open4 p/ x* A- R k# p% w' s
9 X% \ I7 E4 i. Y3 U" Y1 S; B 初始化操作就是对 SensorIC 中寄存器的操作,调试主要由 IC 原厂支持。Open 函数结束后返回 ERROR_NONE 表示初始化成功,可以正常使用; u, M( |. S% k+ b! G" ]5 T
% m) I; j: B+ f4 ]+ x/ H' k5 [
b)OV5647GetInfo
: V8 r. c9 U$ Q d; J% z4 q0 N- A) U8 u$ V' n( Y" u. {
UINT32 OV5647GetInfo(MSDK_SCENARIO_ID_ENUM ScenarioId,MSDK_SENSOR_INFO_STRUCT *pSensorInfo,MSDK_SENSOR_CONFIG_STRUCT *pSensorConfigData) D" x% s2 a" U. Q3 }1 b
第一个参数 ScenarioId 来自于 MSDK_SCENARIO_ID_ENUM 这个数组,在kd_imgsensor_define.h 中是这样定义的:& j; V: N; c6 Y- {4 Z) N+ i X- g
#define MSDK_SCENARIO_ID_ENUM ACDK_SCENARIO_ID_ENUM2 E8 X% b2 R7 G8 \) i* e5 _, W3 x
typedef enum{
& L! o0 S& ?7 ^& T ACDK_SCENARIO_ID_CAMERA_PREVIEW=0,
5 [1 h/ }4 j* z& q+ a ACDK_SCENARIO_ID_VIDEO_PREVIEW,8 m% N0 J7 l ?
ACDK_SCENARIO_ID_VIDEO_CAPTURE_MPEG4,9 Q; I" R0 G5 }1 e
ACDK_SCENARIO_ID_CAMERA_CAPTURE_JPEG,
/ p/ y9 A3 A) h4 @6 y% I6 d [1 h ACDK_SCENARIO_ID_CAMERA_CAPTURE_MEM,
" K7 [- i, |4 T# l ACDK_SCENARIO_ID_CAMERA_BURST_CAPTURE_JPEG,
* Y0 h% {5 T* U7 ]; { ACDK_SCENARIO_ID_VIDEO_DECODE_MPEG4,$ M4 [" @2 a1 z( L6 W
ACDK_SCENARIO_ID_VIDEO_DECODE_H263,$ d. U- _7 `( u7 O5 V9 v$ }, }5 J
ACDK_SCENARIO_ID_VIDEO_DECODE_H264,
: Y. ~" G0 L; n2 [# G ACDK_SCENARIO_ID_VIDEO_DECODE_WMV78,
8 S) h4 S+ a7 u$ \/ b8 n ACDK_SCENARIO_ID_VIDEO_DECODE_WMV9,
! G1 S s' ~: y( p& f0 f ACDK_SCENARIO_ID_VIDEO_DECODE_MPEG2,( D$ @4 \- S ~. @- a# ?/ `
ACDK_SCENARIO_ID_IMAGE_YUV2RGB,
% K. @1 K) u3 K ACDK_SCENARIO_ID_IMAGE_RESIZE,! L% ~+ m; G! V, l! N; B1 {
ACDK_SCENARIO_ID_IMAGE_ROTATE,/ H. P4 s, i) Y6 ]: J1 M
ACDK_SCENARIO_ID_IMAGE_POST_PROCESS,9 P+ t8 s; V2 C2 k5 L) m9 U
ACDK_SCENARIO_ID_JPEG_RESIZE,; t% C7 K- a! O9 B2 E, y) O X% n
ACDK_SCENARIO_ID_JPEG_DECODE,% B' g. N! M7 v; ]: o
ACDK_SCENARIO_ID_JPEG_PARSE,% |1 l7 i1 W4 c7 u6 K
ACDK_SCENARIO_ID_JPEG_ENCODE,
! _$ `5 c- s" i; q6 D8 V7 B% Q% m5 x ACDK_SCENARIO_ID_JPEG_ENCODE_THUMBNAIL, g, q% x, q1 v7 U: H
ACDK_SCENARIO_ID_DRIVER_IO_CONTROL,
3 m" a# j( X# i- p* l ACDK_SCENARIO_ID_DO_NOT_CARE,
+ ^5 R$ t% J9 ]. r" F% b L9 m ACDK_SCENARIO_ID_IMAGE_DSPL_BUFFER_ALLOC,& _* h( B$ ]# u( y' @- k$ T
ACDK_SCENARIO_ID_TV_OUT,
7 E' U4 d% z8 S' j ACDK_SCENARIO_ID_MAX,8 B( I6 ]; i# j K! i* S) F" L+ c
ACDK_SCENARIO_ID_VIDOE_ENCODE_WITHOUT_PREVIEW,
: ~. r+ l/ V* j ACDK_SCENARIO_ID_CAMERA_CAPTURE_JPEG_BACK_PREVIEW,
! H* @/ t3 x/ {$ f( K0 B ACDK_SCENARIO_ID_VIDEO_DECODE_RV8,6 p0 _4 C7 J! ]' i6 G( W
ACDK_SCENARIO_ID_VIDEO_DECODE_RV9,
; z% `0 y% u* _" [3 b( h6 R ACDK_SCENARIO_ID_CAMERA_ZSD,0 y; H. e8 Z3 S) i- N& q( L
}ACDK_SCENARIO_ID_ENUM;" E' q8 S2 O2 n: f: \# q- R/ t/ T( Y
通过这个数组定义 Camera 的各种模式,并且给他们从 0 开始给一个模拟的 ID,通过这个ScenarioID 来控制 Camera 的工作模式是在拍照、摄像等等。
# Q% z; j3 l6 K 想要了解*pSensorInfo 这个指针的内容就得看 MSDK_SENSOR_INFO_STRUCT 的定义
& K( x, F. L9 m0 w3 ]( e+ C #define MSDK_SENSOR_INFO_STRUCT ACDK_SENSOR_INFO_STRUCT
( H `9 t; t! H9 g- X# E" }$ e' A6 l- s# {# h6 z# h" w% Y; b" y
|
|