首页 / 操作系统 / Linux / s3c2440的摄像接口应用
s3c2440提供了一个摄像接口,使开发人员很容易地实现摄像、照相等功能。摄像接口包括8位来自摄像头的输入数据信号,一个输出主时钟信号,三个来自摄像头的输入同步时钟信号和一个输出复位信号。摄像接口的主时钟信号由USB PLL产生,它的频率为96MHz,再经过分频处理后输出给摄像头,摄像头再根据该时钟信号产生三个同步时钟信号(像素时钟、帧同步时钟和行同步时钟),反过来再输入回s3c2440。 s3c2440仅仅提供了一个摄像接口,因此要实现其功能,还需要摄像头。在这里,我们使用OV9650。OV9650内部有大量的寄存器需要配置,这就需要另外的数据接口。OV9650的数据接口称为SCCB(串行摄像控制总线),它由两条数据线组成:一个是用于传输时钟信号的SIO_C,另一个是用于传输数据信号的SIO_D。SCCB的传输协议与IIC的极其相似,只不过IIC在每传输完一个字节后,接收数据的一方要发送一位的确认数据,而SCCB一次要传输9位数据,前8位为有用数据,而第9位数据在写周期中是Don’t-Care位(即不必关心位),在读周期中是NA位。SCCB定义数据传输的基本单元为相(phase),即一个相传输一个字节数据。SCCB只包括三种传输周期,即3相写传输周期(三个相依次为设备从地址,内存地址,所写数据),2相写传输周期(两个相依次为设备从地址,内存地址)和2相读传输周期(两个相依次为设备从地址,所读数据)。当需要写操作时,应用3相写传输周期,当需要读操作时,依次应用2相写传输周期和2相读传输周期。因此SCCB一次只能读或写一个字节。下面我们就用s3c2440的IIC总线接口分别与OV9650的SIO_C和SIO_D相连接来实现SCCB的功能。具体的读、写函数为: //配置IIC接口rGPEUP = 0xc000; //上拉无效rGPECON = 0xa0000000; //GPE15:IICSDA,GPE14:IICSCL //IIC中断void __irq IicISR(void){ rSRCPND |= 0x1<<27; rINTPND |= 0x1<<27; flag = 0; } //写操作//输入参数分别为要写入的内存地址和数据void Wr_SCCB(unsigned char wordAddr, unsigned char data){ //3相写传输周期 //写OV9650设备从地址字节flag =1; rIICDS =0x60; //OV9650设备从地址为0x60 rIICSTAT = 0xf0; rIICCON &= ~0x10; while(flag == 1) delay(100); //写OV9650内存地址字节 flag = 1; rIICDS = wordAddr; rIICCON &= ~0x10; while(flag) delay(100); //写具体的数据字节 flag = 1; rIICDS = data; rIICCON &= ~0x10; while(flag) delay(100); rIICSTAT = 0xd0; //停止位 rIICCON = 0xe3; //为下一次数据传输做准备 delay(100); } //读操作//参数分别为要读取的内存地址和数据void Rd_SCCB (unsigned char wordAddr,unsigned char *data){ unsigned char temp; //2相写传输周期 //写入OV9650设备从地址字节 flag =1; rIICDS = 0x60; rIICSTAT = 0xf0; rIICCON &= ~0x10; while(flag) delay(100); //写入内存地址字节 flag = 1; rIICDS = wordAddr; rIICCON &= ~0x10; while(flag) delay(100); rIICSTAT = 0xd0; //停止位 rIICCON = 0xe3; //为下一次数据传输做准备 delay(100); //2相读传输周期 //写入OV9650设备从地址字节 flag = 1; rIICDS = 0x60; rIICSTAT = 0xb0; rIICCON &= ~0x10; while (flag) delay(100); //读取一个无用字节 flag = 1; temp = rIICDS; rIICCON &= ~((1<<7)|(1<<4)); while(flag) delay(100); //读取数据 flag = 1; *data= rIICDS; rIICCON &= ~((1<<7)|(1<<4)); while(flag) delay(100); rIICSTAT = 0x90; //停止位 rIICCON = 0xe3; //为下一次传输做准备 delay(100); } 当然我们也可以用两个通用IO口来模拟SCCB总线,下面我们给出具体的程序,其中GPE15为SIO_D,GPE14??SIO_C。 #define CLOCK_LOW() (rGPEDAT&=(~(1<<14))) //时钟信号低#define CLOCK_HIGH() (rGPEDAT|=(1<<14)) //时钟信号高#define DATA_LOW() (rGPEDAT&=(~(1<<15))) //数据信号低#define DATA_HIGH() (rGPEDAT|=(1<<15)) //数据信号高 //配置IOrGPEUP = 0xc000; //上拉无效rGPECON = 5<<28; //GPE15为SIO_D,GPE14为SIO_C,都为输出 void delay(int a){ int k; for(k=0;k<a;k++) ;} //启动SCCBvoid __inline SCCB_start(void){ CLOCK_HIGH(); DATA_HIGH(); delay(10); DATA_LOW(); delay(10); CLOCK_LOW(); delay(10);} //结束SCCBvoid __inline SCCB_end(void){ DATA_LOW(); delay(10); CLOCK_HIGH(); delay(10); DATA_HIGH(); delay(10);} //SCCB发送一个字节void __inline SCCB_sendbyte(unsigned char data){ int i=0; //并行数据转串行输出,串行数据输出的顺序为先高位再低位 for(i=0;i<8;i++) { if(data & 0x80) DATA_HIGH(); else DATA_LOW(); delay(10); CLOCK_HIGH(); delay(10); CLOCK_LOW(); delay(10); DATA_LOW(); delay(10); data <<= 1; } //第9位,Don’t Care DATA_HIGH(); delay(10); CLOCK_HIGH(); delay(10); CLOCK_LOW(); delay(10);} // SCCB接收一个字节void __inline SCCB_receivebyte(unsigned char *data){ int i=0; int svalue=0; int pvalue = 0; rGPECON = 1<<28; //把GPE15输出改变为输入 //串行数据转并行输入,高位在前for(i=7;i>=0;i--) { CLOCK_HIGH(); delay(10); svalue = rGPEDAT>>15; CLOCK_LOW(); delay(10); pvalue |= svalue <<i; } rGPECON =5<<28; //再把GPE15改回为输出 //第9位,N.A. DATA_HIGH(); delay(10); CLOCK_HIGH(); delay(10); CLOCK_LOW(); delay(10); *data = pvalue &0xff; } //写操作void SCCB_senddata(unsigned char subaddr, unsigned char data){ //3相写传输周期 SCCB_start(); //启动SCCB SCCB_sendbyte(0x60); //OV9650设备从地址,写操作 SCCB_sendbyte(subaddr); //设备内存地址 SCCB_sendbyte(data); //写数据字节 SCCB_end(); //结束SCCB delay(20);} //读操作unsigned char SCCB_receivedata(unsigned char subaddr){ unsigned char temp; //2相写传输周期 SCCB_start(); //启动SCCB SCCB_sendbyte(0x60); //OV9650设备从地址,写操作 SCCB_sendbyte(subaddr); //设备内存地址 SCCB_end(); //结束SCCB //2相读传输周期 SCCB_start(); //启动SCCB SCCB_sendbyte(0x61); //OV9650设备从地址,读操作 SCCB_receivebyte(&temp); //读字节 SCCB_end(); //结束SCCB return temp; }