1. 程式人生 > >Camera sensor 基本知識

Camera sensor 基本知識

一、Camera 工作原理介紹 
1. 結構 . 
一般來說,camera 主要是由 lens 和 sensor IC 兩部分組成,其中有的 sensor IC 整合 了 DSP,有的沒有整合,但也需要外部 DSP 處理。細分的來講,camera 裝置由下邊幾部 分構成: 
1) lens(鏡頭) 一般 camera 的鏡頭結構是有幾片透鏡組成,分有塑膠透鏡(Plastic)和玻璃透 鏡(Glass) ,通常鏡頭結構有:1P,2P,1G1P,1G3P,2G2P,4G 等。 
2) sensor(影象感測器) Senor 是一種半導體晶片,有兩種型別:CCD 和 CMOS。Sensor 將從 lens 上傳導過來的光線轉換為電訊號, 再通過內部的 AD 轉換為數字訊號。 由於 Sensor 的每個 pixel 只能感光 R 光或者 B 光或者 G 光, 因此每個畫素此時存貯的是單色的, 我們稱之為 RAW DATA 資料。 要想將每個畫素的 RAW DATA 資料還原成三基色,就需要 ISP 來處理。 
3)ISP(影象訊號處理) 主要完成數字影象的處理工作,把 sensor 採集到的原始資料轉換為顯示支援 的格式。ISP 是Image Signal Processor 的簡稱,也就是影象訊號處理器。ISP一般用來處理Image Sensor(影象感測器)的輸出資料,如做AEC(自動曝光控制)、AGC(自動增益控制)、AWB(自動白平衡)、色彩校正、Lens Shading、Gamma 校正、祛除壞點、Auto Black Level、Auto White Level 等等功能的處理。

 4)CAMIF(camera 控制器) 晶片上的 camera 介面電路,對裝置進行控制,接收 sensor 採集的資料交給 CPU,並送入 LCD 進行顯示。

2. 工作原理 . 
外部光線穿過 lens 後, 經過 color filter 濾波後照射到 Sensor 面上, Sensor 將從 lens 上傳導過來的光線轉換為電訊號,再通過內部的 AD 轉換為數字訊號。如果 Sensor 沒有集 成 DSP,則通過 DVP 的方式傳輸到 baseband,此時的資料格式是 RAW DATA。如果整合 了 DSP, RAW DATA 資料經過 AWB、 則 color matrix、 lens shading、 gamma、 sharpness、 AE 和 de-noise 處理,後輸出 YUV 或者 RGB 格式的資料。 
最後會由 CPU 送到 framebuffer 中進行顯示,這樣我們就看到 camera 拍攝到的景象 了。

3. YUV 與 YCbCr . 
YUV 和 RGB 一樣,是色彩空間中常用的色彩模型之一,兩者可以相互轉換。YUV 中 得 Y 表示亮度,U 和 V 表示色度。與 RGB 相比,它的優點在於佔用更少的空間。 YCbCr 則是在世界數字組織視訊標準研製過程中作為 ITU - R BT601 建議的一部分, 其實是 YUV 經過縮放和偏移的翻版。 其中 Y 與 YUV 中的 Y 含義一致, Cb , Cr 同樣都指色 彩, 只是在表示方法上不同而已。在 YUV 家族中, YCbCr 是在計算機系統中應用最多的成 員, 其應用領域很廣泛,JPEG、 MPEG 均採用此格式。 一般人們所講的 YUV 大多是指 YCbCr。 YCbCr 有許多取樣格式, 如 4∶4∶4 , 4∶2∶2 , 4∶1∶1 和 4∶2∶0。

二、Camera 硬體 
1. CAMIF . 
如下是 S5PV210 的 camera 系統的結構圖:

S5PV210 的 camera 介面控制器叫 FIMC4.3,它支援 ITU R BT-601/656、AXI 和 MIPI(CSI)三種介面方式,最大輸入畫素是 8192*8192。S5PV210 有三組 camera 介面。

主要特性: 支援多種輸入介面型別:

ITU-R BT 601/656 模式 DMA(AXI 64 位)模式 MIPI(CSI)模式 Direct FIFO 模式 支援多種輸出介面:DMA 模式/Direct FIFO 模式 支援數碼調焦(DZI) 最大輸入畫素 8192*8192 支援影象翻轉、旋轉等處理效果 生成多種圖片格式 支援採集幀控制

2. 介面訊號 . 
FIMC 訊號定義如下所示(YCbCr 模式) 
Signal 
VSYNC HREF PCLK DATA[7:0] FIELD CAM_MCLK I I I I

