Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1209462
  • 博文数量: 232
  • 博客积分: 7563
  • 博客等级: 少将
  • 技术积分: 1930
  • 用 户 组: 普通用户
  • 注册时间: 2008-05-21 11:17
文章分类

全部博文(232)

文章存档

2011年(17)

2010年(90)

2009年(66)

2008年(59)

分类:

2009-05-26 16:35:03

经过将近一个星期的摸索,终于能让OV7640出正确图像了。

Image1.jpg

我直接把MCU和OV7640接到一起,MCU外扩了一个SDRAM用来放数据。没有使用专门的压缩芯片或者CPLD辅助。

下面总结一下研究OV7640的一些经验,希望对后来人有些帮助。
1、OV7640的寄存器设置,不用管那个SCCB那些资料,写操作就是完全标准的I2C协议,用器件的I2C或者IO口模拟能用的I2C代码就可以了。唯一需要注意的是器件地址。芯片手册上说42是写,43是读。这个42、43是包含I2C7位地址+1位读写操作的16进制数,说白了OV7640的实际I2C地址就是0x21。OmniVision搞这种猫腻还不就是为了避免I2C的专利授权。
2、OV7640返回的RGB565格式的图像可以直接拿来显示,唯一需要注意的就是那个数据,并不是按照R5位,G6位,B5位的格式传输的。芯片手册上的那个Timeline写的有错误。根据这个出来的图像颜色不正确。正确的顺序是高位先5位G,然后6位B,最后5位R。
3、OV7640初始化。我只用了最最基本的几个寄存器。
        // 配置摄像头
        // 可用的RGB565配置,QVGA
        Delay(MCK/50);
        if (!I2CCameraConfig(0x14,0x4|(1<<5)))                        // 输出格式QVGA(320*240)
        {
                GoError();
        }
        Delay(MCK/50);
        if (!I2CCameraConfig(0x12,(1<<3)|(1<<2)))                        // RGB格式输出
        {
                GoError();
        }
        Delay(MCK/50);
        if (!I2CCameraConfig(0x1F,1|(0<<2)|(1<<4)))                // RGB565格式,默认RGB4:2:2格式
        {
                GoError();
        }
最后还有降低时钟频率。
        if (!I2CCameraConfig(0x11,0x19))        // 320*240 0x19正好符合52Mhz的ARM7的采样速度
        {
                GoError();
        }
