ライントレーサ走行
ライントレーサは、床面にあるラインに沿って
動き回るロボットの1種。
マイコンで動かすための体験会で、BASICによる
制御をしていましたが、シーケンサを使えば
デジタル回路でもライントレーサを実現可能。
体験会にあったライントレーサは、以下。
BASICプログラムは、次のようになっています。
20 R=IN(1):L=IN(2)
25 ? L,R
30 OUT 4,1:OUT 2,1
50 IF R=0 THEN OUT 4,0:WAIT 15
60 IF L=0 THEN OUT 2,0:WAIT 15
100 GOTO 20
左右のセンサー値を見て、ラインの外に出ていれば
ラインの中央に車体を持っていくように制御。
左あるいは右にターンするときには、モータの回転を
止めて、片側のモータだけを動かしています。
右に外れていれば、左のモータを止めます。
左に外れていれば、右のモータを止めます。
センサー情報は、R(右)とL(左)の変数に
論理値で含まれています。
モータの回転は、論理値の'1'を出力すればよく
モータの停止は、論理値の'0'を出力すればOK。
BASICコードの50行目を見ると、変数Rが論理値の'0'を
持っているとき、OUT_4に論理値の'0'を出力。
右に外れていれば、左のモータを止めるので
OUT_4は左モータのドライブ回路に接続されて
います。論理値は'0'出力なので、BASICでは
次のように書いてもOK。
50 IF R=0 THEN OUT 4,R:WAIT 15
60 IF L=0 THEN OUT 2,L:WAIT 15
右に外れていれば(R=0)、左のモータを止める(OUT 4,R)
左に外れていれば(L=0)、左のモータを止める(OUT 2,L)
というのは、次の操作論理を適用できるとなります。
センサー情報を、反対側のモータのための制御信号に使う。
これで制御には、センサー情報を襷がけにして
使えばよいと理解できるでしょう。
それでは、シーケンサを考えます。
プログラムは、シーケンサそのものなので
ステートマシンを使えば同じことを実現可能。
BASICプログラムから、並列に処理できることを考えます。
センサー情報は、シーケンサ内で取得しないで
シーケンサが特定のステートになったなら記憶
すればよいでしょう。
センサー情報を取得するVHDLコードは、以下。
process ( nRESET , CLOCK )
begin
if ( nRESET = '0' ) then
iSDAT <= "11" ;
elsif rising_edge( CLOCK ) then
if ( iSEQ = "0010" ) then
iSDAT <= SDAT ;
end if ;
end if ;
end process ;
SDATがCPLDチップに接続する、センサー情報とします。
内部では2ビットのレジスタを用意。
signal iSDAT : std_logic_vector(1 downto 0) ;
残っているのは、待ち時間をどう扱うのか。
シーケンサで、モータの駆動回路にドライブ回路に
制御信号を出力したら、何もしないステートを作り
時間潰しをしてしまいます。
4ビットのバイナリカウンタをシーケンサのベースに
すると、各ステートは、次のように記述できます。
- 左右のモータに、回転指令を与える
- 何もしない
- 何もしない(他ブロックで、センサー情報を記憶)
- 左右のモータに、センサー情報を襷がけして与える
- 何もしない
- 何もしない
- 何もしない
- 何もしない
- 何もしない
- 何もしない
- 何もしない
- 何もしない
- 何もしない
- 何もしない
- 何もしない
- 1に戻る
ステートを4ビットの整数で表現したときは
ある値より下では、回転指令は、ともに'1'し
それ以外では回転指令はセンサー情報を襷がけ
にして、駆動回路に与えると考えました。
センサー情報は、iSDATに含まれています。
左モータの駆動回路の動作指令がiSDAT(1)に格納
されているとすれば、モータの回転指令は、次の
VHDLコードで記述可能。
iDRV(1) <= iSDAT(0) when ( conv_integer(iSEQ) > 2 ) else '1' ;
右モータの駆動回路の動作指令は、iSDAT(0)なので
次のように記述すれば、充分。
iDRV(0) <= iSDAT(1) when ( conv_integer(iSEQ) > 2 ) else '1' ;
シーケンサをバイナリカウンタベースにするので
バイナリカウンタを定義。
process ( nRESET , CLOCK )
begin
if ( nRESET = '0' ) then
iSEQ <= "0000" ;
elsif rising_edge( CLOCK ) then
if ( iSEQ = "1111" ) then
iSEQ <= "0000" ;
else
iSEQ <= iSEQ + '1' ;
end if ;
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 lfw is
Port (
-- system
nRESET : in std_logic ; -- system reset
CLOCK : in std_logic ; -- 100Hz
-- sensor data
SDAT : in std_logic_vector(1 downto 0) ;
-- output control
ENA : in std_logic ;
-- output
DRVOUT : out std_logic_vector(1 downto 0) --;
);
end lfw;
architecture Behavioral of lfw is
-- sequencer
signal iSEQ : std_logic_vector(3 downto 0) ;
-- sensor data
signal iSDAT : std_logic_vector(1 downto 0) ;
-- driver signals
signal iDRV : std_logic_vector(1 downto 0) ;
begin
-- output
DRVOUT <= iDRV when ( ENA = '1' ) else "00" ;
-- latch sensor data
process ( nRESET , CLOCK )
begin
if ( nRESET = '0' ) then
iSDAT <= "11" ;
elsif rising_edge( CLOCK ) then
if ( iSEQ = "0010" ) then
iSDAT <= SDAT ;
end if ;
end if ;
end process ;
-- sequencer
process ( nRESET , CLOCK )
begin
if ( nRESET = '0' ) then
iSEQ <= "0000" ;
elsif rising_edge( CLOCK ) then
if ( iSEQ = "1111" ) then
iSEQ <= "0000" ;
else
iSEQ <= iSEQ + '1' ;
end if ;
end if ;
end process ;
-- generate drive signal
iDRV(1) <= iSDAT(0) when ( conv_integer(iSEQ) > 2 ) else '1' ;
iDRV(0) <= iSDAT(1) when ( conv_integer(iSEQ) > 2 ) else '1' ;
end Behavioral;
ピン割りてもしておきます。
# system
NET "nRESET" loc = "P39" ;
NET "CLOCK" loc = "P5" ;
# sensor
NET "SDAT<0>" loc = "P2" ;
NET "SDAT<1>" loc = "P1" ;
# driver
NET "DRVOUT<0>" loc = "P6" ;
NET "DRVOUT<1>" loc = "P7" ;
NET "ENA" loc = "P9" ;
XilinxのXC9536にインプリメントしてみたところ
フリップフロップ数は、わずか8だけでした。
待ち時間を調整するには、シーケンサで扱うステート
数を増減させて対応すればよいでしょう。
シーケンサを構成する、ベースカウンタのビット数を
1ビット増やすと、2倍のステートを得られます。
4ビットを5ビットに増やすと、ステート数は16から
32へと増えるので、カウンタのMSB(最上位ビット)
を利用し、時間待ちとそれ以外の処理に分けられます。
VHDLコードにすると、デコーダが簡単になって
いると理解できます。
-- latch sensor data
process ( nRESET , CLOCK )
begin
if ( nRESET = '0' ) then
iSDAT <= "11" ;
elsif rising_edge( CLOCK ) then
if ( iSEQ = "01111" ) then
iSDAT <= SDAT ;
end if ;
end if ;
end process ;
-- generate drive signal
iDRV(1) <= iSDAT(0) when ( iSEQ(4) = 1 ) else '1' ;
iDRV(0) <= iSDAT(1) when ( iSEQ(4) = 1 ) else '1' ;
待ち時間をカウンタ値の16から31に割当てると
最大16ステートまでの無駄時間を生成できます。
100Hzでカウンタを回すと、1ステートは10ms。
250msを生成したいのなら、25ステートを無駄
時間に割当てます。
32-25=7より、ステートに関係するVHDLコードを
次のように変更します。
-- latch sensor data
process ( nRESET , CLOCK )
begin
if ( nRESET = '0' ) then
iSDAT <= "11" ;
elsif rising_edge( CLOCK ) then
if ( conv_integer(iSEQ) = 5 ) then
iSDAT <= SDAT ;
end if ;
end if ;
end process ;
-- generate drive signal
iDRV(1) <= iSDAT(0) when ( conv_integer(iSEQ(4)) > 6 ) else '1' ;
iDRV(0) <= iSDAT(1) when ( conv_integer(iSEQ(4)) > 6 ) else '1' ;
ディスクリートのICを組み合わせて、制御回路を
実現すると、以下。
上の回路が、シーケンサとデコーダ。
下の回路が、センサー情報の記憶とドライバに
制御信号を出力するセレクタになります。
PLDを使うと、ディスクリートのICで7チップだった
のに対して、1チップでまとまります。また、後での
修正も可能。
目次
前
次