I/O 
幀同步訊號 行同步訊號 畫素時鐘 畫素資料 FIELD 訊號

Description

Type

Muxed

O O

系統時鐘訊號

通過 CAM_MCLK 給攝像頭提供時鐘,RST 是復位線,PWDN 在攝像頭工作時應該始終 為低。HREF 是行參考訊號,PCLK 是畫素時鐘,VSYNC 是場同步訊號。一旦給攝像頭提供了 時鐘,並且復位攝像頭,攝像頭就開始工作了,通過 HREF,PCLK 和 VSYNC 同步傳輸數字圖 像訊號。資料是通過 DATA0~DATA7 這八根資料線並行送出的。

3. 工作時序 . 
FIMC43 支援如下兩種視訊資料:

ITU-R BT 601 輸入時序圖 這種方式下行和幀同步訊號獨立於視訊資料,因此需要同步訊號。

ITU-R BT 656 輸入時序圖

這種方式下同步訊號已經內嵌到視訊資料中了,因此不需要額外的行和幀同步訊號。

(ITU-R BT 601: 16 位資料傳輸;21 芯;Y、U、V 訊號同時傳輸。 ITU-R BT 656: 9 芯,不需要同步訊號;8 位資料傳輸;序列視訊傳輸;傳輸速率是 601 的 2 倍;先傳 Y, 後傳 UV。 )

同步訊號的時延引數 t1:表示 VSYNC 前、後插入週期 t2:表示 HREF 前插入週期 t3:表示 HREF 寬度 t4:表示 HREF 後插入週期

4. 外部介面 . 外部介面 
硬體原理圖的 CAM A 部分:

CAM B 部分

5. Camera 內部結構圖 . 
下圖是 camera 內部結構框圖,以 OV sensor 為例:

三、Camera 驅動 
1. V4L2 . 
1)簡介 ) 在 Linux 中,攝像頭方面的標準化程度比較高,這個標準就是 V4L2 驅動程式,這也是 業界比較公認的方式。 V4L 全稱是 Video for Linux,是 Linux 核心中標準的關於視訊驅動程式,目前使用比 較多的版本是 Video for Linux 2, 簡稱 V4L2。 它為 Linux 下的視訊驅動提供了統一的介面, 使得應用程式可以使用統一的 API 操作不同的視訊裝置。從核心空間到使用者空間,主要的 資料流和控制類均由 V4L2 驅動程式的框架來定義。 V4L2 驅動程式一般只提供 Video 資料的獲得,而如何實現視訊預覽,如何向上層傳送 資料,如何把純視訊流和取景器、視訊錄製等實際業務組織起來,都是 camera 的硬體抽象 層需要負責的工作。 
V4L2 驅動核心實現為如下檔案:drivers/media/video/v4l2-dev.c。 V4l2-dev.h 中定義的 video_device 是 V4L2 驅動程式的核心資料結構,它為具體的攝 像頭 sensor 驅動提供了介面呼叫。 V4l2 的採集過程(應用程式): 
1)開啟裝置,獲得檔案描述符; 
2) 設定圖片格式; 
3) 分配緩衝區; 
4)啟動採集過程,讀取資料; 
5) 停止採集,關閉裝置。

2)資料結構 ) 
V4L2 的主要資料結構是 video_device,定義在 v4l2_dev.h 中: 
struct video_device { 
/* device ops */ 
const struct v4l2_file_operations fops; /介面函式指標*/

/* sysfs */ 
struct device dev; struct cdev cdev; / v4l 裝置結構 */ 
/* 字元裝置結構*/

/* Set either parent or v4l2_dev if your driver uses v4l2_device */ 
struct device parent; struct v4l2_device *v4l2_dev; / 裝置父指標 */ 
/* v4l2 裝置指標*/

/* device info */ 
char name[32]; 
int vfl_type; /* ‘minor’ is set to -1 if the registration failed */ 
int minor; u16 num; /* use bitops to set/clear/test flags */ 
unsigned long flags; /* attribute to differentiate multiple indices on one physical device */ 
int index; /次裝置號
/裝置名稱/

/* V4L2 file handles */ 
spinlock_t fh_lock; /* Lock for all v4l2_fhs */

struct list_head fh_list; /* List of struct v4l2_fh */

int debug;

/* debug 級別*/

/* Video 標準變數 */ 
v4l2_std_id tvnorms; /* Supported tv norms */

v4l2_std_id current_norm; /* Current tvnorm */

/* 回撥函式 */ 
void (*release)(struct video_device *vdev);

/* ioctl 回撥函式 */ 
const struct v4l2_ioctl_ops *ioctl_ops; 
};