然后就可以输出图像了。这个0x19是我用示波器量出来的,52Mhz或者更高主频的ARM7芯片在这个分频数下可以通过查询IO口的状态正确地根据PCLK上升沿读出图像数据。
这里有一点要注意:这个0x19只针对QVGA分辨率,如果对于VGA分辨率,示波器显示PCLK的上升沿后的正脉宽非常窄,ns级别,ARM7芯片是无法正确捕获到数据的。
4、关于一个HREF里PCLK的数量。这个芯片手册里有误,实际情况是,只要没有采用Raw-RGB格式输出数据,其它任何格式里,一个HREF里包含的PCLK都是宽度像素的2倍。也就是说都是2个BYTE表示一个象素点。这点在OV7660的芯片手册里写的非常清楚,实际情况也是如此。那些什么RGB4:2:2(GRB4:2:2)格式,不是说只有1个字节,里面G占4位,R和B各占2位。而是2个字节一起看,G占8位,R和B各占4位。这个和YUV4:2:2是一样的。只不过YUV4:2:2要求更多一些,要4个字节才能表示出2个像素。至于Raw-RGB到底传回了哪个像素的值?再简单不过,一个BYTE对应传感器上一个点,Google上搜一个bayer pattern,看看那个传感器的颜色分布就知道到底是哪个点了。
5、关于Bayer-pattern的解析。实际是只有采用Raw-RGB形势,才需要自己根据传感器的颜色阵列去推敲缺失的颜色。别的格式里都可以直接使用另外2种颜色。如果非要在RGB565下做Bayer-pattern解析也可以,就是感觉有些画蛇添足了。
下面给出我现在正常使用的QVGA的图像数据获取代码:
                // 等待VSYNC信号。
                while (!(AT91F_PIO_IsInputSet(AT91C_BASE_PIOA,VSYNC )));
                // VSYNC信号等到,表明新一帧图像开始。
                nIndex = 0;
                pDataCachePointer = pDataCache;
                // 等到HREF,表明行数据开始。
                while (!(AT91F_PIO_IsInputSet(AT91C_BASE_PIOA,HREF )));                       
                AT91F_PIO_SetOutput( AT91C_BASE_PIOA, LED1 ) ;               
                for (i=0;i<240;i++)
                {
                        // 下面获取数据,QVGA 320像素宽,所以需要获得320*2=640个BYTE的数据。
                        for (j=0;j<320*2;j++)
                        {                       
                                while (!(AT91C_BASE_PIOA->PIO_PDSR & PCLK));
                                AT91F_PIO_SetOutput( AT91C_BASE_PIOA, LED0);
                                nData = AT91C_BASE_PIOB->PIO_PDSR;
                                AT91F_PIO_ClearOutput( AT91C_BASE_PIOA, LED0);
                                nData = nData >> 22;
                                nData &= 0xFF;
                                *pDataCachePointer = (BYTE)nData;
                                pDataCachePointer++;
                                nIndex++;
                                while ((AT91C_BASE_PIOA->PIO_PDSR & PCLK));
                        }
                        // 这时候HREF应该为低了。
                        if (AT91F_PIO_IsInputSet(AT91C_BASE_PIOA,HREF))
                        {
                                // 出现错误,说明要么HREF提供了超过我们需要的像素信息,要么我们获取太慢,第二行的图像数据已经过来了。
                                while (1)
                                {
                                        AT91F_PIO_SetOutput( AT91C_BASE_PIOA, LED0|LED1);//亮灯提示错误
                                }
                        }
                        while (!(AT91F_PIO_IsInputSet(AT91C_BASE_PIOA,HREF )));                       
                }
                AT91F_PIO_ClearOutput( AT91C_BASE_PIOA, LED1) ;
我用的是AT91SAM7SE512的MCU,外扩一个16MB的SDRAM。晶振20Mhz,PLLRC的2个电容和1个电阻都是公板的配置,PLL分频DIV=5 MUL=25,算下来时钟是52Mhz。
下面是PC上显示图像部分里的RGB565转RGB888的代码。
COLORREF CSliderScanDlg::TranslateRGBData(BYTE nByte1,BYTE nByte2)
{
WORD wData = 0;
wData = (nByte1<<8)|nByte2;
int  R = 0;
int  G = 0;
int  B = 0;
// RGB565应该是 GBR565
G = (wData & 0xF800) >> 11;  
B = (wData & 0x07E0) >> 5;
R = (wData & 0x001F);
  //增大亮度
G <<= 3;
B <<= 2;
R <<= 3;
   return RGB(R,G,B); // 返回的图像颜色信息就可以直接在Windows的绘图API中使用了。
}
那个扫描起始列和行,都是默认配置。目前QVGA大约能做到1FPS吧,VGA没兴趣尝试了。
由于增加了OV7640的时钟分频数,所以曝光、白平衡什么的速度都很慢了,拍静态的还可以,动态的就别想了,肯定一片模糊。
这种方案毕竟简单,省却了一个专用压缩芯片或者一个CPLD。如果在设置好比较小的扫描行和列,速度还可以提高些。
别的那些寄存器我还没有完全搞懂有什么用,反正目前已经可以出正确图像了,大家有兴趣可以继续研究。OmniVision写的那么简单,真让人头疼。
我看的是Google上搜到的1.7版本的OV7640芯片手册,里面确实纠正了以前1.3版本的很多错误,有些错误是关键性置的,比如0x1F寄存器里的RGB565还是RGB555的输出格式选择。
希望对大家能有参考价值,如果各位发现错误,欢迎指出。

阅读(2563) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~