转载请注明出处:http://blog.csdn.net/lxk7280
    
   
    
     
    
   
    
     有人会奇怪为什么使用OV系列的摄像头每次都要进行SCCB的操作呢?难道它自己不会保存上次的操作结果吗?
    
   
    
     
     
     原因是:OV系列的摄像头的寄存器是EEPROM,不稳定,数据很容易丢失,因此程序每次初始化时我们都要重新写入寄存器设置。
    
   
    
     PS:常见需要修改的寄存器有,PCLK速率,帧率、图像亮度、对比度、色饱和度、镜像等功能。
    
   
    
     
    
   
    
     
    
   
    
     
     
     智能车摄像头组的初期学习中,虽然有不少摄像头优于OV7620,但是相信大部分的车友第一个接触的都是OV7620。下面从其特性和性能等角度,剖析摄像头的特点。
    
   
    
     
     
     摄像头的输出格式有RGB565,YUY422等格式,我所接触的第一个摄像头
     
      OV7620的输出格式是YUV422
     
     。下面给大家介绍一下YUV422。
    
   
    
     什么是YUV422?
    
   
    
     
     
     人的眼睛对低频信号比对高频信号具有更高的敏感度,事实上,人的眼睛对明视度的改变比对色彩的改变要敏感的多。因此,人们将RGB三色信号改为YUV来表示,其中Y为灰度,UV为色差。如果是表示一副彩色图像,同样的道理,YUV444是无损的存储方式,但是需要3个字节,存储空间开销很大。由于Y分量比UV分量重要的多,因此人们用YUV422来表示。这样一来图像被压缩了很多,一个字节就可以表示其彩色的信息。
     
    
   
    
     
     
     对于OV7620,它有2 组并行的数据口Y[7..0]和UV[7..0],其中对于数据口Y[7..0],输出的是灰度值Y,对于UV[7..0]输出的色度信号UV。下图给出了k 个像素(K 个字节)输出的格式。
    
   
    
    
   
     
   
    
    
    OV762的控制采用SCCB(Serial Camera ControlBus)协议。SCCB的简化的I2C协议,SIO-I是串行时钟输入线,SIO-O是串行双向数据线,分别相当于I2C协议的SCL和SDA。SCCB的总线时序与I2C基本相同,他的响应信号ACK被陈伟一个传输单元的第9位,分别Do not care和NA.Do not care位由从机产生;NA位由主机产生,由于SCCB不支持多字节的读写,NA位必须为高电平。另外SCCB没有重复起始的概念,因此在SCCB的读周期中,当主机发送读命令时,从机将不能产生Do not care响应信号。
   
    
    
    由于I2C和SCCB的一些细微差别,所以采用GPIO模拟SCCB总线的方式,SCL所连接的引脚始终设为输出方式,而SDA所连接的引脚在数据传输过程中,通过设置IODIR的值,动态改变引脚的输入/输出方式。SCCB的写周期直接使用I2C总线协议的写周期时序;而SC-CB的读周期,则增加一个总线停止条件。
   
    
     OV7620的几个优点:
     
     
     
     第一,OV7620的电平兼容3.3V和5V。目前智能车用户用到的处理器基本上可以分为XS128和K60和KL25三种控制器,而这三种控制器的工作电平分别是5V和3.3V和3.3V。OV7620可以完全适应这两种电平,XS128和K60和KL25可以随性切换,无需做电平匹配。
     
      (要注意的是当OV7620接5v和3.3v的时候,输出的效果是不同的,建议在5v的电压下使用,因为在3.3v的电压下使用比较难调,输出的16进制数据清一色偏小。)
     
    
    
   
    
     
      
     
    
   
    
     
      
     
    
   
    
     同样的情况下:
    
   
    
     3.3V下:                                                                                                       5v下:
    
   
    
     
     
     
    
   
    
    
    
     第二,OV7620的帧率是60帧/s。新手学习摄像头的时候,误以为摄像头帧率越快越好,其实不然。就拿OV7620来说,其PCLK(像素中断)的周期是73ns,该频率下的PCLK很容易被K60的IO捕捉,如果帧率更快的摄像头,其PCLK的周期就会更小,该频率下PCLK不易被K60的IO捕捉到。
     
      (但是鹰眼摄像头不然,火哥的鹰眼摄像头理论上宣传的是150帧每秒,但是他并不是通过PCLK的周期减小从而获得效果的,鹰眼摄像头的高明之处在于它在硬件二值化之后,每一次PCLK中断对外输出了8个像素,而不是1个像素。鹰眼摄像头已经买来了,以后有机会会试试效果。)
     
     
     
     
     第三:OV7620的分辨率也是非常合适的,在第三篇也提到OV7620是隔行扫描,采集VSYN的话,其输出分辨率是640*240。如果改为QVGA格式,默认输出分辨率是320*120,该分辨率下非常适合采集赛道,数据容量有限又不会失真图像。
     
      (OV7620的分辨率可以通过SCCB修改,有兴趣修改的可以去查看OV7620的寄存器配置,然后通过SCCB修改。)
     
     
    
   
    
     
    
   
    
     
    
   
    
     
     
     只有掌握了OV7620的时序,才能灵活得使用OV7620。下面开始本篇的重点:OV7620时序分析。
     
     
     
     对于OV7620,我们只关心场中断信号VSYN、行中断信号HREF、像素中断信号PCLK的波形。用示波器去监控这三个波形,可以看到一下关系
    
    。
    
   
    
    
   
    
     VSYN 的周期是16.64ms,高电平时间为换场时间,约80us;低电平时间内像素输出。我们在采集VSYN脉冲时,既可以采集上升沿,也可以采集下降沿,采集下降沿更准确些,这也是一场的开始。从VSYN的周期可以算出,1s/16.64ms=60帧,OV7620的帧率是60帧/s。
     
     HREF的周期63.6us,高电平时间为像素输出时间,约47us;低电平时间为换行时间,因此采集HREF一定要采集其上升沿,下降沿后的数据是无效的。从HREF的周期可以算出,16.64ms/63.6us≈261,除去期间的间隙时间,可以算出每场图像有240行。
     
    
   
    
     PCLK的周期是73ns,高电平输出像素,低电平像素无效。PCLK是一直输出的,因此一定要在触发VSYN并且触发HREF以后,再去捕捉PCLK才能捕捉到像素数据。从PCLK的周期可以算出,47us/73ns≈640,可以算出每行图像中有640个像素点。
    
   
    
     介绍完基本知识之后,下面开始写程序了(Keil–K60–C语言):
    
   
    
    
   
    
     在这我分成两部分着重介绍7620的时序程序和贴上SCCB的协议程序(其实原理和处理情况和I2C差不多):
    
   
    
     
    
   
    
     First :
    
   
    
     首先要对使用到的一些IO口进行初始化处理,四个部分的初始化,
    
   
    
     A.像素中断PCLK
    
   
    
     B.行中断HREF
    
   
    
     C.场中断VSYNC
    
   
    
     D.DMA
    
   
    
     程序如下:
    
   
