目次
前
次
Simple A/D converter
A/D変換器は、D/A変換器とコンパレータを利用すれば
低速、低精度という条件つきですが、実現できます。
天秤による秤のように基準となる錘を使い、重い
軽いと判断し、全体で6ビットから12ビットの
精度を作れます。
CPLD/FPGAに接続する回路は、以下。
D/A変換器は、R-2Rの抵抗だけで簡単に作れます。その
出力をOPアンプで受け、インピーダンス変換します。
A/D変換したい電圧を出力するデバイスを一度OPアンプに
接続し、バッファ後、必要な電圧値まで増幅します。
増幅は、非反転増幅にしておきます。反転増幅にすると
2電源が必要になります。非反転増幅であれば、正側の
電源だけで対応できます。
電圧比較をするために、OPアンプをコンパレータとして
使っています。
ブロック図では、次のようになります。
動作は、次のシーケンスで実行します。
- カウンタを0にする
- カウンタ値に対応した値を、出力
- コンパレータの出力値を、レジスタのLSBに設定
- レジスタを1ビット左にシフトのLSBに設定
- カウンタ値を+1
- カウンタ値が8なら7に、そうでなければ1にもどる
- レジスタ値を出力
カウンタ値を出力するには、デコーダを使います。
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を
利用した回路でも、充分でしょう。
目次
前
次