s3c2440的网卡接口扩展_第1页
s3c2440的网卡接口扩展_第2页
s3c2440的网卡接口扩展_第3页
s3c2440的网卡接口扩展_第4页
s3c2440的网卡接口扩展_第5页
已阅读5页,还剩15页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

s3c2440提供了一个摄像接口,使开发人员很容易地实现摄像、照相等功能。摄像接口包括8位来自摄像头的输入数据信号,一个输出主时钟信号,三个来自摄像头的输入同步时钟信号和一个输出复位信号。摄像接口的主时钟信号由USBPLL产生,它的频率为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__irqIicISR(void){

rSRCPND|=0x1<<27;

rINTPND|=0x1<<27;

flag=0;

}

//写操作//输入参数分别为要写入的内存地址和数据voidWr_SCCB(unsignedcharwordAddr,unsignedchardata){

//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);

}

//读操作//参数分别为要读取的内存地址和数据voidRd_SCCB(unsignedcharwordAddr,unsignedchar*data){

unsignedchartemp;

//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。

#defineCLOCK_LOW()

(rGPEDAT&=(~(1<<14)))

//时钟信号低#defineCLOCK_HIGH()

(rGPEDAT|=(1<<14))

//时钟信号高#defineDATA_LOW()

(rGPEDAT&=(~(1<<15)))

//数据信号低#defineDATA_HIGH()

(rGPEDAT|=(1<<15))

//数据信号高

//配置IOrGPEUP=0xc000;

//上拉无效rGPECON=5<<28;

//GPE15为SIO_D,GPE14为SIO_C,都为输出

voiddelay(inta){

intk;

for(k=0;k<a;k++)

;}

//启动SCCBvoid__inlineSCCB_start(void){

CLOCK_HIGH();

DATA_HIGH();

delay(10);

DATA_LOW();

delay(10);

CLOCK_LOW();

delay(10);}

//结束SCCBvoid__inlineSCCB_end(void){

DATA_LOW();

delay(10);

CLOCK_HIGH();

delay(10);

DATA_HIGH();

delay(10);}

//SCCB发送一个字节void__inlineSCCB_sendbyte(unsignedchardata){

inti=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’tCare

DATA_HIGH();

delay(10);

CLOCK_HIGH();

delay(10);

CLOCK_LOW();

delay(10);}

//SCCB接收一个字节void__inlineSCCB_receivebyte(unsignedchar*data){

inti=0;

intsvalue=0;

intpvalue=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;

}

//写操作voidSCCB_senddata(unsignedcharsubaddr,unsignedchardata){

//3相写传输周期

SCCB_start();

//启动SCCB

SCCB_sendbyte(0x60);

//OV9650设备从地址,写操作

SCCB_sendbyte(subaddr);

//设备内存地址

SCCB_sendbyte(data);

//写数据字节

SCCB_end();

//结束SCCB

delay(20);}

//读操作unsignedcharSCCB_receivedata(unsignedcharsubaddr){

unsignedchartemp;

//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

returntemp;

}

OV9650的寄存器较多,要想配置好这些寄存器是需要花费一些精力的。下面数组给出了一个VGA(640×480)模式下YUV彩色空间的配置例子,括号内第一个元素表示寄存器地址,第二个元素表示要写入的数据。

