目次

カメラインタフェース設計

 入手したカメラをマイコンあるいはFPGAに接続
 するためのインタフェース回路を設計します。

 利用するカメラOV7670の16ピンコネクタは
 次のピン割当てになっています。
  1. Vcc
  2. GND
  3. SIC(SCL)
  4. SID(SDA)
  5. VSYNC
  6. HREF
  7. PCLK
  8. CLK
  9. D7
  10. D6
  11. D5
  12. D4
  13. D3
  14. D2
  15. D1
  16. D0
 カメラの動作クロックはCLKに与えればよいので  手持ちのセラミック振動子20MHzを利用した回路  を挟むことにしました。回路図、配線図は以下。  最低周波数は10MHzですが、10MHzのセラミック振動子  を使ったとき、SCCBインタフェースがうまくいかずに  20MHzに交換しました。  マイコン、FPGAを3.3Vで動かすので、電源を  コネクタから貰う仕様にしました。  制御用の10ピンコネクタの割当ては、以下です。
  1. Vcc
  2. (no connection)
  3. (no connection)
  4. (no connection)
  5. PCLK
  6. HREF
  7. VSYNC
  8. SIC(SCL)
  9. SID(SDA)
  10. GND
 データ用の10ピンコネクタの割当ては、次の  ようにしました。
  1. Vcc
  2. D7
  3. D6
  4. D5
  5. D4
  6. D3
  7. D2
  8. D1
  9. D0
  10. GND
 実際の基板は、以下のようにしました。  10ピンコネクタを2つ用意し、マイコンか  FPGAボードにケーブルで接続します。  マイコンにATmega644Pを使う場合、IICバス準拠の  SCCBインタフェースが必要になるので、このための  関数を定義します。  ATmega644PのポートDの7、6ビットをSCL、SDAに割当てます。  start condition   スタートは、SCL、SDAが1の状態から、SDAだけを   0に設定します。 #define MASKC0 0xc0 #define MASK40 0x40   void SCCBStart(void)   {    PORTD |= MASKC0 ; /* (SCL,SDA) = (1,1) */    SCCBwait();    PORTD &= ~MASK40 ; /* (SCL,SDA) = (1,0) */    SCCBwait();   }  stop condition   ストップは、SCL、SDAが0の状態から、SCL、SDAの順に   1に設定します。 #define MASK80 0x80   void SCCBStop(void)   {    PORTD &= ~MASK80 ; /* (SCL,SDA) = (0,1) */    SCCBwait();    PORTD &= ~MASK40 ; /* (SCL,SDA) = (0,0) */    SCCBwait();    PORTD |= MASK80 ; /* (SCL,SDA) = (1,0) */    SCCBwait();    PORTD |= MASK40 ; /* (SCL,SDA) = (1,1) */    SCCBwait();   }  delay   SCLに対して、SDAのセットアップ、ホールドタイムが   必要なので、ソフトウエアループで遅延時間を作ります。 #define I2C_MAX 16   void SCCBwait(void)   {    UBYTE i ;    for ( i = 0 ; i < I2C_MAX ; i++ ) ;   }  バスに値出力   SDAに1ビットごとにデータを出力し、SCLにH、Lを印加します。   IICバスと異なり、ACK、NAKの判断をしないように、SDAからデータ   を入力します。   void SCCBOut(UBYTE data)   {    UBYTE i;    /* Data Out */    for ( i = 0 ; i < 8 ; i++ ) {    /* impress data */    PORTD &= ~MASK40 ;    if ( data & MASK80 ) { PORTD |= MASK40 ; }    /* shift */    data <<= 1 ;    /* impress clock : H */    PORTD |= MASK80 ;    /* delay */    SCCBwait();    /* impress clock : L */    PORTD &= ~MASK80 ;    /* delay */    SCCBwait();    }    /* get acknowlege */    DDRB &= ~MASK40 ; /* change input */    /* impress clock : H */    PORTD |= MASK80 ;    /* shift (dummy) */    data <<= 1 ;    /* impress clock : L */    PORTD &= ~MASK80 ;    /* change output */    DDRB |= MASK40 ;   }  バスから値取得   SDAを入力に設定後、SCLにHを与えて、SDAから論理値を入力します。   論理値を入力したならば、SCLをLにします。   UBYTE SCCBIn(void)   {    UBYTE i;    UBYTE result ;    /* change input */    DDRB &= ~MASK40 ;    /* Data Out */    result = 0 ;    for ( i = 0 ; i < 8 ; i++ ) {    /* shift */    result <<= 1 ;    /* impress clock : H */    PORTD |= MASK80 ;    /* delay */    SCCBwait();    /* get data */    if ( PIND & MASK40 ) { result |= ON ; }    /* impress clock : L */    PORTD &= ~MASK80 ;    /* delay */    SCCBwait();    }    /* get acknowlege */    /* impress clock : H */    PORTD |= MASK80 ;    /* delay */    SCCBwait();    /* impress clock : L */    PORTD &= ~MASK80 ;    /* change output */    DDRB |= MASK40 ;    return result ;   }  レジスタへ値設定   スタート状態、ID出力、アドレス出力、データ出力、ストップ   状態を構成します。   void put_cam_value(UBYTE xadr,UBYTE xdat)   {    SCCBStart();    SCCBOut(CAMERA_ID);    SCCBOut(xadr);    SCCBOut(xdat);    SCCBStop();   }  レジスタから値入力   レジスタアドレスを指定後、スタート状態、ID出力、データ   入力、ストップ状態で構成します。   UBYTE get_cam_value(UBYTE xadr)   {    UBYTE result;    /* send address */    SCCBStart();    SCCBOut(CAMERA_ID);    SCCBOut(xadr);    SCCBStop();    SCCBwait();    /* get data */    SCCBStart();    SCCBOut(CAMERA_ID+ON);    result = SCCBIn();    SCCBStop();    return result ;   }  カメラ初期化   トランジスタ技術のパラメータを参考に   次のように設定します。   void init_cam(void)   {    put_cam_value(0x12, 0x80);    put_cam_value(0x12, 0x04 | 0x20);    put_cam_value(0x8c, 0x02 | 0x01);    put_cam_value(0x04, 0x40);    put_cam_value(0x40, 0x80 | 0x10);    put_cam_value(0x14, 0x38);    put_cam_value(0xaa, 0x94);    put_cam_value(0x3a, 0x04);    put_cam_value(0x4f,0x80);    put_cam_value(0x50,0x80);    put_cam_value(0x51,0x00);    put_cam_value(0x52,0x22);    put_cam_value(0x53,0x5e);    put_cam_value(0x54,0x80);    put_cam_value(0x56,0x40);    put_cam_value(0x58,0x9e);    put_cam_value(0x59,0x88);    put_cam_value(0x5a,0x88);    put_cam_value(0x5b,0x44);    put_cam_value(0x5c,0x67);    put_cam_value(0x5d,0x49);    put_cam_value(0x5e,0x0e);    put_cam_value(0x69,0x00);    put_cam_value(0x6a,0x40);    put_cam_value(0x6b,0x0a);    put_cam_value(0x6c,0x0a);    put_cam_value(0x6d,0x55);    put_cam_value(0x6e,0x11);    put_cam_value(0x6f,0x9f);    put_cam_value(0xb0,0x84);    put_cam_value(REG_CLKRC,0xAF);   }  FIFO付のカメラボードのインタフェース基板も  作成しました。  ピンアサインは、以下でした。
  1. Vcc
  2. GND
  3. SIC
  4. SID
  5. VS(VSYNC)
  6. HREF
  7. WE
  8. XCLK
  9. RRST
  10. OE
  11. RCLK
  12. GND
  13. D0
  14. D1
  15. D2
  16. D3
  17. D4
  18. D5
  19. D6
  20. D7
 これらの信号をFPGA、マイコンに接続するため  接続図を作成してから、半田付け。  HREFは、FIFOに対して画像データを書込む時に  有効データであることを示すだけなので、信号  としてはマイコン、FPGAには接続しなくても  動作は可能です。  XCLKは、FIFOには入力されているだけで、外部  コネクタには信号として出ていません。HREFは  有効データ出力中を示すので、マイコン、FPGA  に接続することにしました。  データだけを10ピンコネクタに集めています。
  1. Vcc
  2. D7
  3. D6
  4. D5
  5. D4
  6. D3
  7. D2
  8. D1
  9. D0
  10. GND
 制御関係は、他の10ピンコネクタに配置。
  1. Vcc
  2. SIC(SCL)
  3. SID(SDA)
  4. WE
  5. RRST
  6. OE
  7. VS(VSYNC)
  8. RCLK
  9. HREF
  10. GND
 FIFOにデータを書込むときには、VS、WEを利用します。  FIFOからデータを取得する場合、OE、RRST、RCLKを  使い、FIFOはDRAM構成なのでリフレッシュのために  書込みか読出しクロックを与えます。カメラの基板  では、書込みクロックが常に与えられています。  RCLKは、マイコン、FPGAの自分の都合で出したり  止めたりしてよいのでした。  SCCBとデータ入出力の操作は、別に分けてテスト  しました。  FIFOのデータ入出力テストは、次の信号接続に。  Spartan3のVHDLコードは、以下です。 library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity fcamx is generic ( TOPX : integer := 4 ; XMAX : integer := 12 ; TOPY : integer := 8 ; YMAX : integer := 240 --; ); port ( -- system nRESET : in std_logic ; CLOCK : in std_logic ; -- camera clock XCLK : out std_logic ; -- FIFO write OTRG : in std_logic ; VSYNC : in std_logic ; WE : out std_logic ; -- FIFO read ITRG : in std_logic ; RRST : out std_logic ; RCLK : out std_logic ; nROE : out std_logic ; FDIN : in std_logic_vector(7 downto 0) ; -- monitor MFDIN : out std_logic_vector(15 downto 0) --; ); end fcamx ; architecture Behavioral of fcamx is -- component clock generator component clkgenx is generic ( TOPX : integer ; RMAX : integer --; ); port ( -- system nRESET : in std_logic ; CLOCK : in std_logic ; -- output CLKOUT : out std_logic -- ; ); end component ; -- address(8) + data(8) type SCCBR is array(0 to 16) of std_logic_vector(15 downto 0) ; signal iSCCBR : SCCBR ; -- interface clock signal iSCLK : std_logic ; signal iSBCLK : std_logic ; -- trigger signal iOTRG_SFT : std_logic_vector(2 downto 0) ; signal iITRG_SFT : std_logic_vector(2 downto 0) ; signal iOTRG : std_logic ; signal iITRG : std_logic ; -- FIFO write signal iVSYNC_SFT : std_logic_vector(2 downto 0) ; signal iVSYNC_TRG : std_logic ; signal iWSTA : std_logic_vector(1 downto 0) ; -- FIFO read signal iRCLK_CNT : std_logic_vector(1 downto 0) ; signal iRCLK : std_logic ; signal iRSTA : std_logic_vector(2 downto 0) ; signal iRCNT : integer range 0 to 76800 ; constant iRCNT_LAST : integer := 76800 ; signal iRFDin : std_logic_vector(15 downto 0) ; begin -- component clock generator (48MHz/12 = 4MHz) CLKX : clkgenx generic map (TOPX,XMAX) port map (nRESET,CLOCK,iSCLK); -- component clock generator (48MHz/240kHz = 200kHz) CLKY : clkgenx generic map (TOPY,YMAX) port map (nRESET,CLOCK,iSBCLK); -- output XCLK <= not iRCLK ; RCLK <= iRCLK ; nROE <= '0' when ( conv_integer(iRSTA) > 1 ) else '1' ; RRST <= '0' when ( iRSTA = "001" ) else '1' ; MFDIN <= iRFDin ; WE <= '1' when ( iWSTA = "11" ) else '0' ; -- trigger process (nRESET,iSCLK) begin if ( nRESET = '0' ) then iOTRG_SFT <= "000" ; iITRG_SFT <= "000" ; elsif rising_edge(iSCLK) then iOTRG_SFT <= iOTRG_SFT(1 downto 0) & OTRG ; iITRG_SFT <= iITRG_SFT(1 downto 0) & ITRG ; end if ; end process ; iOTRG <= '1' when ( iOTRG_SFT = "011" or iOTRG_SFT = "001" ) else '0' ; iITRG <= '1' when ( iITRG_SFT = "011" or iITRG_SFT = "001" ) else '0' ; -- FIFO write process (nRESET,CLOCK) begin if ( nRESET = '0' ) then iVSYNC_SFT <= "111" ; elsif rising_edge(CLOCK) then iVSYNC_SFT <= iVSYNC_SFT(1 downto 0) & VSYNC ; end if ; end process ; iVSYNC_TRG <= '1' when ( iVSYNC_SFT = "110" or iVSYNC_SFT = "100" ) else '0' ; process (nRESET,CLOCK) begin if ( nRESET = '0' ) then iWSTA <= "00" ; elsif rising_edge(CLOCK) then case conv_integer(iWSTA) is -- wait trigger when 0 => if ( iOTRG = '1' ) then iWSTA <= "01" ; else iWSTA <= "00" ; end if ; -- synchronous VSYNC when 1 => if ( iVSYNC_TRG = '1' ) then iWSTA <= "11" ; else iWSTA <= "01" ; end if ; -- delay when 3 => if ( iVSYNC_TRG = '1' ) then iWSTA <= "10" ; else iWSTA <= "11" ; end if ; -- return first state when 2 => iWSTA <= "00" ; -- default when others => iWSTA <= "00" ; end case ; end if ; end process ; -- generate RCLK (48MHz/4 = 12MHz) process (nRESET,CLOCK) begin if ( nRESET = '0' ) then iRCLK_CNT <= "00" ; elsif rising_edge(CLOCK) then case conv_integer(iRCLK_CNT) is when 0 => iRCLK_CNT <= "01" ; when 1 => iRCLK_CNT <= "11" ; when 3 => iRCLK_CNT <= "10" ; when 2 => iRCLK_CNT <= "00" ; when others => iRCLK_CNT <= "00" ; end case ; end if ; end process ; iRCLK <= iRCLK_CNT(1); -- FIFO data state machine process (nRESET,CLOCK) begin if ( nRESET = '0' ) then iRSTA <= "000" ; iRFDin <= (others => '0') ; elsif rising_edge(CLOCK) then case conv_integer(iRSTA) is -- wait trigger when 0 => if ( iITRG = '1' ) then iRSTA <= "001" ; iRCNT <= 2 ; else iRSTA <= "000" ; iRCNT <= 0 ; end if ; -- enable RRST when 1 => if ( conv_integer(iRCNT) = 0 ) then iRSTA <= "011" ; iRCNT <= 0 ; else if ( iRCLK_CNT = "01" ) then iRCNT <= iRCNT - 1 ; end if ; iRSTA <= "001" ; end if ; -- get upper byte when 3 => if ( iRCLK_CNT = "01" ) then iRFDin(15 downto 8) <= FDIN ; iRSTA <= "111" ; else iRSTA <= "011" ; end if ; -- get lower byte when 7 => if ( iRCLK_CNT = "01" ) then iRFDin(7 downto 0) <= FDIN ; iRSTA <= "110" ; else iRSTA <= "111" ; end if ; -- get store and increment when 6 => iRCNT <= iRCNT + 1 ; iRSTA <= "100" ; -- judge when 4 => if ( iRCNT /= iRCNT_LAST ) then iRSTA <= "011" ; else iRSTA <= "000" ; end if ; -- others when others => iRSTA <= "000" ; end case ; end if ; end process ; end Behavioral;  FIFOにOV7670からのデータをライトするには、次の  タイミングチャートを参考にしました。  トリガー信号OTRGを用意し、外部からトリガーを  与えると、撮影データをFIFOに入力します。  撮影タイミングをWE信号で与えます。  FIFO付カメラの基板に、FIFOのデータ入力制御信号  が用意されているので、FIFO内部の書込みアドレス  カウンタをリセットしてから、WEを使いアドレスを  +1していきます。  FIFO付カメラのFIFOは、最大でQQVGA画面を1枚入れる  程度なので、QQVGAあるいはQCIF画像を保存するのが  適していると言えるでしょう。  FIFOに入っているデータは、次のタイミングチャートに  対応したシーケンサで取り出します。  シーケンサは、外部から与えられたトリガーを利用して  次の手順でFIFOからデータを取り出します。
  1. トリガー待ち
  2. FIFO内部出力カウンタ、データカウンタリセット
  3. RCLKに同期して、上位データ入力
  4. RCLKに同期して、下位データ入力
  5. 1ピクセルデータ保存とデータカウンタインクリメント
  6. データ取得が途中ならステート3に、完了していればステートに
 利用FPGAは、内部にSRAMを用意できるのでシーケンサが  取得してきた画像データを16ビットにしてからSRAMに  保存します。その部分は、後で付加します。  Spartan3は、48MHzで動作しているのでシーケンサは  この周波数で動かします。FIFOを動かすクロックは  4分周して12MHzで動かしてリアルタイム動作を実現  できそう。  OV7670は、多機能なので、SCCBにより内部レジスタに  いろいろなパラメータを設定しないと、思うような  画像データを取得できません。  3項目に分けて、内部レジスタの設定値を検討します。  動作スピード指定   OV7670は、8MHz〜48MHzのクロックを入れて動作します。   標準は12MHzですが、8MHz〜48MHzから外れたクロックで   あっても、内蔵PLLを利用して最高周波数48MHzで動かす   ことは可能です。   レジスタDIVL、CLKRCの組み合わせで、内部動作クロック   の「てい倍」と「分周」を実現します。   「てい倍」処理の例として、12MHzを入力し、48MHzにします。    レジスタDIVL(0x6B)に0x4Aを、CLKRC(0x80)に0x0Cを設定。    レジスタDIVLは、上位2ビット(2^7、2^6)の2ビットで    x1、x4、x6、x8の「てい倍」が可能です。(CLKRCは0x0C)    ビットの組み合わせは、以下。     x1 00     x4 01     x6 10     x8 11    デフォルトだと、DIVL(0x6B)は0x0Aなので、x1です。    最高周波数の48MHzを利用したいときは、12MHz、8MHz、6MHzの    どれかのクロックを与えて、内部レジスタDIVLのパラメータを    指定すれば、高速動作が可能になります。    マイコンのクロックは、20MHz程度が最高なので    内部PLLによる「てい倍」は、OV7670を高速に動    かしたい場合は、積極的に利用すべきでしょう。   「分周」処理の例として、12MHzを入力し、12分周して   動かす場合を考えます。    レジスタDIVL(0x6B)に0x0Aを、CLKRC(0x80)に0x8Bを設定。    レジスタCLKRCは、下位6ビットで分周比を設定できる    仕様です。6ビットなので、0〜63を設定でき、この    数値に1を加えた値である1〜64を「分周比」として    利用できます。    12分周なら11を設定して、(0x80+0x0B)となります。   「てい倍」と「分周」により、OV7670が出力する信号   VSYNC、HREF、PCLKを変化させられます。   マイコン、FPGAの動作スピードに合わせて、DIVL、CLKRC   の設定値を指定します。   「てい倍」と「分周」の設定をするための16進コードを   生成するAWKスクリプトを作成しました。   「てい倍」で48MHzを超えても、エラー処理はなしです。 { divl = 10 ; clkrc = 128 ; # multiply if ( $1 == "M" || $1 == "m" ) { printf("frequency => %d MHz\n",$2); printf(" CLKRC = 0x%02X \n",clkrc); printf(" DIVL = 0x%02X(x1) %d MHz\n",divl,$2); printf(" DIVL = 0x%02X(x4) %d MHz\n",divl+64,$2*4); printf(" DIVL = 0x%02X(x6) %d MHz\n",divl+128,$2*6); printf(" DIVL = 0x%02X(x8) %d MHz\n",divl+192,$2*8); } # divide if ( $1 == "D" || $1 == "d" ) { printf("frequency => %d MHz divider = %d \n",$2,$3); printf(" DIVL = 0x%02X \n",divl); printf(" CLKRC = 0x%02X\n",clkrc+$3-1); if ( $3 > 0 ) { printf(" internal frequency = %d MHz\n",$2/$3); } else { printf(" Error !\n"); } } }   テキストファイルに、「てい倍」か「分周」の種別と   周波数、「分周」の場合は分周比を指定します。   設定例は、以下です。     m 12     d 16 4   上段は、12MHzの周波数を「てい倍」。   下段は、16MHzの周波数を「4分周」。   使い方は、単純でDOS窓、端末で次のタイプします。   gawk -f md.awk txt.txt{enter}   結果は、以下です。   frequency => 12 MHz    CLKRC = 0x80    DIVL = 0x0A(x1) 12 MHz    DIVL = 0x4A(x4) 48 MHz    DIVL = 0x8A(x6) 72 MHz    DIVL = 0xCA(x8) 96 MHz   frequency => 16 MHz divider = 4    DIVL = 0x0A    CLKRC = 0x83    internal frequency = 4 MHz  画像データフォーマット指定   OV7670からは、指定の画像フォーマットでデータを   出力できます。   VGA、QVGA、QQVGA、CIF、QCIFのどのフォーマットに   するかは、COM7(0x12)で指定します。   COM7の、5、4、3ビットを排他的にセットすると   次のフォーマットを指定できます。 0x20 CIF 0x10 QVGA 0x04 QCIF 0x02 RGB   1ピクセルを16ビットとして、この16ビットの並べ方を   指定できます。画像処理には、RGBの方が適しているので  領域指定   OV7670からは、画像の一部を指定して出力できます。   VGA(640x480)、QVGA(320x240)、QQVGA(160x120)、CIF(352x288)、QCIF(176x144)  FIFO付カメラを制御するファームウエアを作成しました。  3.3V動作が可能な、AnalogDevicesのADuC7026を使いました。  (カメラのパラメータ設定の一部は、CQ出版のダウンロード   ファイルから抽出して使っています。) #include <ADuC7026.h> #define OFF 0 #define ON OFF+1 /* data definitions */ typedef unsigned char UBYTE ; typedef signed char SBYTE ; typedef unsigned short UWORD ; typedef signed short SWORD ; typedef unsigned long ULONG ; typedef signed long SLONG ; void IRQ_Handler(void) __irq; void init_usr(void); #define MASKFF 0xFF #define MASK0F 0x0F #define MASK80 0x80 #define MASK40 0x40 #define MASK20 0x20 #define MASK10 0x10 #define MASK08 0x08 #define MASK04 0x04 #define MASK02 0x02 #define MASK01 0x01 #define MASKF0 0xF0 #define SCCB_ID_WR 0x42 #define SCCB_ID_RD 0x43 #define S_SCL 23 #define S_SDA 22 #define S_WEN 21 #define S_RRST 20 #define S_OE 18 #define S_RCLK 17 #define S_VS 2 ULONG timcnt ; UBYTE lcnt ; UBYTE pcnt ; UWORD gtmp ; #define MAX_LCNT 120 #define MAX_PCNT 160 void rs_putchar(UBYTE x); void crlf(void); void rs_puts(UBYTE *x); void show_help(void); void init_cam(void); void dump_reg(void); void vs_handler(void); void put_sccb_start(void); void put_sccb_stop(void); void put_sccb_data(UBYTE x); UBYTE get_sccb_data(void); UBYTE load_cam(UBYTE adr); void send_cam(UBYTE adr,UBYTE dat); void delay_ms(UWORD x); void delay_100us(UWORD x); UBYTE gdat(void); void get_gdat(void); void set_rclk(UBYTE x); void set_oe(UBYTE x); void set_rrst(UBYTE x); void out_gdat(UWORD x); void init_pla(void); /* global variables */ volatile UBYTE uflag ; volatile UBYTE sbuf[8] ; volatile UBYTE sindex ; volatile UBYTE cmd ; volatile UBYTE asc_hex[16] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; volatile UBYTE state ; volatile UWORD vscnt ; volatile UBYTE strg ; volatile UBYTE gtrg ; volatile UBYTE vtrg ; volatile UBYTE vstmp ; volatile UBYTE gstate ; volatile UWORD lbuf[160] ; #define REG_GAIN 0x00 /* Gain lower 8 bits (rest in vref) */ #define REG_BLUE 0x01 /* blue gain */ #define REG_RED 0x02 /* red gain */ #define REG_VREF 0x03 /* Pieces of GAIN, VSTART, VSTOP */ #define REG_COM1 0x04 /* Control 1 */ #define COM1_CCIR656 0x40 /* CCIR656 enable */ #define REG_BAVE 0x05 /* U/B Average level */ #define REG_GbAVE 0x06 /* Y/Gb Average level */ #define REG_AECHH 0x07 /* AEC MS 5 bits */ #define REG_RAVE 0x08 /* V/R Average level */ #define REG_COM2 0x09 /* Control 2 */ #define COM2_SSLEEP 0x10 /* Soft sleep mode */ #define REG_PID 0x0a /* Product ID MSB */ #define REG_VER 0x0b /* Product ID LSB */ #define REG_COM3 0x0c /* Control 3 */ #define COM3_SWAP 0x40 /* Byte swap */ #define COM3_SCALEEN 0x08 /* Enable scaling */ #define COM3_DCWEN 0x04 /* Enable downsamp/crop/window */ #define REG_COM4 0x0d /* Control 4 */ #define REG_COM5 0x0e /* All "reserved" */ #define REG_COM6 0x0f /* Control 6 */ #define REG_AECH 0x10 /* More bits of AEC value */ #define REG_CLKRC 0x11 /* Clocl control */ #define CLK_EXT 0x40 /* Use external clock directly */ #define CLK_SCALE 0x3f /* Mask for internal clock scale */ #define REG_COM7 0x12 /* Control 7 */ #define COM7_RESET 0x80 /* Register reset */ #define COM7_FMT_MASK 0x38 #define COM7_FMT_VGA 0x00 #define COM7_FMT_CIF 0x20 /* CIF format */ #define COM7_FMT_QVGA 0x10 /* QVGA format */ #define COM7_FMT_QCIF 0x08 /* QCIF format */ #define COM7_RGB 0x04 /* bits 0 and 2 - RGB format */ #define COM7_YUV 0x00 /* YUV */ #define COM7_BAYER 0x01 /* Bayer format */ #define COM7_PBAYER 0x05 /* "Processed bayer" */ #define REG_COM8 0x13 /* Control 8 */ #define COM8_FASTAEC 0x80 /* Enable fast AGC/AEC */ #define COM8_AECSTEP 0x40 /* Unlimited AEC step size */ #define COM8_BFILT 0x20 /* Band filter enable */ #define COM8_AGC 0x04 /* Auto gain enable */ #define COM8_AWB 0x02 /* White balance enable */ #define COM8_AEC 0x01 /* Auto exposure enable */ #define REG_COM9 0x14 /* Control 9 - gain ceiling */ #define REG_COM10 0x15 /* Control 10 */ #define COM10_HSYNC 0x40 /* HSYNC instead of HREF */ #define COM10_PCLK_HB 0x20 /* Suppress PCLK on horiz blank */ #define COM10_HREF_REV 0x08 /* Reverse HREF */ #define COM10_VS_LEAD 0x04 /* VSYNC on clock leading edge */ #define COM10_VS_NEG 0x02 /* VSYNC negative */ #define COM10_HS_NEG 0x01 /* HSYNC negative */ #define REG_HSTART 0x17 /* Horiz start high bits */ #define REG_HSTOP 0x18 /* Horiz stop high bits */ #define REG_VSTART 0x19 /* Vert start high bits */ #define REG_VSTOP 0x1a /* Vert stop high bits */ #define REG_PSHFT 0x1b /* Pixel delay after HREF */ #define REG_MIDH 0x1c /* Manuf. ID high */ #define REG_MIDL 0x1d /* Manuf. ID low */ #define REG_MVFP 0x1e /* Mirror / vflip */ #define MVFP_MIRROR 0x20 /* Mirror image */ #define MVFP_FLIP 0x10 /* Vertical flip */ #define REG_AEW 0x24 /* AGC upper limit */ #define REG_AEB 0x25 /* AGC lower limit */ #define REG_VPT 0x26 /* AGC/AEC fast mode op region */ #define REG_HSYST 0x30 /* HSYNC rising edge delay */ #define REG_HSYEN 0x31 /* HSYNC falling edge delay */ #define REG_HREF 0x32 /* HREF pieces */ #define REG_TSLB 0x3a /* lots of stuff */ #define TSLB_YLAST 0x04 /* UYVY or VYUY - see com13 */ #define REG_COM11 0x3b /* Control 11 */ #define COM11_NIGHT 0x80 /* NIght mode enable */ #define COM11_NMFR 0x60 /* Two bit NM frame rate */ #define COM11_HZAUTO 0x10 /* Auto detect 50/60 Hz */ #define COM11_50HZ 0x08 /* Manual 50Hz select */ #define COM11_EXP 0x02 #define REG_COM12 0x3c /* Control 12 */ #define COM12_HREF 0x80 /* HREF always */ #define REG_COM13 0x3d /* Control 13 */ #define COM13_GAMMA 0x80 /* Gamma enable */ #define COM13_UVSAT 0x40 /* UV saturation auto adjustment */ #define COM13_UVSWAP 0x01 /* V before U - w/TSLB */ #define REG_COM14 0x3e /* Control 14 */ #define COM14_DCWEN 0x10 /* DCW/PCLK-scale enable */ #define REG_EDGE 0x3f /* Edge enhancement factor */ #define REG_COM15 0x40 /* Control 15 */ #define COM15_R10F0 0x00 /* Data range 10 to F0 */ #define COM15_R01FE 0x80 /* 01 to FE */ #define COM15_R00FF 0xc0 /* 00 to FF */ #define COM15_RGB565 0x10 /* RGB565 output */ #define COM15_RGB555 0x30 /* RGB555 output */ #define REG_COM16 0x41 /* Control 16 */ #define COM16_AWBGAIN 0x08 /* AWB gain enable */ #define REG_COM17 0x42 /* Control 17 */ #define COM17_AECWIN 0xc0 /* AEC window - must match COM4 */ #define COM17_CBAR 0x08 /* DSP Color bar */ #define REG_RGB444 0x8c /* RGB 444 control */ #define R444_ENABLE 0x02 /* Turn on RGB444, overrides 5x5 */ #define R444_RGBX 0x01 /* Empty nibble at end */ #define REG_HAECC1 0x9f /* Hist AEC/AGC control 1 */ #define REG_HAECC2 0xa0 /* Hist AEC/AGC control 2 */ #define REG_HAECC7 0xaa /* Hist AEC/AGC control 7 */ int main(void) { /* initialize user */ init_usr(); /* show message */ rs_puts("Hello"); crlf(); /* endless loop */ while(ON) { /* command interrpreter */ if ( uflag == ON ) { /* clear flag */ uflag = OFF ; /* judge */ cmd = *(sbuf+0) ; if ( cmd == '?' ) { show_help(); } /* initialize CAMERA */ if ( cmd == 'I' ) { crlf(); init_cam() ; } /* take shot */ if ( cmd == 'S' ) { strg = ON ; } /* get picture and show */ if ( cmd == 'G' ) { gtrg = ON ; } /* dump register */ if ( cmd == 'R' ) { crlf(); dump_reg() ; } } /* VSYNC interrupt handler */ vs_handler(); /* get graphic data from camera */ get_gdat(); } /* dummy return */ return (0); } void IRQ_Handler(void) __irq { volatile UBYTE ch ; /* judge UART receive interruption */ if ( (IRQSTA & UART_BIT) == UART_BIT ) { /* judge */ if ( COMSTA0 & 1 ) { /* clear flag */ ch = COMRX ; *(sbuf+sindex) = ch ; sindex++ ; if ( ch == '\r' ) { sindex = 0 ; uflag = ON ; } } } /* judge timer0 interruption (100us) */ if ( (IRQSTA & RTOS_TIMER_BIT) == RTOS_TIMER_BIT ) { /* clear timer0 interrupt flag */ T0CLRI = 0xff ; /* increment */ timcnt++ ; /* judge */ if ( (timcnt & 0x3ff) == 0 ) { GP4DAT ^= (1 << 23); } } /* judge timer1 interruption (100Hz) */ if ( (IRQSTA & GP_TIMER_BIT) == GP_TIMER_BIT ) { /* clear timer0 interrupt flag */ T1CLRI = 0xff ; /* increment */ vscnt++ ; /* 600Hz handling */ if ( vscnt == 600 ) { vscnt = 0 ; /* get vsync signal */ GP3DAT ^= (1 << 23) ; } } /* judge PLA0 interruption (exteranl interruption) */ if ( (IRQSTA & PLA_IRQ0_BIT) == PLA_IRQ0_BIT ) { vtrg = OFF ; /* enable */ if ( strg == ON ) { /* set flag */ vtrg = ON ; } /* get vsync signal Debug */ GP3DAT ^= (1 << 22) ; } } void init_usr(void) { /* select clock 41.78MHz initialized in start up routine */ PLLKEY1 = 0xaa ; PLLCON = 0x01 ; PLLKEY2 = 0x55 ; /* power control initialized in start up routine */ /* clear flag */ uflag = OFF ; strg = OFF ; vtrg = OFF ; gtrg = OFF ; /* clear counter */ timcnt = 0 ; sindex = 0 ; state = 0 ; gstate = 0 ; vscnt = 0 ; vstmp = MASKFF ; /* initialize UART */ { /* set baud rate 38400 bps CD = 0 DL = 0x11 */ COMCON0 = 0x80 ; /* select COMDIV1 and COMDIV0 */ COMDIV0 = 0x22 ; //COMDIV0 = 0x44 ; COMDIV1 = 0x00 ; /* set conditions */ COMCON0 = 0x03 ; /* select COMRX and COMTX , 8bit data , 1 stop bit , no parity */ /* enable interrupt */ COMIEN0 = 0x01 ; /* ERBFI */ } /* P0 */ { /* */ GP0DAT = 0xFF3C0000 ; } /* P1 */ { /* GP1.4 (PLA IN) use UART */ GP1CON = 0x00030011 ; /* */ GP1DAT = 0x6E000000 ; } /* P2 */ { /* all bits input */ GP2DAT = 0x00000000 ; } /* P3 */ { GP3DAT = 0xffff0000 ; } /* P4 */ { GP4DAT = 0xff000000 ; } /* initialize timer 0 (100us) */ { T0LD = 4178 ; /* (41.78MHz / 1) / 10kHz */ T0CON = 0xc0 ; /* enable , cyclic , 1/1 */ } /* initialize timer 1 (600Hz) */ { T1LD = 4352 ; /* (41.78MHz / 16) / 600Hz */ T1CON = 0xc4 ; /* enable , cyclic , 1/16 */ } /* initialize PLA */ init_pla(); /* enable timer 0 interrupt , timer 1 interrupt and UART interrupt */ IRQEN = RTOS_TIMER_BIT | GP_TIMER_BIT | UART_BIT | PLA_IRQ0_BIT ; } /* UART putchar */ void rs_putchar(UBYTE x) { /* ? transmmit buffer empty */ while( (COMSTA0 & 0x40) == 0 ) ; /* set value */ COMTX = x ; } /* carriage return and line feed */ void crlf(void) { rs_putchar('\r'); rs_putchar('\n'); } /* UART puts */ void rs_puts(UBYTE *x) { while ( *x != '\0' ) { rs_putchar( *x ) ; x++ ; } } /* show help */ void show_help(void) { rs_puts("? help") ; crlf(); rs_puts("I initialize camera"); crlf(); rs_puts("S shot picture") ; crlf(); rs_puts("G get picture") ; crlf(); rs_puts("R dump register") ; crlf(); } void put_sccb_start(void) { /* both high level */ GP0DAT |= ((1 << S_SCL) | (1 << S_SDA)) ; /* SCL = ON ; SDA = ON */ delay_100us(3); /* 0 -> SDA */ GP0DAT &= ~(1 << S_SDA) ; /* SCL = ON ; SDA = OFF */ delay_100us(3); /* 0 -> SCL */ GP0DAT &= ~(1 << S_SCL) ; /* SCL = OFF ; SDA = OFF */ delay_100us(3); } void put_sccb_stop(void) { /* both high level */ GP0DAT |= (1 << S_SCL) ; /* SCL = ON ; SDA = ? */ delay_100us(3); /* 0 -> SDA */ GP0DAT &= ~(1 << S_SDA) ; /* SCL = ON ; SDA = OFF */ delay_100us(3); /* both high level */ GP0DAT |= (1 << S_SDA) ; /* SCL = ON ; SDA = ON */ delay_100us(3); } void put_sccb_data(UBYTE x) { UBYTE tmp ; UBYTE i ; /* load raw data */ tmp = x ; /* transfer data with write code */ for ( i = 0 ; i < 8 ; i++ ) { /* send bit datum */ GP0DAT &= ~(1 << S_SDA) ; if ( tmp & MASK80 ) { GP0DAT |= (1 << S_SDA) ; } delay_ms(1) ; /* 1 -> SCL */ GP0DAT |= (1 << S_SCL) ; delay_100us(5) ; /* shift */ tmp <<= 1 ; /* 0 -> SCL */ GP0DAT &= ~(1 << S_SCL) ; delay_100us(5) ; } /* data low */ GP0DAT &= ~(1 << S_SDA) ; delay_100us(5) ; /* change input */ GP0DAT &= ~(1 << 30) ; delay_100us(5) ; /* 1 -> SCL */ GP0DAT |= (1 << S_SCL) ; delay_100us(5) ; /* get acknowledge */ tmp = OFF ; if ( (GP0DAT & MASK40) == MASK40 ) { tmp = ON ; } /* 0 -> SCL */ GP0DAT &= ~(1 << S_SCL) ; delay_100us(5) ; /* change output */ GP0DAT |= (1 << 30) ; } UBYTE get_sccb_data(void) { UBYTE result ; UBYTE i ; UBYTE tmp ; /* change inupt */ GP0DAT &= ~(1 << 30) ; /* default */ result = 0 ; /* loop */ for ( i = 0 ; i < 8 ; i++ ) { /* shift */ result <<= 1 ; /* 1 -> SCL */ GP0DAT |= (1 << S_SCL) ; delay_100us(5) ; /* get bit datum */ if ( (GP0DAT & MASK40) == MASK40 ) { result |= ON ; } /* 0 -> SCL */ GP0DAT &= ~(1 << S_SCL) ; delay_100us(5) ; } /* 1 -> SCL */ GP0DAT |= (1 << S_SCL) ; delay_100us(5) ; /* get acknowledge */ tmp = OFF ; if ( (GP0DAT & MASK40) == MASK40 ) { tmp = ON ; } /* 0 -> SCL */ GP0DAT &= ~(1 << S_SCL) ; delay_100us(5) ; /* change output */ GP0DAT |= (1 << 30) ; return result ; } void send_cam(UBYTE adr,UBYTE dat) { /* start conditon */ put_sccb_start() ; /* send ID */ put_sccb_data(SCCB_ID_WR); /* send address */ put_sccb_data(adr); /* send parameter */ put_sccb_data(dat); /* stop condition */ put_sccb_stop(); /* delay */ delay_100us(5); } UBYTE load_cam(UBYTE adr) { UBYTE result ; /* start conditon */ put_sccb_start() ; /* send ID (write) */ put_sccb_data(SCCB_ID_WR); /* send address */ put_sccb_data(adr); /* stop condition */ put_sccb_stop(); /* start conditon */ put_sccb_start() ; /* send ID (read) */ put_sccb_data(SCCB_ID_RD); /* get parameter */ result = get_sccb_data(); /* stop condition */ put_sccb_stop(); return result ; } void delay_100us(UWORD x) { ULONG last ; /* calculate */ last = timcnt + x ; /* wait */ while ( timcnt < last ) ; } void delay_ms(UWORD x) { ULONG last ; /* calculate */ last = timcnt + 10 * x ; /* wait */ while ( timcnt < last ) ; } void init_cam(void) { send_cam(REG_COM7,COM7_RESET); //0x12:COM7(Reset,QCIF,Cbar,RGBformat) delay_ms(200); //200ms rs_puts("Complete reset"); crlf(); send_cam(REG_COM7,COM7_RGB | COM7_RGB); //0x12:COM7 , 0x0C) //RGB send_cam(REG_RGB444,R444_ENABLE | R444_RGBX); //0x8c:RGB 444 control send_cam(REG_COM1,0x40); //0x04:COM1(CCIR656,AEC) //0) //0x40) send_cam(REG_COM15,COM15_R01FE|COM15_RGB565); //0x40:COM15 send_cam(REG_COM9, 0x38); //0x14:COM9=max AGC gain ceiling, Freeze AGC/AEC //c(0x3d,0xc3) //(REG_COM13, COM13_GAMMA|COM13_UVSAT|0x2) //0x3d:COM13 send_cam(REG_HAECC7,0x94); //0xaa:Hist AEC/AGC control 7 c(0xAA,0x94) //AEC algorithm send_cam(REG_TSLB,0x04); //0x3a:Neg,UVval,YUYV,window TSLB_YLAST) //0x04) //0x0C) //0x80) //0x00) //0x04) send_cam(0x20,0x0f); //ADCCTR0, A/D range&ref, mu0102 send_cam(REG_COM8,0x9a); //mu0103 send_cam(0x10,0x01); //mu0103 /* CLKRC */ send_cam(0x11,0x80); /* COM1 */ send_cam(0x3b,0x0a); /* disable night mode */ /* TSLB */ send_cam(0x3a,0x04); /* COM7 */ send_cam(0x12,0x04); /* RGB444 */ send_cam(0x8c,0x02); /* COM15 */ send_cam(0x40,0xd0); /* HSTART */ send_cam(0x17,0x16); /* HSTOP */ send_cam(0x18,0x04); /* HREF */ send_cam(0x32,0x24); /* VSTRT */ send_cam(0x19,0x02); /* VSTOP */ send_cam(0x1a,0x7a); /* VREF */ send_cam(0x03,0x0a); /* COM10 */ //send_cam(0x15,0x02); /* COM3 */ send_cam(0x0c,0x04); /* COM4 */ send_cam(0x3e,0x1a); /* SCALING_DCWCTR */ send_cam(0x72,0x22); /* SCALING_PCLK_DIV */ send_cam(0x73,0xf2); rs_puts("Exit initialize"); crlf(); } void dump_reg(void) { UWORD loop ; UBYTE result ; UBYTE cdat[4] ; /* */ for ( loop = 0 ; loop < 128 ; loop++ ) { /* get information from camera */ result = load_cam(loop) ; /* separate */ *(cdat+3) = result & MASK0F ; *(cdat+2) = (result >> 4) & MASK0F ; *(cdat+1) = loop & MASK0F ; *(cdat+0) = (loop >> 4) & MASK0F ; /* conversion */ *(cdat+3) = asc_hex[ *(cdat+3) ] ; *(cdat+2) = asc_hex[ *(cdat+2) ] ; *(cdat+1) = asc_hex[ *(cdat+1) ] ; *(cdat+0) = asc_hex[ *(cdat+0) ] ; /* send terminal */ rs_putchar( '0' ); rs_putchar( 'x' ); rs_putchar( *(cdat+0) ) ; rs_putchar( *(cdat+1) ) ; rs_putchar( '=' ) ; rs_putchar( *(cdat+2) ) ; rs_putchar( *(cdat+3) ) ; if ( (loop % 8) == 7 ) { crlf(); } else { rs_putchar( ' ' ) ; } } } void vs_handler(void) { /* enable or disable WEN */ switch ( state ) { /* wait trigger */ case 0 : if ( strg == ON ) { /* next state */ state++ ; rs_putchar('G'); } break ; /* wait trigger */ case 1 : state = 1 ; if ( vtrg == ON ) { /* clear flag */ vtrg = OFF ; /* next state */ state = 2 ; } break ; /* enable WEN */ case 2 : state = 3 ; /* next state */ /* debug */ rs_putchar('e'); /* enable WEN */ GP0DAT |= (1 << S_WEN); break ; /* wait trigger */ case 3 : state = 3 ; if ( vtrg == ON ) { /* clear flag */ vtrg = OFF ; /* next state */ state = 4 ; } break ; /* disable WEN */ case 4 : state = 5 ; /* next state */ /* debug */ rs_puts("t\r\n"); /* disable WEN */ GP0DAT &= ~(1 << S_WEN); break ; /* return first state */ case 5 : state = 0 ; /* clear flag */ strg = OFF ; break ; /* NULL */ default : state = 0 ; break ; } } UBYTE gdat(void) { UBYTE result ; /* get graphic data */ result = GP2DAT & MASKFF ; return result ; } void get_gdat(void) { UBYTE i ; /* state machine */ switch (gstate) { /* wait trigger */ case 0 : if ( gtrg == ON ) { gtrg = OFF ; /* Debug */ rs_puts("Start !"); crlf(); lcnt = 0 ; pcnt = 0 ; gstate = 1 ; } break ; /* send reset signal */ case 1 : set_rrst(ON) ; /* RRST <= 0 */ set_oe(ON) ; /* OE <= 0 */ delay_100us(5); set_rclk(ON) ; /* RCLK <= 1 */ set_rclk(OFF) ; /* RCLK <= 0 */ set_rrst(OFF) ; /* RRST <= 1 */ /* next state */ gstate = 2 ; break ; /* enable */ case 2 : /* next state */ gstate = 3 ; break ; /* judge max line */ case 3 : /* next state */ gstate = 4 ; /* exit loop */ if ( lcnt >= MAX_LCNT ) { set_oe(OFF) ; /* OE <= 1 */ lcnt = 0 ; gstate = 9 ; /* Debug */ rs_puts("Complete !"); /* new line */ crlf(); } break ; /* judge max pixel */ case 4 : /* next state */ gstate = 5 ; /* exit line handling */ if ( pcnt >= MAX_PCNT ) { pcnt = 0 ; gstate = 8 ; } break ; /* get upper byte */ case 5 : /* H -> RCLK */ set_rclk(ON) ; /* get data and shift */ gtmp = gdat() ; gtmp <<= 8 ; /* L -> RCLK */ set_rclk(OFF) ; /* next state */ gstate = 6 ; break ; /* get lower byte */ case 6 : /* H -> RCLK */ set_rclk(ON) ; /* get data and concatenate */ gtmp |= gdat() ; /* L -> RCLK */ set_rclk(OFF) ; /* next state */ gstate = 7 ; break ; /* save data to line buffer and update pixel counter */ case 7 : *(lbuf+pcnt) = gtmp ; pcnt++ ; /* next state */ gstate = 4 ; break ; /* transfer */ case 8 : crlf(); for ( i = 0 ; i < MAX_PCNT ; i++ ) { out_gdat( *(lbuf+i) ); /* new line */ if ( (i % 8) == 15 ) { crlf(); } } lcnt++ ; /* next state */ gstate = 3 ; break ; /* return first state */ case 9 : gstate = 0 ; break ; default : break ; } } void set_rclk(UBYTE x) { if ( x ) { GP0DAT |= (1 << S_RCLK) ; } else { GP0DAT &= ~(1 << S_RCLK) ; } } void set_oe(UBYTE x) { if ( x ) { GP0DAT &= ~(1 << S_OE) ; } else { GP0DAT |= (1 << S_OE) ; } } void set_rrst(UBYTE x) { if ( x ) { GP0DAT &= ~(1 << S_RRST) ; } else { GP0DAT |= (1 << S_RRST) ; } } void out_gdat(UWORD x) { SBYTE i ; UBYTE cdat[4] ; UWORD tmp ; /* */ tmp = x ; /* separate */ for ( i = 3 ; i > -1 ; i-- ) { /* get nibble */ *(cdat+i) = tmp & MASK0F ; /* shift */ tmp >>= 4 ; } /* loop */ for ( i = 0 ; i < 4 ; i++ ) { /* conversion */ *(cdat+i) = asc_hex[ *(cdat+i) ] ; /* send SCI */ rs_putchar( *(cdat+i) ); } rs_putchar(' '); } void init_pla(void) { /* initialize element */ PLAELM0 = 0x549 ; /* NOT B and A */ PLAELM4 = 0x34 ; /* select GP1.4 , B */ PLAELM5 = 0x458 ; /* select E4 , A */ /* select clock */ PLACLK = 0x05 ; /* Timer1 overflow */ /* interrupt enable */ PLAIRQ = 0x10 ; /* element 0 H */ }  シリアルインタフェースでPCと接続するので  通信プロトコルは、以下としてあります。  1文字コマンドを用意し、PC上の端末ソフトから  コマンド文字をタイプすることで、FIFO付カメラ  を制御します。  1文字コマンドは、以下としました。  FIFOなしカメラのSCCBインタフェースによる  レジスタ値ライトとリードができるテスト用  ファームウェアをARM用に書いてあります。 #include <ADuC7026.h> #include "stdio.h" #define OFF 0 #define ON OFF+1 /* data definitions */ typedef unsigned char UBYTE ; typedef signed char SBYTE ; typedef unsigned short UWORD ; typedef signed short SWORD ; typedef unsigned long ULONG ; typedef signed long SLONG ; void IRQ_Handler(void) __irq; void init_usr(void); #define MASKFF 0xFF #define MASK0F 0x0F #define MASK80 0x80 #define MASK40 0x40 #define MASK20 0x20 #define MASK10 0x10 #define MASK08 0x08 #define MASK04 0x04 #define MASK02 0x02 #define MASK01 0x01 #define MASKF0 0xF0 #define SCCB_ID_WR 0x42 #define SCCB_ID_RD 0x43 ULONG timcnt ; void rs_putchar(UBYTE x); void crlf(void); void rs_puts(UBYTE *x); void show_help(void); UBYTE conv_asc(UBYTE x); void init_cam(UBYTE x); void put_sccb_start(void); void put_sccb_stop(void); void put_sccb_data(UBYTE x); UBYTE get_sccb_data(void); void send_cam(UBYTE adr,UBYTE dat); UBYTE load_cam(UBYTE adr); void show_reg(void); void delay_ms(UWORD x); void delay_100us(UWORD x); /* global variables */ volatile UBYTE uflag ; volatile UBYTE sbuf[8] ; volatile UBYTE sindex ; volatile UBYTE cmd ; #define REG_GAIN 0x00 /* Gain lower 8 bits (rest in vref) */ #define REG_BLUE 0x01 /* blue gain */ #define REG_RED 0x02 /* red gain */ #define REG_VREF 0x03 /* Pieces of GAIN, VSTART, VSTOP */ #define REG_COM1 0x04 /* Control 1 */ #define COM1_CCIR656 0x40 /* CCIR656 enable */ #define REG_BAVE 0x05 /* U/B Average level */ #define REG_GbAVE 0x06 /* Y/Gb Average level */ #define REG_AECHH 0x07 /* AEC MS 5 bits */ #define REG_RAVE 0x08 /* V/R Average level */ #define REG_COM2 0x09 /* Control 2 */ #define COM2_SSLEEP 0x10 /* Soft sleep mode */ #define REG_PID 0x0a /* Product ID MSB */ #define REG_VER 0x0b /* Product ID LSB */ #define REG_COM3 0x0c /* Control 3 */ #define COM3_SWAP 0x40 /* Byte swap */ #define COM3_SCALEEN 0x08 /* Enable scaling */ #define COM3_DCWEN 0x04 /* Enable downsamp/crop/window */ #define REG_COM4 0x0d /* Control 4 */ #define REG_COM5 0x0e /* All "reserved" */ #define REG_COM6 0x0f /* Control 6 */ #define REG_AECH 0x10 /* More bits of AEC value */ #define REG_CLKRC 0x11 /* Clocl control */ #define CLK_EXT 0x40 /* Use external clock directly */ #define CLK_SCALE 0x3f /* Mask for internal clock scale */ #define REG_COM7 0x12 /* Control 7 */ #define COM7_RESET 0x80 /* Register reset */ #define COM7_FMT_MASK 0x38 #define COM7_FMT_VGA 0x00 #define COM7_FMT_CIF 0x20 /* CIF format */ #define COM7_FMT_QVGA 0x10 /* QVGA format */ #define COM7_FMT_QCIF 0x08 /* QCIF format */ #define COM7_RGB 0x04 /* bits 0 and 2 - RGB format */ #define COM7_YUV 0x00 /* YUV */ #define COM7_BAYER 0x01 /* Bayer format */ #define COM7_PBAYER 0x05 /* "Processed bayer" */ #define REG_COM8 0x13 /* Control 8 */ #define COM8_FASTAEC 0x80 /* Enable fast AGC/AEC */ #define COM8_AECSTEP 0x40 /* Unlimited AEC step size */ #define COM8_BFILT 0x20 /* Band filter enable */ #define COM8_AGC 0x04 /* Auto gain enable */ #define COM8_AWB 0x02 /* White balance enable */ #define COM8_AEC 0x01 /* Auto exposure enable */ #define REG_COM9 0x14 /* Control 9 - gain ceiling */ #define REG_COM10 0x15 /* Control 10 */ #define COM10_HSYNC 0x40 /* HSYNC instead of HREF */ #define COM10_PCLK_HB 0x20 /* Suppress PCLK on horiz blank */ #define COM10_HREF_REV 0x08 /* Reverse HREF */ #define COM10_VS_LEAD 0x04 /* VSYNC on clock leading edge */ #define COM10_VS_NEG 0x02 /* VSYNC negative */ #define COM10_HS_NEG 0x01 /* HSYNC negative */ #define REG_HSTART 0x17 /* Horiz start high bits */ #define REG_HSTOP 0x18 /* Horiz stop high bits */ #define REG_VSTART 0x19 /* Vert start high bits */ #define REG_VSTOP 0x1a /* Vert stop high bits */ #define REG_PSHFT 0x1b /* Pixel delay after HREF */ #define REG_MIDH 0x1c /* Manuf. ID high */ #define REG_MIDL 0x1d /* Manuf. ID low */ #define REG_MVFP 0x1e /* Mirror / vflip */ #define MVFP_MIRROR 0x20 /* Mirror image */ #define MVFP_FLIP 0x10 /* Vertical flip */ #define REG_AEW 0x24 /* AGC upper limit */ #define REG_AEB 0x25 /* AGC lower limit */ #define REG_VPT 0x26 /* AGC/AEC fast mode op region */ #define REG_HSYST 0x30 /* HSYNC rising edge delay */ #define REG_HSYEN 0x31 /* HSYNC falling edge delay */ #define REG_HREF 0x32 /* HREF pieces */ #define REG_TSLB 0x3a /* lots of stuff */ #define TSLB_YLAST 0x04 /* UYVY or VYUY - see com13 */ #define REG_COM11 0x3b /* Control 11 */ #define COM11_NIGHT 0x80 /* NIght mode enable */ #define COM11_NMFR 0x60 /* Two bit NM frame rate */ #define COM11_HZAUTO 0x10 /* Auto detect 50/60 Hz */ #define COM11_50HZ 0x08 /* Manual 50Hz select */ #define COM11_EXP 0x02 #define REG_COM12 0x3c /* Control 12 */ #define COM12_HREF 0x80 /* HREF always */ #define REG_COM13 0x3d /* Control 13 */ #define COM13_GAMMA 0x80 /* Gamma enable */ #define COM13_UVSAT 0x40 /* UV saturation auto adjustment */ #define COM13_UVSWAP 0x01 /* V before U - w/TSLB */ #define REG_COM14 0x3e /* Control 14 */ #define COM14_DCWEN 0x10 /* DCW/PCLK-scale enable */ #define REG_EDGE 0x3f /* Edge enhancement factor */ #define REG_COM15 0x40 /* Control 15 */ #define COM15_R10F0 0x00 /* Data range 10 to F0 */ #define COM15_R01FE 0x80 /* 01 to FE */ #define COM15_R00FF 0xc0 /* 00 to FF */ #define COM15_RGB565 0x10 /* RGB565 output */ #define COM15_RGB555 0x30 /* RGB555 output */ #define REG_COM16 0x41 /* Control 16 */ #define COM16_AWBGAIN 0x08 /* AWB gain enable */ #define REG_COM17 0x42 /* Control 17 */ #define COM17_AECWIN 0xc0 /* AEC window - must match COM4 */ #define COM17_CBAR 0x08 /* DSP Color bar */ #define REG_RGB444 0x8c /* RGB 444 control */ #define R444_ENABLE 0x02 /* Turn on RGB444, overrides 5x5 */ #define R444_RGBX 0x01 /* Empty nibble at end */ #define REG_HAECC1 0x9f /* Hist AEC/AGC control 1 */ #define REG_HAECC2 0xa0 /* Hist AEC/AGC control 2 */ #define REG_HAECC7 0xaa /* Hist AEC/AGC control 7 */ int main(void) { /* initialize user */ init_usr(); /* show message */ rs_puts("Hello"); crlf(); /* endless loop */ while(ON) { /* command interrpreter */ if ( uflag == ON ) { /* clear flag */ uflag = OFF ; /* judge */ cmd = *(sbuf+0) ; if ( cmd == '?' ) { show_help(); } /* initialize CAMERA */ if ( cmd == 'I' ) { crlf(); init_cam( *(sbuf+1)-'0' ) ; } /* show register values */ if ( cmd == 'V' ) { crlf(); show_reg() ; } } } /* dummy return */ return (0); } void IRQ_Handler(void) __irq { volatile UBYTE ch ; /* judge UART receive interruption */ if ( (IRQSTA & UART_BIT) == UART_BIT ) { /* judge */ if ( COMSTA0 & 1 ) { /* clear flag */ ch = COMRX ; *(sbuf+sindex) = ch ; sindex++ ; if ( ch == '\r' ) { sindex = 0 ; uflag = ON ; } } } /* judge timer0 interruption (100us) */ if ( (IRQSTA & RTOS_TIMER_BIT) == RTOS_TIMER_BIT ) { /* clear timer0 interrupt flag */ T0CLRI = 0xff ; /* increment */ timcnt++ ; /* judge */ if ( (timcnt & 0xfff) == 0 ) { GP4DAT ^= (1 << 23); } } } void init_usr(void) { /* select clock 10.44MHz initialized in start up routine */ PLLKEY1 = 0xaa ; PLLCON = 0x01 ; PLLKEY2 = 0x55 ; /* power control initialized in start up routine */ /* clear flag */ uflag = OFF ; /* clear counter */ timcnt = 0 ; sindex = 0 ; /* initialize UART */ { /* set baud rate 19200 bps CD = 2 */ COMCON0 = 0x80 ; /* select COMDIV1 and COMDIV0 */ COMDIV0 = 0x11 ; COMDIV1 = 0x00 ; /* set conditions */ COMCON0 = 0x03 ; /* select COMRX and COMTX , 8bit data , 1 stop bit , no parity */ /* enable interrupt */ COMIEN0 = 0x01 ; /* ERBFI */ } /* P0 */ { /* */ GP0DAT = 0xFF3C0000 ; } /* P1 */ { /* use UART */ GP1CON = 0x00000011 ; /* */ GP1DAT = 0xFE000000 ; } /* P2 */ { /* 0,1 output , rest inputs */ GP2DAT = 0x0300000 ; } /* P3 */ { GP3DAT = 0xffff0000 ; } /* P4 */ { GP4DAT = 0xff000000 ; } /* initialize timer 0 (1kHz) */ { T0LD = 1044 ; /* (10.44MHz / 1) / 10kHz */ T0CON = 0xc0 ; /* enable , cyclic , 1/1 */ } /* enable timer 0 and UART interrupt */ IRQEN = RTOS_TIMER_BIT | UART_BIT ; } /* UART putchar */ void rs_putchar(UBYTE x) { /* ? transmmit buffer empty */ while( (COMSTA0 & 0x40) == 0 ) ; /* set value */ COMTX = x ; } /* carriage return and line feed */ void crlf(void) { rs_putchar('\r'); rs_putchar('\n'); } /* UART puts */ void rs_puts(UBYTE *x) { while ( *x != '\0' ) { rs_putchar( *x ) ; x++ ; } } /* show help */ void show_help(void) { rs_puts("? help") ; crlf(); rs_puts("I initialize camera"); crlf(); rs_puts("V show registers") ; crlf(); } void put_sccb_start(void) { /* both high level */ GP2DAT = 0x03030000 ; delay_100us(1); /* 0 -> SDA */ GP2DAT = 0x03020000 ; delay_100us(1); /* 0 -> SCL */ GP2DAT = 0x03000000 ; delay_100us(1); } void put_sccb_stop(void) { /* 0 -> SDA , 0 -> SCL */ GP2DAT = 0x03000000 ; delay_100us(1); /* 0 -> SDA , 1 -> SCL */ GP2DAT = 0x03020000 ; delay_100us(1); /* 1 -> SDA */ GP2DAT = 0x03030000 ; delay_100us(1); } void put_sccb_data(UBYTE x) { volatile UBYTE tmp ; volatile UBYTE i ; /* 0 -> SCL , 0 -> SDA */ GP2DAT = 0x03000000 ; /* transfer data with write code */ for ( i = 0 ; i < 8 ; i++ ) { /* send bit datum */ GP2DAT = 0x03000000 ; if ( x & MASK80 ) { GP2DAT = 0x03010000 ; } /* dummy */ tmp = x ; /* 1 -> SCL */ GP2DAT |= 0x00020000 ; delay_100us(1); /* shift */ x <<= 1 ; /* 0 -> SCL */ GP2DAT &= 0xfffdffff ; delay_100us(1); /* data low */ GP2DAT &= 0xfffcffff ; } /* change input */ GP2DAT &= 0xfeffffff ; delay_100us(1) ; /* 1 -> SCL */ GP2DAT |= 0x00020000 ; delay_100us(1) ; /* get acknowledge */ tmp = OFF ; if ( GP2DAT & MASK01 ) { tmp = ON ; } /* 0 -> SCL */ GP2DAT &= 0xfffdffff ; delay_100us(1) ; /* change output */ GP2DAT |= 0x03000000 ; /* delay */ delay_100us(1); } UBYTE get_sccb_data(void) { volatile UBYTE tmp ; volatile UBYTE i ; /* change inupt and 0 -> SCL */ GP2DAT = 0x02000000 ; /* default */ tmp = 0 ; /* loop */ for ( i = 0 ; i < 8 ; i++ ) { /* shift */ tmp <<= 1 ; /* 1 -> SCL */ GP2DAT |= 0x00020000 ; delay_100us(1) ; /* get bit datum */ if ( GP2DAT & MASK01 ) { tmp |= ON ; } /* 0 -> SCL */ GP2DAT &= 0xfffdffff ; delay_100us(1) ; } /* 1 -> SCL */ GP2DAT |= 0x00020000 ; delay_100us(1) ; /* dummy shift */ i <<= 1 ; /* 0 -> SCL */ GP2DAT &= 0xfffdffff ; delay_100us(1) ; /* change output */ GP2DAT = 0x03000000 ; return tmp ; } void send_cam(UBYTE adr,UBYTE dat) { /* start conditon */ put_sccb_start() ; /* send ID */ put_sccb_data(SCCB_ID_WR); /* send address */ put_sccb_data(adr); /* send parameter */ put_sccb_data(dat); /* stop condition */ put_sccb_stop(); } UBYTE load_cam(UBYTE adr) { UBYTE result ; /* default */ result = 0 ; /* start conditon */ put_sccb_start() ; /* send ID */ put_sccb_data(SCCB_ID_WR); /* send address */ put_sccb_data(adr); /* stop condition */ put_sccb_stop(); /* start conditon */ put_sccb_start() ; /* send ID */ put_sccb_data(SCCB_ID_RD); /* get 1 byte */ result = get_sccb_data(); /* stop condition */ put_sccb_stop(); return result ; } void show_reg(void) { volatile UWORD i ; volatile UBYTE tmp ; volatile UBYTE msg[2] ; /* horizontal ruler */ { for ( i = 0 ; i < 4 ; i++ ) { rs_putchar(' ') ; } /* 0 -> F */ for ( i = 0 ; i < 16 ; i++ ) { rs_putchar('+'); rs_putchar( conv_asc( i ) ) ; rs_putchar(' '); } /* new line */ crlf(); } /* show */ for ( i = 0 ; i < 208 ; i++ ) { /* vertical ruler */ if ( (i % 16) == 0 ) { rs_putchar('+'); rs_putchar( conv_asc( i >> 4 ) ) ; rs_putchar('0'); rs_putchar(' '); } /* judge */ if ( i > 202 ) { rs_putchar( '0' ) ; rs_putchar( '0' ) ; } else { /* get register data */ tmp = load_cam((UBYTE)i); /* separate */ *(msg+0) = ((tmp >> 4) & MASK0F) ; *(msg+1) = (tmp & MASK0F) ; /* show */ rs_putchar( conv_asc(*(msg+0)) ) ; rs_putchar( conv_asc(*(msg+1)) ) ; } /* add space */ rs_putchar(' '); /* new line */ tmp = i & MASK0F ; if ( tmp == 15 ) { crlf(); } } } void delay_100us(UWORD x) { ULONG last ; /* calculate */ last = timcnt + x ; /* wait */ while ( timcnt < last ) ; } void delay_ms(UWORD x) { ULONG last ; /* calculate */ last = timcnt + 10 * x ; /* wait */ while ( timcnt < last ) ; } UBYTE conv_asc(UBYTE x) { UBYTE result ; /* default */ result = '0' ; /* judge */ if ( x < 10 ) { result = x + '0' ; } if ( 9 < x && x < 16 ) { result = x - 10 + 'A' ; } return result ; } void init_cam(UBYTE x) { /* reset camera */ send_cam(REG_COM7,COM7_RESET); rs_puts("Complete reset"); crlf(); /* delay 200ms */ delay_ms(200); send_cam(REG_COM7,0x00); //send_cam(REG_RGB444,R444_ENABLE | R444_RGBX); //0x8c:RGB 444 control send_cam(REG_COM1,0x40); //0x04:COM1(CCIR656,AEC) //0) //0x40) send_cam(REG_COM15,COM15_R01FE|COM15_RGB565); //0x40:COM15 send_cam(REG_COM9, 0x38); //0x14:COM9=max AGC gain ceiling, Freeze AGC/AEC //c(0x3d,0xc3) //(REG_COM13, COM13_GAMMA|COM13_UVSAT|0x2) //0x3d:COM13 send_cam(REG_HAECC7,0x94); //0xaa:Hist AEC/AGC control 7 c(0xAA,0x94) //AEC algorithm send_cam(REG_TSLB,0x04); //0x3a:Neg,UVval,YUYV,window TSLB_YLAST) //0x04) //0x0C) //0x80) //0x00) //0x04) send_cam(0x20,0x0f); //ADCCTR0, A/D range&ref, mu0102 send_cam(REG_COM8,0x9a); //mu0103 send_cam(0x10,0x01); //mu0103 if ( x ) { /* CLKRC divide /4 -> 5MHz */ send_cam(0x11,0x83); } /* COM1 */ send_cam(0x3b,0x0a); /* disable night mode */ /* TSLB 3 bit => select YUV format */ send_cam(0x3a,0x05); /* COM7 2 bit , 0 bit => select YUV format */ send_cam(REG_COM7,0x00); /* COM15 */ send_cam(0x40,0xd0); /* HSTART */ send_cam(0x17,0x16); /* HSTOP */ send_cam(0x18,0x04); /* HREF */ send_cam(0x32,0x24); /* VSTRT */ send_cam(0x19,0x02); /* VSTOP */ send_cam(0x1a,0x7a); /* VREF */ send_cam(0x03,0x0a); /* COM10 */ send_cam(0x15,0x02); /* COM3 */ send_cam(0x0c,0x04); /* COM4 */ send_cam(0x3e,0x1a); /* SCALING_DCWCTR */ send_cam(0x72,0x22); /* SCALING_PCLK_DIV */ send_cam(0x73,0xf2); /* DM_LNL */ send_cam(0x92,0x00); /* DM_LNH */ send_cam(0x93,0x00); /* decrease frame rate put dummy lines 510 (0x01FE) */ if ( x ) { rs_puts("insert dummy line"); crlf(); /* DM_LNL */ send_cam(0x92,0xFE); /* DM_LNH */ send_cam(0x93,0x01); } rs_puts("Exit initialize"); crlf(); }  シリアルインタフェースを使い、SCCBインタ  フェースによるカメラの動作パラメータを変更  してみると、次のようになりました。  設定変更により、ダミーラインを入れて  VSYNCの周波数を下げることができました。 1文字コマンドは、以下としました。
目次

inserted by FC2 system