Jump to content
  • 0

MCP3008 Interfaced with basys 3 using SPI


Archana Narayanan

Question

Hello, 

I am trying to interface MCP3008 with basys 3 using SPI and store the values in a FIFO and transmit the values to PC using UART.

Initially, I designed for ADC to  convert input waveform and display results by increment or decrements of LED's. The MCP3008 ADC clock is 1.3 MHz clock. 

This works and led's increment as the amplitude of the input waveform is increased from signal generator . But when i receive through UART and plot on  SerialPlot , the signal is distorted

please find the code for ADC below:

entity ADC is
  port
    (
      -- command input
      clock    : in  std_logic;         -- 100MHz onboard oscillator
     trigger  : in  std_logic;         -- assert to sample ADC
      diffn    : in  std_logic;         -- single/differential inputs
      channel  : in  std_logic_vector(2 downto 0);  -- channel to sample
      -- data output
      Dout     : out std_logic_vector(14 downto 0);  -- data from ADC
      OutVal   : out std_logic;         -- pulsed when data sampled
      -- ADC connection
      adc_miso : in  std_logic;         -- ADC SPI MISO
      adc_mosi : out std_logic;         -- ADC SPI MOSI
      adc_cs   : out std_logic;         -- ADC SPI CHIP SELECT
      adc_clk  : out std_logic          -- ADC SPI CLOCK
      );

end ADC;

 architecture behavioural ofADC is

  -- clock
  signal adc_clock : std_logic := '0';

  -- command
  signal trigger_flag : std_logic                    := '0';
  signal sgl_diff_reg : std_logic;
  signal channel_reg  : std_logic_vector(2 downto 0) := (others => '0');
  signal done         : std_logic                    := '0';
  signal done_prev    : std_logic                    := '0';

  -- output registers
  signal val : std_logic                    := '0';
  signal D   : std_logic_vector(9 downto 0) := (others => '0');

  -- state control
  signal state     : std_logic                    := '0';
  signal spi_count : unsigned(4 downto 0)         := (others => '0');
  signal Q         : std_logic_vector(9 downto 0) := (others => '0');
  
begin

    -- clock divider
  -- input clock: 100Mhz    
  --100MHz/1.3MHz = 74/2
  -- adc clock: 1.3MHz 
  clock_divider : process(clock)
  variable cnt : integer := 0;
  begin
    if rising_edge(clock) then
      cnt := cnt + 1;
      if cnt = 37 then
        cnt := 0;
        adc_clock <= not adc_clock;
      end if;
    end if;
  end process;

  -- produce trigger flag
  trigger_cdc : process(adc_clock)
  begin
    if rising_edge(adc_clock) then
    if trigger = '1' and state = '0' then
      sgl_diff_reg <= diffn;
      channel_reg  <= channel;
      trigger_flag <= '1';
    elsif state = '1' then
      trigger_flag <= '0';
    end if;
  end if;
