s3c2440的摄像接口应用

发布者:SerendipityRose最新更新时间:2016-05-30 来源: eefocus关键字: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))                  //数据信号高
 
//配置IO
rGPEUP = 0xc000;               //上拉无效
rGPECON = 5<<28;             //GPE15为SIO_D,GPE14为SIO_C,都为输出
 
void delay(int a)
{
       int k;
       for(k=0;k               ;
}
 
//启动SCCB
void __inline SCCB_start(void)
{
       CLOCK_HIGH();
       DATA_HIGH();
       delay(10);
       DATA_LOW();
       delay(10);
       CLOCK_LOW();
       delay(10);
}
 
//结束SCCB
void __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 <        }
      
       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;   
}
 
       OV9650的寄存器较多,要想配置好这些寄存器是需要花费一些精力的。下面数组给出了一个VGA(640×480)模式下YUV彩色空间的配置例子,括号内第一个元素表示寄存器地址,第二个元素表示要写入的数据。
 
const unsigned char ov9650_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。
 
void config_ov9650(void)
{
       unsigned char temp;
       int i;
 
       //读取OV9650厂商ID
i=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上)。
 
 
……   ……
 
int com;
 
……   ……
 
//计算主突发长度和剩余突发长度,用于CIPRCTRL寄存器
void CalculateBurstSize(U32 hSize,U32 *mainBurstSize,U32 *remainedBurstSize)
{
       U32 tmp;
       tmp=(hSize/4)%16;
       switch(tmp) {
              case 0:
                     *mainBurstSize=16;
                     *remainedBurstSize=16;
                     break;
              case 4:
                     *mainBurstSize=16;
                     *remainedBurstSize=4;
                     break;
              case 8:
                     *mainBurstSize=16;
                     *remainedBurstSize=8;
                     break;
              default:
                     tmp=(hSize/4)%8;
                     switch(tmp) {
                            case 0:
                                   *mainBurstSize=8;
                                   *remainedBurstSize=8;
                                   break;
                            case 4:
                                   *mainBurstSize=8;
                                   *remainedBurstSize=4;
                            default:
                                   *mainBurstSize=4;
                                   tmp=(hSize/4)%4;
                                   *remainedBurstSize= (tmp) ? tmp: 4;
                                   break;
                     }
                     break;
       }                                        
}
 
//计算预缩放比率及移位量,用于CICOSCPRERATIO寄存器
void CalculatePrescalerRatioShift(U32 SrcSize, U32 DstSize, U32 *ratio,U32 *shift)
{
       if(SrcSize>=64*DstSize) {
              //Uart_Printf("ERROR: out of the prescaler range: SrcSize/DstSize = %d(< 64)/n",SrcSize/DstSize);
              while(1);
       }
       else if(SrcSize>=32*DstSize) {
              *ratio=32;
              *shift=5;
       }
       else if(SrcSize>=16*DstSize) {
              *ratio=16;
              *shift=4;
       }
       else if(SrcSize>=8*DstSize) {
              *ratio=8;
              *shift=3;
       }
       else if(SrcSize>=4*DstSize) {
              *ratio=4;
              *shift=2;
       }
       else if(SrcSize>=2*DstSize) {
              *ratio=2;
              *shift=1;
       }
       else {
              *ratio=1;
              *shift=0;
       }        
}
 
