logo资料库

OV9650CMOS摄像头驱动解析.pdf

第1页 / 共29页
第2页 / 共29页
第3页 / 共29页
第4页 / 共29页
第5页 / 共29页
第6页 / 共29页
第7页 / 共29页
第8页 / 共29页
资料共29页,剩余部分请下载后查看
我们解析一下 MINI2440 开发板的 CMOS 摄像头的驱动。该驱动总共 包括以下文件:sccb.c、sccb.h、s3c2440_ov9650.c、s3c2440camif.h、 s3c2440camif.c 五个文件! s3c2440camif.c 用于从 cmos 接口获取图像数据和将数据传输到进程 空间(在有 app 读取时)。 s3c2440_ov9650.c 读取和配置 ov9650 寄存器。通过iic 接口传输数据。 设备地址是 60(#defineOV9650_SCCB_ADDR 0x60).比如进行初 始化和 productid 获取. 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 的功能。 接下来对一些重要的函数讲一讲。 staticvoid__inline__sccb_start(void) { CFG_WRITE(SIO_D); Low(SIO_D); WAIT_STABLE(); //延时一会,保持一定的低电平,以此代表 sccb 总线开始传输数据 这个函数原型是: 批注[A1]: #defineCFG_WRITE(x) do{s3c2410_gpio_cfgpin(x,S3 C2410_GPIO_OUTPUT);smp _mb();}while(0)。通过这个宏 定义可以看出它是配置 GPE14 为输出管脚。 这个函数的原型 批注[A2]: 是:#defineLow(x) do{s3c2410_gpio_setpin(x,0) ;smp_mb();}while(0) 可见是输出低电平的功能!
} 值得提出的是内核在对管脚的寄存器配置中用到了一个概念:内存屏 障!即 smp_mb()函数。 什么叫内存屏障?下面给出例子: #defineset_mb(var,value)do {var=value;mb();}while(0) #definemb()__asm____volatile__("":::"memory") 1 ) set_mb(),mb(),barrier() 函 数 追 踪 到 底 , 就 是 __asm__ __volatile__("":::"memory"),而这行代码就是内存屏障。 2)__asm__用于指示编译器在此插入汇编语句 3)__volatile__用于告诉编译器,严禁将此处的汇编语句与其它的语 句重组合优化。即:原原本本按原来的样子处理这这里的汇编。 4)memory 强制 gcc 编译器假设 RAM 所有内存单元均被汇编指令修 改,这样 cpu 中的 registers 和 cache 中已缓存的内存单元中的数据将 作废。cpu 将不得不在需要的时候重新读取内存中的数据。这就阻止 了 cpu 又将 registers,cache 中的数据用于去优化指令,而避免去访问 内存。 5)"":::表示这是个空指令。barrier()不用在此插入一条串行化汇编指 令。在后文将讨论什么叫串行化指令。 6)__asm__,__volatile__,memory 在前面已经解释 在 linux/include/asm-i386/system.h 定义: #define mb() __asm__ __volatile__ ("lock; addl $0,0(%%esp)":::"memory")
7)lock 前缀表示将后面这句汇编语句:"addl$0,0(%%esp)" 作为 cpu 的一个内存屏障。 8)addl$0,0(%%esp) 表示将数值 0 加到 esp 寄存器中,而该寄存器指 向栈顶的内存单元。加上一个 0,esp 寄存器的数值依然不变。即这 是一条无用的汇编指令。在此利用这条无价值的汇编指令来配合 lock 指令,在__asm__,__volatile__,memory 的作用下,用作 cpu 的内存屏 障。 9)set_task_state()带有一个 memorybarrier ,set_task_state()肯定是安 全的,但 __set_task_state()可能会快些。 关于 barrier()宏实际上也是优化屏障: #definebarrier()__asm____volatile__("":::"memory") CPU 越过内存屏障后,将刷新自己对存储器的缓冲状态。这条语句 实际上不生成任何代码,但可使 gcc 在 barrier()之后刷新寄存器对变 量的分配。 不论是 gcc 编译器的优化还是处理器本身采用的大量优化,如 Write buffer, Lock-up free, Non- blocking reading, Register allocation, Dynamicscheduling,Multipleissues 等,都可能使得实际执行可能违 反程序顺序,因此,引入内存屏障来保证事件的执行次序严格按程序 顺序来执行。 使用内存屏障强加的严格的 CPU 内存事件次序,保证程序的执行看 上去象是遵循顺序一致性模型。 接下去的函数是写一个字节到芯片中:
staticvoid__inline__sccb_write_byte(u8data) { inti; CFG_WRITE(SIO_D); WAIT_STABLE(); /*write8-bitsoctet.*/ for(i=0;i<8;i++) { Low(SIO_C); WAIT_STABLE(); if(data&0x80) { } High(SIO_D); else { Low(SIO_D); } data=data<<1; 批注[A3]: 低电平!并等待一会儿! 首先置数据总线为 批注[A4]: 字节。很据时序,首先置时 然后开始传输一个 钟为低电平!然后等待一会! 并行数据转串行数 批注[A5]: 据,我们先传输最高位!将 我们要传输的字节与 0x80 位 与,没位与一次,该字节就 左移一次,以保证每位都与 0x80 都位与过,以得到整个 数据传输图!通过程序可以 看出字节是在时钟低电平时 变化的!这与 IIC 总线协议一 致!
批注[A6]: 然后将时钟信号置 高!这样循环下去直到 8 位 有效数据都传输完! WAIT_CYCLE(); High(SIO_C); WAIT_CYCLE(); } /*writebytedone,waittheDon'tcarebitnow.*/ Low(SIO_C); High(SIO_D); CFG_READ(SIO_D); WAIT_CYCLE(); High(SIO_C); WAIT_CYCLE(); { } } 之后读一个字节函数与此类似!记住这里是串行数据转并行,同样高 位在前!这里的读写都是以 ov9650 芯片为参考物的! Sccb 总线的 stop 函数与开始类似! 有了上面四个基本函数,我们就可以根据 sccb 总线的协议来定义读
写函数了。如下:(记住读写周期不一样的) voidsccb_write(u8IdAddr,u8SubAddr,u8data) { } down(&bus_lock); sccb_start(); sccb_write_byte(IdAddr); sccb_write_byte(SubAddr); sccb_write_byte(data); sccb_stop(); up(&bus_lock); u8sccb_read(u8IdAddr,u8SubAddr) { u8data; down(&bus_lock); sccb_start(); sccb_write_byte(IdAddr); sccb_write_byte(SubAddr); sccb_stop(); 批注[A7]: 操作寄存器,所以在操作前 因为我们在这里要 都应该先给其上锁!避免操 作过程中芯片的寄存器被修 改! 批注[A8]: 相,即设备从地址,内存地 写函数只需要三 址,要写的数据! 批注[A9]: 相在三相:写设备从地址, 读操作则首先是两 内存地址,然后设备从地址、 读的数据!
sccb_start(); sccb_write_byte(IdAddr|0x01); data=sccb_read_byte(); sccb_stop(); up(&bus_lock); returndata; } 至于 sccb 总线的初始化与清除函数就不多讲了! 接着我们来分析 s3c2440_ov9650.c! 同样看其结构图: 首先有一个结构体: staticstructov9650_reg
分享到:
收藏