主要介面函式有: int video_register_device(struct video_device *vdev, int type, int nr);

static int v4l2_ioctl(struct inode *inode, struct file *filp, long arg);

unsigned int cmd, unsigned

  1. FIMC 
    1)簡介 ) FIMC 這個模組不僅僅是一個攝像頭的控制介面,它還承擔著 V4L2 的 output 功能和 overlay 的功能。 FIMC 的驅動在核心中的位置:drivers/media/video/samsung/fimc 它包含下邊的檔案: 
    fimc_regs.c fimc_capture.c fimc_dev.c fimc_output.c fimc_overlay.c fimc_v4l2.c

它們的組織關係如下:

可以看到,FIMC 的驅動實現了 v4l2 所有的介面,可以分為 v4l2-input 裝置介面, v4l2-output 裝置介面以及 v4l2-overlay 裝置介面。這裡我們主要關注 v4l2-input 裝置介面, 因為攝像頭屬於視訊輸入裝置。 fimc_v4l2.c 裡面註冊了很多的回撥函式,都是用於實現 v4l2 的標準介面的,但是這些 回撥函式基本上都不是在 fimc_v4l2.c 裡面實現的,而是有相應的.c 分別去實現。比如:

v4l2-input 裝置的操作實現: fimc_capture.c v4l2-output 
裝置的操作實現: fimc_output.c v4l2-overlay 
裝置的操作實現: fimc_overlay.c

這些程式碼其實都是和具體硬體操作無關的, 這個驅動把所有操作硬體暫存器的程式碼都寫 到一個檔案裡面了,就是 fimc40_regs.c。這樣把硬體相關的程式碼和硬體無關的程式碼分開來 實現是非常好的方式,可以最大限度的實現程式碼複用。 
2) 資料結構 ) FIMC 的主要資料結構 fimc_control,定義在 fimc.h 中:

struct fimc_control { 
int id; /* controller id */ 
char name[16]; 
atomic_t in_use; 
void __iomem regs; / register i/o */ 
struct clk clk; / interface clock */ 
struct regulator regulator; / pd regulator */ 
struct fimc_meminfo mem; /* for reserved mem */

    /* kernel helpers */
    struct mutex                    lock;           /* controller lock */
    struct mutex                    alloc_lock;
    struct mutex                    v4l2_lock;
    wait_queue_head_t               wq;
    struct device                   *dev;
    int                             irq;


    /* v4l2 related */
    struct video_device             *vd;
    struct v4l2_device              v4l2_dev;


    /* fimc specific */
    struct fimc_limit               *limit;         /* H/W limitation */
    struct s3c_platform_camera      *cam;           /* activated camera */
    struct fimc_capinfo             *cap;           /* capture dev info */
    struct fimc_outinfo             *out;           /* output dev info */
    struct fimc_fbinfo              fb;             /* fimd info */
    struct fimc_scaler              sc;             /* scaler info */
    struct fimc_effect              fe;             /* fimc effect info */


    enum fimc_status                status;
    enum fimc_log                   log;


    u32                             ctx_busy[FIMC_MAX_CTXS];

};

對於 GPIO 的配置程式碼在 /drivers/ arch/arm/mach-s5pv210/setup-fimc0.c 中: 
void s3c_fimc0_cfg_gpio(struct platform_device *pdev) { 
int i = 0;

/* CAM A port(b0010) : PCLK, VSYNC, HREF, DATA[0-4] */ 
for (i = 0; i < 8; i++) { 
s3c_gpio_cfgpin(S5PV210_GPE0(i), S3C_GPIO_SFN(2)); 
s3c_gpio_setpull(S5PV210_GPE0(i), S3C_GPIO_PULL_NONE); 

/* CAM A port(b0010) : DATA[5-7], CLKOUT(MIPI CAM also), FIELD */

for (i = 0; i < 5; i++) { 
s3c_gpio_cfgpin(S5PV210_GPE1(i), S3C_GPIO_SFN(2)); 
s3c_gpio_setpull(S5PV210_GPE1(i), S3C_GPIO_PULL_NONE); 
} /* CAM B port(b0011) : DATA[0-7] */

for (i = 0; i < 8; i++) { 
s3c_gpio_cfgpin(S5PV210_GPJ0(i), S3C_GPIO_SFN(3)); 
s3c_gpio_setpull(S5PV210_GPJ0(i), S3C_GPIO_PULL_NONE); 
} /* CAM B port(b0011) : PCLK, VSYNC, HREF, FIELD, CLCKOUT */

for (i = 0; i < 5; i++) { 
s3c_gpio_cfgpin(S5PV210_GPJ1(i), S3C_GPIO_SFN(3)); 
s3c_gpio_setpull(S5PV210_GPJ1(i), S3C_GPIO_PULL_NONE); 
} }

4)介面函式 ) 
FIMC 的主要回調函式如下,實現在 fimc_v4l2.c 中:

