ov9650学习(1)

来源:互联网 发布:网站怎么连接数据库 编辑:程序博客网 时间:2024/06/05 18:16

前言:对于ov9650的学习做一个记录,目标是在我的fl2440的板子上移植ov9650的驱动并实现在lcd上显示摄像头拍摄的内容,虽然最后还是花屏,但是还需要做一个记录,记下自己学习到的一点东西。希望发到网上然后得到广大网友的帮助,如果能对你们学习的过程中有帮助我也是荣幸。


内核:linux-3.0

开发板:fl2440

交叉编译器:arm-linux-gcc 2012.08

下篇地址:http://blog.csdn.net/liuzijiang1123/article/details/48247605

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

遇到的难题(从浅到深)

源码的寻找   

  我用的3.0的内核里面并不自带ov9650的驱动,飞凌公司自带的驱动是2.6.32内核。(由于内核版本差别比较大,所以内核自身的一些api也有变化,编译出现了很多问题)

ps:这个问题其实就是最大的一个问题,找不到合适的驱动嵌入合适的内核中,最后失败的原因我估计也是跟这个问题有一定联系的。


自己解决的办法:

a.首先肯定是重新编译它的驱动,由于版本差别过大很多api都不一样了,改了一些错误,后面出现的错误也越来越多,导致放弃,后面直接找了2.6.32的内核

经行编译,运行内核的时候总是卡在一个地方进不去,这个待解决。

b.网上查询关于ov9650的驱动源码,各个网站留言求代码,得都的一些代码( ov9650近几年才开源,所以网上相关文章并不是很多),可是平台不一样,有一些自己也看不懂,最后决定用天嵌公司的ov9650的源码,进行移植。


驱动的移植

对于驱动的移植有两种方法,一驱动集成到内核,二是生产.ko文件,在内核运行后再加载进去。经后面的对于花屏的测试的经验,还是第二种方法好,生产.ko文件,不用反复编译内核,一直擦出nand flash容易损坏。

ps:下面我就把内核移植的过程贴一下给新手看看。


一共6个文件:s3c2440camif.c    s3c2440camif.h     s3c2440_ov9650.c     sccb.c     sccb.h     Makefile


第一种方法不适合代码调试的阶段,但是也是有用的,所以得学会。

首先还是将这6个文件放在一起文件夹cmos130下

将这个文件夹放在    ./drivers/media/video/cmos130/


然后在video这个目录下的Kconfig文件中添加ov9650的选项,因为你make menuconfig进行配置的适合需要读取Kconfig

---------------------------------------------------------------------------------------------------------------------------------------------------------------

config VIDEO_OV9650

       tristate "OmniVision OV9650/OV9652 sensor support"    
       depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
       ---help---
         This is a V4L2 sensor-level driver for the Omnivision
         OV9650 and OV9652 camera sensors.
 source "drivers/media/video/cmos130/Kconfig"

--------------------------------------------------------------------------------------------------------------------------------------

ps:---tristate 代表三种状态:1.[ ]不选择,2.[*]选择直接编译进内核,加载驱动到内核里,3.[m]动态加载驱动;

     denpends on 后面的是它所依赖的,必须选上它们才能出现* 或者是出现个选项


在video下的Makefile中添加:

***********************************************************************************************************************

obj-$(CONFIG_S3C2440_CMOS_CAMERA130)  += cmos130/    (进去coms130这个文件中寻找.o文件)

***********************************************************************************************************************

进去cmos130,vim Kconfig

***********************************************************************************************************************

menu CMOS_Camera_syd168

  config  S3C2440_CMOS_CAMERA130

         tristate "OV9650 on the S3C2440 driver for SMDK2440"
         depends on VIDEO_DEV && SMDK2440_CPU2440
           default y if (VIDEO_DEV && SMDK2440_CPU2440)
endmenu

************************************************************************************************************************

然后你就可以make menuconfig,选上ov9650的驱动了。

下面是图解:








下面是Makefile 中的内容:

  

 s3c2440camera-objs    :=  s3c2440_ov9650.o sccb.o s3c2440camif.o obj-$(CONFIG_S3C2440_CMOS_CAMERA130) += s3c2440camera.o




第二种方法:

这个方法只需要改变Makefile中的内容即可


 

