|
|
一、 手机 Camera 的物理结构:
! h; q0 M! x( O7 z/ [ 
! ]) k) B3 L: \1 }1 Z
! w% N* `+ J8 _" t! n6 ]二、 Camera 的成像原理:
/ n* M7 A# a. l& w' q8 {2 j& f. m" w8 o4 b" T. R
景物通过镜头(LENS)生成的光学图像投射到图像传感器(Sensor)表面上,然后转为模拟的电信号,经过 A/D(模数转换)转换后变为数字图像信号,再送到数字信号处理芯片(DSP)中加工处理,再通过 IO 接口传输到 CPU 中处理,通过 LCD 就可以看到图像了。; [- |2 X/ H$ F8 y9 v5 F
& x; C! N& D$ D/ X: P( o
8 h3 y8 L, q' W; P# S% k+ r1 j
图像传感器(SENSOR)是一种半导体芯片,其表面包含有几十万到几百万的光电二极管。光电二极管受到光照射时,就会产生电荷。目前的 SENSOR 类型有两种:. q! p O) S. A( N
CCD(Charge Couple Device),电荷耦合器件,它是目前高像素类 sensor 中比较成熟的成像器件,是以一行为单位的电流信号。& d* L \5 r5 p! O* V( X
CMOS(Complementary Metal Oxide Semiconductor),互补金属氧化物半导体。CMOS的信号是以点为单位的电荷信号,更为敏感,速度也更快,更为省电。) |0 d! r. N' G2 G" z( V+ Y& I2 G
ISP 的性能是决定影像流畅的关键,JPEG encoder 的性能也是关键指标之一。而 JPEG encoder 又分为硬件 JPEG 压缩方式,和软件 RGB 压缩方式。
7 Q1 r6 @+ p5 y* \5 ?' [% e DSP 控制芯片的作用是:将感光芯片获取的数据及时快速地传到 baseband 中并刷新感光芯片,因此控制芯片的好坏,直接决定画面品质(比如色彩饱和度、清晰度)与流畅度。9 |9 i9 j# \# \: I ^9 X
$ q! k/ S/ H* N! C) f) R
三、 Camera 常见的数据输出格式:/ |' v0 M) Z' k6 c5 b
常见的数据输出格式有:Rawdata 格式、YUV 格式、RGB 格式。
. g: O* Z1 f% J0 } RGB 格式:采用这种编码方法,每种颜色都可用三个变量来表示红色、绿色以及蓝色的强度。每一个像素有三原色 R 红色、G 绿色、B 蓝色组成。; N& K. i% n6 `
YUV 格式:其中“Y”表示明亮度(Luminance 或 Luma),就是灰阶值;而“U”和“V”表示色度(Chrominance 或 Chroma),是描述影像色彩及饱和度,用于指定像素的颜色。
9 v5 J0 h( y7 B/ [" V RAW DATA 格式:是 CCD 或 CMOS 在将光信号转换为电信号时的电平高低的原始记录,单纯地将没有进行任何处理的图像数据,即摄像元件直接得到的电信号进行数字化处理而得到的。
^4 ?9 I# `! `7 j, g 支持 YUV/RGB 格式的模组,一般会在模组上集成 ISP(Image Single Processor),经过A/D 转换过的原始数据经过 ISP 处理生成 YUV 标准格式传到 BB。一般来说,这种设计适用于低像素 Camera 的要求,会在主板上省去一个 DSP,可降低成本。在调试过程中,YUV/RGB 格式的摄像头,其所有参数都可在 kernel 层通过寄存器来控制。调试一般由 sensor的原厂支持。
" C8 c# b, O5 n9 `; L( N 支持 RawData 格式的模组,由于感光区域的需求,不会再模组内集成 ISP 以最大程度的增大感光区域的面积,提高照片质量。模组把原始的数字信号传给 BB 上的 DSP 进行处理,MTK 自带的 DSP 一般包含 ISP、JPEG encoder、和 DSP 控制芯片。在调试的时候图像的效果需要 MTK 在 HAL 层的参数进行支持。
. }; W7 |6 g/ u% `. P7 P6 ^; i9 M S
四、 阅读 Camera 的规格书(以 Truly 模组 OV5647_Raw 为例):$ B* ?7 r6 x2 R/ ^7 Q/ x; b
 * @2 `' G3 b9 i9 z H
2 P- B) C. {$ ~9 U9 b% r- e9 t
- C; P. k6 v$ z- D五、 Camera 的硬件原理图及引脚:; S* S/ O1 [- P" P8 N0 t0 h+ ]" Y

* H1 {8 X1 b. {7 `$ D 从上面可看出,连接 Camera 的 30 根 Pin 脚可大致分为以下几类:
7 I/ B! h p3 o5 j3 O9 n: f 1、电源部分:. I4 P/ H0 T( N7 }# e
a)VCAMD 就是 DVDD 数字供电,主要给 ISP 供电,由于 RAWDATA格式的sensor其ISP是在 BB 端,所以将其引脚将其 NC。从上面的规格书上可以看出 DVDD 是内部 BB 端供电。模组已将其 NC 掉了;" A7 F$ g$ V* p1 G- a& H5 X0 m
b) VCAM_IO 就是 VDDIO 数字 IO 电源主要给 I2C 部分供电;
\9 C) W% ~$ D x c) VCAMA 就是 AVDD 模拟供电,主要给感光区和 ADC 部分供电;, i+ f* h" D* T7 M
d) VCAM_AF 是对 Camera 自动对焦马达的供电。
3 ?0 |, u* ` N% V; I: j+ m9 H 2、Sensor Input 部分:6 a. p" x: r' m/ G, A' e
a) Reset 信号,用于复位、初始化。
+ U5 o: x+ f5 b( w4 x b) Standby/PowerDown 信号,用于进入待机模式,降低功耗。
" e- S3 _% R1 k$ h( y. c c) Mclk,即 MasterClock 信号,是由 BB 端提供。
1 `# s# d+ V1 p& K2 }: u* C 3、Sensor OutPut 部分:
3 z# V6 w" J2 E- b6 P6 [- S a)Pclk,即 PixelClock 信号,由 MCLK 分频得到,作为外部时钟控制图像传输帧率/ x+ z8 e9 E+ Y. G
b) HSYNC,行同步信号,其上升沿表示新一列行图像数据的开始。+ f% A7 w# y$ z% O% }* _8 p+ M
c) VSYNC,帧同步信号,其下降沿表示新的一帧图片的开始。
2 E/ M3 Y" v: e7 A5 |) {/ G/ h d) D0-D9 一共 10 根数据线(8/10 根等);+ Z: n: i9 I. J9 u9 h3 ~) \
4、I2C 部分:SCL,I2C 时钟信号线和 SDA,I2C 数据信号线。# z- J8 D$ M O3 c7 J0 K- N
/ ^6 |& O0 ~( ]* A* D
0 \$ e6 Z0 O8 [, w
六、 MTK 平台 Camera 相关代码文件(以下代码均为 MTK6575 平台)
. s$ b4 t* B* \3 ` 1、 CameraSensor 驱动相关文件2 i: W0 D7 \! H5 `$ n7 L" @/ ]
* I$ J. |+ i# ^$ \/ G2 Q
2、 Sensor ID 和一些枚举类型的定义# N. `' g e p) }% B
4 S6 K- w _: v, G; J. F7 P
3、 Sensor 供电
1 X4 }6 m# [9 ~" X( g
A6 W- M& m+ ~; R 
1 s; e! m) Y! D( `" j. \: m1 d 4、 Kernel Space 的 SensorList,imgsensor 模块注册6 p6 a0 q% [; v) }& L! K

: X7 W w) V5 t+ D* ~ 5、 User Space 的 SensorList,向用户空间提供支持的 SensorList
* |1 i$ j" O' w) o
6 \- H6 H M( t0 U1 ?" @' k& z( ]& W8 _% F
6、 Sensor 效果调整的接口
- ]' g ?! `9 ?# j, d) i$ A) D) S & h. X7 m6 s& |2 ^( y

6 H6 T' Q/ v }0 m" J7 ]9 [& w v% D! o$ S: |2 x
七、 Camera 模块驱动、设备与总线结构:1 T4 N" ]! Q. y3 M; B$ ~0 _" F9 x/ z
一般在 Linux 设备驱动模型中,我们只需要关心总线、设备、驱动这三个实体。总线会充当红娘对加载于其上的设备与驱动进行配对,对于 Camera 模块也不例外,下面从总线、设备、驱动的角度来分析 Camera 模块驱动的注册、匹配与加载过程。7 g) k+ l; J+ ]8 @
a) 驱动的注册:
( g5 _& Z9 G U2 \1 k& M3 h, o7 p; U 在(\custom\common\kernel\imgsensor\src\Kd_sensorlist.c)CAMERA_HW_i2C_init 这个函数里通过 Platform_driver_register(&g_stCAMERA_HW_Driver)把 Camera 模块驱动注册
( X- B" }: `# ]- N1 C& H到 Platform 总线上。而 g_stCAMERA_HW_Driver 是对结构体 Platform_driver 这个结构体的填充。
8 Q, |) Q* n6 K 
/ s! N. X: x3 j/ Q (Kernel\include\linux\Platform_device.h)
9 ?, ]- }1 E- l$ V 
; U5 ?2 e" O/ z7 ] b) 设备的注册:8 R0 d9 e, ?5 B e! f" |
对 platform_device 的定义通常在 BSP 的板级文件:( R* K9 ` r5 G) Y0 C1 h+ S
(kernel\arch\sh\boards\mach-ap325rxa\Setup.c)中实现,在板级文件中,将 platform_device归纳为一个数组,最终通过 platform_add_device()函数统一注册:
7 c X8 \1 m9 T, ]5 {/ S8 s! Z) L0 K , ^5 D, S( B N1 r0 w

' v" h' ^* n- H A- ?# ^/ \ 
4 [: S2 s- K3 H1 G, y! n& N c) 总线的匹配:( p. Y* p Y0 [+ O7 G
既 然 是 驱 动 Platform_device 那 对 应 的 设 备 必 然 是 挂 载 Platform 总 线 上 的Platform_device,Platform 总线是 Linux 系统提供的一种机制,不同于 I2C、I2S 等总线,它
3 P+ ^) U% ?7 l/ f, [& R! h9 q是一种虚拟的总线。Linux 系统为 Platform 总线定义了一个 bus_type 的实例 Platform_bus_type:
% z# f& \% ^. y7 |3 l0 L2 t8 M (Kernel\drivers\base\platform.c)
i) `. r3 p' C- g3 d6 Z + q( F. {% l( X% d! V& M
Platform 总线通过 platform_match 这个成员函数来确定 platform_device 与 platform_driver 如何进行匹配:6 C" H7 m. u0 D
: V7 X$ ^( Y z0 N
" @" u1 N" X( J2 {! b八、 Camera 驱动工作流程:
) m0 v/ ^# V E% H' {) t' @0 i' B3 z) o3 V8 T9 \* L8 B# i( y
' ?* u" `1 K& t4 n9 i, V
从上图可以清晰的了解到 Camera 的一个工作流程主要分为这么七步:+ O/ s, j# d8 s6 [2 O4 N
1. 打开 Camera Power LDO,让 Camera 有能量保证。& M8 u; n8 ^6 t9 o; y% c) ^
2. 打开 IIC,设置 PDN 引脚,使 Camera 退出 Standby 模式,按照要求让 Reset 脚做一个复位动作。
: X$ q* g! }) w# c6 ^0 L; Q 3. 读一下 sensor 的版本 ID,这样可以让你确认是否连接上你想要的 sensor。
$ n" k) j4 K# W* Z2 K 4. 对 Sensor 进行初始化下载最基本的参数让 Sensor 工作起来,可能包括软复位。
0 N m" U4 J, x! ]9 `7 I 5. 下载 preview 的参数,为预览动作准备。
4 {( \2 ~8 W1 A4 e8 c. a 6. 下载 Capture 的参数,为拍照动作准备。
4 g% C* i+ l0 _0 i4 m 7. 设置 PDN 引脚,使 Sensor 进入 Standby 模式, 或者关掉 LDO 等动作,退出 Camera。
- r( N$ o/ \' Z# d X. Q 我们都知道,Linux 内核是通过模块的机制来加载设备驱动的,那么接下来我们就从设备模块加载的角度来看下 Camera 工作流程的驱动代码是如何工作的。% S9 c9 U3 e5 y0 w
在-alps\mediatek\custom\common\kernel\imgsensor\src\kd_sensorlist.c 中可以看到:# |9 J( e# `) ]" C h: l
module_init(CAMERA_HW_i2C_init); b5 o d/ v0 O. e
module_exit(CAMERA_HW_i2C_exit);
; a/ u! t3 O7 p5 |1 c# S- g6 T 在这里 Linux 内核加载和卸载 Camera 模块。
* O$ g- B" h& i static struct platform_driver g_stCAMERA_HW_Driver = {. \' n+ B$ Y+ G4 [, t
.probe = CAMERA_HW_probe,
0 O8 P8 i% K1 w* ^, R+ P .remove = CAMERA_HW_remove,( i7 w6 O! Y: P
.suspend = CAMERA_HW_suspend,
6 s3 q6 N8 z- V! c6 Q# O! U( Z( r .resume = CAMERA_HW_resume,
6 O, u. }' d+ P- s# s) x .driver ={
- U& r X- `6 c7 h b .name = "image_sensor",
2 F/ N \- o! r# S8 | .owner = THIS_MODULE,$ L3 p# g) q5 S% E- @# t. ]% a
}; t9 l- ]+ y+ J# j
};
, ?' l& }+ C- t9 {! I Camera 模块初始化开始向总线注册驱动,在 Platform_driver 的成员函数.probe()中,通过 i2c_add_driver(&CAMERA_HW_i2c_driver)向 I2C 申请,而 CAMERA_HW_i2c_driver 这个结构体里填充的是将 Camera 作为一个字符设备在 I2C 上进行注册:
: b7 I+ E" ]* Z. ?% i; \" G/ A! D1 B) b 8 N: i# V8 u5 e
6 _8 M& c2 G* Y
在 RegisterCAMERA_HWCharDrv()中cdev_init(g_pCAMERA_HW_CharDrv, &g_stCAMERA_HW_fops);对设备进行初始化,并将g_stCAMERA_HW_fops 这个文件操作函数作为上层对 Camera 设备操作的接口留给上层进行调用:6 U3 u) { v4 E( @

$ D3 W1 s( J6 {9 m0 v- q 其中成员函数 open()只是初始化一个原子变量留给系统调用。ioctl()才是整个 Camera驱动的入口:
3 E6 E* W/ g' @7 _* M 
/ E. x8 C' q( }, l- u3 |9 c CAMERA_HW_Ioctl()是上层文件操作系统操作底层硬件的方法,它先对 Camera 需要的Buffer 做一个初始化,然后建立对 Cameraopen、getinfo 等操作的接口:/ B/ k# `- C& o" p+ B8 ^# B

" k& t& W1 M5 H/ _7 q% h$ m3 f 通过判断 Sensor 状态的逻辑值来进行具体的操作,对于这个值的定义在:
. v: @* x# n2 U" n9 e$ P8 _6 i Mediatek\custom\common\kernel\imgsensor\inc\Kd_imgsensor.h 中6 \2 B; z* z6 f
# _6 G4 j6 j+ f. b. g, z
在 KdSetDriver()中通过判断 name 和 ID 匹配具体型号的 sensor 的驱动,判断它是主摄还是次摄,并对它进行初始化:
. f! d9 E9 D' N" w2 ?! u5 z + S" U7 J0 f. l) W- c

, w8 o [# V( D+ q% ? 通过 NAME 和 ID 匹配完成后会将 PSENSOR_FUNCTION_STRUCT *pfFunc 这个结构体匹配到具体型号的驱动代码中:& B* \* j6 { e* t! a f* y
3 i( _6 _& F- I9 I! T5 T4 u
到这里,整个 Camera 驱动从总线注册到完成具体 sensor 的初始化的流程就完成了,CAMERA_HW_Ioctl()中其他的 ioctl 操作函数最后都会在$sensor$_sensor.c 中实现。
" q: S8 F5 a0 O: X7 s; ^
8 j7 m9 z5 [: M7 M# x! P九、 Camera 驱动添加、调试流程:
5 ?0 b8 i$ }2 j0 ]% n 1、 修改系统配置文件 ProjectConfig.mk:7 {; _& _( L4 t: y. O; U8 X
-alps\mediatek\config\$project$\ProjectConfig.mk
2 A5 d% {: l$ j7 \$ O% [ ) f; X3 R& ?% Q* S9 p/ l: [
" d9 C. a; Y1 M# d 2、 检查、配置供电文件:- F8 ~ p \; t8 q
-alps\mediatek\custom\$project$\Kernel\Camera\Camera\kd_camera_hw.c
8 `& L1 ~) \' W8 k# p Camera 供电流程(以 3M 前摄 MT9V114+5M 后摄 OV5647 为例):
1 h6 D/ L4 E Z) q; R, H 
' ?- R( L( W5 F1 W5 p! t1 t 其实在 kd_camera_hw.c 中只有一个函数 kdCISModulePowerOn(),在这个函数中需要注意的是通过 GPIO 口控制 PDN 和 RST 引脚的时候,对于其相关的定义,由其在切换平台的时候。例如 MT6573 和 MT6575 的定义顺序就不同
, s" S5 m Z3 s9 T8 _4 b, j % c# T! P* l" |" e' p
3、 添加 Camera 驱动(以 ov5647 为例):; p* I' a w, K& S
创建 SensorFuncOV5647 这样一个数据结构; Y! x' V1 w3 K8 m
SENSOR_FUNCTION_STRUCT SensorFuncOV5647={
& m6 S/ w, a \" S9 H% ^ OV5647Open,
0 P# s! A# m& u1 c OV5647GetInfo,
- y0 ]. x9 I1 S8 J( ?+ e+ |. Q; u8 p3 Y OV5647GetResolution,
/ q% V2 O; u. g3 l OV5647FeatureControl,
$ [$ g; I4 r5 g2 a* w/ Y OV5647Control,5 b% {) P8 z! v; G- D
OV5647Close! T3 U/ X2 H$ y- |' g0 R/ _' s
};1 X; g1 Q0 W, T' ^9 w! A
a)OV5647Open
3 D4 m& e, {4 y# ~, N ! o9 O( b d9 q5 f1 K
初始化操作就是对 SensorIC 中寄存器的操作,调试主要由 IC 原厂支持。Open 函数结束后返回 ERROR_NONE 表示初始化成功,可以正常使用
' k# U3 ^2 R& i! }- m9 k, M' P! m0 c6 y: c3 k
b)OV5647GetInfo# @9 f7 C' ]) y ]8 j5 H/ c5 w
' E: r, H# Y1 C' t, g
UINT32 OV5647GetInfo(MSDK_SCENARIO_ID_ENUM ScenarioId,MSDK_SENSOR_INFO_STRUCT *pSensorInfo,MSDK_SENSOR_CONFIG_STRUCT *pSensorConfigData)
% U3 a$ @3 G9 e( q 第一个参数 ScenarioId 来自于 MSDK_SCENARIO_ID_ENUM 这个数组,在kd_imgsensor_define.h 中是这样定义的:
; `! K% G. f8 T% }. w #define MSDK_SCENARIO_ID_ENUM ACDK_SCENARIO_ID_ENUM
0 I1 r" `& n% Y( f typedef enum{
; p7 Q; h( L8 q, I/ ~! c, j ACDK_SCENARIO_ID_CAMERA_PREVIEW=0,
4 J$ U( [/ p9 }7 Z9 d, B ACDK_SCENARIO_ID_VIDEO_PREVIEW,3 r% b% ^+ p5 c( {
ACDK_SCENARIO_ID_VIDEO_CAPTURE_MPEG4,/ g8 C8 H/ l2 j1 [/ @/ ~- Q4 i
ACDK_SCENARIO_ID_CAMERA_CAPTURE_JPEG,
0 t2 o* I+ q1 y% b% l0 l5 D+ y ACDK_SCENARIO_ID_CAMERA_CAPTURE_MEM,. Z# R0 R- l* ~, r! K: ]( P& _
ACDK_SCENARIO_ID_CAMERA_BURST_CAPTURE_JPEG,5 b% Q" O, |5 S) T x3 a) s' G
ACDK_SCENARIO_ID_VIDEO_DECODE_MPEG4,
6 @% J2 T' }: K" b4 a* B ACDK_SCENARIO_ID_VIDEO_DECODE_H263,% z( _4 s! V6 w' Z: H
ACDK_SCENARIO_ID_VIDEO_DECODE_H264,2 O+ U; N( b; w& ~7 o; x* R9 m
ACDK_SCENARIO_ID_VIDEO_DECODE_WMV78,6 O2 a3 j W+ @
ACDK_SCENARIO_ID_VIDEO_DECODE_WMV9," Z. f6 j# k# V p
ACDK_SCENARIO_ID_VIDEO_DECODE_MPEG2,
. `: k$ g! b `0 c9 n0 U ACDK_SCENARIO_ID_IMAGE_YUV2RGB,; C0 A( i5 n7 p+ X% N' y7 r" C
ACDK_SCENARIO_ID_IMAGE_RESIZE,
2 Z1 X/ c, D, r ACDK_SCENARIO_ID_IMAGE_ROTATE,
7 v5 C' b! Q2 j6 k5 x2 i ACDK_SCENARIO_ID_IMAGE_POST_PROCESS,
- C7 o" c1 {- O" R0 m ACDK_SCENARIO_ID_JPEG_RESIZE,
4 Y# @5 `3 i* X7 @/ x1 ]& { ACDK_SCENARIO_ID_JPEG_DECODE,( c) \% `5 R# T2 O8 B
ACDK_SCENARIO_ID_JPEG_PARSE,' N8 A1 @5 O3 P: d) u9 r# c
ACDK_SCENARIO_ID_JPEG_ENCODE,
& K o6 U# u+ m' r ACDK_SCENARIO_ID_JPEG_ENCODE_THUMBNAIL,4 d* }; J( w: x5 k$ R. i
ACDK_SCENARIO_ID_DRIVER_IO_CONTROL,
# C, V& v% W# ^ ACDK_SCENARIO_ID_DO_NOT_CARE,
! X* M$ N) a) } ACDK_SCENARIO_ID_IMAGE_DSPL_BUFFER_ALLOC,4 E) {3 Q) ?$ E/ J
ACDK_SCENARIO_ID_TV_OUT,
! C/ C' I! L( f5 O0 w/ w7 ^ ACDK_SCENARIO_ID_MAX,
% Y) u+ k/ X' M' G9 C ACDK_SCENARIO_ID_VIDOE_ENCODE_WITHOUT_PREVIEW,' n @) s: t% }7 V# t% Z& y
ACDK_SCENARIO_ID_CAMERA_CAPTURE_JPEG_BACK_PREVIEW,
9 s: K7 S) s2 {+ V9 P, y8 O! N ACDK_SCENARIO_ID_VIDEO_DECODE_RV8,
p, K' G+ i: o* m ACDK_SCENARIO_ID_VIDEO_DECODE_RV9,% E6 `# j2 U7 ?' [
ACDK_SCENARIO_ID_CAMERA_ZSD,% e/ X# \9 ~& z! O+ r
}ACDK_SCENARIO_ID_ENUM;4 F& q F1 @0 X8 G/ R: f
通过这个数组定义 Camera 的各种模式,并且给他们从 0 开始给一个模拟的 ID,通过这个ScenarioID 来控制 Camera 的工作模式是在拍照、摄像等等。
& a) U# f. B9 P: `, I 想要了解*pSensorInfo 这个指针的内容就得看 MSDK_SENSOR_INFO_STRUCT 的定义 J3 E& [8 W* Z5 `% ^% x
#define MSDK_SENSOR_INFO_STRUCT ACDK_SENSOR_INFO_STRUCT
2 N6 N2 i! n4 h* V6 S5 |- G( H1 i* n9 B- h
|
|