end process;

  adc_clk <= adc_clock;
  adc_cs  <= not state;

  -- SPI state machine (falling edge)
  adc_sm : process(adc_clock)
  begin
    if adc_clock'event and adc_clock = '0' then
      if state = '0' then
        done <= '0';
        if trigger_flag = '1' then
          state <= '1';
        else
          state <= '0';
        end if;
      else
        if spi_count = "10000" then
          spi_count <= (others => '0');
          state     <= '0';
          done      <= '1';
        else
          spi_count <= spi_count + 1;
          state     <= '1';
        end if;
      end if;
    end if;
  end process;

  -- Register sample 
  outreg : process(adc_clock)
  begin
    if rising_edge(adc_clock) then
      done_prev <= done;
      if done_prev = '0' and done = '1' then
        D   <= Q;
        Val <= '1';
      else
        Val <= '0';
      end if;
    end if;
  end process;
  
  -- LED outputs
 PROCESS (adc_clock)
                 
               BEGIN
               IF (adc_clock'EVENT AND adc_clock = '1') THEN
               
               CASE D(9 DOWNTO 6) IS
                                       WHEN "0001" =>
                                          Dout <= "000000000000011";
                                       WHEN "0010" =>
                                          Dout <= "000000000000111";
                                       WHEN "0011" =>
                                         Dout<= "000000000001111";
                                       WHEN "0100" =>
                                         Dout <= "000000000011111";
                                       WHEN "0101" =>
                                          Dout <= "000000000111111";
                                       WHEN "0110" =>
                                          Dout <= "000000001111111";
                                       WHEN "0111" =>
                                         Dout <= "000000011111111";
                                       WHEN "1000" =>
                                          Dout <= "000000111111111";
                                       WHEN "1001" =>
                                          Dout <= "000001111111111";
                                       WHEN "1010" =>
                                          Dout <= "000011111111111";
                                       WHEN "1011" =>
                                          Dout <= "000111111111111";
                                       WHEN "1100" =>
                                          Dout <= "001111111111111";
                                       WHEN "1101" =>
                                          Dout <= "011111111111111";
                                       WHEN "1110" =>
                                          Dout <= "111111111111111";
                                       WHEN "1111" =>
                                         Dout <= "111111111111111";
                                       WHEN OTHERS =>
                                          Dout <= "000000000000001";
                                    END CASE;
                                 END IF;
                             -- END IF;
                           END PROCESS;
               
               
          
  OutVal <= Val;

  -- MISO shift register (rising edge)
  shift_in : process(adc_clock)
  begin
    if adc_clock'event and adc_clock = '1' then
      if state = '1' then
        Q(0)          <= adc_miso;
        Q(9 downto 1) <= Q(8 downto 0);
      end if;
    end if;
  end process;

  -- Decode MOSI output
  shift_out : process(state, spi_count, sgl_diff_reg, channel_reg)
  begin
    if state = '1' then
      case spi_count is
        when "00000" => adc_mosi <= '1';  -- start bit
        when "00001" => adc_mosi <= sgl_diff_reg;
        when "00010" => adc_mosi <= channel_reg(2);
        when "00011" => adc_mosi <= channel_reg(1);
        when "00100" => adc_mosi <= channel_reg(0);
        when others  => adc_mosi <= '0';
      end case;
    else
      adc_mosi <= '0';
    end if;
  end process;

end behavioural;
    
    --much of the code is of credit to micronova electronics.

For fifo, I use the Xilinx IP fifo generator with no FWFT working on 100Mhz clock both on write and read sides.

FIFO width = 10

Depth = 2046 and tried increasing upto 131072 with no progress.

This is my top level code with UART

entity top_module is
  Generic (
      PARITY_BIT  : string := "none" -- type of parity
  );

	port(
		clk, rst,trigger,diffn: in std_logic;
		adc_mosi,adc_clk,adc_cs : out std_logic;
		adc_miso : in std_logic;
		channel : in std_logic_vector ( 2 downto 0);
		 wr_uart,uart_clk_en : in std_logic;
		  WriteEn	, ReadEn : in std_logic;
		full,  empty : out std_logic;
		--w_data: in std_logic_vector(7 downto 0);
		Dout : inout std_logic_vector(9 downto 0);
		busy : out std_logic;
		tx,OutVal: out std_logic
	);
end top_module;

architecture structural of top_module is
signal 	fifo_data_out	: STD_LOGIC_VECTOR (9 downto 0);

 component fifo is
  port (
    reset_rtl_0 : in STD_LOGIC;
    clk_100MHz : in STD_LOGIC;
    full_0 : out STD_LOGIC;
    din_0 : in STD_LOGIC_VECTOR ( 9 downto 0 );
    wr_en_0 : in STD_LOGIC;
    empty_0 : out STD_LOGIC;
    dout_0 : out STD_LOGIC_VECTOR ( 9 downto 0 );
    rd_en_0 : in STD_LOGIC
  );
  end component fifo;

begin
	MercuryADC : entity work.ADC 
	
	port map (
     clock   => clk,
    trigger  => trigger,
    diffn  =>  diffn,
    channel => channel,
    -- data output
    Dout   => Dout,
    OutVal  => Outval,
    -- ADC connection
    adc_miso => adc_miso,
    adc_mosi =>  adc_mosi,
    adc_cs  => adc_cs,
    adc_clk  =>adc_clk 
    );
	
    
fifo_i: component fifo
     port map (
      clk_100MHz => clk,
      din_0(9 downto 0) => Dout(9 downto 0),
      dout_0(9 downto 0) => fifo_data_out(9 downto 0),
      empty_0 => empty,
      full_0 => full,
      rd_en_0 => ReadEn,
      reset_rtl_0 => rst,
      wr_en_0 => WriteEn
    );
                    

			     
		uart_trx : entity work.UART_TX	     
	    Port map (
                     CLK         => clk, -- system clock
                     RST         => rst, -- high active synchronous reset
                     -- UART INTERFACE
                     UART_CLK_EN => uart_clk_en, -- oversampling (16x) UART clock enable
                     UART_TXD    => tx, -- serial transmit data
                     -- USER DATA INPUT INTERFACE
                     DATA_IN    =>fifo_data_out (9 downto 2) , -- input data
                     DATA_SEND   => wr_uart,-- when DATA_SEND = 1, input data are valid and will be transmit
                     BUSY        => busy  -- when BUSY = 1, transmitter is busy and you must not set DATA_SEND to 1
      );
	


end structural;


PFA the schematic of my design and waveform as well.

input is 650 hz and Vpp= 1.5V; continuous sine wave.

My output waveform appears to be distorted.

  • I'm not sure if there has to be a delay incorporated while sampling the input signal or a is the issue between FIFO and UART.
  • When WriteEn signal is asserted on FIFO, the full flag is asserted at the same instant, does that mean the size of FIFO is not enough.

Kindly help, any inputs will be appreciated.

 

schematic.JPG

650Hz_1.5Vpp_sine_wave.JPG

MCP3008(3).pdf

Link to comment
Share on other sites

4 answers to this question

Recommended Posts

11 hours ago, jpeyron said:

Hi @Archana Narayanan,

Welcome to the forums. I do not see anything directly wrong with your project. I would first try  expanding the FiFO size.  What is the UART baud rate? To confirm you are using Pmod VDD to power the MCP3008 at 3.3V.

thank you,

Jon

Hi @jpeyron,

I tried using a FIFO with width=10 and depth 131087. I get the same result. PFA the summary of FIFO IP that I used. The sampling frequency of ADC is 75ksps at 2.7V My input is 650 hz and Vpp= 1.5V; continuous sine wave.

I'm using the Pmod VDD at 3.3V to power the ADC. The baud rate is set to 9600 for UART.

Thanks, 

Archana

 

fifo_summary.JPG

Link to comment
Share on other sites

Hi @Archana Narayanan,

In situations like this I like to break down the different parts of the project and make sure that they are working individually. I would verify that the  data from the MCP3008 to the Basys 3 is correct. I typically do this by using exposing the signals using a bread board, or something like the Pmod TPH2, and a logic analyzer.  I would also verify the UART and the FIFO.

thank you,

Jon

Link to comment
Share on other sites

20 hours ago, jpeyron said:

Hi @Archana Narayanan,

In situations like this I like to break down the different parts of the project and make sure that they are working individually. I would verify that the  data from the MCP3008 to the Basys 3 is correct. I typically do this by using exposing the signals using a bread board, or something like the Pmod TPH2, and a logic analyzer.  I would also verify the UART and the FIFO.

thank you,

Jon

Hey @jpeyron

Yes, looks like that would be the right path to isolate the cause.

Thanks, will try that.

-Archana

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.

×
×
  • Create New...