目次

ライントレーサ走行

 ライントレーサは、床面にあるラインに沿って
 動き回るロボットの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. 左右のモータに、回転指令を与える
  2. 何もしない
  3. 何もしない(他ブロックで、センサー情報を記憶)
  4. 左右のモータに、センサー情報を襷がけして与える
  5. 何もしない
  6. 何もしない
  7. 何もしない
  8. 何もしない
  9. 何もしない
  10. 何もしない
  11. 何もしない
  12. 何もしない
  13. 何もしない
  14. 何もしない
  15. 何もしない
  16. 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チップでまとまります。また、後での  修正も可能。
目次

inserted by FC2 system