//摄像接口初始化
//输入参数分别为预览目标宽和高(即LCD尺寸),以及水平和垂直偏移量
void CamInit(U32 PrDstWidth, U32 PrDstHeight, U32 WinHorOffset, U32 WinVerOffset)
{
       U32 WinOfsEn;
       U32 MainBurstSizeRGB, RemainedBurstSizeRGB;
       U32 H_Shift, V_Shift, PreHorRatio, PreVerRatio, MainHorRatio, MainVerRatio;
       U32 SrcWidth, SrcHeight;
       U32 ScaleUp_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
       else
ScaleUp_H_Pr=1;         //up
 
       if(SrcHeight>=PrDstHeight)
ScaleUp_V_Pr=0;  
       else
ScaleUp_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<        MainVerRatio=(SrcHeight<<8)/(PrDstHeight<  
       //设置缩放所需的各类参数
       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 __irq CamIsr(void)
{
       rSUBSRCPND |= 1<<12;
       rSRCPND |= 1<<6;
       rINTPND |= 1<<6;
}
 
//UART中断
void __irq uartISR(void)
{
       unsigned char ch;
      
       rSUBSRCPND |= 0x3;
       rSRCPND = 0x1<<28;
       rINTPND = 0x1<<28;
      
       ch = rURXH0; //接收字节数据
       switch(ch)
       {
              case 0x11:                     //正常显示视频
                     com=1;
                     break;
              case 0x22:                    //定格图像
                     com=2;
                     break;
              case 0x33:                    //放大尺寸
                     com=3;
                     break;
              case 0x44:                    //缩小尺寸
                     com =4;
                     break;
       }
       rUTXH0 = ch;
}
 
void Main(void)
{
       int HOffset,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 = (U32)CamIsr;   
      
       rCIPRSCCTRL|=(1<<15);                   //预览缩放开启
       rCIIMGCPT =(1<<31)|(1<<29);          //预览缩放捕捉使能
 
       com=0;
 
       while(1)
       {
              switch(com)
              {
                     case 1:                   //正常显示
                            com=0;
                            rCIPRSCCTRL|=(1<<15);           
                            rCIIMGCPT =(1<<31)|(1<<29);
                     break;
                     case 2:                   //定格图像
                            com=0;
                            rCIPRSCCTRL&=~(1<<15);
                            rCIIMGCPT &=~((1<<31)|(1<<29));
                     break;
                     case 3:                   //放大视频
                            com=0;
                            if(HOffset==160)
                                   break;
                            HOffset += 8;
                            VOffset += 8;
                            rCIPRSCCTRL&=~(1<<15);
                            rCIIMGCPT &=~((1<<31)|(1<<29));
                            CamInit(320,240,HOffset,VOffset);
                            rCIPRSCCTRL|=(1<<15);
                            rCIIMGCPT =(1<<31)|(1<<29);
                     break;
                     case 4:                   //缩小视频
                            com=0;
                            if(HOffset==0)
                                   break;
                            HOffset -= 8;
                            VOffset -= 8;
                            rCIPRSCCTRL&=~(1<<15);
                            rCIIMGCPT &=~((1<<31)|(1<<29));
                            CamInit(320,240,HOffset,VOffset);
                            rCIPRSCCTRL|=(1<<15);
                            rCIIMGCPT =(1<<31)|(1<<29);
                     break;
              }
}
}
关键字:s3c2440  摄像接口 引用地址:s3c2440的摄像接口应用

上一篇:s3c2440的网卡接口扩展
下一篇:s3c2440的DMA应用

推荐阅读最新更新时间:2024-03-16 14:55

S3C2440裸机------异常与中断__swi异常模示程序示例
一般来说,我们的app运行于用户模式,用户模式是一种受限的模式,不能访问硬件,如果app想访问硬件,必须切换模式,当发生中断或者异常时会自动切换模式,但是中断和异常时可遇不可求的,这时候我们通过软中断切换模式。 1.start.S 由于复位之后cpu处于svc管理模式,所以我们先修改cpsr让cpu处于用户模式,然后设置用户模式下的栈。 .text .global _start _start: b reset /* vector 0 : reset */ ldr pc, und_addr /* vector 4 : und */ ldr pc, swi_addr /* vector 8 :
[单片机]
<font color='red'>S3C2440</font>裸机------异常与中断__swi异常模示程序示例
S3C2440的时钟原理
S3C2440的datesheet上说,可以达到400M,但是也不是说,必须在400M的频率下工作,主时钟晶振来自于外部晶振(XTIPLL)或者是外部时钟(EXTCLK)。时钟生成器包含了一个振荡器(振荡放大器),其连接外部晶振,并且还有2个PLL,可以产生需要的高频。 通过引脚OM 来决定时钟源是Crystal还是EXTCLK,不过我用的开发板将OM 固定接地了,都是用外部晶振。有一点值得注意,在对MPLLCON写入有效值之前,系统使用外部晶振或外部时钟源的时钟。即使用户不准备改变MPLLCON的值,也应当重新写一次。 简单说一下,S3C2440的时钟构成。 S3C2440具有2个PLL(Phase Locked L
[单片机]
学习笔记--- S3C2440 对NANDFLASH操作原理与测试代码分析
首先来看一下NANDFLASH接口: 图上看出只有数据口和控制口,没有地址线,所以它不能像网卡,SDRAM那样统一寻址,它的操作方法步骤: 1 使能芯片 2 发操作指令(读,写,擦,复位) 3 发操作地址 5 等待忙信号释放(只有读) 6 发送数据(写),接收数据(读) 7 等待忙信号,低电平忙 8 禁能芯片 这些控制信号线都是通过软件设置寄存器,然后硬件自动产生其他控制线的控制信号; 操作信号含义如下: R/B :读/忙状态线 CE : 片选 CLE :发送指令信号 ALE :发送地址信号 WE:写信号 RE:读信号 这里要注意的: 1 数据按字节传输,刚好8位
[单片机]
学习笔记--- <font color='red'>S3C2440</font> 对NANDFLASH操作原理与测试代码分析
基于S3C2440的嵌入式Linux驱动——看门狗(watchdog)驱动解读
本文将介绍看门狗驱动的实现。 目标平台:TQ2440 CPU:s3c2440 内核版本:2.6.30 1. 看门狗概述 看门狗其实就是一个定时器,当该定时器溢出前必须对看门狗进行 喂狗“,如果不这样做,定时器溢出后则将复位CPU。 因此,看门狗通常用于对处于异常状态的CPU进行复位。 具体的概念请自行百度。 2. S3C2440看门狗 s3c2440的看门狗的原理框图如下: 可以看出,看门狗定时器的频率由PCLK提供,其预分频器最大取值为255+1;另外,通过MUX,可以进一步降低频率。 定时器采用递减模式,一旦到0,则可以触发看门狗中断以及RESET复位信号。 看门狗定时器的频率的计算公式如下
[单片机]
基于<font color='red'>S3C2440</font>的嵌入式Linux驱动——看门狗(watchdog)驱动解读
嵌入式Linux之我行——S3C2440上LCD驱动(FrameBuffer)实例开发讲解
③、帧缓冲设备驱动对底层硬件操作的函数接口实现(即:my2440fb_ops的实现): static struct fb_ops my2440fb_ops = { .owner = THIS_MODULE, .fb_check_var = my2440fb_check_var, .fb_set_par = my2440fb_set_par, .fb_blank = my2440fb_blank, .fb_setcolreg = my2440fb_setcolreg, .fb_fillrect
[单片机]
1.1__S3C2440启动过程分析
s3c2440内置4KB的SRAM,可以选择NOR或者NAND启动。韦东山的S3C2440开发板接有256MB的Nand Flash和Nor Flash,可以通过M0和M1引脚进行选择,其中M1固定拉低。 所以,拉低M0选择Nand启动,拉高M0选择Nor启动。 Nand启动时,片内4K RAM的基地址为0,Nor Flash不可访问,2440硬件会把Nand Flash前4K的内控复制到片内RAM,然后CPU从0地址去除第一条指令执行。 Nor启动时,Nor Flash的基地址为0,片内RAM的起始地址为0x4000 0000,CPU读出Nor上的第一条指令(前4个字节),执行CPU继续读出其他指令执行。 所以
[单片机]
1.1__S3C2440启动过程分析
S3C2440 测试程序(七) IIC实验2--读写EEPROM(软件模拟IIC)
使用S3C2440的GPE14--SCL和GPE15--SDA来软件模拟IIC: 由于不清楚如何像51那样位定义,就特意进行了如下定义: #define IIC_SDA_L (rGPEDAT = rGPEDAT & ~(1 15)) #define IIC_SDA_H (rGPEDAT = rGPEDAT | (1 15)) #define IIC_SCL_L (rGPEDAT = rGPEDAT & ~(1 14)) #define IIC_SCL_H (rGPEDAT = rGPEDAT | (1 14)) 初始化: void IIC_Init() { //GPE15--SDA GPE14--SCL rG
[单片机]
<font color='red'>S3C2440</font> 测试程序(七) IIC实验2--读写EEPROM(软件模拟IIC)
一种测试系统数字稳压电源的设计方案
  引 言   直流稳压电源是一种比较常见的电子设备,一直被广泛地应用在电子电路、实验教学、科学研究等诸多领域。近年来,嵌入式技术发展极为迅速,出现了以单片机、嵌入式ARM 为核心的高集成度处理器,并在自动化、通信等领域得到了广泛应用。电源行业也开始采用内部集成资源丰富的嵌入式控制器来实现数字稳压电源的控制系统。数字稳压电源是用脉宽调制波(PWM)来控制MOS管等开关器件的开通和关闭,从而实现电压电流的稳定输出。数字稳压电源还具备自诊断功能,能实现过压过流保护、故障警告等。   相比之前的模拟电源,数字稳压电源大大减少了在模拟电源中常见的误差、老化、温度漂移、非线性不易补偿等诸多问题,提高了电源的灵活性和适应性。将SAMSUNG
[电源管理]
一种测试系统数字稳压电源的设计方案
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved