OV7620的使用
2014-03-26 22:24
519 查看
转载请注明出处: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
程序如下:
然后开始编写场中断函数,编写之前我们需要在心里理一下思绪,在场中断函数里我们要按照顺序,做以下几件事情:
A.确认是否是场中断,确认之后进入处理。
B.清除标志位Flag。(Flag是用来观察是否处理完一场图像的标志)
C.清除中断标志。
D.计数全部清零。(因为新的一场已经开始)
E.打开行中断,关闭场中断。
接着编写行中断函数,在行中断中,我们要做以下几件事情:
A.确认是否是行中断。
B.关闭DMA中断,防止提前进入PCLK的采集。
C.跳过消隐区。(消隐区:消隐区的出现,在电视机原理上,是因为电子束结束一行扫描,从一行尾换到另一行头,期间的空闲期,这叫做行消隐信号;同理,从一场尾换到另一场尾,期间也会有空闲期,这叫做场消隐信号。)
D.进入行采集处理。
E.配置DMA,并且打开DMA中断。
F.行计数加1,表示已经采集完了一行。(因为PCLK的中断周期远远小于HREF的中断周期,所以不需要杞人忧天,担心中断搞得混乱。)
G.当采集完了自己的目标行数之后,标志位Flag修改。并关闭行中断,打开场中断,等待下一次的场中断。
最后给大家看一下,DMA的初始化函数,这个函数是超核的库里面的,不是我写的,但是上面的解释很详细了,相信都能看懂。
Second:
讲完OV7620的一些中断处理函数之后,我们来看看SCCB的库程序,这个库可以通用,需要的车友可以直接添加,只需要对照自己使用的库,在IO口初始化里面做出相应的修改即可。
贴上使用的SCCB的库之后,给大家看一下对SCCB的一段实例操作程序。程序上有详细的解释,我就不赘述了。
以上就是关于OV7620的使用了,看完之后大家是不是会使用了呢。关于后期图像的处理和调试,我目前正在使用一款智能车调试助手,感觉非常好用,完全免费,并且可以配合Visual Studio,在Visual Studio里面用C#编写一些图像处理的算法,生成dll文件,然后在调试助手的界面里面直接观察。非常好非常好。给大家看看图。
如果有需要关于OV7620资料或者调试软件或者有什么赐教,请留言共同探讨。.
End。2014、03、26
有人会奇怪为什么使用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 OV7620_Init() { //像素中断 PCLK GPIO_InitStruct1.GPIO_Pin = OV7620_PCLK_PIN; GPIO_InitStruct1.GPIO_InitState = Bit_SET; GPIO_InitStruct1.GPIO_IRQMode = GPIO_IT_DMA_RISING; GPIO_InitStruct1.GPIO_Mode = GPIO_Mode_IPD; GPIO_InitStruct1.GPIOx = OV7620_PCLK_PORT; GPIO_Init(&GPIO_InitStruct1); //行中断 HREF GPIO_InitStruct1.GPIO_Pin = OV7620_HREF_PIN; GPIO_InitStruct1.GPIO_InitState = Bit_SET; GPIO_InitStruct1.GPIO_IRQMode = GPIO_IT_RISING; GPIO_InitStruct1.GPIO_Mode = GPIO_Mode_IPD; GPIO_InitStruct1.GPIOx = OV7620_HREF_PORT; GPIO_Init(&GPIO_InitStruct1); // 场中断 VSYNC GPIO_InitStruct1.GPIO_Pin = OV7620_VSYNC_PIN; GPIO_InitStruct1.GPIO_InitState = Bit_SET; GPIO_InitStruct1.GPIO_IRQMode = GPIO_IT_RISING; //GPIO_IT_RISING GPIO_InitStruct1.GPIO_Mode = GPIO_Mode_IPD; //GPIO_Mode_IPD GPIO_InitStruct1.GPIOx = OV7620_VSYNC_PORT; GPIO_Init(&GPIO_InitStruct1); //配置DMA DMA_InitStruct1.Channelx = DMA_CH1; //DMA 1通道 DMA_InitStruct1.PeripheralDMAReq =PORTC_DMAREQ; //C端口(PCLK) 上升呀触发 DMA_InitStruct1.MinorLoopLength = 170; //传输次数 超过摄像头每行像素数即可 DMA_InitStruct1.TransferBytes = 1; //每次传输1个字节 DMA_InitStruct1.DMAAutoClose = ENABLE; //连续采集 DMA_InitStruct1.EnableState = ENABLE; //初始化后立即采集 DMA_InitStruct1.SourceBaseAddr =(uint32_t)&PTD->PDIR;//摄像头端口接D0-D7 DMA_InitStruct1.SourceMajorInc = 0; //地址不增加 DMA_InitStruct1.SourceDataSize = DMA_SRC_8BIT; //8BIT数据 DMA_InitStruct1.SourceMinorInc = 0; DMA_InitStruct1.DestBaseAddr =(uint32_t)DMABuffer; //DMA 内存 //uint8_t DMABuffer[400]; DMA_InitStruct1.DestMajorInc = 0; DMA_InitStruct1.DestDataSize = DMA_DST_8BIT; DMA_InitStruct1.DestMinorInc = 1; //每次传输 +1个字节 DMA_Init(&DMA_InitStruct1); }
然后开始编写场中断函数,编写之前我们需要在心里理一下思绪,在场中断函数里我们要按照顺序,做以下几件事情:
A.确认是否是场中断,确认之后进入处理。
B.清除标志位Flag。(Flag是用来观察是否处理完一场图像的标志)
C.清除中断标志。
D.计数全部清零。(因为新的一场已经开始)
E.打开行中断,关闭场中断。
void PORTB_IRQHandler(void)//功 能:PORTB 外部中断服务 //V { u8 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 { u8 i=14; DMA_SetEnableReq(DMA_CH1,DISABLE); //close DMA ISr if((PORTA->ISFR>>i)==1); { PORTA->ISFR|=(1<<14); if(Row_Num++ > 15) //消隐区啦 { if(Row_Num%5) //进入行采集 { //配置DMA DMA_InitStruct1.Channelx = DMA_CH1; //DMA 1通道 DMA_InitStruct1.PeripheralDMAReq =PORTC_DMAREQ; //C端口(PCLK) 上升呀触发 DMA_InitStruct1.MinorLoopLength = 170; //传输次数 超过摄像头每行像素数即可 DMA_InitStruct1.TransferBytes = 1; //每次传输1个字节 DMA_InitStruct1.DMAAutoClose = ENABLE; //连续采集 DMA_InitStruct1.EnableState = ENABLE; //初始化后立即采集 DMA_InitStruct1.SourceBaseAddr =(uint32_t)&PTD->PDIR;//摄像头端口接D0-D7 DMA_InitStruct1.SourceMajorInc = 0; //地址不增加 DMA_InitStruct1.SourceDataSize = DMA_SRC_8BIT; //8BIT数据 DMA_InitStruct1.SourceMinorInc = 0; DMA_InitStruct1.DestBaseAddr =(uint32_t)Image[Row]; //DMA 内存 //uint8_t DMABuffer[400]; DMA_InitStruct1.DestMajorInc = 0; DMA_InitStruct1.DestDataSize = DMA_DST_8BIT; DMA_InitStruct1.DestMinorInc = 1; //每次传输 +1个字节 DMA_Init(&DMA_InitStruct1); /////////////////////////////////////////////////////// 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->SCGC6 |= SIM_SCGC6_DMAMUX_MASK; SIM->SCGC7 |= SIM_SCGC7_DMA_MASK; //配置DMA触发源 DMAMUX->CHCFG[DMA_InitStruct->Channelx] = DMAMUX_CHCFG_SOURCE(DMA_InitStruct->PeripheralDMAReq); //设置源地址信息 DMA0->TCD[DMA_InitStruct->Channelx].SADDR = DMA_InitStruct->SourceBaseAddr; //执行完源地址操作后,是否在源地址基础上累加 DMA0->TCD[DMA_InitStruct->Channelx].SOFF = DMA_SOFF_SOFF(DMA_InitStruct->SourceMinorInc); //设置源地址传输宽度 DMA0->TCD[DMA_InitStruct->Channelx].ATTR = 0; DMA0->TCD[DMA_InitStruct->Channelx].ATTR |= DMA_ATTR_SSIZE(DMA_InitStruct->SourceDataSize); //主循环进行完后 是否更改源地址 DMA0->TCD[DMA_InitStruct->Channelx].SLAST = DMA_InitStruct->SourceMajorInc; //设置目的地址信息 DMA0->TCD[DMA_InitStruct->Channelx].DADDR = DMA_InitStruct->DestBaseAddr; //执行完源地址操作后,是否在源地址基础上累加 DMA0->TCD[DMA_InitStruct->Channelx].DOFF = DMA_DOFF_DOFF(DMA_InitStruct->DestMinorInc); //设置目的地址传输宽度 DMA0->TCD[DMA_InitStruct->Channelx].ATTR |= DMA_ATTR_DSIZE(DMA_InitStruct->DestDataSize); //主循环进行完后 是否更改源地址 DMA0->TCD[DMA_InitStruct->Channelx].DLAST_SGA = DMA_InitStruct->DestMajorInc; //设置计数器长度 循环次数 //设置数据长度 长度每次递减 也被称作当前主循环计数 current major loop count DMA0->TCD[DMA_InitStruct->Channelx].CITER_ELINKNO = DMA_CITER_ELINKNO_CITER(DMA_InitStruct->MinorLoopLength ); //起始循环计数器 当主循环计数器为0 时候 将装载起始循环计数器的值 DMA0->TCD[DMA_InitStruct->Channelx].BITER_ELINKNO = DMA_BITER_ELINKNO_BITER(DMA_InitStruct->MinorLoopLength); //设置每一次传输字节的个数 个数到达上限时 DMA便将数据存入RAM DMA0->TCD[DMA_InitStruct->Channelx].NBYTES_MLNO = DMA_NBYTES_MLNO_NBYTES(DMA_InitStruct->TransferBytes); //设置DMA TCD控制寄存器 DMA0->TCD[DMA_InitStruct->Channelx].CSR = 0; if(DMA_InitStruct->DMAAutoClose == ENABLE) { DMA0->TCD[DMA_InitStruct->Channelx].CSR |=DMA_CSR_DREQ_MASK; } else { DMA0->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); //停止标志 u8 sccb_sendByte(u8 data); void sccb_regWrite(u8 device,u8 address,u8 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_InitStruct1; for(i=0;i<8;i++) { GPIO_InitStruct1.GPIO_Pin = i; GPIO_InitStruct1.GPIO_InitState = Bit_RESET; //change as Bit_Set , it will shut. GPIO_InitStruct1.GPIO_IRQMode = GPIO_IT_DISABLE; GPIO_InitStruct1.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_InitStruct1.GPIOx = PTD; GPIO_Init(&GPIO_InitStruct1); } GPIO_InitStruct1.GPIO_Pin = 0; GPIO_InitStruct1.GPIO_InitState = Bit_RESET; GPIO_InitStruct1.GPIO_IRQMode = GPIO_IT_DISABLE; GPIO_InitStruct1.GPIO_Mode = GPIO_Mode_OPP; GPIO_InitStruct1.GPIOx = PTE; GPIO_Init(&GPIO_InitStruct1); GPIO_InitStruct1.GPIO_Pin = 1; GPIO_InitStruct1.GPIO_InitState = Bit_RESET; GPIO_InitStruct1.GPIO_IRQMode = GPIO_IT_DISABLE; GPIO_InitStruct1.GPIO_Mode = GPIO_Mode_OPP; GPIO_InitStruct1.GPIOx = PTE; GPIO_Init(&GPIO_InitStruct1); } /************************************************************************ * 函数名称:sccb_wait * 功能说明:SCCB延时,不应太小 *************************************************************************/ void sccb_wait(void) { u8 i; u16 j; for( i=0; i<100; 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 要发送的字节内容 *************************************************************************/ u8 sccb_sendByte(u8 data) { u8 i; u8 ack; SDA_OUT; for( i=0; i<8; i++) { if(data & 0x80) 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(u8 device,u8 address,u8 data) { // u8 i; u8 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(0x42,0x11,0x01); //地址0X11-中断四分频(1280*480) PCLK:166ns HREF:254.6us VSYN:133.6ms sccb_regWrite(0x42,0x14,0x24); //地址0X14-QVGA(320*240) PCLK:332ns HREF:509.6us VSYN:133.6ms sccb_regWrite(0x42,0x28,0x40); //地址0X28-黑白模式(320*240 PCLK:332ns HREF:127us VSYN:33.6ms sccb_wait();
以上就是关于OV7620的使用了,看完之后大家是不是会使用了呢。关于后期图像的处理和调试,我目前正在使用一款智能车调试助手,感觉非常好用,完全免费,并且可以配合Visual Studio,在Visual Studio里面用C#编写一些图像处理的算法,生成dll文件,然后在调试助手的界面里面直接观察。非常好非常好。给大家看看图。
如果有需要关于OV7620资料或者调试软件或者有什么赐教,请留言共同探讨。.
End。2014、03、26
相关文章推荐
- OV7620摄像头使用
- 回到使用智能客户端的将来
- Mcad学习笔记之异步编程(AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用小总结)
- [Oracle]对数据库字段使用默认值
- showModalDialog()、showModelessDialog()方法使用详解[转]
- Java Doc 建议不要使用 JPasswordField.getText()
- 使用 OProfile for Linux on POWER 识别性能瓶颈
- 使用权限标签控制View层展示的内容
- 接口的使用——针对初学者
- ASP.Net环境下使用Jmail组件发送邮件[收藏]
- DoEvents+Sleep 使用=Timer
- Ext布局类的介绍与使用
- java.sql.resultset方法与使用技巧
- GridView編輯時使用DropDownList的錄影教學
- JDK6_TOMCAT6_Eclipse3.3_Myeclipse 6.0安装使用_破解_下载教程
- XDOWNPAGE ASP版本 1.02版的改良及使用
- netcat使用指南
- 使用using指令呵using语句
- 如何使用VSTS写单元测试
- fuser命令使用说明