LoginSignup
6
3

More than 5 years have passed since last update.

FPGAでADCを制御する

Last updated at Posted at 2018-08-16

前置き

A/D Converter制御用コードを作成したので載せます。
今回使用したICはAD7982です。
変換時間はadc_tconv,回路遅延によるデータ取得の同期ずれはadc_taqcで調整してます。
あとは普通のSPIです。

AD7982.vhd
library IEEE;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;

entity ad7982 is
port(
        clk:    in  std_logic;
        cnv_st: in  std_logic;
        cnv_ed: out std_logic;
        dres:   out std_logic_vector(17 downto 0);
        --To AD5542
        sdi:    out std_logic;
        sdo:    in  std_logic;
        cnv:    out std_logic;
        sclk:   out std_logic
    );
end ad7982;

Architecture RTL of ad7982 is

signal   pclk:          std_logic :='0';
constant pclk_cnt_bit:  integer := 4;
constant pclk_cnt_max:  std_logic_vector(pclk_cnt_bit -1 downto 0) :=x"e";
signal   pclk_cnt:      std_logic_vector(pclk_cnt_bit -1 downto 0) :=(others=>'0');

constant adc_bit:       integer :=18;
constant adc_rectime:   std_logic_vector(5 downto 0) := "011000"; --24
constant adc_cnt_zero:  std_logic_vector(5 downto 0) := (others=>'0'); --0
constant adc_tconv:     std_logic_vector(5 downto 0) := "111111"; --63
constant adc_taqc:      std_logic_vector(5 downto 0) := "010011"; --19
signal   adc_cnv:       std_logic :='0';
signal   adc_cnvcnt:    std_logic_vector(5 downto 0) := (others=>'0'); --0
signal   adc_cnvend:    std_logic :='0';
signal   adc_data:      std_logic_vector(adc_bit-1 downto 0) :=(others=>'0');
signal   adc_data_spl:  std_logic_vector(adc_bit-1 downto 0) :=(others=>'0');
signal   adc_streamcnt: std_logic_vector(5 downto 0) := (others=>'0');
signal   adc_streamend: std_logic :='0';
signal   adc_start:     std_logic :='0';

signal sclk_in: std_logic :='0';
signal cnv_in:  std_logic :='0';
signal sdi_in:  std_logic :='0';
signal rec_ed:  std_logic :='0';

--state machine
signal adc_state:   std_logic_vector(1 downto 0) :=(others=>'0');
    --state 0: wait;
    --state 1: change a clock source from main one to divided one;
    --state 2: conv start;
    --state 3: data acquistion;

begin

--to ad5542a
sclk <= sclk_in;
sdi<=sdi_in;
cnv<=adc_cnv;
dres<=adc_data_spl;
cnv_ed<=rec_ed;

--From master system 
adc_start <= cnv_st;

--Clock divider
process(clk) begin
    if clk'event and clk='1' then
        --Timing manager for clock generator
        if pclk_cnt=pclk_cnt_max then pclk_cnt <= (others=>'0');
        else pclk_cnt <= pclk_cnt+'1'; end if;
        --Clock generator
        if pclk_cnt=pclk_cnt_max then pclk <= not pclk;
        else pclk <= pclk; end if;
    end if;
end process;

--State machine
process(clk) begin
    if clk'event and clk='1' then
        case adc_state is
            when "00" =>
                if adc_start='1' then adc_state <="01";
                else adc_state <= adc_state; end if;
            when "01" =>
                if adc_cnvend='1' then adc_state <="10";
                else adc_state <= adc_state; end if;
            when "10" =>
                if pclk_cnt=pclk_cnt_max and pclk='1' then adc_state <= "11";
                else adc_state <= adc_state; end if;
            when "11" =>
                if rec_ed='1' then adc_state <="00";
                else adc_state <= adc_state; end if;
            when others =>
                adc_state <= adc_state;
        end case;


    end if;
end process;