ARCH=arm920t   CROSS_COMPILE ?=/opt/buildroot-2011.11/${ARCH}/usr/bin/arm-linux-   PWD := $(shell pwd)   KERNELDIR ?= ${PWD}/../fl2440/kernel/linux-3.0/         obj-m := s3c2440camera.o   s3c2440camera-objs :=s3c2440_ov9650.o sccb.o s3c2440camif.o     default:      $(MAKE) -C $(KERNELDIR) M=$(PWD) modules      @make clear   clear:      @rm -f *.o *.cmd *.mod.c      @rm -rf  *~ core .depend  .tmp_versions Module.symvers modules.order -f       @rm -f .*ko.*  *ko.*  .*.o.cmd              clean:               rm -f *.ko


 

make 后即可生成.ko文件,然后insmod s3c2440camera.ko进内核就行  卸载的时候是rmmod s3c2440camera          


对于驱动理解

这个是学习中最精华的部分。

首先我们需要有一定的基础知识,下面是根据我的理解给出的需要理解的一些基础知识和概念。

sccb协议:

sccb是OmniVision 公司定制的串行摄像头控制总线(Serial Camera Control Bus)  ,它用于对摄像头的寄存器进行读写,以达到对摄像头输出图像的控制。     

两线SCCB接口有两条通迅连接线,即SIO_D(数据线)和 SIO_C(时钟线),下面是双总线功能原理图:     

        




数据都是在时钟线高电平的时候有效,起始是数据线的时钟从高变低,终止是数据线时钟从低到高。


它与i2c的协议大致相同,不过有一些细微的差别:

从机地址因为I2C是7位地址,最后一位是读写位,而SCCB是8位地址,比如ov9650,它是SCCB协议,他的地址是0x60,那么如果挂接到I2C总线上,他的地址就变成0x30了,这样算的:
SCCB地址:::  0x60:          0 1 1 0_0 0 0 0     这个0还是地址位
I2C地址 :::  0x30:          0 1 1 0_0 0 0 <0>最后的0是读写位,那么地址变成了7 位 +读写位 即 0 1 1_ 0 0 0 0 +0( 读写位 )  所以从机地址变成了0x30

如果你用i2c协议的话那么地址就要写成0x30,如果用gpio模拟sccb的话地址就要写成0x60,天嵌他们的代码是用后者。

在传输过程中它们需要检测厂商ID是否正确也就是从机地址,如果正确则进行数据传输。

在sccb.c中具体实现了sccb的初始化和操作。

ps:模拟的时候用gpe14 gpe15





这些是需要配置的pin脚:


我们看一下关于sccb的相关函数,在sccb.c中

