目次
前
次
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を動かして、データを入力します。
シーケンサは、ステートを与えて、ステートごとの処理を
決めていきます。
- トリガー待ち トリガーが着たら、カウンタを初期化
- iCLKが'1'のとき、次のステートにいく
- カウンタが21を超えれば、ステート2へ、それ以外はステート1へ
- ステート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チャネルから入力し、ユニポーラ、シングルエンド、内部
クロック利用とすると、カウンタ値と出力値は、以下のよう
にします。
- カウンタ = 0 : 1 出力
- カウンタ = 1 : 0 出力
- カウンタ = 2 : 0 出力
- カウンタ = 3 : 0 出力
- カウンタ = 4 : 1 出力
- カウンタ = 5 : 1 出力
- カウンタ = 6 : 1 出力
- カウンタ = 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;
目次
前
次