const struct v4l2_ioctl_ops fimc_v4l2_ops = { 
.vidioc_querycap = fimc_querycap, 
.vidioc_reqbufs = fimc_reqbufs, 
.vidioc_querybuf = fimc_querybuf, 
.vidioc_g_ctrl = fimc_g_ctrl, 
.vidioc_s_ctrl = fimc_s_ctrl, 
.vidioc_s_ext_ctrls = fimc_s_ext_ctrls, 
.vidioc_cropcap = fimc_cropcap, 
.vidioc_g_crop = fimc_g_crop, 
.vidioc_s_crop = fimc_s_crop, 
.vidioc_streamon = fimc_streamon, 
.vidioc_streamoff = fimc_streamoff, 
.vidioc_qbuf = fimc_qbuf, 
.vidioc_dqbuf = fimc_dqbuf, 
.vidioc_enum_fmt_vid_cap = fimc_enum_fmt_vid_capture, 
.vidioc_g_fmt_vid_cap = fimc_g_fmt_vid_capture, 
.vidioc_s_fmt_vid_cap = fimc_s_fmt_vid_capture, 
.vidioc_try_fmt_vid_cap = fimc_try_fmt_vid_capture, 
.vidioc_enum_input = fimc_enum_input, 
.vidioc_g_input = fimc_g_input, 
.vidioc_s_input = fimc_s_input, 
.vidioc_g_parm = fimc_g_parm, 
.vidioc_s_parm = fimc_s_parm, 
.vidioc_queryctrl = fimc_queryctrl, 
.vidioc_querymenu = fimc_querymenu, 
.vidioc_g_fmt_vid_out = fimc_g_fmt_vid_out, 
.vidioc_s_fmt_vid_out = fimc_s_fmt_vid_out, 
.vidioc_try_fmt_vid_out = fimc_try_fmt_vid_out, 
.vidioc_g_fbuf = fimc_g_fbuf, 
.vidioc_s_fbuf = fimc_s_fbuf, 
.vidioc_try_fmt_vid_overlay = fimc_try_fmt_overlay, 
.vidioc_g_fmt_vid_overlay = fimc_g_fmt_vid_overlay, 
.vidioc_s_fmt_vid_overlay = fimc_s_fmt_vid_overlay, 
};

5)暫存器操作(fimc_regs.c) )暫存器操作( )

對於暫存器的操作,實現都在 fimc_regs.c 檔案中,如 
int fimc_hwset_camera_source(struct fimc_control *ctrl) {

struct s3c_platform_camera *cam = ctrl->cam; u32 cfg = 0;

cfg |= S3C_CISRCFMT_ITU601_8BIT; cfg |= cam->order422;

if (cam->type == CAM_TYPE_ITU)

cfg |= cam->fmt;

cfg |= S3C_CISRCFMT_SOURCEHSIZE(cam->width); 
cfg |= S3C_CISRCFMT_SOURCEVSIZE(cam->height);

writel(cfg, ctrl->regs + S3C_CISRCFMT);

return 0; 
}

int fimc_hwset_enable_irq(struct fimc_control *ctrl, int overflow, int level) { 
u32 cfg = readl(ctrl->regs + S3C_CIGCTRL);

cfg &= ~(S3C_CIGCTRL_IRQ_OVFEN | S3C_CIGCTRL_IRQ_LEVEL); 
cfg |= S3C_CIGCTRL_IRQ_ENABLE;

if (overflow) cfg |= S3C_CIGCTRL_IRQ_OVFEN;

if (level) cfg |= S3C_CIGCTRL_IRQ_LEVEL;

writel(cfg, ctrl->regs + S3C_CIGCTRL);

return 0; 
}

  1. Sensor 驅動 
    1)簡介, 
    )簡介 本方案中使用了兩個攝像頭模組: MT9P111 和 S5K6AAFX。 其中 MT9P111 是 APTINA 公司推出的 1/4 英寸光學格式 5M 單晶片感測器,用作後攝像頭;S5K6AAFX 是三星出的 1.3M CMOS 高清影象感測器,用作前攝像頭。 
    2)引數設定 
    )引數設定 MT9P111 的引數設定

ifdef MT9P111_ENABLED

