目次
前
次
BarCodeScanner処理再考
BarCodeScanner(BCS)は、4信号で路面情報を
2048ピクセルのデータとして送信してきます。
4信号は、以下。
ピクセルデータは、CLKA、CLKBの立上がりに
同期して、OSに出力されてきます。
SHは、ピクセルデータの出力開始を外部に提示します。
3.3Vで動作するFPGAでは、BCSの5Vを使った信号では
入出力ピンを破壊することがあるので、バスバッファ
を入れて対応します。
74HC14を利用したのは、ヒステリシスを持つので
信号の変化を確実にとらえるため。
FPGA側に、プルアップ抵抗を接続し、3.3Vで電圧を
クランプしています。
BCSから2048ピクセルのデータが出力されてくるので
カウンタ値で、8ピクセルだけを記憶してセンサー
データにします。
FPGAの内部にシーケンサ(ステートマシン)を用意し
他の同期化回路から出力されるトリガーで、ピクセル
データの記憶タイミングを確定します。
CLKA、CLKBの立上りエッジを利用してピクセルデータ
が出力されてくるので、CLKA、CLKBの立上りエッジを
少し遅らせて、記憶用トリガーを生成します。
記憶用トリガーは、クロックを遅延させるのでシフト
レジスタを使います。
CLKA、CLKBは、各々100kHzなので、シフトレジスタを
動かすクロックは、4倍以上にします。
MCR_VCマシンは移動メカですから、載せる基板の面積は
極力小さくしたいので、回路はFPGAの中に入れます。
FPGAの中に入れるためにVHDLで、回路を定義します。
SH、CLKA、CLKBのシフトレジスタを定義します。
SHのシフトレジスタ
クロックは、FPGA内部にあるシーケンサと同じにします。
process (nRESET,CLOCK)
begin
if ( nRESET = '0' ) then
iSH_SFT <= "00" ;
elsif rising_edge( CLOCK ) then
iSH_SFT <= iSH_SFT(0) & iSH ;
end if ;
end process ;
iSH_TRG <= '1' when ( iSH_SFT = "10" ) else '0' ;
CLKA、CLKBのシフトレジスタ
クロックは、1MHz程度にします。このクロックは
別に定義して使います。
process (nRESET,iDCK)
begin
if ( nRESET = '0' ) then
iCLKA_SFT <= "00" ;
iCLKB_SFT <= "00" ;
elsif rising_edge( iDCK ) then
iCLKA_SFT <= iCLKA_SFT(0) & iCLKA ;
iCLKB_SFT <= iCLKB_SFT(0) & iCLKB ;
end if ;
end process ;
iCLKAB <= '1' when ( iCLKA_SFT = "01" ) else
'1' when ( iCLKB_SFT = "01" ) else
'0' ;
クロックiDCKを1MHzにすると、CLKA、CLKBの立上りエッジ
から1usの遅延で、幅が1usのトリガーが生成できます。
内部シーケンサ
シーケンサは、iSH_TRGを利用して動作を開始します。
また、iCLKABを利用してピクセルデータを取込みます。
取込んだピクセルデータは、別途決めるカウント値で
8ビットのセンサーデータに仕立てます。
わかりやすい記述にすると、以下。
process (nRESET,CLOCK)
begin
if ( nRESET = '0' ) then
iSTATE <= 0 ;
iREGX <= X"00" ;
iREG <= X"00" ;
iTMP <= '0' ;
iSCNT <= 0 ;
elsif rising_edge( CLOCK ) then
case iSTATE is
-- wait trigger
when 0 => if ( iSH_TRG = '1' ) then
iSTATE <= 1 ; -- next
iREGX <= X"00" ; -- clear register
iSCNT <= 0 ; -- clear counter
else
iSTATE <= 0 ; -- stay
end if ;
-- get data
when 1 => if ( iCLKAB = '1' ) then
iSTATE <= 2 ; -- next
iTMP <= iDOS ; -- latch 1 bit
else
iSTATE <= 1 ; -- stay
end if ;
-- data counter increment
when 2 => iSTATE <= 3 ; -- next
iSCNT <= iSCNT + 1 ; -- counter increment
-- store datum to temporary register
when 3 => iSTATE <= 4 ; -- next
-- store
if ( iSCNT = S0 or
iSCNT = S1 or
iSCNT = S2 or
iSCNT = S3 or
iSCNT = S4 or
iSCNT = S5 or
iSCNT = S6 or
iSCNT = S7 ) then
iREGX <= iREGX(6 downto 0) & iTMP ;
end if ;
-- judge (? complete)
when 4 => if ( iSCNT = SLAST ) then
iSTATE <= 6 ; -- return first state
else
iSTATE <= 5 ; -- loop control
end if ;
-- loop control
when 5 => iSTATE <= 1 ;
-- return first state
when 6 => iSTATE <= 0 ;
iREG <= iREGX ; -- copy
-- default
when others =>
iSTATE <= 0 ;
end case ;
end if ;
end process ;
最終VHDLコードは、以下としました。
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity taa is
port (
-- system
nRESET : in std_logic ;
CLOCK : in std_logic ;
-- divided clock
DCLK : out std_logic ;
-- BCS
SH : in std_logic ;
CLKA : in std_logic ;
CLKB : in std_logic ;
XOS : in std_logic ;
-- monitor output
LOUT : out std_logic_vector(7 downto 0) ;
-- output
OST : out std_logic_vector(7 downto 0) --;
);
end taa ;
architecture behavioral of taa is
--
CONSTANT SLAST : integer := 2080 ;
-- sensing location
CONSTANT S0 : integer := 800 ; -- 32+1024+(-256)
CONSTANT S1 : integer := 864 ; -- 32+1024+(-192)
CONSTANT S2 : integer := 928 ; -- 32+1024+(-128)
CONSTANT S3 : integer := 992 ; -- 32+1024+( -64)
CONSTANT S4 : integer := 1120 ; -- 32+1024+( 64)
CONSTANT S5 : integer := 1184 ; -- 32+1024+( 128)
CONSTANT S6 : integer := 1248 ; -- 32+1024+( 192)
CONSTANT S7 : integer := 1312 ; -- 32+1024+( 256)
-- divided clock
signal iDCNT : std_logic_vector(1 downto 0) ;
signal iDCK : std_logic ;
-- input
signal iSH : std_logic ;
signal iCLKA : std_logic ;
signal iCLKB : std_logic ;
signal iDOS : std_logic ;
-- shift register
signal iCLKA_SFT : std_logic_vector(1 downto 0) ;
signal iCLKB_SFT : std_logic_vector(1 downto 0) ;
signal iCLKAB : std_logic ;
signal iSH_SFT : std_logic_vector(1 downto 0) ;
signal iSH_TRG : std_logic ;
-- state machine
signal iSTATE : integer range 0 to 6 ;
signal iTMP : std_logic ;
signal iREGX : std_logic_vector(7 downto 0) ;
signal iREG : std_logic_vector(7 downto 0) ;
signal iSCNT : integer range 0 to SLAST ;
begin
-- input
iSH <= SH ;
iCLKA <= CLKA ;
iCLKB <= CLKB ;
iDOS <= XOS ;
-- output
DCLK <= iDCK ;
OST <= iREG ;
LOUT <= not iREG ;
-- clock divider
process (nRESET,CLOCK)
begin
if ( nRESET = '0' ) then
iDCNT <= "00" ;
elsif rising_edge( CLOCK ) then
iDCNT <= iDCNT + '1' ;
end if ;
end process ;
iDCK <= '1' when ( iDCNT = "00" ) else '0' ;
-- clock shifter
process (nRESET,iDCK)
begin
if ( nRESET = '0' ) then
iCLKA_SFT <= "00" ;
iCLKB_SFT <= "00" ;
elsif rising_edge( iDCK ) then
iCLKA_SFT <= iCLKA_SFT(0) & iCLKA ;
iCLKB_SFT <= iCLKB_SFT(0) & iCLKB ;
end if ;
end process ;
-- internal lacth clock trigger
iCLKAB <= '1' when ( iCLKA_SFT = "01" ) else
'1' when ( iCLKB_SFT = "01" ) else
'0' ;
-- synchronizer
process (nRESET,CLOCK)
begin
if ( nRESET = '0' ) then
iSH_SFT <= "00" ;
elsif rising_edge( CLOCK ) then
iSH_SFT <= iSH_SFT(0) & iSH ;
end if ;
end process ;
iSH_TRG <= '1' when ( iSH_SFT = "10" ) else '0' ;
-- state machine
process (nRESET,CLOCK)
begin
if ( nRESET = '0' ) then
iSTATE <= 0 ;
iREGX <= X"00" ;
iREG <= X"00" ;
iTMP <= '0' ;
iSCNT <= 0 ;
elsif rising_edge( CLOCK ) then
case iSTATE is
-- wait trigger
when 0 => if ( iSH_TRG = '1' ) then
iSTATE <= 1 ; -- next
iREGX <= X"00" ; -- clear register
iSCNT <= 0 ; -- clear counter
else
iSTATE <= 0 ; -- stay
end if ;
-- get data
when 1 => if ( iCLKAB = '1' ) then
iSTATE <= 2 ; -- next
iTMP <= iDOS ; -- latch 1 bit
else
iSTATE <= 1 ; -- stay
end if ;
-- data counter increment
when 2 => iSTATE <= 3 ; -- next
iSCNT <= iSCNT + 1 ; -- counter increment
-- store datum to temporary register
when 3 => iSTATE <= 4 ; -- next
-- store
if ( iSCNT = S0 or
iSCNT = S1 or
iSCNT = S2 or
iSCNT = S3 or
iSCNT = S4 or
iSCNT = S5 or
iSCNT = S6 or
iSCNT = S7 ) then
iREGX <= iREGX(6 downto 0) & iTMP ;
end if ;
-- judge (? complete)
when 4 => if ( iSCNT = SLAST ) then
iSTATE <= 6 ; -- return first state
else
iSTATE <= 5 ; -- loop control
end if ;
-- loop control
when 5 => iSTATE <= 1 ;
-- return first state
when 6 => iSTATE <= 0 ;
iREG <= iREGX ; -- copy
-- default
when others =>
iSTATE <= 0 ;
end case ;
end if ;
end process ;
end behavioral;
CLKA、CLKBは、シフトレジスタで変化をとらえて
DOSの値を記憶するトリガーを生成します。
トリガーとして利用するパルスの幅は、シフトレジスタ
に使うクロックを1MHzとしたとき、1usになります。
ひとつのトリガーで、2回カウントすると、左によった
センサー情報になります。
1usのトリガーに対し、シーケンサは4MHzのクロックで
動かすため、5クロック後にトリガーの検出をする仕様
にしてあります。
トリガー検出→カウンタに+1→センサー値とするか判定→
終了判定→トリガー検出にもどる
上のように処理しているので、トリガー検出後
次のトリガー検出まで5クロック必要です。
5クロックで1.25usですから、トリガーの1usより
長くなり、ひとつのトリガーで2回のカウントは
しないとわかります。
CLKA、CLKBをシフトレジスタでサンプリングする
クロックを2MHzにしたときは、以下の回路での
トリガー生成になります。
2レジスタを増やし、センサーデータが確定する
までの時間800nsを確保できるように工夫。
VHDLコードでは、次のようにします。
-- clock shifter
process (nRESET,iDCK)
begin
if ( nRESET = '0' ) then
iCLKA_SFT <= "00" ;
iCLKB_SFT <= "00" ;
elsif rising_edge( iDCK ) then
iCLKA_SFT <= iCLKA_SFT(0) & iCLKA ;
iCLKB_SFT <= iCLKB_SFT(0) & iCLKB ;
end if ;
end process ;
-- internal lacth clock trigger
iCLKAB <= '1' when ( iCLKA_SFT = "01" ) else
'1' when ( iCLKB_SFT = "01" ) else
'0' ;
トリガーのパルス幅が0.5usになったので、シーケンサを
4MHzで動かしても、ひとつのトリガーで2回カウントする
ことはなくなります。
Arduinoで移動制御をさせようとすれば、利用できる
ピン数が少ないので、8ビットのセンサーデータを
4ビットずつ2回に分けて出力する工夫が必要。
セレクタを入れ、8ビットの上位、下位の4ビット
を出力するようにVHDLコードを変更。
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity taa is
port (
-- system
nRESET : in std_logic ;
CLOCK : in std_logic ; -- 4MHz
-- divided clock
DCLK : out std_logic ; -- 2MHz
-- BCS
SH : in std_logic ;
CLKA : in std_logic ;
CLKB : in std_logic ;
DOS : in std_logic ;
-- monitor output
LOUT : out std_logic_vector(7 downto 0) ;
-- output
RSEL : in std_logic ;
OST : out std_logic_vector(3 downto 0) --;
);
end taa ;
architecture behavioral of taa is
--
CONSTANT SLAST : integer := 2080 ;
-- sensing location
CONSTANT S0 : integer := 800 ; -- 32+1024+(-256)
CONSTANT S1 : integer := 864 ; -- 32+1024+(-192)
CONSTANT S2 : integer := 928 ; -- 32+1024+(-128)
CONSTANT S3 : integer := 992 ; -- 32+1024+( -64)
CONSTANT S4 : integer := 1120 ; -- 32+1024+( 64)
CONSTANT S5 : integer := 1184 ; -- 32+1024+( 128)
CONSTANT S6 : integer := 1248 ; -- 32+1024+( 192)
CONSTANT S7 : integer := 1312 ; -- 32+1024+( 256)
-- divided clock
signal iDCNT : std_logic_vector(1 downto 0) ;
signal iDCK : std_logic ;
-- input
signal iSH : std_logic ;
signal iCLKA : std_logic ;
signal iCLKB : std_logic ;
signal iDOS : std_logic ;
-- shift register
signal iCLKA_SFT : std_logic_vector(1 downto 0) ;
signal iCLKB_SFT : std_logic_vector(1 downto 0) ;
signal iCLKAB : std_logic ;
signal iSH_SFT : std_logic_vector(1 downto 0) ;
signal iSH_TRG : std_logic ;
-- state machine
signal iSTATE : integer range 0 to 6 ;
signal iTMP : std_logic ;
signal iREGX : std_logic_vector(7 downto 0) ;
signal iREG : std_logic_vector(7 downto 0) ;
signal iSCNT : integer range 0 to SLAST ;
begin
-- output
DCLK <= iDCK ;
OST <= iREG(7 downto 4) when ( RSEL = '1' ) else iREG(3 downto 0) ;
LOUT <= not iREG ;
-- input
iSH <= SH ;
iCLKA <= CLKA ;
iCLKB <= CLKB ;
iDOS <= DOS ;
-- clock divider
process (nRESET,CLOCK)
begin
if ( nRESET = '0' ) then
iDCNT <= "00" ;
elsif rising_edge( CLOCK ) then
iDCNT <= iDCNT + '1' ;
end if ;
end process ;
iDCK <= not iDCNT(1) ;
-- clock shifter
process (nRESET,iDCK)
begin
if ( nRESET = '0' ) then
iCLKA_SFT <= "00" ;
iCLKB_SFT <= "00" ;
elsif rising_edge( iDCK ) then
iCLKA_SFT <= iCLKA_SFT(0) & iCLKA ;
iCLKB_SFT <= iCLKB_SFT(0) & iCLKB ;
end if ;
end process ;
-- internal lacth clock trigger
iCLKAB <= '1' when ( iCLKA_SFT = "01" ) else
'1' when ( iCLKB_SFT = "01" ) else
'0' ;
-- synchronizer
process (nRESET,CLOCK)
begin
if ( nRESET = '0' ) then
iSH_SFT <= "00" ;
elsif rising_edge( CLOCK ) then
iSH_SFT <= iSH_SFT(0) & iSH ;
end if ;
end process ;
iSH_TRG <= '1' when ( iSH_SFT = "10" ) else '0' ;
-- get datum
process (nRESET,CLOCK)
begin
if ( nRESET = '0' ) then
iTMP <= '0' ;
elsif rising_edge( CLOCK ) then
if ( iCLKAB = '1' ) then
iTMP <= iDOS ; -- latch 1 bit
end if ;
end if ;
end process ;
-- state machine
process (nRESET,CLOCK)
begin
if ( nRESET = '0' ) then
iSTATE <= 0 ;
iREGX <= X"00" ;
iREG <= X"00" ;
iSCNT <= 0 ;
elsif rising_edge( CLOCK ) then
case iSTATE is
-- wait trigger
when 0 => if ( iSH_TRG = '1' ) then
iSTATE <= 1 ; -- next
iREGX <= X"00" ; -- clear register
iSCNT <= 0 ; -- clear counter
else
iSTATE <= 0 ; -- stay
end if ;
-- get datum
when 1 => if ( iCLKAB = '1' ) then
iSTATE <= 2 ; -- next
else
iSTATE <= 1 ; -- stay
end if ;
-- data counter increment
when 2 => iSTATE <= 3 ; -- next
iSCNT <= iSCNT + 1 ; -- counter increment
-- store datum to temporary register
when 3 => iSTATE <= 4 ; -- next
-- store
if ( iSCNT = S0 or
iSCNT = S1 or
iSCNT = S2 or
iSCNT = S3 or
iSCNT = S4 or
iSCNT = S5 or
iSCNT = S6 or
iSCNT = S7 ) then
iREGX <= iREGX(6 downto 0) & iTMP ;
end if ;
-- judge (? complete)
when 4 => if ( iSCNT = SLAST ) then
iSTATE <= 6 ; -- next(return first state)
else
iSTATE <= 5 ; -- next(loop control)
end if ;
-- loop control
when 5 => iSTATE <= 1 ;
-- return first state
when 6 => iSTATE <= 0 ;
iREG <= iREGX ; -- copy
-- default
when others =>
iSTATE <= 0 ;
end case ;
end if ;
end process ;
end behavioral;
利用マクロセル数は、53になりました。
BCSはセンサーデータの更新を20msに一度とするので
センサーデータ8ビットを2回に分けてArduinoで
取得しても問題ありません。
ラインデータを、どこでサンプリングして、8ビットに
仕立てるかを計算するのは、AWKスクリプトを使います。
データは、テキストで次のように書いておきます。
32 1024 64 -4
32 1024 64 -3
32 1024 64 -2
32 1024 64 -1
32 1024 64 1
32 1024 64 2
32 1024 64 3
32 1024 64 4
AWKスクリプトは、以下。
{
result = $1 + $2 + ($3 * $4)
printf("%d => %s\n",result,$0)
}
テキストの内容を変えると、サンプリング間隔を
狭めたり、広げたりするのは自由自在です。
また、等間隔にサンプリングしないことも可能。
目次
前
次