sccb开始函数
static void __inline__ sccb_start(void){    CFG_WRITE(SIO_D);//设置“i2c”为输出模式    Low(SIO_D);     //设置低电平    WAIT_STABLE();  //延迟10us}


sccb写一个字节函数

static void __inline__ sccb_write_byte(u8 data){    int i;     CFG_WRITE(SIO_D);    WAIT_STABLE(); /*  write 8-bits octet. */    for (i=0;i<8;i++)<span style="line-height: 21px; background-color: rgb(242, 243, 245);"><span style="font-family:SimSun;font-size:10px;"> //并行数据转串行输出,串行数据输出的顺序为先高位再低位</span></span>    {        Low(SIO_C);        WAIT_STABLE();        if (data & 0x80)  //从高位开始写        {            High(SIO_D);        }        else        {            Low(SIO_D);        }        data = data<<1;        WAIT_CYCLE();        High(SIO_C);        WAIT_CYCLE();    }    /*  write byte done, wait the Don't care bit now. */  //第九位don not care 位    Low(SIO_C);     High(SIO_D);    CFG_READ(SIO_D);    WAIT_CYCLE();    High(SIO_C);    WAIT_CYCLE();}


sccb读一个字节

<pre name="code" class="cpp">static u8 __inline__ sccb_read_byte(void){    int i;    u8 data;    CFG_READ(SIO_D);    WAIT_STABLE();    Low(SIO_C);    WAIT_CYCLE();    data = 0;    for (i=0;i<8;i++)    {        High(SIO_C);        WAIT_STABLE();        data = data<<1;        data |= State(SIO_D)?1:0;        WAIT_CYCLE();        Low(SIO_C);        WAIT_CYCLE();    }    /*  read byte down, write the NA bit now.*/        CFG_WRITE(SIO_D);    High(SIO_D);    WAIT_CYCLE();    High(SIO_C);    WAIT_CYCLE();      return data;}

sccb停止函数

static void __inline__ sccb_stop(void){    Low(SIO_C);    WAIT_STABLE();    CFG_WRITE(SIO_D);    Low(SIO_D);    WAIT_CYCLE();    High(SIO_C);    WAIT_STABLE();    High(SIO_D);    WAIT_CYCLE();    CFG_READ(SIO_D);}

sccb写函数,需要三个参数,主线地址idaddr,寄存器地址subaddr,数据data,是单开始信号

void sccb_write(u8 IdAddr, u8 SubAddr, u8 data){    down(&bus_lock);    sccb_start();    sccb_write_byte(IdAddr);    sccb_write_byte(SubAddr);    sccb_write_byte(data); 
</pre><pre name="code" class="cpp">    
     sccb_stop();    up (&bus_lock);}

sccb读函数,主线地址idaddr,寄存器地址subaddr,双开始信号

u8 sccb_read(u8 IdAddr, u8 SubAddr) {    u8 data;    down(&bus_lock);    sccb_start();    sccb_write_byte(IdAddr);    sccb_write_byte(SubAddr);
</pre><pre name="code" class="cpp">    
     sccb_stop();     sccb_start();    sccb_write_byte(IdAddr|0x01);    data = sccb_read_byte();    sccb_stop();    up(&bus_lock);    return data;}

sccb初始化函数

int sccb_init(void){    CFG_WRITE(SIO_C);    CFG_WRITE(SIO_D);    High(SIO_C);    High(SIO_D);    WAIT_STABLE();    return 0;}


对于驱动程序的追踪和理解可以参考这个博客:http://blog.csdn.net/mirkerson/article/details/8170227


camer技术指标:

图像格式(image Format/ Color space)

 RGB24,I420是目前最常用的两种图像格式。RGB24:表示R、G、B三种颜色各8bit,最多可表现色。

 I420:YUV格式之一。


分辨率(Resolution)

所谓分辨率就是指画面的解析度,由多少象素构成的数值越大,图像也就越清晰。分辨率不仅与显示尺寸有关,还会受到显像管点距、视频带宽等因素的影 响。我们通常所看到的分辨率都以乘法形式表现的,比如1024*768,其中的1024表示屏幕上水平方向显示的点数,768表示垂直方向的点数。

QXGA (2048 X 1536)又称300万像素

UXGA (1600X 1200)又称200万像素

SXGA(1280 x1024)又称130万像素

XGA(1024 x768)又称80万像素

SVGA(800 x600)又称50万像素

VGA(640x480)又称30万像素(35万是指648X488)

CIF(352x288) 又称10万像素

SIF/QVGA(320x240)

QCIF(176x144)

QSIF/QQVGA(160x120)


图像压缩方式JPEG

    (joint photographic expert group)静态图像压缩方式。一种有损图像的压缩方式。压缩比越大,图像质量也就越差。当图像精度要求不高存储空间有限时,可以选择这种格式。目前大部分数码相机都使用JPEG格式。


彩色深度(色彩位数)

    反映对色彩的识别能力和成像的色彩表现能力,就是用多少位的二进制数字来记录三种原色。实际就是A/D转换器的量化精度,是指将信号分成多少个等级,常用 色彩位数(bit)表示。彩色深度越高,获得的影像色彩就越艳丽动人。非专业的SENSOR一般是24位;专业型SENSOR至少是36位。24位的 SENSOR,感光单元能记录的光亮度值最多有2^8=256级,每一种原色用一个8位的二进制数字来记录,最多记录的色彩是256×256×256约 16,77万种。

36位的SENSOR,感光单元能记录的光亮度值最多有2^12=4096级,每一种原色用一个12位的二进制数字来记录,最多记录的色彩是4096×4096×4096约68.7亿种。


ov9650的features:


时钟:

PLL
    S3C2440 CPU主频可达400MHz,开发板上的外接晶振为12M,通过时钟控制逻辑的PLL(phase locked loop,锁相环电路)来倍频这个系统时钟。2440有两个PLL(phase locked loop)一个是MPLL,一个是UPLL。UPLL专用于USB设备,常用频率为48MHz96MHz。MPLL用于CPU及其他外围器件,用于产生FCLK, HCLK, PCLK三种频率,上电时,PLL并没有被启动,FCLK=Fin=12MHz,若要提高系统时钟,需要软件来启动PLL。

    1,FCLKCPU提供的时钟信号。 

    2,HCLK是为AHB总线提供的时钟信号, Advanced High-performance Bus,主要用于高速外设,比如内存控制器,中断控制器,LCD控制器, DMA 等。 

    3,PCLK是为APB总线提供的时钟信号,Advanced Peripherals Bus,主要用于低速外设,比如看门狗,UART控制器, IIS, I2C, SDI/MMC, GPIO,RTC and SPI等。




我们需要通过XVCLK1给摄像头提供时钟,RESET是复位线,PWDN在摄像头工作时应该始终为低。HREF是行参考信号,PCLK是像素时钟,VSYNC是场同步信号。一旦给摄像头提供了时钟,并且复位摄像头,摄像头就开始工作了,通过HREF,PCLK和VSYNC同步传输数字图像信号。数据是通过D0~D7这八根数据线并行送出的。

http://blog.csdn.net/mirkerson/article/details/8192190


camif和fimc:

在三星datasheet和驱动代码中CAMIF和FIMCFully Interactive Mobile Camera)这两个术语根蒂根基上可以互换的,后面我们都用FIMC庖代CAMIF

网上说这2个的概念很模糊,我也百度了很久也不是很清楚,反正以我的理解就是,摄像头接口只是一个人为的说法,只是一个概念,相当于cpu和ov9650的中间人,ov9650将采集到的数据通过摄像头控制器处理后再传给cpu来进行处理。

CAMIF2DMA管道。P-path(预览管道)和C-path(编码管道)被分开接AHB总线上。从系统总线的角度看,这两条管道是独立的。P-path储存RGB格式的图片数据到内存中,以便生成预览。C-path保存YCbCr4:2:0422图片数据到内存中,以便编码成MPEG-4,H.263等视频格式。



v4l2:(天嵌他们的驱动没有用到v4l2的框架,但是这个很有必要知道)

 Video for Linuxtwo(Video4Linux2)简称V4L2,是V4L的改进版。V4L2是linux操作系统下用于采集图片、视频和音频数据的API接口,配合适当的视频采集设备和相应的驱动程序,可以实现图片、视频、音频等的采集。在远程会议、可视电话、视频监控系统和嵌入式多媒体终端中都有广泛的应用。

在Linux下,所有外设都被看成一种特殊的文件,成为“设备文件”,可以象访问普通文件一样对其进行读写。一般来说,采用V4L2驱动的摄像头设备文件是/dev/video0。V4L2支持两种方式来采集图像:内存映射方式(mmap)和直接读取方式(read)。V4L2在include/linux/videodev.h文件中定义了一些重要的数据结构,在采集图像的过程中,就是通过对这些数据的操作来获得最终的图像数据。Linux系统V4L2的能力可在Linux内核编译阶段配置,默认情况下都有此开发接口。

       而摄像头所用的主要是capature了,视频的捕捉。


应用程序通过V4L2接口采集视频数据分为五个步骤:

首先,打开视频设备文件,进行视频采集的参数初始化,通过V4L2接口设置视频图像的采集窗口、采集的点阵大小和格式;

其次,申请若干视频采集的帧缓冲区,并将这些帧缓冲区从内核空间映射到用户空间,便于应用程序读取/处理视频数据;

第三,将申请到的帧缓冲区在视频采集输入队列排队,并启动视频采集;

第四,驱动开始视频数据的采集,应用程序从视频采集输出队列取出帧缓冲区,处理完后,将帧缓冲区重新放入视频采集输入队列,循环往复采集连续的视频数据;

第五,停止视频采集。

具体的程序实现流程可以参考下面的流程图:




我的个人理解为v4l2是提供了一系列的camera功能的api,在驱动中加入它的ioctl然后可以在应用层进行调用,它已经封装好的api,这样对于我们获得视频的信息就方便很多,不用我们自己再去写函数去获得视频的信息

 http://blog.csdn.net/rubyboss/article/details/17410677这个博客写到了v4l2用户空间和内核空间是如何交互的


花屏:





网上百度了一些花屏的原因:

     因为摄像头有很多寄存器,可能一下无法理解里面所有的配置含义,所以开始时希望得到一份可用的配置。但往往从别人的测试代码中拿到配置后,仍然无法使用。我这里列出几个可能的原因:

(1)摄像头中的图像输出格式和你在camera控制器中设置的不一致,同一个摄像头可以设置多种输入格式,如:YCbYCr或CbYCrY。

 (2)图像输出的一些时序和你的camera控制器设置不一致,摄像头可以设置一些时序,如:图像数据在CAMPCLK的上升沿有效还是下降沿有效。

(3)注意输出图像的格式和Framebuffer控制器的匹配,如字节顺序等问题。


未完·等我再去整理一下,期待我的下篇






0 0
原创粉丝点击