s3c2440的攝像接口應用

原文地址:http://blog.csdn.net/zhaocj/archive/2010/06/07/5653479.aspx

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<a;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 <<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;   

}

 

       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<<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 __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;

              }

}

}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章