constunsignedcharov9650_register[][2]={

{0x11,0x80},{0x6a,0x3e},{0x3b,0x09},{0x13,0xe0},{0x01,0x80},{0x02,0x80},{0x00,0x00},{0x10,0x00},{0x13,0xe5},{0x39,0x43},{0x38,0x12},{0x37,0x00},{0x35,0x91},{0x0e,0xa0},{0x1e,0x04},{0xA8,0x80},{0x12,0x40},{0x04,0x00},{0x0c,0x04},{0x0d,0x80},{0x18,0xc6},{0x17,0x26},{0x32,0xad},{0x03,0x00},{0x1a,0x3d},{0x19,0x01},{0x3f,0xa6},{0x14,0x2e},{0x15,0x10},{0x41,0x02},{0x42,0x08},{0x1b,0x00},{0x16,0x06},{0x33,0xe2},{0x34,0xbf},{0x96,0x04},{0x3a,0x00},{0x8e,0x00},{0x3c,0x77},{0x8B,0x06},{0x94,0x88},{0x95,0x88},{0x40,0xc1},{0x29,0x3f},{0x0f,0x42},{0x3d,0x92},{0x69,0x40},{0x5C,0xb9},{0x5D,0x96},{0x5E,0x10},{0x59,0xc0},{0x5A,0xaf},{0x5B,0x55},{0x43,0xf0},{0x44,0x10},{0x45,0x68},{0x46,0x96},{0x47,0x60},{0x48,0x80},{0x5F,0xe0},{0x60,0x8c},{0x61,0x20},{0xa5,0xd9},{0xa4,0x74},{0x8d,0x02},{0x13,0xe7},{0x4f,0x3a},{0x50,0x3d},{0x51,0x03},{0x52,0x12},{0x53,0x26},{0x54,0x38},{0x55,0x40},{0x56,0x40},{0x57,0x40},{0x58,0x0d},{0x8C,0x23},{0x3E,0x02},{0xa9,0xb8},{0xaa,0x92},{0xab,0x0a},{0x8f,0xdf},{0x90,0x00},{0x91,0x00},{0x9f,0x00},{0xa0,0x00},{0x3A,0x01},{0x24,0x70},{0x25,0x64},{0x26,0xc3},{0x2a,0x00},{0x2b,0x00},{0x6c,0x40},{0x6d,0x30},{0x6e,0x4b},{0x6f,0x60},{0x70,0x70},{0x71,0x70},{0x72,0x70},{0x73,0x70},{0x74,0x60},{0x75,0x60},{0x76,0x50},{0x77,0x48},{0x78,0x3a},{0x79,0x2e},{0x7a,0x28},{0x7b,0x22},{0x7c,0x04},{0x7d,0x07},{0x7e,0x10},{0x7f,0x28},{0x80,0x36},{0x81,0x44},{0x82,0x52},{0x83,0x60},{0x84,0x6c},{0x85,0x78},{0x86,0x8c},{0x87,0x9e},{0x88,0xbb},{0x89,0xd2},{0x8a,0xe6},};

另外OV9650有两个只读寄存器——0x1C和0x1D,用于存放厂家ID,数据分别为0x7F和0xA2,我们可以通过读取它们来判断s3c2440是否连接了OV9650。当确认连接了OV9650后,我们就可以把上面的那个数组写入OV9650内,如下所示。在这里我们总是认为s3c2440连接了OV9650。

voidconfig_ov9650(void){

unsignedchartemp;

inti;

//读取OV9650厂商IDi=1;

while(i)

{

temp=SCCB_receivedata(0x1C);

//或Rd_SCCB(0x1C,&temp);

if(temp==0x7F)

i=0;

}

i=1;

while(i)

{

temp=SCCB_receivedata(0x1D);

//或Rd_SCCB(0x1D,&temp);

if(temp==0xA2)

i=0;

}

//复位所有OV9650寄存器

SCCB_senddata(0x12,0x80);

//或Wr_SCCB(0x12,0x80);

delay(10000);

//配置OV9650寄存器

for(i=0;i<((sizeof(ov9650_register))/2);i++)

{

SCCB_senddata(ov9650_register[i][0],ov9650_register[i][1]);

//或Wr_SCCB(ov9650_register[i][0],ov9650_register[i][1]);

}}

上面程序中,我们是用循环语句读取OV9650的寄存器0x1C和0x1D的,之所以这样,是为了防止只读取一次时,会有读取不正确的现象发生。而一旦正确读取了厂商ID信息,再读写OV9650寄存器,一般就不会发生读写的错误。

下面就介绍s3c2440摄像接口的相关配置。摄像接口有两个相互独立的DMA通道——P通道(预览通道)和C通道(编解码通道)。P通道主要是存储用于视频显示的RGB图像数据,C通道主要是存储用于编解码的YCbCr图像数据。在这里我们主要是把OV9650采集到的视频信息实时显示在LCD上,因此只介绍P通道的用法。

设置s3c2440摄像接口一个很重要的步骤就是设置视频尺寸大小。我们把由OV9650采集到的视频尺寸称为源,即源水平尺寸和源垂直尺寸,其中源水平尺寸必须是8的整数倍。这个尺寸是通过配置OV9650的相关寄存器实现的。我们把这两个值分别放入输入源格式寄存器CISRCFMT的第16位至第28位,和第0位至第12位内,例如通过OV9650,采集的到的视频尺寸为640×480,则把640和480分别放入寄存器CISRCFMT中的相应位置即可。我们把实际显示的视频尺寸称为目标,即目标水平尺寸和目标垂直尺寸,这里这个尺寸就是LCD的尺寸。我们把这两个值分别放入预览DMA目标图像格式寄存器CIPRTRGFMT的第16位至第28位,和第0位至第12位内,例如LCD的尺寸为320×240,则把320和240分别放入寄存器CIPRTRGFMT中的相应位置即可。另外还需要把这两个值的乘积放入预览缩放目标面积寄存器CIPRTAREA内。源尺寸和目标尺寸往往是不一样大小的,那么可能还需要设置偏移量,即水平偏移量和垂直偏移量,应该把这两个值分别放入窗口偏移寄存器CIWDOFST的第16位至第26位,和第0位至第10位内,其中这个寄存器的第31位用于控制是否需要设置偏移量,当偏移量为0或不需要设置偏移量时,这一位应为0,否则为1。显然,通过源尺寸、目标尺寸和偏移量的设置,可以实现被摄像物体的缩放效果。当然,要实现这种缩放效果,还需要配置预览预缩放比例控制寄存器CIPRSCPRERATIO、预览预缩放距离格式寄存器CIPRSCPREDST和预览主缩放控制寄存器CIPRSCCTRL,这些寄存器的相关参数是通过计算得到的,数据手册上有详细的说明,而且还有标准的函数可以调用,因此在这里就不过多介绍。

前面已经介绍过,摄像接口都是通过DMA实现数据交换的。s3c2440能够在内存中各开辟四块乒乓存储区域,用于实现P通道和C通道的快速数据传递。在P通道中,寄存器CIPRCLRSA1、CIPRCLRSA2、CIPRCLRSA3和CIPRCLRSA4分别用于表示这四块内存的首地址。另外在DMA数据传递中,还要让DMA知道如何进行传递,即一次传输多少个字节,这需要设置预览DMA控制相关寄存器CIPRCTRL的主突发长度和剩余突发长度,这两个值也可以通过调用标准函数来求得。另外在完成每一帧视频采集后,会触发一个视频中断。

下面就给出一段具体的程序,利用OV9650实时地在LCD上显示视频,并通过UART来控制视频,让视频图像放大,缩小,以及实现照相的功能(让图像定格在LCD上)。

……

……

intcom;

……

……

//计算主突发长度和剩余突发长度,用于CIPRCTRL寄存器voidCalculateBurstSize(U32hSize,U32*mainBurstSize,U32*remainedBurstSize){

U32tmp;

tmp=(hSize/4)%16;

switch(tmp){

case0:

*mainBurstSize=16;

*remainedBurstSize=16;

break;

case4:

*mainBurstSize=16;

*remainedBurstSize=4;

break;

case8:

*mainBurstSize=16;

*remainedBurstSize=8;

break;

default:

tmp=(hSize/4)%8;

switch(tmp){

case0:

*mainBurstSize=8;

*remainedBurstSize=8;

break;

case4:

*mainBurstSize=8;

*remainedBurstSize=4;

default:

*mainBurstSize=4;

tmp=(hSize/4)%4;

*remainedBurstSize=(tmp)?tmp:4;

break;

}

break;

}

}

//计算预缩放比率及移位量,用于CICOSCPRERATIO寄存器voidCalculatePrescalerRatioShift(U32SrcSize,U32DstSize,U32*ratio,U32*shift){

if(SrcSize>=64*DstSize){

//Uart_Printf("ERROR:outoftheprescalerrange:SrcSize/DstSize=%d(<64)/n",SrcSize/DstSize);

while(1);

}

elseif(SrcSize>=32*DstSize){

*ratio=32;

*shift=5;

}

elseif(SrcSize>=16*DstSize){

*ratio=16;

*shift=4;

}

elseif(SrcSize>=8*DstSize){

*ratio=8;

*shift=3;

}

elseif(SrcSize>=4*DstSize){

*ratio=4;

*shift=2;

}

elseif(SrcSize>=2*DstSize){

*ratio=2;

*shift=1;

}

else{

*ratio=1;

*shift=0;

}

}

//摄像接口初始化//输入参数分别为预览目标宽和高(即LCD尺寸),以及水平和垂直偏移量voidCamInit(U32PrDstWidth,U32PrDstHeight,U32WinHorOffset,U32WinVerOffset){

U32WinOfsEn;

U32MainBurstSizeRGB,RemainedBurstSizeRGB;

U32H_Shift,V_Shift,PreHorRatio,PreVerRatio,MainHorRatio,MainVerRatio;

U32SrcWidth,SrcHeight;

U32ScaleUp_H_Pr,ScaleUp_V_Pr;

//判断是否需要设置偏移量

if(WinHorOffset==0&&WinVerOffset==0)

WinOfsEn=0;

else

WinOfsEn=1;

SrcWidth=640/*源水平尺寸*/-WinHorOffset*2;

SrcHeight=480/*源垂直尺寸*/-WinVerOffset*2;

//判断尺寸是放大还是缩小

if(SrcWidth>=PrDstWidth)ScaleUp_H_Pr=0;

//down

elseScaleUp_H_Pr=1;

//up

if(SrcHeight>=PrDstHeight)ScaleUp_V_Pr=0;

elseScaleUp_V_Pr=1;

rCIGCTRL|=(1<<26)|(0<<27);

//PCLK极性反转,外部摄像处理器输入

rCIWDOFST=(1<<30)|(0xf<<12);

//清FIFO溢出

rCIWDOFST=0;

//恢复正常模式

rCIWDOFST=(WinOfsEn<<31)|(WinHorOffset<<16)|(WinVerOffset);

//设置偏移量

rCISRCFMT=(1<<31)|(0<<30)|(0<<29)|(640/*源水平尺寸*/<<16)|(0<<14)|(480/*源垂直尺寸*/);

//设置内存首地址,因为是直接显示,所以设置为LCD缓存数组首地址

rCIPRCLRSA1=(U32)LCD_BUFFER;

rCIPRCLRSA2=(U32)LCD_BUFFER;

rCIPRCLRSA3=(U32)LCD_BUFFER;

rCIPRCLRSA4=(U32)LCD_BUFFER;

//设置目标尺寸,并且不进行镜像和旋转处理

rCIPRTRGFMT=(PrDstWidth<<16)|(0<<14)|(PrDstHeight);

//计算并设置突发长度

CalculateBurstSize(PrDstWidth*2,&MainBurstSizeRGB,&RemainedBurstSizeRGB);

rCIPRCTRL=(MainBurstSizeRGB<<19)|(RemainedBurstSizeRGB<<14);

//计算水平和垂直缩放比率和位移量,以及主水平、垂直比率

CalculatePrescalerRatioShift(SrcWidth,PrDstWidth,&PreHorRatio,&H_Shift);

CalculatePrescalerRatioShift(SrcHeight,PrDstHeight,&PreVerRatio,&V_Shift);

MainHorRatio=(SrcWidth<<8)/(PrDstWidth<<H_Shift);

MainVerRatio=(SrcHeight<<8)/(PrDstHeight<<V_Shift);

//设置缩放所需的各类参数

rCIPRSCPRERATIO=((10-H_Shift-V_Shift)<<28)|(PreHorRatio<<16)|(PreVerRatio);

rCIPRSCPREDST=((SrcWidth/PreHorRatio)<<16)|(SrcHeight/PreVerRatio);

rCIPRSCCTRL=(1<<31)|(1/*24位RGB格式*/<<30)|(ScaleUp_H_Pr<<29)|(ScaleUp_V_Pr<<28)|(MainHorRatio<<16)|(MainVerRatio);

//设置面积

rCIPRTAREA=PrDstWidth*PrDstHeight;}

//摄像中断,在这里,除了清中断标志,没有其他操作void__irqCamIsr(void){

rSUBSRCPND|=1<<12;

rSRCPND|=1<<6;

rINTPND|=1<<6;}

//UART中断void__irquartISR(void){

unsignedcharch;

rSUBSRCPND|=0x3;

rSRCPND=0x1<<28;

rINTPND=0x1<<28;

ch=rURXH0;//接收字节数据

switch(ch)

{

case0x11:

//正常显示视频

com=1;

break;

case0x22:

//定格图像

com=2;

break;

case0x33:

//放大尺寸

com=3;

break;

case0x44:

//缩小尺寸

com=4;

break;

}

rUTXH0=ch;}

voidMain(void){

intHOffset,VOffset;

//初始化UPLL,以得到OV9650的系统时钟

rUPLLCON=(56<<12)|(2<<4)|1;

//UPLL为96MHz

rCLKDIVN|=(1<<3);

//UCLK=UPLL/2=48MHz

rCAMDIVN=(rCAMDIVN&~(0xf))|(1<<4)|(2);

//设置摄像接口时钟分频

……

……

LCD_Init();

//初始化LCD,其中LCD的显示格式为24位RGB格式

rLCDCON1|=1;

//开启LCD

//配置摄像接口引脚

rGPJCON=0x2aaaaaa;

rGPJDAT=0;rGPJUP=0;

//上拉使能

//硬件复位摄像头

rGPJDAT|=1<<12;delay(100);

rGPJDAT&=~(1<<12);

//软件复位摄像接口

rCIGCTRL|=(1<<31);

delay(100);

rCIGCTRL&=~(1<<31);

delay(100);

//软件复位摄像头

rCIGCTRL|=(1<<30);

delay(300);

rCIGCTRL&=~(1<<30);

delay(20000);

config_ov9650();

//配置OV9650寄存器

HOffset=0;

VOffset=0;

//初始化摄像接口

CamInit(320,240,HOffset,VOffset);

//开启摄像接口中断,

rSUBSRCPND|=1<<12;

rSRCPND|=1<<6;

rINTPND|=1<<6;

rINTSUBMSK&=~(1<<12);

rINTMSK&=~(1<<6);

pISR_CAM=

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论