Analog Wing

From Hamsterworks Wiki!

Jump to: navigation, search

This FPGA Project was completed in December 2013.

Analog wing.jpg

This is the newest version of the Papilio ADC Wing - using the ADC128S102 8-channel 12-bit ADC which is capable of up to a million samples per second. The major difference between this and the earlier version is the row of 5V pins, allowing it to be used without soldering with any of the analogue Grove sensors available from Seeed Studios (however you must use the "Grove - Electronic brick 3 pin to Grove 4 pin converter cable", also available from Seeed).

The interface to the ADC is very simple - CS is asserted (Active Low) and bits are clocked in and out 16 bits at a time. The input to the ADC has only three bits that are used - bits 2,3 and 4 indicate which channel will be converted and output during the next frame. For the output bits 4 to 15 are is the result of the current data conversion (MSB first). This is the timing diagram, snipped from the Texas Instruments datasheet:

Adc interface.jpg

The full datasheet can be found at http://www.mouser.com/ds/2/282/snas298d-57361.pdf

Conversions can be performed back-to-back, however for me this caused problems with synchronization between the FPGA and the ADC seemed to be random even though I carefully sequenced when CS would be dropped. To solve this issue, after the 8th conversion I have 16 clock cycles with CS held high.

Here is a simulation trace of the full cycle of reading of all 8 channels:

Analog trace.jpg

Contents

Source Code

Grove_analogue.vhd

----------------------------------------------------------------------------------
-- Engineer:    Mike Field <hamster@snap.net.nz>
-- Module Name: grove_analogue - Behavioral 
--
-- Description: Read the values on all channels as quickly as possible. 
--              clk32 should be 32MHz, (between 16MHz and 32Mhz is within spec 
--              for the DAC)
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

entity grove_analogue is
    Port ( clk32     : in  STD_LOGIC;
           adc_clk   : out  STD_LOGIC;
           adc_dout  : in  STD_LOGIC;
           adc_din   : out  STD_LOGIC;
           adc_cs    : out  STD_LOGIC;
           c0_data   : out  STD_LOGIC_VECTOR (11 downto 0);
           c0_strobe : out  STD_LOGIC;
           c1_data   : out  STD_LOGIC_VECTOR (11 downto 0);
           c1_strobe : out  STD_LOGIC;
           c2_data   : out  STD_LOGIC_VECTOR (11 downto 0);
           c2_strobe : out  STD_LOGIC;
           c3_data   : out  STD_LOGIC_VECTOR (11 downto 0);
           c3_strobe : out  STD_LOGIC;
           c4_data   : out  STD_LOGIC_VECTOR (11 downto 0);
           c4_strobe : out  STD_LOGIC;
           c5_data   : out  STD_LOGIC_VECTOR (11 downto 0);
           c5_strobe : out  STD_LOGIC;
           c6_data   : out  STD_LOGIC_VECTOR (11 downto 0);
           c6_strobe : out  STD_LOGIC;
           c7_data   : out  STD_LOGIC_VECTOR (11 downto 0);
           c7_strobe : out  STD_LOGIC);
end grove_analogue;

architecture Behavioral of grove_analogue is
   signal count     : unsigned(4 downto 0) := "10000";
   signal channel   : unsigned(3 downto 0) := "0000";
   signal cs        : std_logic := '1';
   signal captured  : std_logic_vector(11 downto 0) := (others => '0');
   signal receiving : unsigned(3 downto 0) := "1000";
   signal requested : unsigned(3 downto 0) := "1000";

begin
   adc_cs <= cs;

