|
|
一、 手机 Camera 的物理结构:+ Z- l7 I! _& f0 g. T
7 e# p2 v" Y8 a6 i
6 g s, K) V: J" {' j4 x
二、 Camera 的成像原理:% O. A U& q/ z m$ l& C
, K! T; x) T( u 景物通过镜头(LENS)生成的光学图像投射到图像传感器(Sensor)表面上,然后转为模拟的电信号,经过 A/D(模数转换)转换后变为数字图像信号,再送到数字信号处理芯片(DSP)中加工处理,再通过 IO 接口传输到 CPU 中处理,通过 LCD 就可以看到图像了。
' a/ \7 G) w5 C) v5 H% A$ ^
; f: j- u+ @& V( V2 d0 M+ z; f 
$ v( a/ C7 z: R. g: Y+ N0 I% S 图像传感器(SENSOR)是一种半导体芯片,其表面包含有几十万到几百万的光电二极管。光电二极管受到光照射时,就会产生电荷。目前的 SENSOR 类型有两种:/ q. [0 e; Q4 z5 O" U
CCD(Charge Couple Device),电荷耦合器件,它是目前高像素类 sensor 中比较成熟的成像器件,是以一行为单位的电流信号。' p. M: p" o2 L8 y4 ~8 ?4 c) j) ~
CMOS(Complementary Metal Oxide Semiconductor),互补金属氧化物半导体。CMOS的信号是以点为单位的电荷信号,更为敏感,速度也更快,更为省电。
- Y6 \+ b, @+ B3 ^ T ISP 的性能是决定影像流畅的关键,JPEG encoder 的性能也是关键指标之一。而 JPEG encoder 又分为硬件 JPEG 压缩方式,和软件 RGB 压缩方式。+ Z: z; n6 Z+ B8 l% l
DSP 控制芯片的作用是:将感光芯片获取的数据及时快速地传到 baseband 中并刷新感光芯片,因此控制芯片的好坏,直接决定画面品质(比如色彩饱和度、清晰度)与流畅度。
) O: a' Z9 U% _, q) ?% Z8 H# ?' L+ e/ V- I3 t& Q' M. v
三、 Camera 常见的数据输出格式:/ V# o; g( V% u% _9 \- W: H9 b
常见的数据输出格式有:Rawdata 格式、YUV 格式、RGB 格式。
) \" S3 u. Z& z6 ] RGB 格式:采用这种编码方法,每种颜色都可用三个变量来表示红色、绿色以及蓝色的强度。每一个像素有三原色 R 红色、G 绿色、B 蓝色组成。
7 Z- O0 x! O0 s2 T d! c YUV 格式:其中“Y”表示明亮度(Luminance 或 Luma),就是灰阶值;而“U”和“V”表示色度(Chrominance 或 Chroma),是描述影像色彩及饱和度,用于指定像素的颜色。+ D' _# ]5 K6 D4 M; e6 r0 [, `3 e# S
RAW DATA 格式:是 CCD 或 CMOS 在将光信号转换为电信号时的电平高低的原始记录,单纯地将没有进行任何处理的图像数据,即摄像元件直接得到的电信号进行数字化处理而得到的。/ K, ?: X7 q1 K* {9 w
支持 YUV/RGB 格式的模组,一般会在模组上集成 ISP(Image Single Processor),经过A/D 转换过的原始数据经过 ISP 处理生成 YUV 标准格式传到 BB。一般来说,这种设计适用于低像素 Camera 的要求,会在主板上省去一个 DSP,可降低成本。在调试过程中,YUV/RGB 格式的摄像头,其所有参数都可在 kernel 层通过寄存器来控制。调试一般由 sensor的原厂支持。% J1 M( {9 u% D! \ d. T' D
支持 RawData 格式的模组,由于感光区域的需求,不会再模组内集成 ISP 以最大程度的增大感光区域的面积,提高照片质量。模组把原始的数字信号传给 BB 上的 DSP 进行处理,MTK 自带的 DSP 一般包含 ISP、JPEG encoder、和 DSP 控制芯片。在调试的时候图像的效果需要 MTK 在 HAL 层的参数进行支持。$ Q6 ?1 @6 X5 g# Y
& r7 O0 l! ]2 e* ]% p
四、 阅读 Camera 的规格书(以 Truly 模组 OV5647_Raw 为例):- g0 P9 c3 [/ i
 
5 I/ W2 @8 }9 L7 |6 v! G 5 x/ k1 c* f& u
: U- y& u7 N+ o u3 `& l/ p: o# o
五、 Camera 的硬件原理图及引脚:
7 o6 d/ j' Z2 t9 f+ F 
/ k( I4 @" b/ O0 R. Y2 m 从上面可看出,连接 Camera 的 30 根 Pin 脚可大致分为以下几类:" b ]' H8 X8 y/ z9 h. p% a. }# L0 N
1、电源部分:/ Z) d1 M ] M! X- X
a)VCAMD 就是 DVDD 数字供电,主要给 ISP 供电,由于 RAWDATA格式的sensor其ISP是在 BB 端,所以将其引脚将其 NC。从上面的规格书上可以看出 DVDD 是内部 BB 端供电。模组已将其 NC 掉了;& s7 M1 \/ S4 H5 w7 ^ y: I3 ^
b) VCAM_IO 就是 VDDIO 数字 IO 电源主要给 I2C 部分供电;. Z( t1 P( U) }! L3 Q$ c
c) VCAMA 就是 AVDD 模拟供电,主要给感光区和 ADC 部分供电;2 t5 i0 O$ {! }6 u4 I
d) VCAM_AF 是对 Camera 自动对焦马达的供电。. x8 S# k8 A$ s) ?' X I
2、Sensor Input 部分:* q( H6 A% x, T# ?& K
a) Reset 信号,用于复位、初始化。/ i# \, {+ h2 Q! D+ G9 ]
b) Standby/PowerDown 信号,用于进入待机模式,降低功耗。2 W: [5 o5 a. N* u w3 X
c) Mclk,即 MasterClock 信号,是由 BB 端提供。
- L, D; C. p; q6 E- `! f& y 3、Sensor OutPut 部分:1 c/ ]5 d7 H7 e, c. U G
a)Pclk,即 PixelClock 信号,由 MCLK 分频得到,作为外部时钟控制图像传输帧率
' O- E" y5 Y* p& v- E b) HSYNC,行同步信号,其上升沿表示新一列行图像数据的开始。
1 X3 m: H9 G' A$ J6 R, F c) VSYNC,帧同步信号,其下降沿表示新的一帧图片的开始。' P- Z: i& {, i( ^& j$ b0 l
d) D0-D9 一共 10 根数据线(8/10 根等);
- T& l% o: F9 z' |$ l 4、I2C 部分:SCL,I2C 时钟信号线和 SDA,I2C 数据信号线。4 V2 y+ n2 l8 V. e' T: p* r
/ ~1 E) l, R5 K: w$ D! N
, H5 q1 Z- d7 B+ C
六、 MTK 平台 Camera 相关代码文件(以下代码均为 MTK6575 平台)8 ]& ]# d4 X z- z
1、 CameraSensor 驱动相关文件
; t+ ?, D, I1 w( Z5 q; J( [ 1 c: E5 r& m. S, Z2 W
2、 Sensor ID 和一些枚举类型的定义* k2 P' e- R- e4 j0 C

- N- u; U0 j( Z! a 3、 Sensor 供电, S6 L8 r! f4 q8 d" B% t
; B" O) ]! Q: x. Y 
$ |: w( \' P( i" X 4、 Kernel Space 的 SensorList,imgsensor 模块注册- ^: y3 v1 b) V j) D" r

d* \+ M7 b$ q& R+ H 5、 User Space 的 SensorList,向用户空间提供支持的 SensorList
" P9 f) l5 t4 t 1 k- \9 R* m8 Z; H2 V( T
; X& Y2 {4 A1 h9 h5 f/ k 6、 Sensor 效果调整的接口1 C1 `+ [- B! o6 s0 e: x7 I

7 k6 V4 Z' z7 v: N 
7 l! O( t$ a! O' `" B$ i
6 s; B* f7 m# u) E七、 Camera 模块驱动、设备与总线结构:
5 r/ Q! v4 G A: Y 一般在 Linux 设备驱动模型中,我们只需要关心总线、设备、驱动这三个实体。总线会充当红娘对加载于其上的设备与驱动进行配对,对于 Camera 模块也不例外,下面从总线、设备、驱动的角度来分析 Camera 模块驱动的注册、匹配与加载过程。( r3 n; |) }1 T5 W) k
a) 驱动的注册:
( Q/ I; s8 S( c6 c( S) e' v 在(\custom\common\kernel\imgsensor\src\Kd_sensorlist.c)CAMERA_HW_i2C_init 这个函数里通过 Platform_driver_register(&g_stCAMERA_HW_Driver)把 Camera 模块驱动注册" p# s# s) Y3 v6 g2 L+ |
到 Platform 总线上。而 g_stCAMERA_HW_Driver 是对结构体 Platform_driver 这个结构体的填充。
) V9 A8 v, U4 v' }# A# f, g/ c 
4 C* {3 Q2 } n# F9 P( K% `4 \! O' U (Kernel\include\linux\Platform_device.h)
' R( K( |) K$ Q# F : `3 W' r* g) Y
b) 设备的注册:
& m0 z. e+ i X6 N% e 对 platform_device 的定义通常在 BSP 的板级文件:
0 D- Z- _% {5 K5 _ (kernel\arch\sh\boards\mach-ap325rxa\Setup.c)中实现,在板级文件中,将 platform_device归纳为一个数组,最终通过 platform_add_device()函数统一注册:* L: `! F, B6 L0 R/ s( l* A d, q3 s
3 V) f6 M. M0 G- Z& _$ C

1 s" @; G9 J; s, ` 
, ?0 b: j8 ^$ r- a& L: w) u c) 总线的匹配:- |/ k# B$ ?% `0 ~
既 然 是 驱 动 Platform_device 那 对 应 的 设 备 必 然 是 挂 载 Platform 总 线 上 的Platform_device,Platform 总线是 Linux 系统提供的一种机制,不同于 I2C、I2S 等总线,它' C; o5 Z0 h- p7 y( v8 H
是一种虚拟的总线。Linux 系统为 Platform 总线定义了一个 bus_type 的实例 Platform_bus_type:
* f$ Q/ y% O1 ^; Y& D (Kernel\drivers\base\platform.c)
% e, [; c; M$ u& F# L$ N ) I. m( g. A+ g" Y _7 M
Platform 总线通过 platform_match 这个成员函数来确定 platform_device 与 platform_driver 如何进行匹配:
/ \1 \4 t( }" F3 N ( K. ^! s. C2 m
3 O9 F' t, p+ a0 D8 g0 j
八、 Camera 驱动工作流程:2 p- D2 x7 c+ N) L8 r' ?
3 P. V7 T$ _4 c: l2 W& O( ^" ? X
! D# G }( L) ?' { 从上图可以清晰的了解到 Camera 的一个工作流程主要分为这么七步:
; g3 g0 i+ `- F2 ?3 T 1. 打开 Camera Power LDO,让 Camera 有能量保证。0 C) v5 y" I/ Y& F& e) f4 r
2. 打开 IIC,设置 PDN 引脚,使 Camera 退出 Standby 模式,按照要求让 Reset 脚做一个复位动作。& A ?9 U9 ~7 l" n
3. 读一下 sensor 的版本 ID,这样可以让你确认是否连接上你想要的 sensor。5 m; N+ A* P& L# P8 r6 m
4. 对 Sensor 进行初始化下载最基本的参数让 Sensor 工作起来,可能包括软复位。* s( s( l: ]2 q
5. 下载 preview 的参数,为预览动作准备。+ X! i1 T7 U+ S& [0 d3 q* o
6. 下载 Capture 的参数,为拍照动作准备。
: j3 z1 `; v& L. k: s) j1 Y3 M 7. 设置 PDN 引脚,使 Sensor 进入 Standby 模式, 或者关掉 LDO 等动作,退出 Camera。
k, {6 V# L1 r" y$ o 我们都知道,Linux 内核是通过模块的机制来加载设备驱动的,那么接下来我们就从设备模块加载的角度来看下 Camera 工作流程的驱动代码是如何工作的。
1 B6 @6 |: f& e' k# ~ 在-alps\mediatek\custom\common\kernel\imgsensor\src\kd_sensorlist.c 中可以看到:
, j0 S2 H, } N4 }) i module_init(CAMERA_HW_i2C_init);
( K* C$ f5 W) U L module_exit(CAMERA_HW_i2C_exit);
! _0 t o0 h4 m) w8 K/ Q& _) ` 在这里 Linux 内核加载和卸载 Camera 模块。* W4 Q* H8 }# g- L
static struct platform_driver g_stCAMERA_HW_Driver = {
& x+ F8 s" I1 ~- [' n .probe = CAMERA_HW_probe,1 i8 b6 Y& }& K4 F/ b4 ~8 L
.remove = CAMERA_HW_remove,
/ p" V+ a# H# O5 V9 v' J7 ^ .suspend = CAMERA_HW_suspend,
1 y; H* l1 _: [9 I& W .resume = CAMERA_HW_resume,; M( w) T/ g) R7 i
.driver ={
8 t. J8 j. Z5 ?, m& G' d; a .name = "image_sensor",3 H, d( S* E! ~- }6 [
.owner = THIS_MODULE,
0 ^% O; l; W3 Y. v2 O5 C& ?( E# A }
3 T. N- [: e: p( D j: f; @8 P5 p };
; N/ u2 t& S8 b f6 k& k Camera 模块初始化开始向总线注册驱动,在 Platform_driver 的成员函数.probe()中,通过 i2c_add_driver(&CAMERA_HW_i2c_driver)向 I2C 申请,而 CAMERA_HW_i2c_driver 这个结构体里填充的是将 Camera 作为一个字符设备在 I2C 上进行注册:
( D% _; j$ o, `4 [: C * w3 P' V1 o- B* a# T9 ?
$ k$ \( \) u& D4 L# P
在 RegisterCAMERA_HWCharDrv()中cdev_init(g_pCAMERA_HW_CharDrv, &g_stCAMERA_HW_fops);对设备进行初始化,并将g_stCAMERA_HW_fops 这个文件操作函数作为上层对 Camera 设备操作的接口留给上层进行调用:
" n5 k2 t0 l1 _# g$ R 
4 Z. O6 \6 I: q$ A7 E2 H8 j 其中成员函数 open()只是初始化一个原子变量留给系统调用。ioctl()才是整个 Camera驱动的入口:# U, f2 I. F3 W0 C9 N+ i" B

1 q) B& m8 S6 b6 j D+ G CAMERA_HW_Ioctl()是上层文件操作系统操作底层硬件的方法,它先对 Camera 需要的Buffer 做一个初始化,然后建立对 Cameraopen、getinfo 等操作的接口:
+ W9 b; l0 U& E 3 @5 h! i7 u( @) x0 a7 d
通过判断 Sensor 状态的逻辑值来进行具体的操作,对于这个值的定义在:
0 j. G3 v. I" O Mediatek\custom\common\kernel\imgsensor\inc\Kd_imgsensor.h 中1 |% W5 O, h f9 \) z

