SPI ADC

From Hamsterworks Wiki!

Jump to: navigation, search

This FPGA Project Was added December 2012.

Read a sample from an 8 channel SPI ADC

Spi a2d.png

A2D conversion starts and the address for the next conversion is sent when "counter" rolls over, and due to the way the ADC works the previous sample is then displayed on the LEDs.

If you change "switches" to request data on a different channel then there is a delay of one acquisition before the channel switch occurs.

atod.c

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;

entity AtoD is
   port
   (
      clk        : IN  std_logic;
      -- user interface
      switches   : IN  std_logic_vector(2 downto 0);
      leds       : OUT std_logic_vector(7 downto 0);
      -- Signals to the ADC
      ADC_CS_N   : OUT std_logic;
      ADC_SCLK   : OUT std_logic;
      ADC_DIN    : OUT std_logic;
      ADC_DOUT   : IN  std_logic
   );
end entity;

architecture rtl of AtoD is
   -- Counter - the lowest 6 bits are used to control signals to the ADC.
   -- The rest are used to activate the ADC when 0
   signal counter          : std_logic_vector(22 downto 0) := "11111111111111111100000"; --(others =>'0');
   
   -- shift registers fo delay output signals
   signal clk_shiftreg     : std_logic_vector( 1 downto 0) := (others =>'0');
   signal dataout_shiftreg : std_logic_vector( 2 downto 0) := (others =>'0');
   
   -- shift register to collect incoming bits
   signal datain_shiftreg  : std_logic_vector(11 downto 0) := (others =>'0');

   -- register to hold the current channel
   signal channel_hold     : std_logic_vector( 2 downto 0) := (others =>'0');
   
   signal adc_active         : std_logic;
begin
   -- set outoging signals 
   adc_din  <= dataout_shiftreg(2);   
   adc_sclk <= clk_shiftreg(1);
   
   with counter(22 downto 6) select adc_active <= '1' when "00000000000000000",
                                                  '0' when others;
   
   process (clk)
   begin
      if rising_edge(clk) then
         -- A small shift register delays the clk by one cycle (31.25ns) to ensure timings are met.
         clk_shiftreg(1) <= clk_shiftreg(0);
         
         -- Including adc_cs_n in a clocked process to ensure that it is adc_cs is implemented in a flipflop
         adc_cs_n        <= not(adc_active);
         
         if adc_active = '1' then
            clk_shiftreg(0) <= counter(1);
         else 
            clk_shiftreg(0) <= '1';
         end if;

         -- This controls where we send out the address to the ADC (bits 2,3 and 4 of the stream)
         -- we use a short shift register to ensure that the ADC_DOUT transistions are delayed
         -- 31 ns or so from the clk transitions
         dataout_shiftreg(2 downto 1)  <= dataout_shiftreg(1 downto 0);         
         if adc_active = '1' then 
            case counter(5 downto 2) is
               when "0010" => dataout_shiftreg(0) <= channel_hold(2);
               when "0011" => dataout_shiftreg(0) <= channel_hold(1);
               when "0100" => dataout_shiftreg(0) <= channel_hold(0);
               when others => dataout_shiftreg(0) <= '0';
            end case;
            
            -- As counter(2) is used used to generate sclk, this test ensures that we 
            -- capture bits right in the middle of the clock pulse
            if counter(5 downto 0) = "000000" then
               channel_hold <= switches;
            end if;

            if counter(1 downto 0) = "11" then
               datain_shiftreg <= datain_shiftreg(10 downto 0) & adc_dout;
            end if;

            -- When we have captured the last bit it is the time to update the output.
            if counter(5 downto 0) = "111111" then
               -- Normally you would grab "datain_shiftreg(10 downto 0) & adc_dout" for 12 bits
               LEDs           <= datain_shiftreg(10 downto 3);
            end if;
         else
            dataout_shiftreg(0) <= '0';
         end if;
         

         counter <= counter+1;      
      end if;
   end process;
end rtl;

Personal tools