为了正常的体验网站,请在浏览器设置里面开启Javascript功能!

CMOS摄像头驱动解析

2017-10-17 10页 doc 28KB 12阅读

用户头像

is_713593

暂无简介

举报
CMOS摄像头驱动解析CMOS摄像头驱动解析 我们解析一下 MINI2440 开发板的 CMOS 摄像头的驱动。该驱动总共包括以下文件:sccb.c、sccb.h、s3c2440_ov9650.c、s3c2440camif.h、s3c2440camif.c 五个文件~s3c2440camif.c 用于从 cmos 接口获取图像数据和将数据传输到进程空间在有 app 读取时。s3c2440_ov9650.c 读取和配置 ov9650 寄存器。通过 iic 接口传输数据。设备地址是 60define OV9650_SCCB_ADDR 0x60.比如进...
CMOS摄像头驱动解析
CMOS摄像头驱动解析 我们解析一下 MINI2440 开发板的 CMOS 摄像头的驱动。该驱动总共包括以下文件:sccb.c、sccb.h、s3c2440_ov9650.c、s3c2440camif.h、s3c2440camif.c 五个文件~s3c2440camif.c 用于从 cmos 接口获取图像数据和将数据传输到进程空间在有 app 读取时。s3c2440_ov9650.c 读取和配置 ov9650 寄存器。通过 iic 接口传输数据。设备地址是 60define OV9650_SCCB_ADDR 0x60.比如进行初始化和 product id 获取.sccb.c 定义了去读 ov9650 的寄存器的具体方法,是时序模拟的 iic。而 s3c2440_ov9650.c 里是调用这些具体方法去读写 ov9650 的寄存器的。所以我们先分析 sccb.c~首先看看它的结构:可以看到该程序是处理读写数据到 ov9650 的过程。首先我们应该知道 OV9650 内部有大量的寄存器需要配置,这就需要另外的数据接口。 OV9650 的数据接口称为 SCCB串行摄像控制总线,它由两条数据线组成:一个是用于传输时钟信号的 SIO_C,另一个是用于传输数据信号的 SIO_D。SCCB 的传输与 IIC 的极其相似,只不过 II C 在每传输完一个字节后,接收数据的一方要发送一位的确认数据,而 SCCB 一次要传输 9 位数据,前 8 位为有用数据,而第 9 位数据在写周期中是 Don’t-Care 位即不必关心位,在 ,读周期中是 NA 位。SCCB 定义数据传输的基本单元为相(phase)即一个相传输一个字节数据。SCCB 只包括三种传输周期,即 3 相写 ,2传输周期(三个相依次为设备从地址,内存地址,所写数据) 相写传输周期(两个相依次为设备从地址,内存地址)和 2 相读传输周 。当需要写操作时,应用期(两个相依次为设备从地址,所读数据)3 相写传输周期,当需要读操作时,依次应用 2 相写传输周期和 2 相读传输周期。 因此 SCCB 一次只能读或写一个字节。下面我们就用s3c244 的 IIC 总线接口分别与 OV9650 的 SIO_C 和 SIO_D 相连接来实现 SCCB 的功能。接下来对一些重要的函数讲一讲。static void __inline__ sccb_startvoid 批注A1: 这个函数原型是: define CFG_WRITEx CFG_WRITESIO_D dos3c2410_gpio_cfgpinxS3 C2410_GPIO_OUTPUTsmp _mbwhile0。通过这个宏 定义可以看出它是配置 LowSIO_D GPE14 为输出管脚。 批注A2: 这个函数的原型 WAIT_STABLE //延时一会, 以此代 sccb 保持一定的低电平, 是:define Lowx dos3c2410_gpio_setpinx0总线开始传输数据 smp_mbwhile0 可见是输出低电平的功能~值得提出的是内核在对管脚的寄存器配置中用到了一个概念:内存屏障~即 smp_mb函数。什么叫内存屏障,下面给出例子:define set_mbvar value do var value mb while 0define mb __asm__ __volatile__ quotquot : : : quotmemoryquot1 ) set_mbmbbarrier 函 数 追 踪 到 底 , 就 是 __asm____volatile__quotquot:::quotmemoryquot而这行代码就是内存屏障。2)__asm__用于指示编译器在此插入汇编语句3)__volatile__用于告诉编译器,严禁将此处的汇编语句与其它的语句重组合优化。即:原原本本按原来的样子处理这这里的汇编。4)memory 强制 gcc 编译器假设 RAM 所有内存单元均被汇编指令修改,这样 cpu 中的 registers 和 cache 中已缓存的内存单元中的数据将作废。cpu 将不得不在需要的时候重新读取内存中的数据。这就阻止了 cpu 又将 registers,cache 中的数据用于去优化指令,而避免去访问内存。5)quotquot:::表示这是个空指令。barrier不用在此插入一条串行化汇编指令。在后文将讨论什么叫串行化指令。6)__asm____volatile__memory 在前面已经解释在 linux/include/asm-i386/system.h 定义:define mb __asm__ __volatile__ quotlock addl00espquot: : :quotmemoryquot7)lock 前缀表示将后面这句汇编语句:quotaddl 00espquot作为 cpu的一个内存屏障。8)addl 00esp表示将数值 0 加到 esp 寄存器中,而该寄存器指向栈顶的内存单元。加上一 个 0,esp 寄存器的数值依然不变。即这是一条无用的汇编指令。在此利用这条无 价值的汇编指令来配合 lock指令,在__asm____volatile__memory 的作用下,用作 cpu 的内存屏障。9)set_task_state带有一个 memory barrier,set_task_state肯定是安 全的,但 __set_task_state可能会快些。关于 barrier宏实际上也是优化屏障:define barrier __asm__ __volatile__quotquot: : :quotmemoryquotCPU 越过内存屏障后,将刷 新自己对存储器的缓冲状态。这条语句实际上不生成任何代码,但可使 gcc 在 barrier之后刷新寄存器对变量的分配。不论是 gcc 编译器的优化还是处理器本身采 用的大量优化,如 Writebuffer Lock-up free Non- blocking reading Register allocationDynamic scheduling Multiple issues 等,都可能使得实际执行可能违反程序 顺序,因此,引入内存屏障来保证事件的执行次序严格按程序顺序来执行。使用内 存屏障强加的严格的 CPU 内存事件次序,保证程序的执行看上去象是遵循顺序一 致性模型。接下去的函数是写一个字节到芯片中:static void __inline__ sccb_write_byteu8 data int i CFG_WRITESIO_D 批注A3: 首先置数据总线为 低电 平~并等待一会儿~ WAIT_STABLE / write 8-bits octet. / for i0ilt8i LowSIO_C 批 注A4: 然后开始传输一个 字节。很据时序,首先置时 WAIT_STABLE 钟为低电平~ 然后等待一会~ if data amp 0x80 批注A5: 并行数据转串行数 据,我们先传输最高 80 位 与,没位与一次,该字节就 HighSIO_D 左位~将 我们要传输的字节与 0x 移一次,以保证每位都与 0x80 都位与过,以得到整个 数据传输图~通过程序可 以 看出字节是在时钟低电平时 else 变化的~这与 IIC 总线协议一 致~ LowSIO_D data dataltlt1 WAIT_CYCLE HighSIO_C 批注A6: 然后将时钟信号置 高~这样循环下去直到 8 位 WAIT_CYCLE 有效数据都传输完~ / write byte done wait the Dont care bit now. / LowSIO_C HighSIO_D CFG_READSIO_D WAIT_CYCLE HighSIO_C WAIT_CYCLE 之后读一个字节函数与此类似~记住这 里是串行数据转并行,同样高位在前这里的读写都是以 ov9650 芯片为参考物的~ Sccb 总线的 stop 函数与开始类似~有了上面四个基本函数,我们就可以根据 sccb 总线的协议来定义读 (记住读写周期不一样的)写函数了。如下:void sccb_writeu8 IdAddr u8 SubAddr u8 data downampbus_lock 批注A7: 因为我们在这里要 操作寄存 器,所以在操作前 sccb_start 都应该先给其上锁~避免操 作过程中芯片的寄存器被 修 sccb_write_byteIdAddr 改~ 批注A8: 写函数只需要三 sccb_write_byteSubAddr 相,即设备从地址,内存地 址,要写的数据~ sccb_write_bytedata sccb_stop up ampbus_locku8 sccb_readu8 IdAddr u8 SubAddr u8 data downampbus_lock sccb_start 批注A9: 读操作则首先是两 相在三相:写设备从地址, sccb_write_byteIdAddr 内 存地址,然后设备从地址、 读的数据~ sccb_write_byteSubAddr sccb_stop sccb_start sccb_write_byteIdAddr0x01 data sccb_read_byte sccb_stop upampbus_lock return data 至于 sccb 总线的初始化与清除函数就不多讲了~接着我们来分析 s3c2440_ov9650.c~同样看其结构图:首先有一个结构体:static struct ov9650_reg u8 subaddr u8 value 。。。。regs 。。。。。该结构体是配置 ov9650 芯片寄存器的。 这个是厂家提供的,我们无需多管,只要知道它的变量是子地址与对应的值~然后 有打开与关闭 0v9650 芯片的函数:static void __inline__ ov9650_poweronvoid s3c2410_gpio_cfgpinS3C2410_GPG12S3C2410_GPIO_OUTPUT s3c2410_gpio_setpinS3C2410_GPG12 0 mdelay20static void __inline__ ov9650_poweroffvoid s3c2410_gpio_cfgpinS3C2410_GPG12S3C2410_GPIO_OUTPUT s3c2410_gpio_setpinS3C2410_GPG12 1 mdelay20然后就用到我们之前定义的 sccb 总线的函数了:static int __inline__ ov9650_checkvoid u32 mid mid sccb_readOV9650_SCCB_ADDR 0x1cltlt8 批注A10: OV9650_SCCB_ ADDR 定义为 设备从地址: mid sccb_readOV9650_SCCB_ADDR 0x1d 0x60~OV9650 有两个只 读寄 存器 —— 0x1C 和 0x1D , printkquotSCCB address 0x02X manufacture ID 0x04X expect 用于存放厂家 ID,数据分别 0x7F 和 0xA2 。0x04Xnquot OV9650_SCCB_ADDR mid 该函数是检测从 ov9650 芯读 回来的制造厂商的 ID 与我们OV9650_MANUFACT_ID 定义的是否一样,以决定是 否调用该驱动~ return midOV9650_MANUFACT_ID1:0然后再读取该产品的 ID 号:static u32 __inline__ show_ov9650_product_idvoid u32 pid pid sccb_readOV9650_SCCB_ADDR 0x0altlt8 pid sccb_readOV9650_SCCB_ADDR 0x0b printkquotSCCB address 0x02X product ID 0x04X expect0x04Xnquot OV9650_SCCB_ADDR pid OV9650_PRODUCT_ID return pid读取一切正确后我们来配置寄存器,将刚才定义的结构体里的值依据前面 的地址一次写进寄存器中,实现 OV9650 intialization parametertable for SXGA10241280 application :static void ov9650_init_regsvoid int i downampregs_mutex for i0 iltARRAY_SIZEregs i if regsi.subaddr 0xff mdelayregsi.value continue sccb_writeOV9650_SCCB_ADDR regsi.subaddrregsi.value upampregs_mutex这就是整个驱动函数执行的过程,这些执 行正确后就应该转到最重要的驱动程序中:s3c2440camif.c~首次看看它的结构图: 首先定义了一个结构体,该结构体用来定义摄像头会用到一些信息,比如输入图像 的格式、大小、输出地大小等:static struct s3c2440camif_dev camera下面先介绍 s3c2440 摄像接口的相关配置。 摄像接口有两个相互独立 的 DM A 通道 —— P 通道(预览通 和道) C 通道 。 (编解码通道)P 通道主要是存储用于视频显示 的 RGB图像数据,C 通道主要是存储用于编解码的 YCbCr 图像数据 。设置 s3c2440 摄像接口一个很重要的步骤就是设置视频尺寸大小 。我们把由 OV9650 采集到的视频尺寸称为源,即源水平尺寸和源垂直尺寸,其中源水平尺寸必须是 8 的整数倍。这个尺寸是通过配置OV9650 的相关寄存器实现的。我们把这两个值分 别放入输入源格式寄存器 CISRCFMT 的第 16 位至第 28 位 ,和第 0 位至第 12 位内 , 例如通过 OV9650 , 采集的到的视频尺寸为 640 × 480 ,则把 640 和 480 分别放入寄存器 CISRCFMT 中的相应位置即可 。 我们把实际显示的视频尺 寸称为目标,即目标水平尺寸和目标垂直尺寸,这里这个尺寸就是 LCD 的尺寸 。 我们把这两个值分别放入预览 DMA 目标图像格式寄存器 CIPRTRGFMT 的第 16 位至第 28 位 ,和第 0 位至第 12 位内,例如 LCD 的尺寸为 320 ×240 ,则 把 320 和 240 分别放入寄存 器 CIPRTRGFMT 中的相应位置即可 。 另外还需 要把这两个值的乘积放入预览缩放目标面积寄存 器 CIPRTAREA 内。源尺寸和目 标尺寸往往是不一样大小的,那么可能还需要设置偏移量,即水平偏移量和垂直偏 移量,应该把这两个值分别放入窗口偏移寄存器CIWDOFST 的第 16 位至 第 26 位 , 和第 0 位至第 10 位内,其中这个寄存器的第 31 位用于控制是否需要设置 偏移量,偏移量为 0或不需要设置偏移量时,这一位应为 0,否则为 1。显然,通 过源尺寸、目标尺寸和偏移量的设置,可以实现被摄像物体的缩放效果。当然,要 实现这种缩放效果,还需要配置预览预缩放比例控制寄存器CIPRSCPRERATIO 、 预览预缩放距离格式寄存器 CIPRSCPREDST和预览主缩放控制寄存器 CIPRSCCTRL ,这些寄存器的相关参数是通过计算得到的 , 数据手册上有详细 的说明,而且还有的函数可以调用,因此在这里就不过多介绍。 前面已经介绍 过,摄像接口都是通过 DMA 实现数据交换的。在DMA 数据传递中,还要让 DMA 知道如何进行传递,即一次传输多少个字节,这需要设置预览 DMA 控制相关寄存 器 CIPRCTRL 的主突发长度和剩余突发长度,这两个值也可以通过调用标准函数 来求得。另外在完成每一帧视频采集后,会触发一个视频中断。 批注A11: s3c2440 能够在 接着有初始化了一个缓冲结构体,开辟四块内存: 内存中各开辟四块乒乓 存储 区域,用于实现 P 通道和 C struct s3c2440camif_buffer img_buff 通道的快速 数据传递。在 P 通道中,寄存 器 CIPRCLRSA1 、 CIPRCLRSA2 、 CIPRCLRSA3 和 CIPRCLRSA4 分别用于表 .state CAMIF_BUFF_INVALID 示 A12: 给每块内存先提 .img_size 0 供一个状态,其中这四块内存的首地址~ 批注 状态可以 有:enum .order 0 CAMIF_BUFF_INVALID 0 .virt_base unsigned longNULL CAMIF_BUFF_RGB565 1 .phy_base unsigned longNULL CAMIF_BUFF_RGB24 2 CAMIF_BUFF_YCbCr420 3 CAMIF_BUFF_YCbCr422 4 然后大小初蓟 ?0, .state CAMIF_BUFF_INVALID 物理与虚拟地址待定,这 待 会通过系统直接赋值~ .img_size 0 .order 0 .virt_base unsigned longNULL .phy_base unsigned longNULL .state CAMIF_BUFF_INVALID .img_size 0 .order 0 .virt_base unsigned longNULL .phy_base unsigned longNULL .state CAMIF_BUFF_INVALID .img_size 0 .order 0 .virt_base unsigned longNULL .phy_base unsigned longNULL 现在我们开始从驱动模块的最开始执行 的函数讲起:static int __init camif_initvoid int ret struct s3c2440camif_dev pdev struct clk camif_upll_clk printkKERN_ALERTquotinitializing s3c2440 camera interface......nquot pdev ampcamera / set gpio-j to camera mode. / s3c2410_gpio_cfgpinS3C2440_GPJ0S3C2440_GPJ0_CAMDATA0 批注A13: 配置 GPIO 口的 J 部分引脚为摄像头模式~ ................. / init cameras virtual memory. / if request_mem_regionunsigned longS3C2440_PA_CAMIF 批注A14: 给摄像头分配并 .
/
本文档为【CMOS摄像头驱动解析】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。 本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。 网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。

历史搜索

    清空历史搜索