! F6 Y% r- l/ I+ f( w 在 KdSetDriver()中通过判断 name 和 ID 匹配具体型号的 sensor 的驱动,判断它是主摄还是次摄,并对它进行初始化:7 R7 a2 W* A3 z: p* v% X

" Q& s4 ^* X+ F 
4 K& S3 f$ V" T) m% @ 通过 NAME 和 ID 匹配完成后会将 PSENSOR_FUNCTION_STRUCT *pfFunc 这个结构体匹配到具体型号的驱动代码中: A1 x- n0 M Q! R+ i9 D1 J3 Z
* g; H" u% o3 p2 x$ a2 P0 v
到这里,整个 Camera 驱动从总线注册到完成具体 sensor 的初始化的流程就完成了,CAMERA_HW_Ioctl()中其他的 ioctl 操作函数最后都会在$sensor$_sensor.c 中实现。- K' j$ x1 H4 B6 W V F2 _6 j6 l
1 ? ~, {$ h/ V8 H1 `6 I/ y九、 Camera 驱动添加、调试流程:) c# v) Y$ g! s) T, ?
1、 修改系统配置文件 ProjectConfig.mk:
; D& \0 A7 f% w/ h -alps\mediatek\config\$project$\ProjectConfig.mk) s8 U, @$ [8 z" l2 [0 u- d" A

9 W7 f7 o: g& \0 I: x T
4 a( W% p Z# v) _ 2、 检查、配置供电文件:
; ~8 Y' j" q @, K" _8 g -alps\mediatek\custom\$project$\Kernel\Camera\Camera\kd_camera_hw.c! F3 l# f8 o2 d$ l% r9 I1 x/ u
Camera 供电流程(以 3M 前摄 MT9V114+5M 后摄 OV5647 为例):" a+ T8 U# T# r7 w
3 L, K, A* ] W0 u6 M! @% H
其实在 kd_camera_hw.c 中只有一个函数 kdCISModulePowerOn(),在这个函数中需要注意的是通过 GPIO 口控制 PDN 和 RST 引脚的时候,对于其相关的定义,由其在切换平台的时候。例如 MT6573 和 MT6575 的定义顺序就不同) S9 J4 h+ d+ u6 X: ^

4 M. F9 t0 Y) X) K 3、 添加 Camera 驱动(以 ov5647 为例):
# H2 r2 y$ X& ?5 X& z( [; z: K 创建 SensorFuncOV5647 这样一个数据结构- K$ P( F" p# K
SENSOR_FUNCTION_STRUCT SensorFuncOV5647={
1 F- \: q, Z" Q2 ^ OV5647Open,* w/ [* R' m, r$ g f6 @( P5 l
OV5647GetInfo,
9 P. y9 ]& E* k OV5647GetResolution,
& r8 `& z2 w" Y0 [" U3 D) N OV5647FeatureControl,
# m" M, @+ v4 x. S! J OV5647Control,
! U6 q8 S* L2 v; F+ R& ]6 E OV5647Close) p( Y9 h7 {& W- g
};
; v2 Z6 q0 X! ~$ F a)OV5647Open
/ J( P5 M P9 ~3 | ( H7 P2 c( O' ~# ?7 C; b
初始化操作就是对 SensorIC 中寄存器的操作,调试主要由 IC 原厂支持。Open 函数结束后返回 ERROR_NONE 表示初始化成功,可以正常使用1 v* k/ d# {5 H
2 S7 c D0 N' R, D
b)OV5647GetInfo
' ~; m9 F9 o5 c: K/ A# w% I
- v" P! u Q9 [, P2 e. \ UINT32 OV5647GetInfo(MSDK_SCENARIO_ID_ENUM ScenarioId,MSDK_SENSOR_INFO_STRUCT *pSensorInfo,MSDK_SENSOR_CONFIG_STRUCT *pSensorConfigData)9 x% v- A5 A2 I# j! r" j
第一个参数 ScenarioId 来自于 MSDK_SCENARIO_ID_ENUM 这个数组,在kd_imgsensor_define.h 中是这样定义的:
. Z* L9 B$ \6 M8 k- f6 V #define MSDK_SCENARIO_ID_ENUM ACDK_SCENARIO_ID_ENUM
$ g" ~8 G& P1 x7 q+ J, P( U4 |8 P typedef enum{8 h4 G, a; r% W* r& G4 h
ACDK_SCENARIO_ID_CAMERA_PREVIEW=0,+ X( P2 D- A! M( m+ b
ACDK_SCENARIO_ID_VIDEO_PREVIEW, e6 H" K, u( D9 x! ^9 ~
ACDK_SCENARIO_ID_VIDEO_CAPTURE_MPEG4,
8 W0 i( X" f0 \+ `* f5 Z$ m ACDK_SCENARIO_ID_CAMERA_CAPTURE_JPEG,
, n# H3 K! w+ x8 a% K/ E! G ACDK_SCENARIO_ID_CAMERA_CAPTURE_MEM,
; G+ e% h9 m2 N+ k8 u. K L7 ? ACDK_SCENARIO_ID_CAMERA_BURST_CAPTURE_JPEG,
2 O. y' V! K' R, X ACDK_SCENARIO_ID_VIDEO_DECODE_MPEG4,& A3 Q4 `/ M9 z, {
ACDK_SCENARIO_ID_VIDEO_DECODE_H263,6 [+ U# r3 u$ z$ v; y
ACDK_SCENARIO_ID_VIDEO_DECODE_H264,( b8 _/ s/ n: f' f V
ACDK_SCENARIO_ID_VIDEO_DECODE_WMV78,
" b8 B- c1 l b1 ^ ] ACDK_SCENARIO_ID_VIDEO_DECODE_WMV9,9 t" j! A7 l3 |9 A( G9 A5 W3 o a
ACDK_SCENARIO_ID_VIDEO_DECODE_MPEG2,
2 r3 a! u/ L/ c( ^& n: [" @ ACDK_SCENARIO_ID_IMAGE_YUV2RGB,
" ]: G6 w( y+ O: s, ~) H ACDK_SCENARIO_ID_IMAGE_RESIZE,! Z& k$ ?! u+ U
ACDK_SCENARIO_ID_IMAGE_ROTATE,9 f* o3 w( F$ Q. z. v% E
ACDK_SCENARIO_ID_IMAGE_POST_PROCESS,- ~: {# q) G& Y- f# X
ACDK_SCENARIO_ID_JPEG_RESIZE,
4 i) m( }9 {! g* N! S+ r ACDK_SCENARIO_ID_JPEG_DECODE, ?1 N+ H5 P5 n1 I4 P$ [ \
ACDK_SCENARIO_ID_JPEG_PARSE,
0 B$ C$ }# M4 `% E ACDK_SCENARIO_ID_JPEG_ENCODE,
; A& {2 R) B0 V: r ACDK_SCENARIO_ID_JPEG_ENCODE_THUMBNAIL,* Y5 |. J+ ~ j0 y
ACDK_SCENARIO_ID_DRIVER_IO_CONTROL,8 N, m9 J0 u6 r1 K$ H
ACDK_SCENARIO_ID_DO_NOT_CARE,/ R- V: n& i/ D+ T9 ^, e
ACDK_SCENARIO_ID_IMAGE_DSPL_BUFFER_ALLOC,
3 o; |5 M# E6 P ACDK_SCENARIO_ID_TV_OUT,
9 R" Y7 m' N" `. s3 ~ ACDK_SCENARIO_ID_MAX,. }4 x0 |/ i6 `$ S% s
ACDK_SCENARIO_ID_VIDOE_ENCODE_WITHOUT_PREVIEW,% {" w+ v! V4 W; n
ACDK_SCENARIO_ID_CAMERA_CAPTURE_JPEG_BACK_PREVIEW,
/ m9 f: L, R1 b" L& V, a ACDK_SCENARIO_ID_VIDEO_DECODE_RV8,3 _, C: G: N+ b' Z/ t
ACDK_SCENARIO_ID_VIDEO_DECODE_RV9,
( i3 V% ^1 s5 K. @+ R/ `; F ACDK_SCENARIO_ID_CAMERA_ZSD,
1 e7 B/ J. P- a) I7 y6 O }ACDK_SCENARIO_ID_ENUM; s* D. _' L5 j2 L
通过这个数组定义 Camera 的各种模式,并且给他们从 0 开始给一个模拟的 ID,通过这个ScenarioID 来控制 Camera 的工作模式是在拍照、摄像等等。4 S8 ]- N* O0 {' B& ]
想要了解*pSensorInfo 这个指针的内容就得看 MSDK_SENSOR_INFO_STRUCT 的定义' `$ A( T7 k" |9 g
#define MSDK_SENSOR_INFO_STRUCT ACDK_SENSOR_INFO_STRUCT, n' ]" v& l5 ^( L% ^# [5 ^
6 l5 s1 x2 X/ J' C7 C7 I# M |
|