process(clk32)
   begin
      if rising_edge(clk32) then
         -- Generate the serial clock
         adc_clk <= not count(0);
         
         if count(0) = '0' then
            captured <= captured(captured'high-1 downto 0) & adc_dout;
         end if;
         
         -- Default to no strobe being asserted
         c0_strobe <= '0'; c1_strobe <= '0'; c2_strobe <= '0'; c3_strobe <= '0';
         c4_strobe <= '0'; c5_strobe <= '0'; c6_strobe <= '0'; c7_strobe <= '0';
         
         -- Assert a strobe when a frame is received 
         if count = "00000" then  
            case receiving is
               when "0000" => c0_data <= captured; c0_strobe <= '1';
               when "0001" => c1_data <= captured; c1_strobe <= '1';
               when "0010" => c2_data <= captured; c2_strobe <= '1';
               when "0011" => c3_data <= captured; c3_strobe <= '1';
               when "0100" => c4_data <= captured; c4_strobe <= '1';
               when "0101" => c5_data <= captured; c5_strobe <= '1';
               when "0110" => c6_data <= captured; c6_strobe <= '1';
               when "0111" => c7_data <= captured; c7_strobe <= '1';
               when others =>
            end case;
         end if;
         
         case count is 
            when "00011" => adc_din   <= channel(2);
            when "00101" => adc_din   <= channel(1);
            when "00111" => adc_din   <= channel(0);
            when "01001" => adc_din   <= '0';
            when "11111" => receiving <= requested;
                            requested <= channel;
                            
                            -- On the ninth frame set CS to '1', ensuring that the DAC is in sync
                            if channel = "1000" then
                              cs        <= '1';
                            else
                              cs        <= '0';
                            end if;
                            adc_din   <= '0';

                            -- If we have just had the 9th (sync) frame then drop back to channel 0
                            if channel = "1000" then
                              channel <= (others => '0');
                            else
                              channel   <= channel+1;
                            end if;
            when others =>
         end case;        
         count <= count + 1;
      end if;
   end process;
end Behavioral;

Grove_analogue_demo.vhd

----------------------------------------------------------------------------------
-- Company: 
-- Engineer: Mike Field <hamster@snap.net.nz>
-- Module Name:    grove_analogue_demo
--
-- Description: Top level for the Analog Wing demo - display the ADC selected by 
--              the switches on the LEDs 
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity grove_analogue_demo is
    Port ( clk32    : in  STD_LOGIC;
           adc_clk  : out  STD_LOGIC;
           adc_cs   : out  STD_LOGIC;
           adc_din  : out  STD_LOGIC;
           adc_dout : in  STD_LOGIC;
           leds     : out  STD_LOGIC_VECTOR (7 downto 0);
           switches : in  STD_LOGIC_VECTOR (7 downto 0));
end grove_analogue_demo;

architecture Behavioral of grove_analogue_demo is
	COMPONENT grove_analogue
	PORT(
		clk32     : IN std_logic;
		adc_dout  : IN std_logic;          
		adc_clk   : OUT std_logic;
		adc_din   : OUT std_logic;
		adc_cs    : OUT std_logic;
		c0_data   : OUT std_logic_vector(11 downto 0);
		c0_strobe : OUT std_logic;
		c1_data   : OUT std_logic_vector(11 downto 0);
		c1_strobe : OUT std_logic;
		c2_data   : OUT std_logic_vector(11 downto 0);
		c2_strobe : OUT std_logic;
		c3_data   : OUT std_logic_vector(11 downto 0);
		c3_strobe : OUT std_logic;
		c4_data   : OUT std_logic_vector(11 downto 0);
		c4_strobe : OUT std_logic;
		c5_data   : OUT std_logic_vector(11 downto 0);
		c5_strobe : OUT std_logic;
		c6_data   : OUT std_logic_vector(11 downto 0);
		c6_strobe : OUT std_logic;
		c7_data   : OUT std_logic_vector(11 downto 0);
		c7_strobe : OUT std_logic
		);
	END COMPONENT;
   signal c0_data : std_logic_vector(11 downto 0);
   signal c1_data : std_logic_vector(11 downto 0);
   signal c2_data : std_logic_vector(11 downto 0);
   signal c3_data : std_logic_vector(11 downto 0);
   signal c4_data : std_logic_vector(11 downto 0);
   signal c5_data : std_logic_vector(11 downto 0);
   signal c6_data : std_logic_vector(11 downto 0);
   signal c7_data : std_logic_vector(11 downto 0);

   attribute IOB: string;
   attribute IOB of adc_cs  : signal is "true";
   attribute IOB of adc_clk  : signal is "true";
   attribute IOB of adc_din  : signal is "true";
   attribute IOB of adc_dout : signal is "true";

begin

select_channel: process(switches, c0_data, c1_data, c2_data, c3_data, c4_data, c5_data, c6_data, c7_data)
   begin
      case switches(3 downto 0) is
         when "0000" => leds <= c0_data(11 downto 4);
         when "0001" => leds <= c1_data(11 downto 4);
         when "0010" => leds <= c2_data(11 downto 4);
         when "0011" => leds <= c3_data(11 downto 4);
         when "0100" => leds <= c4_data(11 downto 4);
         when "0101" => leds <= c5_data(11 downto 4);
         when "0110" => leds <= c6_data(11 downto 4);
         when "0111" => leds <= c7_data(11 downto 4);
         when others => leds <= (others => '0');
      end case;
   end process;
   
Inst_grove_analogue: grove_analogue PORT MAP(
		clk32     => clk32,
		adc_clk   => adc_clk,
		adc_dout  => adc_dout,
		adc_din   => adc_din,
		adc_cs    => adc_cs,
		c0_data   => c0_data,
		c1_data   => c1_data,
		c2_data   => c2_data,
		c3_data   => c3_data,
		c4_data   => c4_data,
		c5_data   => c5_data,
		c6_data   => c6_data,
		c7_data   => c7_data,
		c0_strobe => open,
		c1_strobe => open,
		c2_strobe => open,
		c3_strobe => open,
		c4_strobe => open,
		c5_strobe => open,
		c6_strobe => open,
		c7_strobe => open
	);
end Behavioral;

Papilio.ucf

This is the constraints file for the Papilio One plus the LogicStart Megawing and the Analogue wing, set up as shown below, with LogicStart's LEDs and switches on the 16-bit 'B' slot, and the ADC wing on the 8-bit 'CH' slot:

Adc test setup.jpg

NET CLK32       LOC="P89"  | IOSTANDARD=LVTTL | PERIOD=31.25ns;               # CLK

NET switches(7) LOC="P85" | IOSTANDARD=LVTTL;
NET switches(6) LOC="P83" | IOSTANDARD=LVTTL;
NET switches(5) LOC="P78" | IOSTANDARD=LVTTL;
NET switches(4) LOC="P71" | IOSTANDARD=LVTTL;
NET switches(3) LOC="P68" | IOSTANDARD=LVTTL;
NET switches(2) LOC="P66" | IOSTANDARD=LVTTL;
NET switches(1) LOC="P63" | IOSTANDARD=LVTTL;
NET switches(0) LOC="P61" | IOSTANDARD=LVTTL;
NET LEDs(7)     LOC="P58" | IOSTANDARD=LVTTL;
NET LEDs(6)     LOC="P54" | IOSTANDARD=LVTTL;
NET LEDs(5)     LOC="P41" | IOSTANDARD=LVTTL;
NET LEDs(4)     LOC="P36" | IOSTANDARD=LVTTL;
NET LEDs(3)     LOC="P34" | IOSTANDARD=LVTTL;
NET LEDs(2)     LOC="P32" | IOSTANDARD=LVTTL;
NET LEDs(1)     LOC="P25" | IOSTANDARD=LVTTL;
NET LEDs(0)     LOC="P22" | IOSTANDARD=LVTTL; 

NET adc_cs      LOC="P12"  | IOSTANDARD=LVTTL;
NET adc_clk     LOC="P15"  | IOSTANDARD=LVTTL;
NET adc_dout    LOC="P16"  | IOSTANDARD=LVTTL;
NET adc_din     LOC="P17"  | IOSTANDARD=LVTTL;

The LogicStart also has the same ADC on it, but it does not have the 5V0 pins so can be quite awkward to use without as using a separate power supply...

Personal tools