--Communicator
process(clk) begin
    if clk'event and clk='1' then


        --Conv start
        if adc_state="01" then
            --Stream Data
            if adc_cnvcnt="000001" then adc_cnv <= '1';
            elsif adc_cnvcnt=adc_tconv then adc_cnv <= '0';
            else adc_cnv<=adc_cnv; end if;
            --Stream Count
            if adc_cnvcnt=adc_tconv then adc_cnvcnt<=(others=>'0');
            else adc_cnvcnt <= adc_cnvcnt+'1'; end if;
            --Conv End Flag
            if adc_cnvcnt=adc_tconv then adc_cnvend<='1';
            else adc_cnvend <= '0'; end if;
        else
            adc_cnv<='0';
            adc_cnvcnt <= (others=>'0');
            adc_cnvend <= '0';
        end if;

        --Data acquisition
        if pclk_cnt=pclk_cnt_max and pclk='1' then
            if adc_state="11" then
                if adc_streamcnt=adc_cnt_zero then adc_data <= (others=>'0');
                else adc_data <= adc_data(adc_bit-2 downto 0) & sdo; end if;
            else adc_data <= (others=>'0'); end if;
            if adc_streamcnt=adc_taqc then adc_data_spl<=adc_data; else adc_data_spl<=adc_data_spl; end if;
        end if;

        --Data acquisition
        if pclk_cnt=pclk_cnt_max and pclk='1' then
            if adc_state="11" then
                --Stream Count
                if adc_streamcnt=adc_rectime then adc_streamcnt<=(others=>'0');
                else adc_streamcnt <= adc_streamcnt+'1'; end if;
                --Stream End Flag
                if adc_streamcnt=adc_rectime then adc_streamend<='1';
                else adc_streamend <= '0'; end if;
                --Receive end
                if adc_streamcnt=adc_rectime then rec_ed<='1';
                else rec_ed<='0'; end if;
            else
                adc_streamcnt <= (others=>'0');
                adc_streamend <= '0';
                rec_ed<='0';
            end if;
        end if;

    end if;
end process;

sclk_in <= pclk  when adc_state ="11" else '0';
sdi_in <= '1'    when adc_state/="00" else '0';

end RTL;
AD7982.vhd
module ad7982(clk,cnv_st,dres,sdo,sdi,sclk,conv);

input clk,cnv_st;
output [17:0] dres;
input sdo;
output sdi,sclk,conv;

reg         pclk;   initial pclk=0;
reg [3:0]   pcnt;   initial pcnt=2'b0;
parameter   pmax=4'h4;

always@(posedge clk)
begin
    pcnt<=(pcnt==pmax)? 4'h0:pcnt+4'h1;
    pclk<=(pcnt==pmax)? ~pclk:pclk;
end

reg [1:0]   state;      initial state=2'b0;
reg [6:0]   convcnt;    initial convcnt=7'b0;
reg         convend;    initial convend=1'b0;
parameter [6:0] convtime=7'd75;
reg [4:0]   reccnt;     initial reccnt=5'b0;
reg         recend;     initial recend=1'b0;
reg [17:0]  recdata;    initial recdata=18'b0;
reg [17:0]  getdata;    initial getdata=18'b0;
parameter [4:0] rectime=5'd22;


assign sdi=(state!=2'b00)? 1'b1:1'b0;
assign conv=(convcnt!=7'd0&convcnt!=convtime)? 1'b1:1'b0;
assign sclk=(state==2'b11)? pclk:1'b0;
assign dres=getdata;

always@(posedge clk)
begin
    case(state)
        2'b00: state<=(cnv_st==1'b1)? state+2'b1:state;
        2'b01: state<=(convend==1'b1)? state+2'b1:state;
        2'b10: state<=(pcnt==pmax&pclk==1'b1)? state+2'b1:state;
        2'b11: state<=(recend==1'b1)? state+2'b1:state;
        default: state=2'b00;
    endcase 
end

always@(posedge clk)
begin

    if(state==2'b01)
    begin
        convcnt<=(convcnt==convtime)? convcnt:convcnt+7'd1;
        convend<=(convcnt==convtime)? 1'b1:1'b0;
    end
    else begin convcnt<=7'd0; convend<=1'b0; end

    if(pcnt==pmax&pclk==1'b1)
    begin       
        if(state==2'b11)
        begin
            reccnt<=(reccnt==rectime)? reccnt:reccnt+5'b1;
            recend<=(reccnt==rectime)? 1'b1:1'b0;
        end
        else begin reccnt<=5'd0; recend<=1'b0; end 
    end 

    if(pcnt==pmax&pclk==1'b1)
    begin       
        if(state==2'b11)
        begin
            recdata<=(reccnt==5'd0)? 18'b0:{recdata[16:0],sdo};
            getdata<=(reccnt==5'd19)? recdata:getdata;
        end
        else begin recdata<=18'b0; getdata<=getdata; end
    end 

end

endmodule
6
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
3