平台信息:
内核:linux
系统:android
平台:S5PV310(samsung exynos 4210)
作者:xubin341719(欢迎转载,请注明作者)
新项目开案,代码他们还没给得到,三星那边办事流程就是多,烦人(嘿嘿只是说说,流程从另一方面说明了人家标准化的程度高)。看看代码,把前一段时间工作的内容整理下,发出来。一方面有相同问题的“同学”可以看下,说不定问题就解决了; 再一方面自己工作方面记录吧,整个流程整理出来,加深自己的印象,技术还得提高呀。这样利人利己的事多做点好……“为人民服务!”(我不是**党,只是技术P民)
这篇比较基础,做为科普知识看一下。
android camera(一):camera模组CMM介绍
android camera(二):摄像头工作原理、s5PV310 摄像头接口(CAMIF)
android camera(三):camera V4L2 FIMC
android camera(四):camera 驱动 GT2005
一、摄像头模组(CCM)介绍:
1、camera特写
摄像头模组,全称CameraCompact Module,以下简写为CCM,是影像捕捉至关重要的电子器件。先来张特写,各种样子的都有,不过我前一段时间调试那个有点丑。
2、摄像头工作原理、camera的组成各组件的作用
想完全的去理解,还得去深入,如果是代码我们就逐步分析,模组的话我们就把它分解开来,看他到底是怎么工作的。看下它是有那些部分构成的,如下图所示:
(1)、工作原理:物体通过镜头(lens)聚集的光,通过CMOS或CCD集成电路,把光信号转换成电信号,再经过内部图像处理器(ISP)转换成数字图像信号输出到数字信号处理器(DSP)加工处理,转换成标准的GRB、YUV等格式图像信号。
(2)、CCM 包含四大件: 镜头(lens)、传感器(sensor)、软板(FPC)、图像处理芯片(DSP)。决定一个摄像头好坏的重要部件是:镜头(lens)、图像处理芯片 (DSP)、传感器(sensor)。CCM的关键技术为:光学设计技术、非球面镜制作技术、光学镀膜技术。
镜头(lens)是相机的灵魂,镜头(lens)对成像的效果有很重要的作用,是利用透镜的折射原理,景物光线通过镜头,在聚焦平面上形成清晰的影像,通过感光材料CMOS或CCD感光器记录景物的影像。镜头厂家主要集中在台湾、日本和韩国,镜头这种光学技术含量高的产业有比较高的门槛,业内比较知名的企业如富士精机、柯尼卡美能达、大立光、Enplas等
传感器(sensor)是CCM的核心模块,目前广泛使用的有两种:一种是广泛使用的CCD(电荷藕合)元件;另一种是CMOS(互补金属氧化物导体)器件。
电荷藕合器件图像传感器CCD(Charge Coupled Device),它使用一种高感光度的半导体材料制成,能把光线转变成电荷,通过模数转换器芯片转换成数字信号。CCD由许多感光单位组成,通常以百万像 素为单位。当CCD表面受到光线照射时,每个感光单位会将电荷反映在组件上,所有的感光单位所产生的信号加在一起,就构成了一幅完整的画面。CCD传感器模块以日本厂商为主导,全球规模市场有90%以上被日本厂商垄断,以索尼、松下、夏普为龙头。
互补性氧化金属半导体CMOS(Complementary Metal-Oxide Semiconductor)主要是利用硅和锗这两种元素所做成的半导体,使其在CMOS上共存着带N(带–电)和 P(带+电)级的半导体,这两个互补效应所产生的电流即可被处理芯片纪录和解读成影像。CMOS 传感器主要美国、台湾和韩国为主导,主要生产厂家有美国 OmniVision、Agilent、Micron,台湾的锐像、原相、泰视等,韩国的三星、现代。
图像处理芯片(DSP)是CCM的重要组成部分,它的作用是将感光芯片获得的数据及时快速地传递中央处理器并刷新感光芯片,因此DSP芯片的好坏,直接影响画面品质(比如色彩饱和度,清晰度等)。
FPC柔性电路板(柔性PCB): 简称"软板", 又称"柔性线路板",连接芯片和手机。起到电信号传输作用。
3、camera模组的装配方式
(1)、定焦模组装配图
CCM分为定焦模组和自动变焦模组,其中定焦模组主要由镜头、镜座、感光集成电路、软性线路板、补强和钢片装配而成,其装配图如下:
(2)、自动变焦模组装配图
以前我不知道手机上的光学变焦是怎么会事,以为那些都是忽悠人的,看这个图这下明白了,如果你也不知道,看下这个图,原来里面有个音圈马达,这个只有高像素的模组才有,这个我们不用管,知道怎么光学变焦就可以了。
自动变焦模组由镜头、音圈马达、底座支架、感光集成电路、驱动集成电路和连接器组成,其装配图如下:
(3)、3D模组
传说中的3D是怎么会事,多几台摄像机拍摄。手机上也有了??有点强大
看人家手机上用的,感觉看着不太好看,高科技的东西开始就是做得丑了一点,我们了解有这个东西就行了:
4、摄像头的一些技术指标
(1)、图像解析度/分辨率(Resolution)
QSIF/QQVGA | 160 x 120 | 19200 |
|
QCIF | 176 x 144 | 25344 |
|
SIF/QVGA | 320 x 240 | 76800 |
|
CIF | 352 x 288 | 101376 | 10万像素 |
VGA | 640 x 480 | 307200 | 30万像素(35万是指648X488) |
SVGA | 800 x 600 | 480000 | 50万像素 |
XGA | 1024 x 768 | 786438 | 80万像素 |
SXGA | 1280 x 1024 | 1310720 | 130万像素 |
UXGA | 1600 x 1200 | 1920000 | 200万像素 |
QXGA | 2048 x 1536 | 3145728 | 300万像素(320W) |
QSXGA | 2592 x 1944 | 5038848 | 500万像素 |
| 2816 x 2112 | 2947392 | 600万像素 |
| 3072 x 2304 | 7077888 | 700万像素 |
| 3200 x 2400 | 7680000 | 770万像素 |
| 3264 x 2448 | 7990272 | 800万像素 |
| 3876 x 2584 | 10015584 | 1000万像素 |
(2)、图像格式(imageFormat/Colorspace)
RGB24,420是目前最常用的两种图像格式。
RGB24:表示R、G、B三种颜色各8bit,最多可表现256级浓淡,从而可以再现256*256*256种颜色。
I420:YUV格式之一。其它格式有:RGB565,RGB444,YUV4:2:2等。
(3)、自动白平衡调整(AWB)
定义:要求在不同色温环境下,照白色的物体,屏幕中的图像应也是白色的。色温表示光谱成份,光的颜色。色温低表示长波光成分多。当色温改变时,光源中三基色(红、绿、蓝)的比例会发生变化,需要调节三基色的比例来达到彩色的平衡,这就是白平衡调节的实际。
(4)、图像压缩方式
JPEG:(joint photo graphicexpert group)静态图像压缩方式。一种有损图像的压缩方式。压缩比越大,图像质量也就越差。当图像精度要求不高存储空间有限时,可以选择这种格式。目前大部分数码相机都使用JPEG格式。
(5)、彩色深度(色彩位数)
反映对色彩的识别能力和成像的色彩表现能力,实际就是A/D转换器的量化精度,是指将信号分成多少个等级。常用色彩位数(bit)表示。彩色深度越高,获得的影像色彩就越艳丽动人。现在市场上的摄像头均已达到24位,有的甚至是32位
(6)、图像噪音
指的是图像中的杂点干挠。表现为图像中有固定的彩色杂点。
(7)、视角
与人的眼睛成像是相成原理,简单说就是成像范围。
(8)、输出/输入接口
串行接口(RS232/422):传输速率慢,为115kbit/s
并行接口(PP):速率可以达到1Mbit/s
红外接口(IrDA):速率也是115kbit/s,一般笔记本电脑有此接口
通用串行总线USB:即插即用的接口标准,支持热插拔。USB1.1速率可达12Mbit/s,USB2.0可达480Mbit/s
IEEE1394(火线)接口(亦称ilink):其传输速率可达100M~400Mbit/s
一、摄像头工作原理
上一篇我们讲了摄像头模组的组成,工作原理,做为一种了解。下面我们析摄像头从寄存器角度是怎么工作的。如何阅读摄像头规格书(针对驱动调节时用到关键参数,以GT2005为例)。
规格书,也就是一个器件所有的说明,精确到器件每一个细节,软件关心的寄存器、硬件关心的电气特性、封装等等。单单驱动方面,我们只看对我们有用的方面就可以了,没必要全部看完。主要这样资料全都是鸟语(En),全部看完一方面时间上会用的比较多,找到关键的地方就行了。
1、camera的总体示意图如下:控制部分为摄像头上电、IIC控制接口,数据输出为摄像头拍摄的图传到主控芯片,所有要有data、行场同步和时钟号。GT2005/GT2015是CMOS接口的图像传感器芯片,可以感知外部的视觉信号并将其转换为数字信号并输出。
我们需要通过MCLK给摄像头提供时钟,RESET是复位线,PWDN在摄像头工作时应该始终为低。PCLK是像素时钟,HREF是行参考信号,VSYNC是场同步信号。一旦给摄像头提供了时钟,并且复位摄像头,摄像头就开始工作了,通过HREF,VSYNC和PCLK同步传输数字图像信号。 数据是通过D0~D7这八根数据线并行送出的。
(1)、Pixel Array
GT2005阵列大小为 1268 列、1248 行,有效像素为 1616 列, 1216 行。也就是说摄像头为1600X1200的时候,像素点要多于这个,去除边缘一部分,保证图像质量吧。
(2)、IIC 这个不用说了,摄像头寄存器初始化的数据都从这里传输的,所有的IIC器件都一样的工作,来张图吧,后面做详细分析;
下面这一部分在调试驱动的过程中比较重要了:
(3)、MCLK
电子元件工作都得要个时钟吧,摄像头要工作,这个就是我们所要的时钟,在主控制芯片提供,这个时钟一定要有,要不然摄像头不会工作的。
(4)、上下电时序,这个要接规格书上来,注间PWDN、RESETB这两个脚,不同的摄像头不太一样,这个图是上电时序,上电时参考一下,知道在那里看就行;
(5)PCLK \D1~D7
摄像头得到的数据要传出来吧,要有数据,当然数据出来要有时钟和同步信号了,看下它的时序,和LCD显示的时序一样,道理是一样的:
(6)、主要的寄存器:分辨率、YUV顺序、X轴、Y轴镜相、翻转
以上工作完成后,也许还有一些问题,分辨率太小; YUV顺序不对图像不对; XY图像方向。这些工作完成后,如果还有什么细节的问题,如果你想花时间,看规格书里面的寄存器可以解决的,如果不想看,找模组厂的FAE,他们专业的,很快会帮你搞定。
(7)、摄像头的硬件接口
二、S5pv310上Camera主控芯片上的硬件接口
1、CAMIF Camera Interface
先看一下摄像头接口框图:(这个看着有点抽像,我们放这里,先了解一下,其实驱动中一般不会涉及到这里,不过我们这里分析了,就把这个带出来了)
(1)、摄像头接口的主要属性:
a、支持多种输入接口:(就是上面我们看到的四模式)
DMA (AXI 64-bitinterface) 模式;
MIPI (CSI) 模式;
ITU-R BT 601/ 656/ 709模式;
Direct FIFO (PlayBack)模式;
b、支持多种输出模式:
DMA (AXI 64-bitinterface) 模式;
Direct FIFO 模式;
c、支持数码变焦Digital Zoom In (DZI) capability;
d、支持多摄像头输入;
e、 支持视频同步信号极性可编程控制;
f、支持最大输入分辨率为8192X8192;
g、支持图像翻转(X轴、Y轴镜相,90、180、270翻转);
h、支持多种图片格式;
i、支持捕获帧控制;
j、支持的图像特效。
2、FIMC Fully InteractiveMobile Camera
摄像头的采集的数据要CPU无法直接处理,主控芯片里面集成了Camera控制器,叫FIMC(FullyInteractive Mobile Camera)。摄像头需要先把图像数据传给控制器,经过控制器处理(裁剪拉升后直接预览或者编码)之后交给CPU处理。实际上摄像头工作需要的时钟(MCLK)也是FIMC给它提供的。
在s5pv310上的摄像头接口是一个FIMC(完全交互式移动相机接口),支持ITUR BT-601-605标准、AMX接口、MIPI接口
MIPI 、ITU、AMX
(1)、ITU国际电信联盟无线电通信部门ITU-RRadiocommunication Sector of ITU 简称ITU-RITU-R BT.601 16位数据传输;Y、U、V信号同时传输,是并行数据,行场同步单独输出。
ITU-R BT.6568/10位数据传输;不需要同步信号;串行数据传输;传输速率是601的2倍;先传Y,后传UV。行场同步信号嵌入在数据流中。
(2)、MIPI(移动行业处理器接口)是MobileIndustry Processor Interface的缩写 MIPI 规范:Camera工作组:MIPI Camera Serial Interface 1.0specification .Camera Serial Interface 2 v1.0 (CSI-2)
(3)、AMX(Advanced eXtensible Interface)是一种总线协议,该协议是ARM公司提出的AMBA(Advanced Microcontroller BusArchitecture)3.0协议中最重要的部分,是一种面向高性能、高带宽、低延迟的片内总线。
3、接口信息
FIMC信号定义如下所示(YCbCr模式)
Signal | I/O | Description | Type |
HREF | I | 行同步信号 | |
PCLK | I | 像素时钟 | |
DATA[7:0] | I | 像素数据 | |
FIELD | O | FIELD信号 | |
MCLK | O | 系统时钟信号 |
通过CAM_MCLK给摄像头提供时钟,RST是复位线,PWDN在摄像头工作时应该始终为低。HREF是行参考信号,PCLK是像素时钟,VSYNC是场同步信号。一旦给摄像头提供了时钟,并且复位摄像头,摄像头就开始工作了,通过HREF,PCLK和VSYNC同步传输数字图像信号。数据是通过DATA0~DATA7这八根数据线并行送出的。
4、不同接口模式的工作时序
ITU-R BT 656输入时序图,这种方式下同步信号已经内嵌到视频数据中了,因此不需要额外的行和帧同步信号。
ITU-R BT 601输入时序图,这种方式下行和帧同步信号独立于视频数据,因此需要同步信号。
(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后插入周期
5、camera的时钟域,三个时钟:系统时钟、PCLK、MCLK
每个摄像头接口包括三个时钟域,每一个时钟域是系统总线时钟,第二个是摄像头像素时钟PCLK,第三个时钟域为内部时钟MCLK。系统总线时钟必需高于PCLK, CAM_MCLK 必需固定频率分频,如PLL时钟。如果有外部时钟晶振,CAM_MCLK 空掉。不需要同步MMCLK,PCLK应该与schmitt-triggered电平移位器连接。
6、硬件接口电路
主控芯片上的接口:
camera 接口
前面两篇说的有点多了,不过多了解点东西也挺好的,遇到问题时可以有更多的思路,真正驱动是从这一块开始。一般BSP的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; /* v4l 设备结构 */
- struct cdev *cdev; /* 字符设备结构*/
- /* 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;
- };
struct video_device
{
/* device ops */
const struct v4l2_file_operations *fops; /*接口函数指针*/
/* sysfs */
struct device dev; /* v4l 设备结构 */
struct cdev *cdev; /* 字符设备结构*/
/* 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;
};
主要接口函数有:
intvideo_register_device(struct video_device *vdev, int type, int nr);
static intv4l2_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg);
2. 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; /* 控制器 id */
- char name[16];
- atomic_t in_use;
- void __iomem *regs; /* 寄存器 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];
- };
struct fimc_control {
int id; /* 控制器 id */
char name[16];
atomic_t in_use;
void __iomem *regs; /* 寄存器 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];
};
因为FIMC一共有三套一样的控制器(fimc0, fimc1, fimc2),所以驱动里使用了一个数组来描述:
- struct video_device fimc_video_device[FIMC_DEVICES] = {
- [0] = {
- .fops = &fimc_fops,
- .ioctl_ops = &fimc_v4l2_ops,
- .release = fimc_vdev_release,
- },
- [1] = {
- .fops = &fimc_fops,
- .ioctl_ops = &fimc_v4l2_ops,
- .release = fimc_vdev_release,
- },
- [2] = {
- .fops = &fimc_fops,
- .ioctl_ops = &fimc_v4l2_ops,
- .release = fimc_vdev_release,
- },
- };
struct video_device fimc_video_device[FIMC_DEVICES] = {
[0] = {
.fops = &fimc_fops,
.ioctl_ops = &fimc_v4l2_ops,
.release = fimc_vdev_release,
},
[1] = {
.fops = &fimc_fops,
.ioctl_ops = &fimc_v4l2_ops,
.release = fimc_vdev_release,
},
[2] = {
.fops = &fimc_fops,
.ioctl_ops = &fimc_v4l2_ops,
.release = fimc_vdev_release,
},
};
fb_ops结构体是针对v4l2设备的基本操作,定义如下:
- static const struct v4l2_file_operations fimc_fops = {
- .owner = THIS_MODULE,
- .open = fimc_open,
- .release = fimc_release,
- .ioctl = video_ioctl2,
- .read = fimc_read,
- .write = fimc_write,
- .mmap = fimc_mmap,
- .poll = fimc_poll,
- };
static const struct v4l2_file_operations fimc_fops = {
.owner = THIS_MODULE,
.open = fimc_open,
.release = fimc_release,
.ioctl = video_ioctl2,
.read = fimc_read,
.write = fimc_write,
.mmap = fimc_mmap,
.poll = fimc_poll,
};
3)FIMC初始设置
在S5PV210中,FIMC初始设置代码在 /drivers/ arch/arm/mach-s5pv210/mach-smdkv310.c中:
- static struct s3c_platform_fimc fimc_plat = {
- .srclk_name = "mout_mpll",
- .clk_name = "sclk_fimc",
- .lclk_name = "sclk_fimc_lclk",
- .clk_rate = 166750000,
- .default_cam = CAMERA_CSI_C,
- .camera = {
- &mt9p111,//5M back cam
- &s5k6aafx,///1.3M front cam
- },
- .hw_ver = 0x43,
- };
static struct s3c_platform_fimc fimc_plat = {
.srclk_name = "mout_mpll",
.clk_name = "sclk_fimc",
.lclk_name = "sclk_fimc_lclk",
.clk_rate = 166750000,
.default_cam = CAMERA_CSI_C,
.camera = {
&mt9p111,//5M back cam
&s5k6aafx,///1.3M front cam
},
.hw_ver = 0x43,
};
对于GPIO的配置代码在 /drivers/ arch/arm/mach-s5pv210/setup-fimc0.c中:
- oid 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);
- }
- }
oid 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中:
- onst 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,
- };
onst 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,
};
对于寄存器的操作,实现都在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;
- }
一、摄像头驱动
我们以GT2005为例,来讲述一个摄像头驱动的调试过程:
摄像头和主控的关系图,如下:
摄像头驱动比较简单,完成一面三大步就可以:
摄像头的上电、时钟这些基本条件;
IIC保证摄像头的初始化;
摄像头工作后传回数据到主控。
完成这三个步骤就完成了摄像头基本工作的调试。
1、 摄像头的上电、时钟这些基本条件的实现
电源部分,上电时对应规格书,确认上电时序;MCLK摄像头的主时钟是否提供,看下面是和电和规格书中的
(1)、主意摄像头工作要两组电压:1.8V(1.5V)、2.8V还有MCLK
程序中对电源控制:我们在arch\arm\mach-smdkv310.c中,完成上面两个步骤的代码,(这一点不同平台也许有所不同,这里这是一个实例,这部分代码只是针对三星平台的)
- #ifdef CONFIG_VIDEO_GT2005
- static void set_cam2005_main_power(int onoff)
- {
- unsigned int gpio;
- int err;
- if(onoff)
- {
- err = gpio_request(S5PV210_GPB(2), "GPB");
- if (err)
- printk(KERN_ERR "#### failed to request GPB2 for CAM\n");
- s3c_gpio_cfgpin(S5PV210_GPB(2),S3C_GPIO_OUTPUT);
- s3c_gpio_setpull(S5PV210_GPB(2), S3C_GPIO_PULL_UP);
- gpio_direction_output(S5PV210_GPB(2), 1);
- gpio_free(S5PV210_GPB(2));
- //RESET
- err = gpio_request(S5PV210_GPE1(4), "GPE1");
- if (err)
- printk(KERN_ERR "#### failed to request GPE1_4 for CAM\n");
- s3c_gpio_setpull(S5PV210_GPE1(4), S3C_GPIO_PULL_NONE);
- gpio_direction_output(S5PV210_GPE1(4),0);
- mdelay(100);
- gpio_direction_output(S5PV210_GPE1(4), 1);
- gpio_free(S5PV210_GPE1(4));
- //PWDN CAM_back
- err = gpio_request(S5PV210_GPB(4), "GPB");
- if (err)
- printk(KERN_ERR "#### failed to request GPB4 for CAM\n");
- s3c_gpio_cfgpin(S5PV210_GPB(4),S3C_GPIO_OUTPUT);
- s3c_gpio_setpull(S5PV210_GPB(4), S3C_GPIO_PULL_UP);
- gpio_direction_output(S5PV210_GPB(4), 0);
- mdelay(100);
- gpio_direction_output(S5PV210_GPB(4), 1);
- gpio_free(S5PV210_GPB(4));
- }
- else
- {
- ………………
- }
- return;
- }
- #endif
#ifdef CONFIG_VIDEO_GT2005
static void set_cam2005_main_power(int onoff)
{
unsigned int gpio;
int err;
if(onoff)
{
err = gpio_request(S5PV210_GPB(2), "GPB");
if (err)
printk(KERN_ERR "#### failed to request GPB2 for CAM\n");
s3c_gpio_cfgpin(S5PV210_GPB(2),S3C_GPIO_OUTPUT);
s3c_gpio_setpull(S5PV210_GPB(2), S3C_GPIO_PULL_UP);
gpio_direction_output(S5PV210_GPB(2), 1);
gpio_free(S5PV210_GPB(2));
//RESET
err = gpio_request(S5PV210_GPE1(4), "GPE1");
if (err)
printk(KERN_ERR "#### failed to request GPE1_4 for CAM\n");
s3c_gpio_setpull(S5PV210_GPE1(4), S3C_GPIO_PULL_NONE);
gpio_direction_output(S5PV210_GPE1(4),0);
mdelay(100);
gpio_direction_output(S5PV210_GPE1(4), 1);
gpio_free(S5PV210_GPE1(4));
//PWDN CAM_back
err = gpio_request(S5PV210_GPB(4), "GPB");
if (err)
printk(KERN_ERR "#### failed to request GPB4 for CAM\n");
s3c_gpio_cfgpin(S5PV210_GPB(4),S3C_GPIO_OUTPUT);
s3c_gpio_setpull(S5PV210_GPB(4), S3C_GPIO_PULL_UP);
gpio_direction_output(S5PV210_GPB(4), 0);
mdelay(100);
gpio_direction_output(S5PV210_GPB(4), 1);
gpio_free(S5PV210_GPB(4));
}
else
{
………………
}
return;
}
#endif
提供MCLK代码如下:
- static int smdkv210_cam_clk_init(void)
- {
- unsigned int tempvalue=0;
- tempvalue = readl(S5P_CLK_DIV1);
- tempvalue = (tempvalue &0xffff0fff);
- writel(tempvalue,S5P_CLK_DIV1);
- tempvalue = readl(S5P_CLK_SRC1);
- tempvalue = (tempvalue &0xffff0fff)|0x00001000;
- writel(tempvalue,S5P_CLK_SRC1);
- return 0;
- }
static int smdkv210_cam_clk_init(void)
{
unsigned int tempvalue=0;
tempvalue = readl(S5P_CLK_DIV1);
tempvalue = (tempvalue &0xffff0fff);
writel(tempvalue,S5P_CLK_DIV1);
tempvalue = readl(S5P_CLK_SRC1);
tempvalue = (tempvalue &0xffff0fff)|0x00001000;
writel(tempvalue,S5P_CLK_SRC1);
return 0;
}
这时拿示波器抓到上电时的波形和MCLK24MHZ的时钟,信号就说明我们完成了这一步,其实在实际工作中,我们一般出问题的时候才会测这些数据,确认问题点。
上电时序:
(1)、上电时序;
(2)、MCLK如下所示。
2、 IIC和一些参数的配置
- #ifdef CONFIG_VIDEO_GT2005
- static struct GT2005_platform_data GT2005_plat = {
- .default_width = 640,
- .default_height = 480,
- .pixelformat = V4L2_PIX_FMT_UYVY, // .freq = 24000000,
- .is_mipi = 0,
- };
- static struct i2c_board_info GT2005_i2c_info = {
- I2C_BOARD_INFO("GT2005", 0x78 >> 1), //1、IIC地址
- .platform_data = >2005_plat,
- };
- static struct s3c_platform_camera GT2005 = {
- #ifdef CAM_ITU_CH_A
- .id = CAMERA_PAR_A,
- #else
- .id = CAMERA_PAR_B,
- #endif
- .type = CAM_TYPE_ITU, //2、数据接口选择(ITU\MIPI等)
- .fmt = ITU_601_YCBCR422_8BIT,
- .order422 = CAM_ORDER422_8BIT_CBYCRY, //3、图像数据格式
- .i2c_busnum = 1,
- .info = >2005_i2c_info,
- .pixelformat = V4L2_PIX_FMT_UYVY,
- .srclk_name = "mout_mpll", //4、这部分关系到时钟
- .clk_name = "sclk_cam1",
- .clk_rate = 24000000, /* 24MHz */
- .line_length = 640, /* 640*480 */
- /* default resol for preview kind of thing */
- .width = 640,
- .height = 480,
- .window = {
- .left = 16,
- .top = 0,
- .width = (640 - 16),
- .height = 480,
- },
- /* Polarity */ //5、信号的极性, .inv_pclk = 0,
- .inv_vsync = 1,
- .inv_href = 0,
- .inv_hsync = 1,
- .initialized = 0,
- .cam_power=set_cam2005_main_power
- };
- #endif
#ifdef CONFIG_VIDEO_GT2005
static struct GT2005_platform_data GT2005_plat = {
.default_width = 640,
.default_height = 480,
.pixelformat = V4L2_PIX_FMT_UYVY, // .freq = 24000000,
.is_mipi = 0,
};
static struct i2c_board_info GT2005_i2c_info = {
I2C_BOARD_INFO("GT2005", 0x78 >> 1), //1、IIC地址
.platform_data = >2005_plat,
};
static struct s3c_platform_camera GT2005 = {
#ifdef CAM_ITU_CH_A
.id = CAMERA_PAR_A,
#else
.id = CAMERA_PAR_B,
#endif
.type = CAM_TYPE_ITU, //2、数据接口选择(ITU\MIPI等)
.fmt = ITU_601_YCBCR422_8BIT,
.order422 = CAM_ORDER422_8BIT_CBYCRY, //3、图像数据格式
.i2c_busnum = 1,
.info = >2005_i2c_info,
.pixelformat = V4L2_PIX_FMT_UYVY,
.srclk_name = "mout_mpll", //4、这部分关系到时钟
.clk_name = "sclk_cam1",
.clk_rate = 24000000, /* 24MHz */
.line_length = 640, /* 640*480 */
/* default resol for preview kind of thing */
.width = 640,
.height = 480,
.window = {
.left = 16,
.top = 0,
.width = (640 - 16),
.height = 480,
},
/* Polarity */ //5、信号的极性, .inv_pclk = 0,
.inv_vsync = 1,
.inv_href = 0,
.inv_hsync = 1,
.initialized = 0,
.cam_power=set_cam2005_main_power
};
#endif
注意下面几个参数:
(1)、IIC地址
(2)、数据接口选择(ITU\MIPI等),这几个数据接口我们在前面提到过;
(3)、图像数据格式 ,这就是我们前面提到的YVU\UVY之类不同的数据顺序;
(4)、这部分关系到时钟;
(5)、输出信号的极性,就是我们PCLK、VSYNC、HSVNC这些信号的极性,不正确时会没有图像之类现象,这个也要注意了。
完成这一步确认IIC通信是否正常,如下图所抓到波形:
3、 完成上面两步,摄像头基本配置完成,我们确认下输出端PCLK、VHSN、SVSN、D1-D7
二、摄像头调试过程中常遇到的问题
1、录相在预览时正常,播放录像时花屏(程序解释为三星s5pv210/s5pv310是的,其它的可以参考,找相应代码)
原因分析:
(1)录相用fimc2,
在android/device/samsung/proprietary/libcamera/SecCamera.cpp中
int SecCamera::startRecord(void)
- m_cam_fd_rec = open(CAMERA_DEV_NAME2, O_RDWR);
- vendor\sec\sec_proprietary\smdkc110\libcamera\SecCamera.h
- #define CAMERA_DEV_NAME2 "/dev/video2"
m_cam_fd_rec = open(CAMERA_DEV_NAME2, O_RDWR);
vendor\sec\sec_proprietary\smdkc110\libcamera\SecCamera.h
#define CAMERA_DEV_NAME2 "/dev/video2"
(2)所用的视频格式,在
android/device/samsung/proprietary/libcamera/SecCameraHWInterface.cpp中
void CameraHardwareSec::m_initDefaultParameters(int camera_id)
- int default_preview_pixel_format = mSecCamera->getPreviewDefaultPixelFormat();
- switch (default_preview_pixel_format) {
- default:
- case V4L2_PIX_FMT_NV21:
- p.setPreviewFormat(CameraParameters::PIXEL_FORMAT_YUV420SP);
- break;
- case V4L2_PIX_FMT_NV12T:
- p.setPreviewFormat("yuv420sp_tiled");
- break;
- }
- 其中mSecCamera->getPreviewDefaultPixelFormat()在
- vendor\sec\sec_proprietary\smdkc110\libcamera\SecCamera.cpp中实现,
- int SecCamera::getPreviewDefaultPixelFormat(void)
- {
- return DEFAULT_PREVIEW_PIXEL_FORMAT;
- }
- 在android/device/samsung/proprietary/libcamera/SecCamera.h中设定初始值。我们这里设的是
- #ifdef DUAL_PORT_RECORDING
- #define DEFAULT_PREVIEW_PIXEL_FORMAT (V4L2_PIX_FMT_NV21)
- #else
- #define DEFAULT_PREVIEW_PIXEL_FORMAT (V4L2_PIX_FMT_NV12T)
- #endif
int default_preview_pixel_format = mSecCamera->getPreviewDefaultPixelFormat();
switch (default_preview_pixel_format) {
default:
case V4L2_PIX_FMT_NV21:
p.setPreviewFormat(CameraParameters::PIXEL_FORMAT_YUV420SP);
break;
case V4L2_PIX_FMT_NV12T:
p.setPreviewFormat("yuv420sp_tiled");
break;
}
其中mSecCamera->getPreviewDefaultPixelFormat()在
vendor\sec\sec_proprietary\smdkc110\libcamera\SecCamera.cpp中实现,
int SecCamera::getPreviewDefaultPixelFormat(void)
{
return DEFAULT_PREVIEW_PIXEL_FORMAT;
}
在android/device/samsung/proprietary/libcamera/SecCamera.h中设定初始值。我们这里设的是
#ifdef DUAL_PORT_RECORDING
#define DEFAULT_PREVIEW_PIXEL_FORMAT (V4L2_PIX_FMT_NV21)
#else
#define DEFAULT_PREVIEW_PIXEL_FORMAT (V4L2_PIX_FMT_NV12T)
#endif
以上得出视频格式为V4L2_PIX_FMT_NV21。
2、图像干扰问题,如下图所示
原因可能是:
(1)、摄像头模组有问题,换一个摄像头试一下;
(2)、数据线驱动能力不足,这个可以在摄像头寄存器里面改,问下摄像头模组FAE,看改那些地方;
(3)、两个摄像头共用数据线时,不工作的摄像头会把工作的数据信号减弱;
(4)、PCB 走线太长,也会有干扰,不过我觉得这个可能性小,调试好就一款这样的的,模组厂FAE说的,不过他们模组本身也有问题,两方面因素都有吧:PCB(线过长)、模组打样也有问题。
3、YUV顺序不对:
yuv顺序不对时,出现如下现象。
看下摄像头规格书,把相应寄存器的值改一下就可以了。如下以红框里是不同yuv顺序,找到改为相应的。
4、 预览方向不对(摄像头寄存器只能改以 180度为基数的,90度的就要在FIMC中改)
想知道和比较明确说明翻转角度,最常用的就是写一个“F”,然后看预览里的是怎么转的。
上面有90度的翻转,因为这是FIMC中的寄存器,一般不会改这一部分的值。要不就在模组寄存器中改,要不就在上层。上面只是说明如何去确认图像翻转。
看下GT2005关于翻转的寄存器吧:
5、 杂光,鬼影:
其行业的专业术语统称为Flare,是指在拍摄光源或者强光物体时,边缘出现光影或出现一个完整物体的影子,而且这种现象只能减轻不能完全消失,原因是由于镜片的材质导致光线不但存在折射还存在反射,整机由于镜头面到保护镜片距离很大会更明显!
6、 常见问题相关
摄像头效果评测都有哪些?
以上以gt2005为例,说了一下摄像头驱动,和驱动中常见到的问题,如果上面的问题解决,摄像头点亮,说明在平台是运行是没问题的,小的问题和细节,要找模组厂的FAE过来协助解决,不过你想多研究一下的话,也可以,如果项目允许,就多看看,做到调试一个驱动,所有问题都了解,这样对工作经验、积累很重要的,希望对大家有用。