分类:
2009-05-26 16:35:03
经过将近一个星期的摸索,终于能让OV7640出正确图像了。
我直接把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的输出格式选择。
希望对大家能有参考价值,如果各位发现错误,欢迎指出。