目次

ADCC(Analog to Digital Converter Controller)

 CPLD/FPGAを利用して、ADCC(Analog to Digital Converter Controller)を
  作成してみます。

  マイクロコンピュータを制御に利用する場合、高速性が要求され
 外付けのADC(Analog to Digital Converter)の面倒を見れない、
 ピン数が不足する場合があります。

 最近は、シリアルインタフェースADCが殆どで、パラレルで出力する
 デバイスがありません。
 MAXIM社のシリアルインタフェースADCであるMAX186が手元にあった
 ので、パラレルADCに変身させてみます。

 MAX186の信号は、以下です。

 SCLKのrising_edgeに同期して、DINに制御コードを転送します。
 データは、SCLKのfalling_edgeに同期して、DOUTからデータを転送します。

 CPLF/FPGAとマイクロコンピュータとは、次の信号でインタフェースします。



ブロック図作成

 動作仕様から、ブロック図を作成します。

entity定義

 ブロック図で外部とやりとりする信号が決まれば、entityを定義します。 entity adcc is port ( -- system nRESET : in std_logic ; CLOCK : in std_logic ; -- A/D control SCLK : out std_logic ; nCS : out std_logic ; DIN : out std_logic ; DOUT : in std_logic ; -- Interface TRG : in std_logic ; nENA : in std_logic ; ADOUT : out std_logic_vector(11 downto 0) -- ; ) ; end adcc;

分周回路定義

 システムクロックは、ADCを動かすクロックよりも高速なので  4分周したクロックを生成し、ADCに与えます。  カウンタを用意し、0〜3をカウントし、3のときにクロックと  して1を出力します。 process ( nRESET , CLOCK ) begin if ( nRESET = '0' ) then iCNT <= "00" ; elsif rising_edge( CLOCK ) then -- increment iCNT <= iCNT + '1' ; end if ; end process ; iCLK <= '1' when ( iCNT = "11" ) else '0' ; SCLK <= iCLK and inCS ;  inCSは、他のブロックで生成するnCSのための信号です。

シフトレジスタ定義

 マイクロコンピュータが、トリガー信号を出力するので、それを  捕らえてシーケンサを動かすトリガーとします。  分周回路の中に組込みます。 ----------------------------------------------------------- process ( nRESET , CLOCK ) begin if ( nRESET = '0' ) then iCNT <= "00" ; iSTRG <= "00" ; elsif rising_edge( CLOCK ) then -- increment iCNT <= iCNT + '1' ; -- shift register iSTRG <= iSTRG(0) & TRG ; end if ; end process ; iCLK <= '1' when ( iCNT = "11" ) else '0' ; -- generate trigger iTRG <= '1' when ( iSTRG = "01" ) else '0' ; -----------------------------------------------------------  ADCは、DOUTからシリアルでデータを出力してくるので  シフトレジスタで、データを直並列変換します。  シフトレジスタのクロックは、iCLKのfalling_edgeを利用します。 process ( nRESET , iCLK ) begin if ( nRESET = '0' ) then iADOUT <= (others => '0') ; elsif falling_edge( iCLK ) then if ( conv_integer(iSCNT) > 8 ) then iADOUT <= iADOUT(10 downto 0) & DOUT ; end if ; end if ; end process ;

シーケンサ定義

 マイクロコンピュータから与えられたトリガーを利用して  MAX186を動かして、データを入力します。  シーケンサは、ステートを与えて、ステートごとの処理を  決めていきます。
  1. トリガー待ち トリガーが着たら、カウンタを初期化
  2. iCLKが'1'のとき、次のステートにいく
  3. カウンタが21を超えれば、ステート2へ、それ以外はステート1へ
  4. ステート0にもどる
 ステートの動作を定義したので、シーケンサを記述します。 type stype is (S0,S1,S2,S3) ; signal iCUR_STA : stype ; process ( nRESET , CLOCK ) begin if ( nRESET = '0' ) then iSCNT <= (others => '0') ; iCUR_STA <= S0 ; elsif rising_edge( CLOCK ) then -- state machine case iCUR_STA is -- wait trigger when S0 => if ( iTRG = '1' ) then iSCNT <= (others => '0') ; iCUR_STA <= S1 ; end if ; -- set DIN when S1 => if ( iCLK = '1' ) then iSCNT <= iSCNT + '1' ; iCUR_STA <= S2 ; end if ; -- judge when S2 => if ( conv_integer( iSCNT ) > 21 ) then iCUR_STA <= S3 ; else iCUR_STA <= S1 ; end if ; -- return first state when S3 => iCUR_STA <= S0 ; when others => iCUR_STA <= S0 ; end case ; end if ; end process ;  シーケンサの動作クロックは、iCLKの4倍高速なので  内部レジスタのセットアップ、ホールドタイムは充分  満足できます。