- 
      
 
 //初始化OV7620模块
 
 
 
 
- 
      
 
 void
 
 
 OV
 
 
 7
 
 
 6
 
 
 2
 
 
 0
 
 
 _Init()
 
 
- 
      
 {
 
- 
      
 
 //像素中断 PCLK
 
 
 
 
- 
      
 GPIO_InitStruct
 
 1
 
 
 .GPIO_Pin
 
 
 = OV
 
 
 7
 
 
 6
 
 
 2
 
 
 0
 
 
 _PCLK_PIN;
 
 
- 
      
 GPIO_InitStruct
 
 1
 
 
 .GPIO_InitState
 
 
 = Bit_SET;
 
 
- 
      
 GPIO_InitStruct
 
 1
 
 
 .GPIO_IRQMode
 
 
 = GPIO_IT_DMA_RISING;
 
 
- 
      
 GPIO_InitStruct
 
 1
 
 
 .GPIO_Mode
 
 
 = GPIO_Mode_IPD;
 
 
- 
      
 GPIO_InitStruct
 
 1
 
 
 .GPIOx
 
 
 = OV
 
 
 7
 
 
 6
 
 
 2
 
 
 0
 
 
 _PCLK_PORT;
 
 
- 
      
 GPIO_Init(&GPIO_InitStruct
 
 1
 
 
 );
 
 
- 
      
 
- 
      
 
 //行中断 HREF
 
 
 
 
- 
      
 GPIO_InitStruct
 
 1
 
 
 .GPIO_Pin
 
 
 = OV
 
 
 7
 
 
 6
 
 
 2
 
 
 0
 
 
 _HREF_PIN;
 
 
- 
      
 GPIO_InitStruct
 
 1
 
 
 .GPIO_InitState
 
 
 = Bit_SET;
 
 
- 
      
 GPIO_InitStruct
 
 1
 
 
 .GPIO_IRQMode
 
 
 = GPIO_IT_RISING;
 
 
- 
      
 GPIO_InitStruct
 
 1
 
 
 .GPIO_Mode
 
 
 = GPIO_Mode_IPD;
 
 
- 
      
 GPIO_InitStruct
 
 1
 
 
 .GPIOx
 
 
 = OV
 
 
 7
 
 
 6
 
 
 2
 
 
 0
 
 
 _HREF_PORT;
 
 
- 
      
 GPIO_Init(&GPIO_InitStruct
 
 1
 
 
 );
 
 
- 
      
 
- 
      
 
 // 场中断 VSYNC
 
 
 
 
- 
      
 GPIO_InitStruct
 
 1
 
 
 .GPIO_Pin
 
 
 = OV
 
 
 7
 
 
 6
 
 
 2
 
 
 0
 
 
 _VSYNC_PIN;
 
 
- 
      
 GPIO_InitStruct
 
 1
 
 
 .GPIO_InitState
 
 
 = Bit_SET;
 
 
- 
      
 GPIO_InitStruct
 
 1
 
 
 .GPIO_IRQMode
 
 
 = GPIO_IT_RISING;
 
 
 //GPIO_IT_RISING
 
 
 
 
- 
      
 GPIO_InitStruct
 
 1
 
 
 .GPIO_Mode
 
 
 = GPIO_Mode_IPD;
 
 
 //GPIO_Mode_IPD
 
 
 
 
- 
      
 GPIO_InitStruct
 
 1
 
 
 .GPIOx
 
 
 = OV
 
 
 7
 
 
 6
 
 
 2
 
 
 0
 
 
 _VSYNC_PORT;
 
 
- 
      
 GPIO_Init(&GPIO_InitStruct
 
 1
 
 
 );
 
 
- 
      
 
- 
      
 
 //配置DMA
 
 
 
 
- 
      
 DMA_InitStruct
 
 1
 
 
 .Channelx
 
 
 = DMA_CH
 
 
 1
 
 
 ;
 
 
 //DMA 1通道
 
 
 
 
- 
      
 DMA_InitStruct
 
 1
 
 
 .PeripheralDMAReq
 
 
 =PORTC_DMAREQ;
 
 
 //C端口(PCLK) 上升呀触发
 
 
 
 
- 
      
 DMA_InitStruct
 
 1
 
 
 .MinorLoopLength
 
 
 =
 
 
 1
 
 
 7
 
 
 0
 
 
 ;
 
 
 //传输次数 超过摄像头每行像素数即可
 
 
 
 
- 
      
 DMA_InitStruct
 
 1
 
 
 .TransferBytes
 
 
 =
 
 
 1
 
 
 ;
 
 
 //每次传输1个字节
 
 
 
 
- 
      
 DMA_InitStruct
 
 1
 
 
 .DMAAutoClose
 
 
 = ENABLE;
 
 
 //连续采集
 
 
 
 
- 
      
 DMA_InitStruct
 
 1
 
 
 .EnableState
 
 
 = ENABLE;
 
 
 //初始化后立即采集
 
 
 
 
- 
      
 
- 
      
 DMA_InitStruct
 
 1
 
 
 .SourceBaseAddr
 
 
 =(uint
 
 
 3
 
 
 2
 
 
 _t)&PTD->PDIR;
 
 
 //摄像头端口接D0-D7
 
 
 
 
- 
      
 DMA_InitStruct
 
 1
 
 
 .SourceMajorInc
 
 
 =
 
 
 0
 
 
 ;
 
 
 //地址不增加
 
 
 
 
- 
      
 DMA_InitStruct
 
 1
 
 
 .SourceDataSize
 
 
 = DMA_SRC_
 
 
 8
 
 
 BIT;
 
 
 //8BIT数据
 
 
 
 
- 
      
 DMA_InitStruct
 
 1
 
 
 .SourceMinorInc
 
 
 =
 
 
 0
 
 
 ;
 
 
- 
      
 
- 
      
 
- 
      
 DMA_InitStruct
 
 1
 
 
 .DestBaseAddr
 
 
 =(uint
 
 
 3
 
 
 2
 
 
 _t)DMABuffer;
 
 
 //DMA 内存 //uint8_t DMABuffer[400];
 
 
 
 
- 
      
 DMA_InitStruct
 
 1
 
 
 .DestMajorInc
 
 
 =
 
 
 0
 
 
 ;
 
 
- 
      
 DMA_InitStruct
 
 1
 
 
 .DestDataSize
 
 
 = DMA_DST_
 
 
 8
 
 
 BIT;
 
 
- 
      
 DMA_InitStruct
 
 1
 
 
 .DestMinorInc
 
 
 =
 
 
 1
 
 
 ;
 
 
 //每次传输 +1个字节
 
 
 
 
- 
      
 DMA_Init(&DMA_InitStruct
 
 1
 
 
 );
 
 
- 
      
 }
 
   
   
    然后开始编写场中断函数,编写之前我们需要在心里理一下思绪,在场中断函数里我们要
    
     按照顺序
    
    ,做以下几件事情:
   
   
   
    
     A.确认是否是场中断,确认之后进入处理。
    
   
    
     B.清除标志位Flag。
     
      (Flag是用来观察是否处理完一场图像的标志)
     
    
   
    
     C.清除中断标志。
    
   
    
     D.计数全部清零。(因为新的一场已经开始)
    
   
    
     E.打开行中断,关闭场中断。
    
   
