目次
前
次
カメラ画像処理
カメラとして、OV7670利用を考えていました。
自分の得意分野であるFPGA/CPLD内部回路開発で
低機能のプロセッサを設計したことから、OV7670
の利用をひかえることに。
MCR_VCでは、カメラ互換センサーを利用することが
前提なので過去のmugenで使ったBacCodeScanner(BCS)
をセンサーにすると方針転換です。
BCSは、LED光をコース面に投射するので利用を
考えていた高輝度LEDによる大電力消費を抑える
こともできます。
BCSはバーコードの縞模様を判断するほどの解像度
をもつので、MCRのコース面程度の判定は朝飯前。
BCSのデータシートから、どのようにコース面の
情報を入力し、センサーデータとして抽出する
かを考えました。
データシートにあったタイミングチャートを眺めて
FPGA内部に2048ビットのデータ入力を考えます。
タイミングチャートから、次のことが理解できました。
- SHで、CCDによる撮影開始
- SHは、11.05msの周期をもつ
- クロックφ1、φ2で、CCDから情報出力
- クロックφ1、φ2の周波数は約100kHz
- 画像データは2048ビット
- 画像データの前後に、不要データが入っている
BCSはバーコードリーダに入っていたので、リーダの
内蔵マイコンで画像データを取得していました。
そのインタフェースは6ピンで次のように接続。
- Vcc
- LED_CON
- (no connection)
- Vout
- enable <-> P50
- GND
タイミングチャートからVoutが、BCSの画像データ
出力とわかったので、FPGAに取り込むために次の
ように接続することに。
- Vcc
- SH(START)
- φ1(CLKA)
- φ2(CLKB)
- Vout(CDAT)
- GND
START、CLKA、CLKBはrising_edgeでBCS内部の
CCDの動作タイミングを決めています。
FPGA内部とBCSを同期させるため、シフトレジスタ
を利用した同期信号を生成します。
-- BCS synchronizer
process (nRESET,iBCLK)
begin
if ( nRESET = '0' ) then
iSTART_SFT <= "000" ;
iCLKA_SFT <= "00" ;
iCLKB_SFT <= "00" ;
elsif rising_edge(iBCLK) then
iSTART_SFT <= iSTART_SFT(1 downto 0) & iSTART ;
iCLKA_SFT <= iCLKA_SFT(0) & iCLKA ;
iCLKB_SFT <= iCLKB_SFT(0) & iCLKB ;
end if ;
end process ;
-- BCS generate trigger signal
iSTART_TRG <= '1' when (iSTART_SFT = "011" or iSTART_SFT = "001") else '0' ;
iCLKA_TRG <= '1' when ( iCLKA_SFT = "01" ) else '0' ;
iCLKB_TRG <= '1' when ( iCLKB_SFT = "01" ) else '0' ;
同期信号を利用し、内部シーケンサで画像データを
取得に必要なカウンタを動かします。
process (nRESET,iMCLK)
begin
if ( nRESET = '0' ) then
iSTATE <= 0 ;
iCNT <= 0 ;
iREGOUT <= (others => '0') ;
elsif rising_edge(iMCLK) then
case iSTATE is
-- wait trigger
when 0 => if ( iSTART_TRG = '1' ) then
iSTATE <= 1 ;
-- clear counter
iCNT <= 0 ;
-- get information
iREGOUT <= iREG ;
else
iSTATE <= 0 ;
end if ;
-- judge last count
when 1 => if ( iCNT = 2048 ) then
iSTATE <= 4 ;
else
iSTATE <= 2 ;
end if ;
-- get data
when 2 => if ( iSCLKA_TRG = '1' ) then
iSTATE <= 3 ;
iCNT <= iCNT + 1 ;
else
iSTATE <= 2 ;
end if ;
-- get data
when 3 => if ( iSCLKB_TRG = '1' ) then
iSTATE <= 1 ;
iCNT <= iCNT + 1 ;
else
iSTATE <= 3 ;
end if ;
-- return first state
when 4 => iSTATE <= 0 ;
-- default
when others =>
iSTATE <= 0 ;
end case ;
end if ;
end process ;
カウンタの値を利用して、ビットデータをシフト
レジスタに入れ、最後にバッファに転送して
他のシーケンサが使えるようにします。
-- BCS data gatter
process (nRESET,iBCLK)
begin
if ( nRESET = '0' ) then
iBCSREGX <= (others => '0') ;
iBCSREG <= (others => '0') ;
elsif rising_edge(iBCLK) then
case iBCSCNT is
when TCNT0 => iBCSREGX <= iBCSREGX(6 downto 0) & iCDAT ;
when TCNT1 => iBCSREGX <= iBCSREGX(6 downto 0) & iCDAT ;
when TCNT2 => iBCSREGX <= iBCSREGX(6 downto 0) & iCDAT ;
when TCNT3 => iBCSREGX <= iBCSREGX(6 downto 0) & iCDAT ;
when TCNT4 => iBCSREGX <= iBCSREGX(6 downto 0) & iCDAT ;
when TCNT5 => iBCSREGX <= iBCSREGX(6 downto 0) & iCDAT ;
when TCNT6 => iBCSREGX <= iBCSREGX(6 downto 0) & iCDAT ;
when TCNT7 => iBCSREGX <= iBCSREGX(6 downto 0) & iCDAT ;
when TCNT8 => iBCSREG <= iBCSREGX ;
when others => NULL ;
end case ;
end if ;
end process ;
同期回路、シーケンサの関係は、次のブロック図で
表現できます。
同期回路、シーケンサを動かすには、クロックが必要。
オシロスコープと周波数カウンタを利用して、3信号の
周波数を測定すると、以下となっていました。
- SH(START) 100Hz
- φ1(CLKA) 100kHz(φ2とは逆位相)
- φ2(CLKB) 100kHz(φ1とは逆位相)
2つのクロックが100kHzなので、シーケンサは
その4倍以上のクロックで動かせば、問題なく
画像データを取得できます。
今回は、500kHzとして同期回路、シーケンサを
動かしました。
BCSは、約10msごとに画像データを更新するので
それにあわせ、サーボモータ、DCモータのパラメータ
を決定していきます。
BCSは、アナログデータを出力するので、デジタル化
するために、若干の回路が必要でした。
(波形写真は、知人のKさんから貰いました)
オシロスコープで見た波形から、BCSはオフセットを
含んだBarCodeの縞を出力しています。オフセットを
とらないと、デジタル回路で処理できません。
ここでアナログ回路の知識が役に立ちました。
オフセットがない交流信号を見たいときには
キャパシタを入れ、直流分を除去します。
回路は簡単で、キャパシタをつけておき、更に
接続するデジタル回路の電圧振幅が、デジタル
回路の電源電圧を超えないよう、抵抗を挿入。
キャパシタの静電容量、抵抗値は厳密に計算した
値ではなく、たまたま手元にあった部品を使った
だけですが、問題なく動作しました。
BCSの画像データ出力は、OPアンプU4の7ピンから
取り出しています。
BCSは、秋月電子から入手したものを分解しています。
赤LEDは常時点灯するように、6ピンに接続されている
LED点灯制御信号を'H'固定で、制御用トランジスタを
常にONさせます。
- Vcc
- LED_CON 'H'固定
- (no connection)
- Vout
- enable <-> P50
- GND
実験用に作成したVHDLコードは、以下です。
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity bcsx is
port (
-- system
nRESET : in std_logic ;
CLOCK : in std_logic ; -- 10MHz
-- input
START : in std_logic ; -- start trigger
SCLKA : in std_logic ; -- 100kHz shift clock
SCLKB : in std_logic ; -- 100kHz shift clock
SDAT : in std_logic ; -- data
-- monitor
MCLK : out std_logic ; -- internal clock monitor 500kHz
MSTART : out std_logic ; -- start trigger monitor
MSCLKA : out std_logic ; -- 100kHz shift clock monitor
MSCLKB : out std_logic ; -- 100kHz shift clock monitor
MSDAT : out std_logic ; -- data monitor
-- output
LOUT : out std_logic_vector(7 downto 0) --;
) ;
end bcsx;
architecture Behavioral of bcsx is
-- constant values
constant TCNT0 : integer := 160 ;
constant TCNT1 : integer := 416 ;
constant TCNT2 : integer := 672 ;
constant TCNT3 : integer := 928 ;
constant TCNT4 : integer := 1184 ;
constant TCNT5 : integer := 1440 ;
constant TCNT6 : integer := 1696 ;
constant TCNT7 : integer := 1952 ;
constant TCNT8 : integer := 2000 ;
-- clock divider
signal iMCNT : integer range 0 to 19 ;
signal iMCLK : std_logic ;
signal iSCNT : integer range 0 to 499 ;
signal iSCLK : std_logic ;
-- state machine
signal iSTATE : integer range 0 to 4 ;
-- trigger
signal iSTART_SFT : std_logic_vector(2 downto 0);
signal iSTART_TRG : std_logic ;
signal iSCLKA_SFT : std_logic_vector(1 downto 0);
signal iSCLKA_TRG : std_logic ;
signal iSCLKB_SFT : std_logic_vector(1 downto 0);
signal iSCLKB_TRG : std_logic ;
-- counter
signal iCNT : integer range 0 to 2048 ;
-- shift register
signal iSFTREG : std_logic_vector(7 downto 0);
-- data register
signal iREG : std_logic_vector(7 downto 0);
signal iSTART : std_logic ;
signal iSCLKA : std_logic ;
signal iSCLKB : std_logic ;
signal iSDAT : std_logic ;
-- show data
signal iREGOUT : std_logic_vector(7 downto 0);
begin
-- output
LOUT <= not iREGOUT ;
-- monitor
MCLK <= iMCLK ;
MSTART <= iSTART ;
MSCLKA <= iSCLKA ;
MSCLKB <= iSCLKB ;
MSDAT <= iSDAT ;
-- input
iSTART <= START ;
iSCLKA <= SCLKA ;
iSCLKB <= SCLKB ;
iSDAT <= SDAT ;
-- clock divider (generate 500kHz)
process (nRESET,CLOCK)
begin
if ( nRESET = '0' ) then
iMCNT <= 0 ;
elsif rising_edge(CLOCK) then
if ( iMCNT = 19 ) then
iMCNT <= 0 ;
else
iMCNT <= iMCNT + 1 ;
end if ;
end if ;
end process ;
iMCLK <= '1' when ( iMCNT = 0 ) else '0' ;
-- trigger sampling with 500kHz
process (nRESET,iMCLK)
begin
if ( nRESET = '0' ) then
iSTART_SFT <= "000" ;
iSCLKA_SFT <= "00" ;
iSCLKB_SFT <= "00" ;
elsif rising_edge(iMCLK) then
iSTART_SFT <= iSTART_SFT(1 downto 0) & iSTART ;
iSCLKA_SFT <= iSCLKA_SFT(0) & iSCLKA ;
iSCLKB_SFT <= iSCLKB_SFT(0) & iSCLKB ;
end if ;
end process ;
iSTART_TRG <= '1' when ( iSTART_SFT = "011" or iSTART_SFT = "001" ) else '0' ;
iSCLKA_TRG <= '1' when ( iSCLKA_SFT = "01" ) else '0' ;
iSCLKB_TRG <= '1' when ( iSCLKB_SFT = "01" ) else '0' ;
-- state machine
process (nRESET,iMCLK)
begin
if ( nRESET = '0' ) then
iSTATE <= 0 ;
iCNT <= 0 ;
elsif rising_edge(iMCLK) then
case iSTATE is
-- wait trigger
when 0 => if ( iSTART_TRG = '1' ) then
iSTATE <= 1 ;
-- clear counter
iCNT <= 0 ;
-- clear counter
iREGOUT <= iREG ;
else
iSTATE <= 0 ;
end if ;
-- judge last count
when 1 => if ( iCNT = 2048 ) then
iSTATE <= 4 ;
else
iSTATE <= 2 ;
end if ;
-- get data
when 2 => if ( iSCLKA_TRG = '1' ) then
iSTATE <= 3 ;
iCNT <= iCNT + 1 ;
else
iSTATE <= 2 ;
end if ;
-- get data
when 3 => if ( iSCLKB_TRG = '1' ) then
iSTATE <= 1 ;
iCNT <= iCNT + 1 ;
else
iSTATE <= 3 ;
end if ;
-- return first state
when 4 => iSTATE <= 0 ;
-- default
when others =>
iSTATE <= 0 ;
end case ;
end if ;
end process ;
-- get data
process (nRESET,iMCLK)
begin
if ( nRESET = '0' ) then
iSFTREG <= (others => '0') ;
iREG <= (others => '0') ;
elsif rising_edge(iMCLK) then
case iCNT is
when TCNT0 => iSFTREG <= iSFTREG(6 downto 0) & iSDAT ;
when TCNT1 => iSFTREG <= iSFTREG(6 downto 0) & iSDAT ;
when TCNT2 => iSFTREG <= iSFTREG(6 downto 0) & iSDAT ;
when TCNT3 => iSFTREG <= iSFTREG(6 downto 0) & iSDAT ;
when TCNT4 => iSFTREG <= iSFTREG(6 downto 0) & iSDAT ;
when TCNT5 => iSFTREG <= iSFTREG(6 downto 0) & iSDAT ;
when TCNT6 => iSFTREG <= iSFTREG(6 downto 0) & iSDAT ;
when TCNT7 => iSFTREG <= iSFTREG(6 downto 0) & iSDAT ;
when TCNT8 => iREG <= iSFTREG ;
when others => NULL ;
end case ;
end if ;
end process ;
end Behavioral;
秋月電子で販売されていたLatticeのCPLDでテスト。
ピンアサインは、以下。
BLOCK RESETPATHS;
BLOCK ASYNCPATHS;
# I/O
BANK 0 VCCIO 3.3 V;
BANK 1 VCCIO 3.3 V;
BANK 2 VCCIO 3.3 V;
BANK 3 VCCIO 3.3 V;
# select 3.3V interface
IOBUF ALLPORTS IO_TYPE=LVCMOS33 ;
# system
LOCATE COMP "nRESET" SITE "70" ;
LOCATE COMP "CLOCK" SITE "56" ; # 10MHz
# LED output
LOCATE COMP "MSDAT" SITE "71" ;
# BCS monitor
LOCATE COMP "LOUT[0]" SITE "97" ;
LOCATE COMP "LOUT[1]" SITE "98" ;
LOCATE COMP "LOUT[2]" SITE "99" ;
LOCATE COMP "LOUT[3]" SITE "100" ;
LOCATE COMP "LOUT[4]" SITE "104" ;
LOCATE COMP "LOUT[5]" SITE "105" ;
LOCATE COMP "LOUT[6]" SITE "106" ;
LOCATE COMP "LOUT[7]" SITE "107" ;
# CLOCK monitor
LOCATE COMP "MSCLKB" SITE "60" ; # SCLKB
LOCATE COMP "MSCLKA" SITE "58" ; # SCLKA
# CCD
LOCATE COMP "SDAT" SITE "139" ; # data
LOCATE COMP "START" SITE "140" ; # START trigger
LOCATE COMP "SCLKB" SITE "141" ; # shift clock 100kHz
LOCATE COMP "SCLKA" SITE "142" ; # shift clock 100kHz
# monitor outputs
LOCATE COMP "MCLK" SITE "61" ;
LOCATE COMP "MSTART" SITE "65" ;
START、CLKA、CLKBは、5V近くまでスイングする
ため、3.3V動作CPLDには、電圧を抵抗による分圧
で与えました。
大会が終わり、復路の飛行機の中で考えると
3.3Vにプルアップして対応すればよいと思い
つきました。
目次
前
次