|
一、 手机 Camera 的物理结构:+ l8 ^* g8 |! `* i6 B' H: k

3 J1 {4 C7 D7 Z
% ~5 H7 _$ k- Y9 V. X$ K- k6 R1 m+ d二、 Camera 的成像原理:) \% _3 P: g3 ^5 M9 C$ O0 t
' X8 @2 A% {5 p, x: f& o2 e 景物通过镜头(LENS)生成的光学图像投射到图像传感器(Sensor)表面上,然后转为模拟的电信号,经过 A/D(模数转换)转换后变为数字图像信号,再送到数字信号处理芯片(DSP)中加工处理,再通过 IO 接口传输到 CPU 中处理,通过 LCD 就可以看到图像了。
- V5 k' x. Q. R6 D1 J0 u
& ~( o, }# m# \/ Q2 x 
9 p% f! n- k7 J& q9 }/ F 图像传感器(SENSOR)是一种半导体芯片,其表面包含有几十万到几百万的光电二极管。光电二极管受到光照射时,就会产生电荷。目前的 SENSOR 类型有两种:
$ M0 `/ M O% i. t+ Q- @, V CCD(Charge Couple Device),电荷耦合器件,它是目前高像素类 sensor 中比较成熟的成像器件,是以一行为单位的电流信号。1 o6 o3 T% {8 k/ j) ?" {
CMOS(Complementary Metal Oxide Semiconductor),互补金属氧化物半导体。CMOS的信号是以点为单位的电荷信号,更为敏感,速度也更快,更为省电。
; I) H3 L' k, y5 b! D* a8 i ISP 的性能是决定影像流畅的关键,JPEG encoder 的性能也是关键指标之一。而 JPEG encoder 又分为硬件 JPEG 压缩方式,和软件 RGB 压缩方式。
; {/ J( m: v8 T, f* V DSP 控制芯片的作用是:将感光芯片获取的数据及时快速地传到 baseband 中并刷新感光芯片,因此控制芯片的好坏,直接决定画面品质(比如色彩饱和度、清晰度)与流畅度。+ g: c$ B) f2 O
& r0 T/ N; K7 v7 j" T3 t1 Q6 }三、 Camera 常见的数据输出格式:
5 M& \" W# v/ E 常见的数据输出格式有:Rawdata 格式、YUV 格式、RGB 格式。
" _" {0 M0 X" D. O$ R& e RGB 格式:采用这种编码方法,每种颜色都可用三个变量来表示红色、绿色以及蓝色的强度。每一个像素有三原色 R 红色、G 绿色、B 蓝色组成。8 v! ]) M0 E, E) c8 a
YUV 格式:其中“Y”表示明亮度(Luminance 或 Luma),就是灰阶值;而“U”和“V”表示色度(Chrominance 或 Chroma),是描述影像色彩及饱和度,用于指定像素的颜色。
( t! t% X9 L2 W7 H RAW DATA 格式:是 CCD 或 CMOS 在将光信号转换为电信号时的电平高低的原始记录,单纯地将没有进行任何处理的图像数据,即摄像元件直接得到的电信号进行数字化处理而得到的。
1 e3 t7 [8 m, E M 支持 YUV/RGB 格式的模组,一般会在模组上集成 ISP(Image Single Processor),经过A/D 转换过的原始数据经过 ISP 处理生成 YUV 标准格式传到 BB。一般来说,这种设计适用于低像素 Camera 的要求,会在主板上省去一个 DSP,可降低成本。在调试过程中,YUV/RGB 格式的摄像头,其所有参数都可在 kernel 层通过寄存器来控制。调试一般由 sensor的原厂支持。
& U8 G5 g$ \# s l 支持 RawData 格式的模组,由于感光区域的需求,不会再模组内集成 ISP 以最大程度的增大感光区域的面积,提高照片质量。模组把原始的数字信号传给 BB 上的 DSP 进行处理,MTK 自带的 DSP 一般包含 ISP、JPEG encoder、和 DSP 控制芯片。在调试的时候图像的效果需要 MTK 在 HAL 层的参数进行支持。- D, D. B$ ^+ q% c# s' X( b
% Z" _5 z7 k9 Z( A l四、 阅读 Camera 的规格书(以 Truly 模组 OV5647_Raw 为例):
3 W* C+ b" B ?2 U- c  
, M$ J+ t' [. ]* {/ m 5 P/ D$ T5 S2 |3 \( b; ~# l
3 X4 A/ F# }' R五、 Camera 的硬件原理图及引脚:' } {/ |3 V% |% d0 {

0 g0 o8 ] ]; I1 W$ o 从上面可看出,连接 Camera 的 30 根 Pin 脚可大致分为以下几类:. k' A S1 |) _- H2 m; Z; s0 r
1、电源部分:
& f, G) Z8 l) B' a a)VCAMD 就是 DVDD 数字供电,主要给 ISP 供电,由于 RAWDATA格式的sensor其ISP是在 BB 端,所以将其引脚将其 NC。从上面的规格书上可以看出 DVDD 是内部 BB 端供电。模组已将其 NC 掉了;
4 Y, Z/ Y2 y( J8 D+ @8 ~ b) VCAM_IO 就是 VDDIO 数字 IO 电源主要给 I2C 部分供电;2 \& f2 K, ^5 |
c) VCAMA 就是 AVDD 模拟供电,主要给感光区和 ADC 部分供电;
& [; |9 K' u' ` d) VCAM_AF 是对 Camera 自动对焦马达的供电。% W) k& g9 P% \# \8 w
2、Sensor Input 部分:1 l: S1 {1 c5 w
a) Reset 信号,用于复位、初始化。9 ~" C3 F) R! v
b) Standby/PowerDown 信号,用于进入待机模式,降低功耗。% y! h) w) q2 B+ @5 }! C6 N
c) Mclk,即 MasterClock 信号,是由 BB 端提供。
3 N8 S1 c# f& w+ _* n; P 3、Sensor OutPut 部分:
L1 b4 Z! |7 s7 y% [& n8 u1 C3 R1 L a)Pclk,即 PixelClock 信号,由 MCLK 分频得到,作为外部时钟控制图像传输帧率. M( f4 X5 w) b& k' w; y
b) HSYNC,行同步信号,其上升沿表示新一列行图像数据的开始。 G) w3 | [; _5 T( \3 x2 R( _
c) VSYNC,帧同步信号,其下降沿表示新的一帧图片的开始。- s% [+ X: c- u' U. { W3 c" i9 @
d) D0-D9 一共 10 根数据线(8/10 根等);
! N6 g/ {& H h! Z" l 4、I2C 部分:SCL,I2C 时钟信号线和 SDA,I2C 数据信号线。 W2 Q6 E4 z' U; s2 v8 r
( B2 j; {, v: c5 g
6 [. g; c4 E+ L7 S六、 MTK 平台 Camera 相关代码文件(以下代码均为 MTK6575 平台). M7 J5 Z/ a9 h: z
1、 CameraSensor 驱动相关文件( S! w+ Z5 }) a5 c+ {3 w
/ g" \; `4 q3 G6 b
2、 Sensor ID 和一些枚举类型的定义
3 g" M& [) f7 i + t& ^$ ^, u/ l* }9 H/ }
3、 Sensor 供电
! E5 R7 j. z3 h" E4 R/ Q( g% V3 E: [/ _0 l7 \6 I3 p

7 p4 T* c7 P+ \. t! a4 T 4、 Kernel Space 的 SensorList,imgsensor 模块注册* l2 `: J* d6 @. w

* z% H2 o5 n7 z: a1 {* H# p 5、 User Space 的 SensorList,向用户空间提供支持的 SensorList* `# ~3 S6 A8 k" M2 s5 v- e! ~: M
; Q e3 _5 U+ k' }$ B2 v( t9 ^. v2 O0 M1 |, W
6、 Sensor 效果调整的接口
% {# f) U. E# d( d7 [ 8 ^" p8 o$ a: A& P% s1 }

; l& a( ~0 O! \4 w- s
* t$ X+ g- O4 {& R& v. ^9 \- i七、 Camera 模块驱动、设备与总线结构:
0 p2 X0 i! A2 Y* z 一般在 Linux 设备驱动模型中,我们只需要关心总线、设备、驱动这三个实体。总线会充当红娘对加载于其上的设备与驱动进行配对,对于 Camera 模块也不例外,下面从总线、设备、驱动的角度来分析 Camera 模块驱动的注册、匹配与加载过程。1 A/ Y5 @( H" I% }
a) 驱动的注册:
: [( ]# e' D" y! v1 J' N# b0 S0 _( M 在(\custom\common\kernel\imgsensor\src\Kd_sensorlist.c)CAMERA_HW_i2C_init 这个函数里通过 Platform_driver_register(&g_stCAMERA_HW_Driver)把 Camera 模块驱动注册
) c0 `! b) C: O* Q' T7 `: B8 c; K到 Platform 总线上。而 g_stCAMERA_HW_Driver 是对结构体 Platform_driver 这个结构体的填充。* _& N" F, s. C. }. i; T

/ @8 O( y9 g! r7 I( c (Kernel\include\linux\Platform_device.h)2 b! R4 i1 w6 b% d3 h

4 H2 B) e ]% Y# [$ K' k b) 设备的注册:# ^. G: X$ u. k
对 platform_device 的定义通常在 BSP 的板级文件:
; O% C; D* O; a( j8 z/ E- J (kernel\arch\sh\boards\mach-ap325rxa\Setup.c)中实现,在板级文件中,将 platform_device归纳为一个数组,最终通过 platform_add_device()函数统一注册:
1 p- j. m$ p' m 
- V+ R V7 ?6 n2 a8 D9 L+ y 5 M* K( H' F( K% j2 A J5 L

( ?: ~% f5 v. Z3 Q5 m c) 总线的匹配:( {/ \6 m. ?" {2 B' u4 u. ~ t
既 然 是 驱 动 Platform_device 那 对 应 的 设 备 必 然 是 挂 载 Platform 总 线 上 的Platform_device,Platform 总线是 Linux 系统提供的一种机制,不同于 I2C、I2S 等总线,它
$ y3 w* w7 J" ?9 b7 x; S是一种虚拟的总线。Linux 系统为 Platform 总线定义了一个 bus_type 的实例 Platform_bus_type:: Y* O8 K0 f$ J: w; `' z& h
(Kernel\drivers\base\platform.c)
. C J, y/ l% ]- Y& k; S# t" N 
7 _2 W7 w9 C+ B$ S! D Platform 总线通过 platform_match 这个成员函数来确定 platform_device 与 platform_driver 如何进行匹配:8 [& n& ^, x8 l1 a9 }! z9 m: k

! u+ ~5 c( P! K, T9 i; P, `( A
$ O B+ N% |7 c5 Z- r/ v$ p$ B( g八、 Camera 驱动工作流程:
# y7 [1 H( ]! u8 D
- ^( E w$ H2 C+ S5 s3 h: G
6 } R' `. [$ O* B 从上图可以清晰的了解到 Camera 的一个工作流程主要分为这么七步:3 M/ e) P& [) Y6 W j* e
1. 打开 Camera Power LDO,让 Camera 有能量保证。
& E8 _! U+ \3 \& J0 `/ o 2. 打开 IIC,设置 PDN 引脚,使 Camera 退出 Standby 模式,按照要求让 Reset 脚做一个复位动作。0 A* @) _3 u) P" F6 ]( n
3. 读一下 sensor 的版本 ID,这样可以让你确认是否连接上你想要的 sensor。) E8 H9 \4 x# D+ h
4. 对 Sensor 进行初始化下载最基本的参数让 Sensor 工作起来,可能包括软复位。
- B: V U" _7 t1 h 5. 下载 preview 的参数,为预览动作准备。
3 L- ^( J; n+ [& f! Z 6. 下载 Capture 的参数,为拍照动作准备。9 j* s/ O' H3 x+ _7 S
7. 设置 PDN 引脚,使 Sensor 进入 Standby 模式, 或者关掉 LDO 等动作,退出 Camera。' b1 {# i9 X$ K2 Y' c
我们都知道,Linux 内核是通过模块的机制来加载设备驱动的,那么接下来我们就从设备模块加载的角度来看下 Camera 工作流程的驱动代码是如何工作的。
9 O: G+ J. P) |9 R. E3 {" s2 r 在-alps\mediatek\custom\common\kernel\imgsensor\src\kd_sensorlist.c 中可以看到:
5 g% b& n- V2 i# Y7 h9 V4 G$ x module_init(CAMERA_HW_i2C_init);1 ^1 W4 \# B5 z# n
module_exit(CAMERA_HW_i2C_exit);
, l8 I" n: A* o3 e8 [/ N 在这里 Linux 内核加载和卸载 Camera 模块。
& O2 X$ n5 E* h p! J2 q$ } static struct platform_driver g_stCAMERA_HW_Driver = {' e& b; I9 R6 X6 W) _, f
.probe = CAMERA_HW_probe,
! r7 l7 e2 _" q# `+ a" p0 W( ] .remove = CAMERA_HW_remove,/ a+ u# M& f' K( ~ d
.suspend = CAMERA_HW_suspend,
' S; N1 n* z8 i: C3 Q% j; K5 s3 q. o .resume = CAMERA_HW_resume,
, |$ j% b) R; _, b* H, G5 B .driver ={0 f l6 ^5 j) U7 ^/ u! v
.name = "image_sensor",
8 B. u+ \5 h$ t! A .owner = THIS_MODULE,, a/ e, C+ ^) E$ O+ D
}9 [/ y$ N& ~ H0 s c) T
};
2 F4 w3 R7 Z1 r7 p n. w Camera 模块初始化开始向总线注册驱动,在 Platform_driver 的成员函数.probe()中,通过 i2c_add_driver(&CAMERA_HW_i2c_driver)向 I2C 申请,而 CAMERA_HW_i2c_driver 这个结构体里填充的是将 Camera 作为一个字符设备在 I2C 上进行注册:' m" P n1 ^6 @" K; q. E9 k

# q) ]6 V B* A; o 9 n9 L& _& ]/ A; v4 O
在 RegisterCAMERA_HWCharDrv()中cdev_init(g_pCAMERA_HW_CharDrv, &g_stCAMERA_HW_fops);对设备进行初始化,并将g_stCAMERA_HW_fops 这个文件操作函数作为上层对 Camera 设备操作的接口留给上层进行调用:( A0 P. ]8 M {3 U1 V
$ M8 k; O: H/ K& Z; M% M
其中成员函数 open()只是初始化一个原子变量留给系统调用。ioctl()才是整个 Camera驱动的入口:: a7 Z1 Q1 J' Y$ s4 J$ |
6 E1 `, T$ o6 \7 `3 h
CAMERA_HW_Ioctl()是上层文件操作系统操作底层硬件的方法,它先对 Camera 需要的Buffer 做一个初始化,然后建立对 Cameraopen、getinfo 等操作的接口:- Y7 u; B5 o2 n) q0 p# y

( D3 d3 ?7 A, W% y! Q! i* w3 H 通过判断 Sensor 状态的逻辑值来进行具体的操作,对于这个值的定义在:
8 w6 L) K7 W& X9 I: b Mediatek\custom\common\kernel\imgsensor\inc\Kd_imgsensor.h 中7 B' o& W1 P$ Z1 ]) E8 j

/ O d" j: J. W8 S9 s5 C7 B% S 在 KdSetDriver()中通过判断 name 和 ID 匹配具体型号的 sensor 的驱动,判断它是主摄还是次摄,并对它进行初始化:
- z6 B' d! u `8 P8 g7 \ - t2 g& v/ @1 V7 F# ?6 p' f, C1 }
: X# H [4 D8 m
通过 NAME 和 ID 匹配完成后会将 PSENSOR_FUNCTION_STRUCT *pfFunc 这个结构体匹配到具体型号的驱动代码中:
, i+ K; X) W3 u! @- {7 L * H2 P w! c' N5 V6 o$ P. W0 z
到这里,整个 Camera 驱动从总线注册到完成具体 sensor 的初始化的流程就完成了,CAMERA_HW_Ioctl()中其他的 ioctl 操作函数最后都会在$sensor$_sensor.c 中实现。$ B4 p8 s1 F" ?4 N
" K0 @) S8 }7 @9 `0 `九、 Camera 驱动添加、调试流程:
- T# h) n- ~2 G3 y" j% F( a7 c 1、 修改系统配置文件 ProjectConfig.mk:
! T/ ~" v+ p9 N -alps\mediatek\config\$project$\ProjectConfig.mk
/ y7 w$ }8 i$ T( f 
8 @( [" A7 Z5 ~& W+ A1 } G, C% k# [$ g0 \
2、 检查、配置供电文件:
4 g7 n9 m# @& i% G7 g7 Z) O -alps\mediatek\custom\$project$\Kernel\Camera\Camera\kd_camera_hw.c' I; B* R) O. t5 Z3 y
Camera 供电流程(以 3M 前摄 MT9V114+5M 后摄 OV5647 为例):7 X# Q/ j- @8 I; g* |0 k# e

. N4 R ~) B! o 其实在 kd_camera_hw.c 中只有一个函数 kdCISModulePowerOn(),在这个函数中需要注意的是通过 GPIO 口控制 PDN 和 RST 引脚的时候,对于其相关的定义,由其在切换平台的时候。例如 MT6573 和 MT6575 的定义顺序就不同
6 [5 N, i' ?( W) v- U 
8 W$ B7 s6 l- K. o 3、 添加 Camera 驱动(以 ov5647 为例):7 J" Y& ?% k g; v$ b- ^
创建 SensorFuncOV5647 这样一个数据结构2 g' S ~" g& ]) ?( T
SENSOR_FUNCTION_STRUCT SensorFuncOV5647={5 |0 ~6 l4 P" R$ g4 w) e0 e
OV5647Open,- A- f! W# y( U
OV5647GetInfo,# _ F8 Y, E. `& d( t
OV5647GetResolution, v% Z( d M2 i7 G. I0 k7 e
OV5647FeatureControl,$ F \. X, \5 k5 `5 y9 E2 {4 ]
OV5647Control,8 p8 ^7 O6 T9 g% J
OV5647Close1 D( u, x8 k. V, k3 B+ Q n
};0 h& V% w; Z, c1 G
a)OV5647Open/ B; B$ E& T! z

& a) L3 H0 w8 q( L 初始化操作就是对 SensorIC 中寄存器的操作,调试主要由 IC 原厂支持。Open 函数结束后返回 ERROR_NONE 表示初始化成功,可以正常使用. B0 g2 v5 b$ k
0 D. j5 f7 o6 e- p6 D5 _! W- S) Q( h b)OV5647GetInfo/ H8 |. h( P+ X0 T3 P
7 ~1 ?( `! T* ] P UINT32 OV5647GetInfo(MSDK_SCENARIO_ID_ENUM ScenarioId,MSDK_SENSOR_INFO_STRUCT *pSensorInfo,MSDK_SENSOR_CONFIG_STRUCT *pSensorConfigData)
# ?( i+ T1 l5 G7 u6 W 第一个参数 ScenarioId 来自于 MSDK_SCENARIO_ID_ENUM 这个数组,在kd_imgsensor_define.h 中是这样定义的:+ n/ y4 \8 g, g# z( i
#define MSDK_SCENARIO_ID_ENUM ACDK_SCENARIO_ID_ENUM0 t. ~7 c4 _ a3 P3 s4 {' R
typedef enum{
& T0 u( U- U$ T% H ACDK_SCENARIO_ID_CAMERA_PREVIEW=0,1 |" B: t& E$ ~6 E* I
ACDK_SCENARIO_ID_VIDEO_PREVIEW,: W# T; C' V+ Y5 F( y, {( S- Q
ACDK_SCENARIO_ID_VIDEO_CAPTURE_MPEG4,- k9 z" B& W" X% U4 @" C0 {( }* M
ACDK_SCENARIO_ID_CAMERA_CAPTURE_JPEG,0 C0 ?: i& f1 v
ACDK_SCENARIO_ID_CAMERA_CAPTURE_MEM,
# x L8 x$ S/ `, L6 j' ^$ Z9 V ACDK_SCENARIO_ID_CAMERA_BURST_CAPTURE_JPEG,
/ ^* h0 X& S7 \ ACDK_SCENARIO_ID_VIDEO_DECODE_MPEG4,
* ? m1 Y( H" t& y0 ^# O- i ACDK_SCENARIO_ID_VIDEO_DECODE_H263,
5 B8 I- H. s' e. Q& c ACDK_SCENARIO_ID_VIDEO_DECODE_H264,
5 I; w P0 T) M ACDK_SCENARIO_ID_VIDEO_DECODE_WMV78,2 ~+ [- F4 R4 v" L/ x, y
ACDK_SCENARIO_ID_VIDEO_DECODE_WMV9,3 m+ R X `! `3 u& _8 e
ACDK_SCENARIO_ID_VIDEO_DECODE_MPEG2,
8 b- J8 H+ n' o$ d* k# u# G. e& g ACDK_SCENARIO_ID_IMAGE_YUV2RGB,
+ P6 X" m' X1 u y5 { ACDK_SCENARIO_ID_IMAGE_RESIZE,/ j) @5 [) w8 H; l# U' S. O% ^
ACDK_SCENARIO_ID_IMAGE_ROTATE,( Q8 n f4 ~" B7 o1 i/ w
ACDK_SCENARIO_ID_IMAGE_POST_PROCESS,% h9 n* c' J) t. D" Y# u' \# C
ACDK_SCENARIO_ID_JPEG_RESIZE,, T& {/ L) _; r6 H) C' d
ACDK_SCENARIO_ID_JPEG_DECODE,- t @$ U5 u, W; q: T5 x1 O
ACDK_SCENARIO_ID_JPEG_PARSE,4 ^- \! d/ C0 a8 t1 a' Y: @
ACDK_SCENARIO_ID_JPEG_ENCODE,
) e8 j$ L2 l+ a. Q ACDK_SCENARIO_ID_JPEG_ENCODE_THUMBNAIL,
- b5 x/ `5 y7 X# Y0 E3 [' O7 \ ACDK_SCENARIO_ID_DRIVER_IO_CONTROL,
3 f9 ~0 k0 [& D; {7 q$ b ACDK_SCENARIO_ID_DO_NOT_CARE,
1 ` u5 \- _- K- A1 j8 V ACDK_SCENARIO_ID_IMAGE_DSPL_BUFFER_ALLOC,1 ^0 [ C4 @0 Y# `* `9 F
ACDK_SCENARIO_ID_TV_OUT,
: N3 Z0 \7 I& j3 `+ E$ }$ B ACDK_SCENARIO_ID_MAX,' F2 T! ~9 Y) b ^2 N. `/ d
ACDK_SCENARIO_ID_VIDOE_ENCODE_WITHOUT_PREVIEW,; x) i% r+ p7 R$ O& F$ I7 ~/ v
ACDK_SCENARIO_ID_CAMERA_CAPTURE_JPEG_BACK_PREVIEW,
4 B9 O6 X$ V4 H3 e1 W7 ]/ E ACDK_SCENARIO_ID_VIDEO_DECODE_RV8,; \: y- Z+ v6 C. F0 ~9 A# c
ACDK_SCENARIO_ID_VIDEO_DECODE_RV9,1 R0 s6 [: W1 F$ S7 o
ACDK_SCENARIO_ID_CAMERA_ZSD,
J2 A$ m: ~& @6 ]8 H& S0 g$ H0 z }ACDK_SCENARIO_ID_ENUM;
/ p% \! s o" z9 `6 x 通过这个数组定义 Camera 的各种模式,并且给他们从 0 开始给一个模拟的 ID,通过这个ScenarioID 来控制 Camera 的工作模式是在拍照、摄像等等。
. _7 H, B! j6 G$ w# v9 H 想要了解*pSensorInfo 这个指针的内容就得看 MSDK_SENSOR_INFO_STRUCT 的定义. W \, c2 ?$ s! X; G" `1 k. {% w' N
#define MSDK_SENSOR_INFO_STRUCT ACDK_SENSOR_INFO_STRUCT, ]5 {7 T. m' Z
% o' k( u, P8 o2 i |
|