カメラインタフェース設計
入手したカメラをマイコンあるいはFPGAに接続
するためのインタフェース回路を設計します。
利用するカメラOV7670の16ピンコネクタは
次のピン割当てになっています。
- Vcc
- GND
- SIC(SCL)
- SID(SDA)
- VSYNC
- HREF
- PCLK
- CLK
- D7
- D6
- D5
- D4
- D3
- D2
- D1
- D0
カメラの動作クロックはCLKに与えればよいので
手持ちのセラミック振動子20MHzを利用した回路
を挟むことにしました。回路図、配線図は以下。
最低周波数は10MHzですが、10MHzのセラミック振動子
を使ったとき、SCCBインタフェースがうまくいかずに
20MHzに交換しました。
マイコン、FPGAを3.3Vで動かすので、電源を
コネクタから貰う仕様にしました。
制御用の10ピンコネクタの割当ては、以下です。
- Vcc
- (no connection)
- (no connection)
- (no connection)
- PCLK
- HREF
- VSYNC
- SIC(SCL)
- SID(SDA)
- GND
データ用の10ピンコネクタの割当ては、次の
ようにしました。
- Vcc
- D7
- D6
- D5
- D4
- D3
- D2
- D1
- D0
- 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付のカメラボードのインタフェース基板も
作成しました。
ピンアサインは、以下でした。
- Vcc
- GND
- SIC
- SID
- VS(VSYNC)
- HREF
- WE
- XCLK
- RRST
- OE
- RCLK
- GND
- D0
- D1
- D2
- D3
- D4
- D5
- D6
- D7
これらの信号をFPGA、マイコンに接続するため
接続図を作成してから、半田付け。
HREFは、FIFOに対して画像データを書込む時に
有効データであることを示すだけなので、信号
としてはマイコン、FPGAには接続しなくても
動作は可能です。
XCLKは、FIFOには入力されているだけで、外部
コネクタには信号として出ていません。HREFは
有効データ出力中を示すので、マイコン、FPGA
に接続することにしました。
データだけを10ピンコネクタに集めています。
- Vcc
- D7
- D6
- D5
- D4
- D3
- D2
- D1
- D0
- GND
制御関係は、他の10ピンコネクタに配置。
- Vcc
- SIC(SCL)
- SID(SDA)
- WE
- RRST
- OE
- VS(VSYNC)
- RCLK
- HREF
- 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からデータを取り出します。
- トリガー待ち
- FIFO内部出力カウンタ、データカウンタリセット
- RCLKに同期して、上位データ入力
- RCLKに同期して、下位データ入力
- 1ピクセルデータ保存とデータカウンタインクリメント
- データ取得が途中ならステート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と接続するので
通信プロトコルは、以下としてあります。
- 38400bps
- 8 bit data length
- 1 stop bit
- no parity
- no flow control
1文字コマンドを用意し、PC上の端末ソフトから
コマンド文字をタイプすることで、FIFO付カメラ
を制御します。
1文字コマンドは、以下としました。
- ? help
- I initialize Camera
- S take Shot
- G Get picture
- R check values of Registers
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文字コマンドは、以下としました。
- ? help
- I initialize Camera
- V show registers
目次
前
次