目次

Simple A/D converter

 A/D変換器は、D/A変換器とコンパレータを利用すれば
 低速、低精度という条件つきですが、実現できます。

 天秤による秤のように基準となる錘を使い、重い
 軽いと判断し、全体で6ビットから12ビットの
 精度を作れます。

 CPLD/FPGAに接続する回路は、以下。



 D/A変換器は、R-2Rの抵抗だけで簡単に作れます。その
 出力をOPアンプで受け、インピーダンス変換します。

 A/D変換したい電圧を出力するデバイスを一度OPアンプに
 接続し、バッファ後、必要な電圧値まで増幅します。

 増幅は、非反転増幅にしておきます。反転増幅にすると
 2電源が必要になります。非反転増幅であれば、正側の
 電源だけで対応できます。

 電圧比較をするために、OPアンプをコンパレータとして
 使っています。

 ブロック図では、次のようになります。



 動作は、次のシーケンスで実行します。
  1. カウンタを0にする
  2. カウンタ値に対応した値を、出力
  3. コンパレータの出力値を、レジスタのLSBに設定
  4. レジスタを1ビット左にシフトのLSBに設定
  5. カウンタ値を+1
  6. カウンタ値が8なら7に、そうでなければ1にもどる
  7. レジスタ値を出力
 カウンタ値を出力するには、デコーダを使います。 iDAC <= X"7F" when ( iSCNT = 1 ) else X"3F" when ( iSCNT = 2 ) else X"1F" when ( iSCNT = 3 ) else X"0F" when ( iSCNT = 4 ) else X"07" when ( iSCNT = 5 ) else X"03" when ( iSCNT = 6 ) else X"01" when ( iSCNT = 7 ) else X"FF" ;  デコーダを使うには、シーケンサが必要なので  Hand Shakingで処理します。 process (nRESET,CLOCK) begin if ( nRESET = '0' ) then iSTATE <= "000" ; iSCNT <= 0 ; iREG <= X"00" ; elsif rising_edge(CLOCK) then case conv_integer(iSTATE) is -- wait request when 0 => if ( iXREQ = '1' ) then iSTATE <= "001" ; else iSTATE <= "000" ; end if ; -- clear internal register and counter when 1 => iSCNT <= 0 ; iREG <= X"00" ; iSTATE <= "011" ; -- judge when 3 => if ( iSCNT = 8 ) then iSTATE <= "100" ; else iSTATE <= "111" ; end if ; -- compare input when 7 => iREG <= iREG(6 downto 0) & iXSEN ; iSTATE <= "110" ; -- loop when 6 => iSTATE <= "011" ; iSCNT <= iSCNT + 1 ; -- return first state when 4 => iSTATE <= "000" ; -- default when others => iSTATE <= "000" ; end case ; end if ; end process ;  リクエストが来たなら、一気にループを回します。  カウンタ、レジスタの値を0にした後、8回電圧  比較器の出力をレジスタにシフトしながら格納。  カウンタ値に対応したD/A変換器の出力で、電圧  比較器を動かします。  リクエストを出した側が、A/D変換が終了したのかを  アクノリッジで示します。 iXACK <= '1' when ( iSTATE = "011" ) else '1' when ( iSTATE = "111" ) else '1' when ( iSTATE = "110" ) else '0' ;  タイミングチャートで示すと、次のようになります。  A/D変換値が必要とするブロックは、次のように動きます。  (a)リクエストを出し  (b)アクノリッジの'H'で変換開始を判断  (c)アクノリッジの'L'で変換終了を判断  (d)変換値を取得  VHDLコードにまとめると、以下。 library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity tstad is port ( -- system nRESET : in std_logic ; CLOCK : in std_logic ; -- DAC DACOUT : out std_logic_vector(7 downto 0) ; -- ADC ADCOUT : out std_logic_vector(7 downto 0) ; -- shake handling XREQ : in std_logic ; XACK : out std_logic ; -- sensing XSEN : in std_logic --; ); end tstad ; architecture behavioral of tstad is -- input signal iXSEN : std_logic ; -- hand shaking signal iXREQ : std_logic ; signal iXACK : std_logic ; signal iDAC : std_logic_vector(7 downto 0) ; -- sequencer signal iSTATE : std_logic_vector(2 downto 0) ; signal iSCNT : integer range 0 to 8 ; signal iREG : std_logic_vector(7 downto 0) ; begin -- input iXSEN <= XSEN ; iXREQ <= XREQ ; -- output XACK <= iXACK ; ADCOUT <= iREG ; DACOUT <= iDAC ; -- decoder iDAC <= X"7F" when ( iSCNT = 1 ) else X"3F" when ( iSCNT = 2 ) else X"1F" when ( iSCNT = 3 ) else X"0F" when ( iSCNT = 4 ) else X"07" when ( iSCNT = 5 ) else X"03" when ( iSCNT = 6 ) else X"01" when ( iSCNT = 7 ) else X"FF" ; -- sequencer process (nRESET,CLOCK) begin if ( nRESET = '0' ) then iSTATE <= "000" ; iSCNT <= 0 ; iREG <= X"00" ; elsif rising_edge(CLOCK) then case conv_integer(iSTATE) is -- wait request when 0 => if ( iXREQ = '1' ) then iSTATE <= "001" ; else iSTATE <= "000" ; end if ; -- clear internal register and counter when 1 => iSCNT <= 0 ; iREG <= X"00" ; iSTATE <= "011" ; -- judge when 3 => if ( iSCNT = 8 ) then iSTATE <= "100" ; else iSTATE <= "111" ; end if ; -- compare input when 7 => iREG <= iREG(6 downto 0) & iXSEN ; iSTATE <= "110" ; -- loop when 6 => iSTATE <= "011" ; iSCNT <= iSCNT + 1 ; -- return first state when 4 => iSTATE <= "000" ; -- default when others => iSTATE <= "000" ; end case ; end if ; end process ; iXACK <= '1' when ( iSTATE = "011" ) else '1' when ( iSTATE = "111" ) else '1' when ( iSTATE = "110" ) else '0' ; end behavioral;  XilinxのXC9572で実現すると、マクロセル数は  半分程度になりました。  UCFファイルは、以下。 # system NET "CLOCK" LOC = "P5" ; NET "nRESET" LOC = "P39" ; # hand shaking NET "XREQ" LOC = "P1" ; NET "XACK" LOC = "P2" ; # sensor NET "XSEN" LOC = "P44" ; # DAC NET "DACOUT<0>" LOC = "P11" ; NET "DACOUT<1>" LOC = "P12" ; NET "DACOUT<2>" LOC = "P13" ; NET "DACOUT<3>" LOC = "P14" ; NET "DACOUT<4>" LOC = "P18" ; NET "DACOUT<5>" LOC = "P19" ; NET "DACOUT<6>" LOC = "P20" ; NET "DACOUT<7>" LOC = "P22" ; # ADC NET "ADCOUT<0>" LOC = "P24" ; NET "ADCOUT<1>" LOC = "P25" ; NET "ADCOUT<2>" LOC = "P26" ; NET "ADCOUT<3>" LOC = "P27" ; NET "ADCOUT<4>" LOC = "P28" ; NET "ADCOUT<5>" LOC = "P29" ; NET "ADCOUT<6>" LOC = "P33" ; NET "ADCOUT<7>" LOC = "P34" ;  外付けのOPアンプの動作周波数は、100kHz程度  なので、内部に分周器を用意して、動作周波数  を下げて使うことになります。  100kHz程度の動作クロック生成なら、CMOSの555を  利用した回路でも、充分でしょう。
目次

inserted by FC2 system