出力制御

 ADCを動かすためには、チップセレクトをイネーブルにする  必要があるので、シーケンサの状態を判定してnCSを決めます。 inCS <= '0' when ( iCUR_STA > S0 ) else '1' ; nCS <= inCS ;  DINは、シーケンサが動かすカウンタにより出力を決めます。  0チャネルから入力し、ユニポーラ、シングルエンド、内部  クロック利用とすると、カウンタ値と出力値は、以下のよう  にします。
  1. カウンタ = 0 : 1 出力
  2. カウンタ = 1 : 0 出力
  3. カウンタ = 2 : 0 出力
  4. カウンタ = 3 : 0 出力
  5. カウンタ = 4 : 1 出力
  6. カウンタ = 5 : 1 出力
  7. カウンタ = 6 : 1 出力
  8. カウンタ = 7 : 0 出力
 カウンタ値をデコードし、DINに出力する値を決めます。 DIN <= '1' when ( iSCNT(1 downto 0) = "00" ) else '1' when ( iSCNT(2 downto 1) = "10" ) else '1' when ( iSCNT(2) = '1' and iSCNT(0) = '0' ) else '0' ;  ADCが入力したデータを、パラレル出力するためにnENAを利用  します。  この信号がディセーブルのとき、ハイインピーダンスになる  ようにします。 ADOUT <= iADOUT when (nENA = '0') else (others => 'Z') ;

全ソースコード

 ブロック図から各ブロックを定義したので、まとめます。 library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity adcc is port ( -- system nRESET : in std_logic ; CLOCK : in std_logic ; -- A/D control SCLK : out std_logic ; nCS : out std_logic ; DIN : out std_logic ; DOUT : in std_logic ; -- Interface TRG : in std_logic ; nENA : in std_logic ; ADOUT : out std_logic_vector(11 downto 0) -- ; ) ; end adcc; architecture Behavioral of adcc is signal iCNT : std_logic_vector( 1 downto 0) ; signal iCLK : std_logic ; signal inCS : std_logic ; signal iTRG : std_logic ; signal iSTRG : std_logic_vector( 1 downto 0) ; -- signal iSCNT : std_logic_vector( 4 downto 0) ; -- type stype is (S0,S1,S2,S3) ; signal iCUR_STA : stype ; signal iADOUT : std_logic_vector(11 downto 0) ; begin -- parallel out ADOUT <= iADOUT when ( nENA = '0' ) else (others => 'Z') ; -- clock divider and shift register process ( nRESET , CLOCK ) begin if ( nRESET = '0' ) then iCNT <= "00" ; iSTRG <= "00" ; elsif rising_edge( CLOCK ) then -- increment iCNT <= iCNT + '1' ; -- shift register iSTRG <= iSTRG(0) & TRG ; end if ; end process ; iCLK <= '1' when ( iCNT = "00" ) else '0' ; iTRG <= '1' when ( iSTRG = "01" ) else '0' ; SCLK <= iCLK and inCS ; -- sequencer process ( nRESET , CLOCK ) begin if ( nRESET = '0' ) then iSCNT <= (others => '0') ; iCUR_STA <= S0 ; elsif rising_edge( CLOCK ) then -- state machine case iCUR_STA is -- wait trigger when S0 => if ( iTRG = '1' ) then iSCNT <= (others => '0') ; iCUR_STA <= S1 ; end if ; -- set DIN when S1 => if ( iCLK = '1' ) then iSCNT <= iSCNT + '1' ; iCUR_STA <= S2 ; end if ; -- judge when S2 => if ( conv_integer( iSCNT ) > 21 ) then iCUR_STA <= S3 ; else iCUR_STA <= S1 ; end if ; -- return first state when S3 => iCUR_STA <= S0 ; when others => iCUR_STA <= S0 ; end case ; end if ; end process ; inCS <= '0' when ( iCUR_STA > S0 ) else '1' ; nCS <= inCS ; DIN <= '1' when ( iSCNT(1 downto 0) = "00" ) else '1' when ( iSCNT(2 downto 1) = "10" ) else '1' when ( iSCNT(2) = '1' and iSCNT(0) = '0' ) else '0' ; -- get DOUT sequencer process ( nRESET , iCLK ) begin if ( nRESET = '0' ) then iADOUT <= (others => '0') ; elsif falling_edge( iCLK ) then if ( conv_integer( iSCNT ) > 8 ) then iADOUT <= iADOUT(10 downto 0) & DOUT ; end if ; end if ; end process ; end Behavioral;


目次 inserted by FC2 system