static struct mt9p111_platform_data mt9p111_plat = { 
.default_width = 1024, 
.default_height = 600, 
.pixelformat = V4L2_PIX_FMT_UYVY, 
.freq = 24000000, 
.is_mipi = 0, 
};

static struct i2c_board_info mt9p111_i2c_info = { 
I2C_BOARD_INFO(“MT9P111”, 0x3D),//0x7a,0x7b . 
platform_data = &mt9p111_plat, 
};

static struct s3c_platform_camera mt9p111 = { 
.id = CAMERA_PAR_A, 
.type= CAM_TYPE_ITU, 
.fmt = = ITU_601_YCBCR422_8BIT,

.order422 = CAM_ORDER422_8BIT_CBYCRY, 
.i2c_busnum 
.info = 7,

= &mt9p111_i2c_info, = V4L2_PIX_FMT_UYVY, = “xusbxti”,

.pixelformat .srclk_name

.clk_name = “sclk_cam0”,

.clk_rate = 24000000, 
.line_length 
.width 
.height 
.window 
.left = 0, 
.top = 0, 
.width 
.height 
}, = 1024, = 600, = 1920, = 1024, = 600, ={

/* Polarity */ .inv_pclk = 0, .inv_vsync .inv_href = 0, .inv_hsync = 0, = 0,

.initialized = 0, .cam_power }; #endif = smdkv210_cam0_power,

S5K6AAFX 的引數設定

ifdef S5K6AAFX_ENABLED

static struct s5k6aafx_platform_data s5k6aafx_plat = { 
.default_width = 800, 
.default_height = 600, 
.pixelformat = V4L2_PIX_FMT_YUYV, 
.freq = 24000000,

.is_mipi = 0, 
}; 
static struct i2c_board_info s5k6aafx_i2c_info = {

I2C_BOARD_INFO(“s5k6aafx”, 0x3c), 
.platform_data = &s5k6aafx_plat, 
};

static struct s3c_platform_camera s5k6aafx = { 
.id 
.type 
.fmt = CAMERA_PAR_B, = CAM_TYPE_ITU, = ITU_601_YCBCR422_8BIT,

.order422 = CAM_ORDER422_8BIT_YCBYCR, .i2c_busnum .info = 4,

= &s5k6aafx_i2c_info, = V4L2_PIX_FMT_YUYV, = “xusbxti”,

.pixelformat .srclk_name

.clk_name = “sclk_cam1”, .clk_rate = 24000000, .line_length = 1280,

/* default resol for preview kind of thing */ 
.width .height 
.window .left = 0, 
.top = 0, .width 
.height }, /* Polarity */ 
.inv_pclk = 0, 
.inv_vsync = 0, = 800, = 600, = 800, = 600, ={

.inv_href = 0, 
.inv_hsync = 0,

.initialized = 0, 
.cam_power };

endif = smdkv210_cam1_power,

3)資料結構 )

(未註釋) 
struct v4l2_subdev { 
struct list_head list; 
struct module *owner; 
u32 flags; 
struct v4l2_device *v4l2_dev; 
const struct v4l2_subdev_ops ops; / name must be unique */ 
char name[V4L2_SUBDEV_NAME_SIZE]; /* can be used to group similar subdevs, value is driver-specific */ 
u32 grp_id; /* pointer to private data */ 
void *priv; 
};

endif

4)介面函式 )

(未註釋) 
static const struct v4l2_subdev_core_ops mt9p111_core_ops = { 
.init = mt9p111_init, /* initializing API */

.s_config = mt9p111_s_config, /* Fetch platform data */ 
.queryctrl = mt9p111_queryctrl, 
.querymenu = mt9p111_querymenu, 
.g_ctrl = mt9p111_g_ctrl, 
.s_ctrl = mt9p111_s_ctrl, 
};

static const struct v4l2_subdev_video_ops mt9p111_video_ops = { 
// .s_crystal_freq = mt9p111_s_crystal_freq, 
.g_fmt = mt9p111_g_fmt, 
.s_fmt = mt9p111_s_fmt, 
.enum_framesizes = mt9p111_enum_framesizes, // // 
.enum_frameintervals = mt9p111_enum_frameintervals, 
.enum_fmt = mt9p111_enum_fmt, 
.try_fmt = mt9p111_try_fmt, 
.g_parm = mt9p111_g_parm, 
.s_parm = mt9p111_s_parm, 
.s_stream = mt9p111_s_stream, };

static const struct v4l2_subdev_ops mt9p111_ops = { 
.core = &mt9p111_core_ops, 
.video = &mt9p111_video_ops, 
}; 
#endif