- 
      
 
 void
 
 
 PORTB_IRQHandler(
 
 
 void
 
 
 )
 
 
 //功 能:PORTB 外部中断服务 //V
 
 
 
 
- 
      
 {
 
- 
      
 u
 
 8
 
 
 i=
 
 
 9
 
 
 ;
 
 
- 
      
 
 if
 
 
 ((PORTB->ISFR>>i)==
 
 
 1
 
 
 )
 
 
- 
      
 {
 
- 
      
 Flag =
 
 0
 
 
 ;
 
 
- 
      
 PORTB->ISFR|=(
 
 1
 
 
 <<
 
 
 9
 
 
 );
 
 
- 
      
 Row =
 
 0
 
 
 ;
 
 
- 
      
 Row_Num =
 
 0
 
 
 ;
 
 
- 
      
 NVIC_EnableIRQ(PORTA_IRQn);
 
 //行
 
 
 
 
- 
      
 NVIC_DisableIRQ(PORTB_IRQn);
 
 //场
 
 
 
 
- 
      
 }
 
   
   
    接着编写行中断函数,在行中断中,我们要做以下几件事情:
   
   
   
    
     A.确认是否是行中断。
    
   
    
     B.关闭DMA中断,防止提前进入PCLK的采集。
    
   
    
     C.跳过消隐区。
     
      (消隐区:消隐区的出现,在电视机原理上,是因为电子束结束一行扫描,从一行尾换到另一行头,期间的空闲期,这叫做行消隐信号;同理,从一场尾换到另一场尾,期间也会有空闲期,这叫做场消隐信号。)
     
    
   
    
     D.进入行采集处理。
    
   
    
     E.配置DMA,并且打开DMA中断。
    
   
    
     F.行计数加1,表示已经采集完了一行。(因为PCLK的中断周期远远小于HREF的中断周期,所以不需要杞人忧天,担心中断搞得混乱。)
    
   
    
     G.当采集完了自己的目标行数之后,标志位Flag修改。并关闭行中断,打开场中断,等待下一次的场中断。
    
   
- 
      
 
 void
 
 
 PORTA_IRQHandler(
 
 
 void
 
 
 )
 
 
 //功 能:PORTA 外部中断服务//Herf
 
 
 
 
- 
      
 {
 
- 
      
 u
 
 8
 
 
 i=
 
 
 1
 
 
 4
 
 
 ;
 
 
- 
      
 DMA_SetEnableReq(DMA_CH
 
 1
 
 
 ,DISABLE);
 
 
 //close DMA ISr
 
 
 
 
- 
      
 
 if
 
 
 ((PORTA->ISFR>>i)==
 
 
 1
 
 
 );
 
 
- 
      
 {
 
- 
      
 PORTA->ISFR|=(
 
 1
 
 
 <<
 
 
 1
 
 
 4
 
 
 );
 
 
- 
      
 
 if
 
 
 (Row_Num++ >
 
 
 1
 
 
 5
 
 
 )
 
 
 //消隐区啦
 
 
 
 
- 
      
 {
 
- 
      
 
 if
 
 
 (Row_Num%
 
 
 5
 
 
 )
 
 
 //进入行采集
 
 
 
 
- 
      
 {
 
- 
      
 
 //配置DMA
 
 
 
 
- 
      
 DMA_InitStruct
 
 1
 
 
 .Channelx
 
 
 = DMA_CH
 
 
 1
 
 
 ;
 
 
 //DMA 1通道
 
 
 
 
- 
      
 DMA_InitStruct
 
 1
 
 
 .PeripheralDMAReq
 
 
 =PORTC_DMAREQ;
 
 
 //C端口(PCLK) 上升呀触发
 
 
 
 
- 
      
 DMA_InitStruct
 
 1
 
 
 .MinorLoopLength
 
 
 =
 
 
 1
 
 
 7
 
 
 0
 
 
 ;
 
 
 //传输次数 超过摄像头每行像素数即可
 
 
 
 
- 
      
 DMA_InitStruct
 
 1
 
 
 .TransferBytes
 
 
 =
 
 
 1
 
 
 ;
 
 
 //每次传输1个字节
 
 
 
 
- 
      
 DMA_InitStruct
 
 1
 
 
 .DMAAutoClose
 
 
 = ENABLE;
 
 
 //连续采集
 
 
 
 
- 
      
 DMA_InitStruct
 
 1
 
 
 .EnableState
 
 
 = ENABLE;
 
 
 //初始化后立即采集
 
 
 
 
- 
      
 
- 
      
 DMA_InitStruct
 
 1
 
 
 .SourceBaseAddr
 
 
 =(uint
 
 
 3
 
 
 2
 
 
 _t)&PTD->PDIR;
 
 
 //摄像头端口接D0-D7
 
 
 
 
- 
      
 DMA_InitStruct
 
 1
 
 
 .SourceMajorInc
 
 
 =
 
 
 0
 
 
 ;
 
 
 //地址不增加
 
 
 
 
- 
      
 DMA_InitStruct
 
 1
 
 
 .SourceDataSize
 
 
 = DMA_SRC_
 
 
 8
 
 
 BIT;
 
 
 //8BIT数据
 
 
 
 
- 
      
 DMA_InitStruct
 
 1
 
 
 .SourceMinorInc
 
 
 =
 
 
 0
 
 
 ;
 
 
- 
      
 
- 
      
 DMA_InitStruct
 
 1
 
 
 .DestBaseAddr
 
 
 =(uint
 
 
 3
 
 
 2
 
 
 _t)Image[Row];
 
 
 //DMA 内存 //uint8_t DMABuffer[400];
 
 
 
 
- 
      
 DMA_InitStruct
 
 1
 
 
 .DestMajorInc
 
 
 =
 
 
 0
 
 
 ;
 
 
- 
      
 DMA_InitStruct
 
 1
 
 
 .DestDataSize
 
 
 = DMA_DST_
 
 
 8
 
 
 BIT;
 
 
- 
      
 DMA_InitStruct
 
 1
 
 
 .DestMinorInc
 
 
 =
 
 
 1
 
 
 ;
 
 
 //每次传输 +1个字节
 
 
 
 
- 
      
 DMA_Init(&DMA_InitStruct
 
 1
 
 
 );
 
 
- 
      
 
 ///
 
 
 
 
- 
      
 Row ++;
 
- 
      
 
 if
 
 
 (Row==MAX_ROW)
 
 
- 
      
 {
 
- 
      
 Flag =
 
 1
 
 
 ;
 
 
- 
      
 NVIC_DisableIRQ(PORTA_IRQn);
 
 //行
 
 
 
 
- 
      
 NVIC_EnableIRQ(PORTB_IRQn);
 
 //场
 
 
 
 
- 
      
 }
 
- 
      
 }
 
- 
      
 }
 
- 
      
 }
 
- 
      
 }
 
   
    
    最后给大家看一下,DMA的初始化函数,这个函数是超核的库里面的,不是我写的,但是上面的解释很详细了,相信都能看懂。
   
   
   
- 
      
 
 void
 
 
 DMA_Init(
 
 
 DMA_InitTypeDef
 
 
 *DMA_InitStruct)
 
 
- 
      
 {
 
- 
      
 
 //参数检查
 
 
 
 
- 
      
 assert_param(IS_DMA_REQ(DMA_InitStruct->PeripheralDMAReq));
 
- 
      
 assert_param(IS_DMA_ATTR_SSIZE(DMA_InitStruct->SourceDataSize));
 
- 
      
 assert_param(IS_DMA_ATTR_DSIZE(DMA_InitStruct->DestDataSize));
 
- 
      
 assert_param(IS_DMA_CH(DMA_InitStruct->Channelx));
 
- 
      
 assert_param(IS_DMA_MINOR_LOOP(DMA_InitStruct->MinorLoopLength));
 
- 
      
 
- 
      
 
 //打开DMA0和DMAMUX时钟源
 
 
 
 
- 
      
 SIM->SCGC
 
 6
 
 
 |= SIM_SCGC
 
 
 6
 
 
 _DMAMUX_MASK;
 
 
- 
      
 SIM->SCGC
 
 7
 
 
 |= SIM_SCGC
 
 
 7
 
 
 _DMA_MASK;
 
 
- 
      
 
 //配置DMA触发源
 
 
 
 
- 
      
 DMAMUX->CHCFG[DMA_InitStruct->Channelx] = DMAMUX_CHCFG_SOURCE(DMA_InitStruct->PeripheralDMAReq);
 
- 
      
 
 //设置源地址信息
 
 
 
 
- 
      
 DMA
 
 0
 
 
 ->TCD[DMA_InitStruct->Channelx]
 
 
 .SADDR
 
 
 = DMA_InitStruct->SourceBaseAddr;
 
 
- 
      
 
 //执行完源地址操作后,是否在源地址基础上累加
 
 
 
 
- 
      
 DMA
 
 0
 
 
 ->TCD[DMA_InitStruct->Channelx]
 
 
 .SOFF
 
 
 = DMA_SOFF_SOFF(DMA_InitStruct->SourceMinorInc);
 
 
- 
      
 
 //设置源地址传输宽度
 
 
 
 
- 
      
 DMA
 
 0
 
 
 ->TCD[DMA_InitStruct->Channelx]
 
 
 .ATTR
 
 
 =
 
 
 0
 
 
 ;
 
 
- 
      
 DMA
 
 0
 
 
 ->TCD[DMA_InitStruct->Channelx]
 
 
 .ATTR
 
 
 |= DMA_ATTR_SSIZE(DMA_InitStruct->SourceDataSize);
 
 
- 
      
 
 //主循环进行完后 是否更改源地址
 
 
 
 
- 
      
 DMA
 
 0
 
 
 ->TCD[DMA_InitStruct->Channelx]
 
 
 .SLAST
 
 
 = DMA_InitStruct->SourceMajorInc;
 
 
- 
      
 
- 
      
 
 //设置目的地址信息
 
 
 
 
- 
      
 DMA
 
 0
 
 
 ->TCD[DMA_InitStruct->Channelx]
 
 
 .DADDR
 
 
 = DMA_InitStruct->DestBaseAddr;
 
 
- 
      
 
 //执行完源地址操作后,是否在源地址基础上累加
 
 
 
 
- 
      
 DMA
 
 0
 
 
 ->TCD[DMA_InitStruct->Channelx]
 
 
 .DOFF
 
 
 = DMA_DOFF_DOFF(DMA_InitStruct->DestMinorInc);
 
 
- 
      
 
 //设置目的地址传输宽度
 
 
 
 
- 
      
 DMA
 
 0
 
 
 ->TCD[DMA_InitStruct->Channelx]
 
 
 .ATTR
 
 
 |= DMA_ATTR_DSIZE(DMA_InitStruct->DestDataSize);
 
 
- 
      
 
 //主循环进行完后 是否更改源地址
 
 
 
 
- 
      
 DMA
 
 0
 
 
 ->TCD[DMA_InitStruct->Channelx]
 
 
 .DLAST_SGA
 
 
 = DMA_InitStruct->DestMajorInc;
 
 
- 
      
 
- 
      
 
 //设置计数器长度 循环次数
 
 
 
 
- 
      
 
 //设置数据长度 长度每次递减 也被称作当前主循环计数 current major loop count
 
 
 
 
- 
      
 DMA
 
 0
 
 
 ->TCD[DMA_InitStruct->Channelx]
 
 
 .CITER_ELINKNO
 
 
 = DMA_CITER_ELINKNO_CITER(DMA_InitStruct->MinorLoopLength );
 
 
- 
      
 
 //起始循环计数器 当主循环计数器为0 时候 将装载起始循环计数器的值
 
 
 
 
- 
      
 DMA
 
 0
 
 
 ->TCD[DMA_InitStruct->Channelx]
 
 
 .BITER_ELINKNO
 
 
 = DMA_BITER_ELINKNO_BITER(DMA_InitStruct->MinorLoopLength);
 
 
- 
      
 
 //设置每一次传输字节的个数 个数到达上限时 DMA便将数据存入RAM
 
 
 
 
- 
      
 DMA
 
 0
 
 
 ->TCD[DMA_InitStruct->Channelx]
 
 
 .NBYTES_MLNO
 
 
 = DMA_NBYTES_MLNO_NBYTES(DMA_InitStruct->TransferBytes);
 
 
- 
      
 
 //设置DMA TCD控制寄存器
 
 
 
 
- 
      
 DMA
 
 0
 
 
 ->TCD[DMA_InitStruct->Channelx]
 
 
 .CSR
 
 
 =
 
 
 0
 
 
 ;
 
 
- 
      
 
 if
 
 
 (DMA_InitStruct->DMAAutoClose == ENABLE)
 
 
- 
      
 {
 
- 
      
 DMA
 
 0
 
 
 ->TCD[DMA_InitStruct->Channelx]
 
 
 .CSR
 
 
 |=DMA_CSR_DREQ_MASK;
 
 
- 
      
 }
 
- 
      
 
 else
 
 
 
 
- 
      
 {
 
- 
      
 DMA
 
 0
 
 
 ->TCD[DMA_InitStruct->Channelx]
 
 
 .CSR
 
 
 &=(~DMA_CSR_DREQ_MASK);
 
 
- 
      
 }
 
- 
      
 
 //使能此寄存器DMA开始工作
 
 
 
 
- 
      
 DMA_SetEnableReq(DMA_InitStruct->Channelx,DMA_InitStruct->EnableState);
 
- 
      
 
 //DMA 通道使能
 
 
 
 
- 
      
 DMAMUX->CHCFG[DMA_InitStruct->Channelx] |= DMAMUX_CHCFG_ENBL_MASK;
 
- 
      
 }
 
   
   
    
   
   
   
    
     Second:
    
   
    
     讲完OV7620的一些中断处理函数之后,我们来看看SCCB的库程序,这个库可以通用,需要的车友可以直接添加,只需要对照自己使用的库,在IO口初始化里面做出相应的修改即可。
    
   
- 
      
 
 #ifndef __SCCB_H
 
 
 
 
- 
      
 
 #define __SCCB_H
 
 
 
 
- 
      
 
- 
      
 
- 
      
 
 #define SCL_HIGH PEout(1) = 1 //设置为输出后输出1
 
 
 
 
- 
      
 
 #define SCL_LOW PEout(1) = 0 //设置为输出后输出0
 
 
 
 
- 
      
 
 #define SCL_OUT PTE->PDDR|=(1<<1) //设置为输出
 
 
 
 
- 
      
 
 //#define SCL_DDR_IN() PTE->PDDR&=~(1<<1)//输入
 
 
 
 
- 
      
 
- 
      
 
 #define SDA_HIGH PEout(0)= 1 //设置为输出后输出1
 
 
 
 
- 
      
 
 #define SDA_LOW PEout(0)= 0 //设置为输出后输出0
 
 
 
 
- 
      
 
 #define SDA_DATA PEin(0)
 
 
 
 
- 
      
 
 #define SDA_OUT PTE->PDDR|=(1<<0) //设置为输出
 
 
 
 
- 
      
 
 #define SDA_IN PTE->PDDR&=~(1<<0) //设置为输入
 
 
 
 
- 
      
 
 #define u8 unsigned char
 
 
 
 
- 
      
 
 #define u16 unsigned short
 
 
 
 
- 
      
 
- 
      
 
 //#define ADDR_OV7725 0x42
 
 
 
 
- 
      
 
- 
      
 
 void
 
 
 sccb_init(
 
 
 void
 
 
 );
 
 
 //初始化SCCB端口为GPIO
 
 
 
 
- 
      
 
 void
 
 
 sccb_wait(
 
 
 void
 
 
 );
 
 
 //SCCB时序延时
 
 
 
 
- 
      
 
 void
 
 
 sccb_start(
 
 
 void
 
 
 );
 
 
 //起始标志
 
 
 
 
- 
      
 
 void
 
 
 sccb_stop(
 
 
 void
 
 
 );
 
 
 //停止标志
 
 
 
 
- 
      
 u
 
 8
 
 
 sccb_sendByte(u
 
 
 8
 
 
 data);
 
 
- 
      
 
 void
 
 
 sccb_regWrite(u
 
 
 8
 
 
 device,u
 
 
 8
 
 
 address,u
 
 
 8
 
 
 data);
 
 
- 
      
 
 #endif
 
 
 
 
- 
      
 
 #include “sys.h”
 
 
 
 
- 
      
 
 #include “gpio.h”
 
 
 
 
- 
      
 
 #include “sccb.h”
 
 
 
 
- 
      
 
 #include “delay.h”
 
 
 
 
- 
      
 
 #include “stdio.h”
 
 
 
 
- 
      
 
- 
      
 
 /*************************************************************************
 
 
- 
      
 
 * 函数名称:sccb_init
 
 
- 
      
 
 * 功能说明:初始化SCCB 其中SCL接PE1 SDA接PTE0
 
 
- 
      
 
 *************************************************************************/
 
 
 
 
- 
      
 
 void
 
 
 sccb_init(
 
 
 void
 
 
 )
 
 
- 
      
 {
 
- 
      
 
 int
 
 
 i ;
 
 
- 
      
 GPIO_InitTypeDef GPIO_InitStruct
 
 1
 
 
 ;
 
 
- 
      
 
 for
 
 
 (i=
 
 
 0
 
 
 ;i<
 
 
 8
 
 
 ;i++)
 
 
- 
      
 {
 
- 
      
 GPIO_InitStruct
 
 1
 
 
 .GPIO_Pin
 
 
 = i;
 
 
- 
      
 GPIO_InitStruct
 
 1
 
 
 .GPIO_InitState
 
 
 = Bit_RESET;
 
 
 //change as Bit_Set , it will shut.
 
 
 
 
- 
      
 GPIO_InitStruct
 
 1
 
 
 .GPIO_IRQMode
 
 
 = GPIO_IT_DISABLE;
 
 
- 
      
 GPIO_InitStruct
 
 1
 
 
 .GPIO_Mode
 
 
 = GPIO_Mode_IN_FLOATING;
 
 
- 
      
 GPIO_InitStruct
 
 1
 
 
 .GPIOx
 
 
 = PTD;
 
 
- 
      
 GPIO_Init(&GPIO_InitStruct
 
 1
 
 
 );
 
 
- 
      
 }
 
- 
      
 
- 
      
 GPIO_InitStruct
 
 1
 
 
 .GPIO_Pin
 
 
 =
 
 
 0
 
 
 ;
 
 
- 
      
 GPIO_InitStruct
 
 1
 
 
 .GPIO_InitState
 
 
 = Bit_RESET;
 
 
- 
      
 GPIO_InitStruct
 
 1
 
 
 .GPIO_IRQMode
 
 
 = GPIO_IT_DISABLE;
 
 
- 
      
 GPIO_InitStruct
 
 1
 
 
 .GPIO_Mode
 
 
 = GPIO_Mode_OPP;
 
 
- 
      
 GPIO_InitStruct
 
 1
 
 
 .GPIOx
 
 
 = PTE;
 
 
- 
      
 GPIO_Init(&GPIO_InitStruct
 
 1
 
 
 );
 
 
- 
      
 
- 
      
 GPIO_InitStruct
 
 1
 
 
 .GPIO_Pin
 
 
 =
 
 
 1
 
 
 ;
 
 
- 
      
 GPIO_InitStruct
 
 1
 
 
 .GPIO_InitState
 
 
 = Bit_RESET;
 
 
- 
      
 GPIO_InitStruct
 
 1
 
 
 .GPIO_IRQMode
 
 
 = GPIO_IT_DISABLE;
 
 
- 
      
 GPIO_InitStruct
 
 1
 
 
 .GPIO_Mode
 
 
 = GPIO_Mode_OPP;
 
 
- 
      
 GPIO_InitStruct
 
 1
 
 
 .GPIOx
 
 
 = PTE;
 
 
- 
      
 GPIO_Init(&GPIO_InitStruct
 
 1
 
 
 );
 
 
- 
      
 }
 
- 
      
 
 /************************************************************************
 
 
- 
      
 
 * 函数名称:sccb_wait
 
 
- 
      
 
 * 功能说明:SCCB延时,不应太小
 
 
- 
      
 
 *************************************************************************/
 
 
 
 
- 
      
 
 void
 
 
 sccb_wait(
 
 
 void
 
 
 )
 
 
- 
      
 {
 
- 
      
 u
 
 8
 
 
 i;
 
 
- 
      
 u
 
 1
 
 
 6
 
 
 j;
 
 
- 
      
 
 for
 
 
 ( i=
 
 
 0
 
 
 ; i<
 
 
 1
 
 
 0
 
 
 0
 
 
 ; i++)
 
 
- 
      
 {
 
- 
      
 j++;
 
- 
      
 }
 
- 
      
 }
 
- 
      
 
 /************************************************************************
 
 
- 
      
 
 * 函数名称:sccb_start
 
 
- 
      
 
 * 功能说明:SCCB启动位
 
 
- 
      
 
 *************************************************************************/
 
 
 
 
- 
      
 
 void
 
 
 sccb_start(
 
 
 void
 
 
 )
 
 
- 
      
 {
 
- 
      
 SCL_OUT;
 
- 
      
 SDA_OUT;
 
- 
      
 
- 
      
 SDA_HIGH;
 
- 
      
 
 //sccb_wait();
 
 
 
 
- 
      
 SCL_HIGH;
 
- 
      
 sccb_wait();
 
- 
      
 SDA_LOW;
 
- 
      
 sccb_wait();
 
- 
      
 SCL_LOW;
 
- 
      
 }
 
- 
      
 
- 
      
 
 /************************************************************************
 
 
- 
      
 
 * 函数名称:sccb_stop
 
 
- 
      
 
 * 功能说明:SCCB停止位
 
 
- 
      
 
 *************************************************************************/
 
 
 
 
- 
      
 
 void
 
 
 sccb_stop(
 
 
 void
 
 
 )
 
 
- 
      
 {
 
- 
      
 SCL_OUT;
 
- 
      
 SDA_OUT;
 
- 
      
 
- 
      
 SDA_LOW;
 
- 
      
 sccb_wait();
 
- 
      
 SCL_HIGH;
 
- 
      
 sccb_wait();
 
- 
      
 SDA_HIGH;
 
- 
      
 sccb_wait();
 
- 
      
 }
 
- 
      
 
- 
      
 
 /************************************************************************
 
 
- 
      
 
 * 函数名称:sccb_sendByte
 
 
- 
      
 
 * 功能说明:在SCCB总线上发送一个字节
 
 
- 
      
 
 * 参数说明:data 要发送的字节内容
 
 
- 
      
 
 *************************************************************************/
 
 
 
 
- 
      
 u
 
 8
 
 
 sccb_sendByte(u
 
 
 8
 
 
 data)
 
 
- 
      
 {
 
- 
      
 u
 
 8
 
 
 i;
 
 
- 
      
 u
 
 8
 
 
 ack;
 
 
- 
      
 SDA_OUT;
 
- 
      
 
 for
 
 
 ( i=
 
 
 0
 
 
 ; i<
 
 
 8
 
 
 ; i++)
 
 
- 
      
 {
 
- 
      
 
 if
 
 
 (data &
 
 
 0
 
 
 x
 
 
 8
 
 
 0
 
 
 )
 
 
- 
      
 SDA_HIGH;
 
- 
      
 
 else
 
 
 
 
- 
      
 SDA_LOW;
 
- 
      
 data <<=
 
 1
 
 
 ;
 
 
- 
      
 sccb_wait();
 
- 
      
 SCL_HIGH;
 
- 
      
 sccb_wait();
 
- 
      
 SCL_LOW;
 
- 
      
 sccb_wait();
 
- 
      
 }
 
- 
      
 SDA_HIGH;
 
- 
      
 SDA_IN;
 
- 
      
 sccb_wait();
 
- 
      
 SCL_HIGH;
 
- 
      
 sccb_wait();
 
- 
      
 ack = SDA_DATA;
 
- 
      
 SCL_LOW;
 
- 
      
 sccb_wait();
 
- 
      
 
 return
 
 
 ack;
 
 
- 
      
 }
 
- 
      
 
- 
      
 
- 
      
 
 /************************************************************************
 
 
- 
      
 
 * 函数名称:sccb_regWrite
 
 
- 
      
 
 * 功能说明:通过SCCB总线向指定设备的指定地址发送指定内容
 
 
- 
      
 
 * 参数说明:device—设备号 读写有区别 42是写,43是写
 
 
- 
      
 
 * address—写数据的寄存器
 
 
- 
      
 
 * data—写的内容
 
 
- 
      
 
 * 函数返回:ack=1未收到应答(失败) ack=0收到应答(成功)
 
 
- 
      
 
 *************************************************************************/
 
 
 
 
- 
      
 
 void
 
 
 sccb_regWrite(u
 
 
 8
 
 
 device,u
 
 
 8
 
 
 address,u
 
 
 8
 
 
 data)
 
 
- 
      
 {
 
- 
      
 
 // u8 i;
 
 
 
 
- 
      
 u
 
 8
 
 
 ack;
 
 
- 
      
 
 // for( i=0; i<20; i++)
 
 
 
 
- 
      
 
 // {
 
 
 
 
 
- 
      
 sccb_start();
 
- 
      
 ack = sccb_sendByte(device);
 
- 
      
 
 while
 
 
 ( ack )
 
 
- 
      
 {
 
- 
      
 ack = sccb_sendByte(device);
 
- 
      
 
 // printf(“device\n\r”);
 
 
 
 
- 
      
 }
 
- 
      
 ack = sccb_sendByte(address);
 
- 
      
 
 while
 
 
 ( ack )
 
 
- 
      
 {
 
- 
      
 ack = sccb_sendByte(address);;
 
- 
      
 
 // printf(“address\n\r”);
 
 
 
 
- 
      
 }
 
- 
      
 ack = sccb_sendByte(data);
 
- 
      
 
 while
 
 
 ( ack )
 
 
- 
      
 {
 
- 
      
 ack = sccb_sendByte(data);
 
- 
      
 
 // printf(“data\n\r”);
 
 
 
 
- 
      
 }
 
- 
      
 sccb_stop();
 
- 
      
 
 // if( ack == 0 ) break;
 
 
 
 
- 
      
 
 // }
 
 
 
 
- 
      
 }
 
   
   
    贴上使用的SCCB的库之后,给大家看一下对SCCB的一段实例操作程序。程序上有详细的解释,我就不赘述了。
   
   
   
- 
      
 
 sccb_init();
 
 
- 
      
 sccb_regWrite(
 
 0
 
 
 x
 
 
 4
 
 
 2
 
 
 ,
 
 
 0
 
 
 x
 
 
 1
 
 
 1
 
 
 ,
 
 
 0
 
 
 x
 
 
 0
 
 
 1
 
 
 );
 
 
 //地址0X11-中断四分频(1280*480) PCLK:166ns HREF:254.6us VSYN:133.6ms
 
 
 
 
- 
      
 sccb_regWrite(
 
 0
 
 
 x
 
 
 4
 
 
 2
 
 
 ,
 
 
 0
 
 
 x
 
 
 1
 
 
 4
 
 
 ,
 
 
 0
 
 
 x
 
 
 2
 
 
 4
 
 
 );
 
 
 //地址0X14-QVGA(320*240) PCLK:332ns HREF:509.6us VSYN:133.6ms
 
 
 
 
- 
      
 sccb_regWrite(
 
 0
 
 
 x
 
 
 4
 
 
 2
 
 
 ,
 
 
 0
 
 
 x
 
 
 2
 
 
 8
 
 
 ,
 
 
 0
 
 
 x
 
 
 4
 
 
 0
 
 
 );
 
 
 //地址0X28-黑白模式(320*240 PCLK:332ns HREF:127us VSYN:33.6ms
 
 
 
 
- 
      
 sccb_wait();
 
   
   
    
   
   
   
   
    以上就是关于OV7620的使用了,看完之后大家是不是会使用了呢。关于后期图像的处理和调试,我目前正在使用一款智能车调试助手,感觉非常好用,完全免费,并且可以配合Visual Studio,在Visual Studio里面用C#编写一些图像处理的算法,生成dll文件,然后在调试助手的界面里面直接观察。非常好非常好。给大家看看图。
   
   
   
    
    
   
    
     如果有需要关于OV7620资料或者调试软件或者有什么赐教,请留言共同探讨。.
    
   
    
     
    
   
    
     End。2014、03、26
    
   
 

