ADC mit MCP3008 auf FPGA -

Ich versuche gerade, MCP3008 als ADC zu verwenden, aber aus irgendeinem Grund konvertiert er die Ausgabe nicht richtig. (Ein Anfängerprojekt).

Ich versorge es mit 3,3 V = vref = Vdd = ch0

Aber meine Ausgabe scheint nie => 1111111111 zu werden, sondern so etwas wie 1111010111 ...

Ich programmiere es auf einem FPGA mit VHDL.

FPGa-CLK: 50 MHz.

Hier ist der Code:

ibrary IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.numeric_std.all;
use IEEE.STD_LOGIC_ARITH.all;
use IEEE.STD_logic_unsigned.all;
use ieee.numeric_std.all;

entity main is
    Port ( MISO : in STD_LOGIC;
           MOSI : out STD_LOGIC;
           CS : out STD_LOGIC;
           SCLK : out STD_LOGIC;
           CLK : in STD_LOGIC;
           );
end main;

architecture Behavioral of main is
constant N : integer := 4;
signal prescaler_counter : integer range 0 to 50000000 := 0;
signal newClock : std_logic := '0';
signal TX :std_logic_vector(N downto 0) := "11000";
signal RX : std_logic_vector(9 downto 0) := "0000000000";
type state_type is (start,state2,state3,state4,state5);  --type of state machine.
signal state : state_type := start;
signal shift_counter: integer range 0 to 750:= N;
begin

prescaler01: process(clk, newClock)
begin
    if rising_edge(clk) then 
        if prescaler_counter < 1000000 then 
            prescaler_counter <= prescaler_counter + 1;
        else
            newClock <= not newClock;
            prescaler_counter <= 0;
       end if;
    end if;            
end process;

SCLK <= newClock;


SPI_state: process(newClock)
begin
   if falling_edge(newClock) then      
        case state is   
            when start =>
                CS <= '1';
                MOSI <= '0';
                busy <= '1';
                RX <= "0000000000";
            state <= state2;
            when state2 => -- Send init bits. 
                CS <= '0';
                shift_counter <= shift_counter - 1;
                TX <= TX(N-1 downto 0) & TX(N); 
                MOSI <= TX(N);
                if shift_counter = 0 then 
                   MOSI <= '0';
                   shift_counter<= 12;
                   state <= state3;
                end if;
            when state3 =>
                --MOSI <= '0';
                CS <= '0';              -- Last bit init bit;
                state <= state5; 
            when state4=>
                CS <= '0';              --T_sample from falling - falling
                state <= state5;     
            when state5=>
               CS <= '0';              -- Read
               if shift_counter = 0 then
                  MOSI <= '0';
                  shift_counter<= N;
                  busy <= '0';
                  state <= start;
              elsif shift_counter < 11 then 
                RX <=  RX(8 downto 0) & MISO;
                shift_counter <= shift_counter - 1;
              else
                 shift_counter <= shift_counter - 1;
              end if;
            when others =>    
                state <= start;           
        end case;
    end if;  
end process;

Ich denke, mein Timing könnte etwas daneben liegen. Obwohl ich es in Simulationen optimiert habe. Es macht also keinen Sinn, warum die Ausgabe nicht korrekt zu sein scheint.

Hilfe wird sehr geschätzt :).

Ich weiß, dass diese Frage aufgrund des Schwierigkeitsgrades der Frage viele negative Stimmen erhalten wird, aber ich muss irgendwo anfangen.

-bearbeiten-

Ich habe die Simulation ausprobiert, die Lincoln als Antwort gepostet hat, die zeigt, dass das Timing nicht ausgeschaltet ist. Ich habe ein debug_tx hinzugefügt, das zeigt, in welchem ​​Zustand sich das Programm gerade befindet.

Simulation

  • debug_tx := "0001" - setzt CS hoch, also wird die Eingabe zurückgesetzt.
  • debug_tx := "0010" - Initbit "11000" senden => Startbit + ADC ausführen, Eingang CH0.
  • debug_tx := "0100" - delay - Zeit, die für den ADC benötigt wird
  • Debug_tx := "1000" - Verzögerung - erstes Nullbit überspringen.
  • debug_tx := "1101" - read - 9 mal und Werteverschiebung nach links als solche durchführen.

Ich bin mir ziemlich sicher, dass etwas mit der Art und Weise, wie ich die Dinge verschiebe, nicht stimmt. Oder vielleicht etwas anderes.

  RX <=  RX(8 downto 0) & MISO;

Rx_Led zeigt den Binärwert des Ausgangs, den es liest. Es scheint, als würden die letzten beiden Verschiebungen jeweils 2 clk-Perioden blockiert ... Was seltsam erscheint.

Nebenbemerkung: Ich lege nur 3,3 V an das System an, aber ich habe den Takt auf 5 bis 10 Hz herunterskaliert, daher sollte es ein Problem mit der Zeitdifferenz beim Anlegen von 5 V oder 3 V geben.

Sie hatten Timing-Probleme in der Simulationsphase? Was lässt Sie denken, dass die Synthese besser wäre :). Haben Sie die Schnittstelle überhaupt eingeschränkt? Hast du schon mal deutlich niedrigere Taktraten probiert? Wie wäre es, wenn Sie einen Logikanalysator auf die Spi-Signale setzen oder wenn Sie keinen haben, Ihre Werkzeuge verwenden, um die Signale mit Instrumenten zu versehen und zu sehen, was wirklich vor sich geht?
Bedenken Sie auch, dass zum Lesen eines Bits von einem Spi-Gerät von einem FPGA oder einem ASIC Ihr Ausgangstakt hoch geht, was dann die Verzögerung Ihrer Spur durch die Eingangsverzögerung Ihres Slave-Geräts plus die Ausgangsverzögerungszeit bis zum Bit nach unten durchläuft gesucht wird, erscheint am Slave-Ausgang und wandert zurück zu Ihrem Chip/FPGA und durch seine Eingangsverzögerung zu jedem Register, an das Sie es angeschlossen haben. Um dies richtig zu erfassen, müssen Sie hier normalerweise Ihre eigene Verzögerung hinzufügen, damit dies entweder konstruktionsbedingt oder mit einigen Einschränkungen usw. funktioniert.
Wenn Sie sich einen kommerziellen Spi-Controller wie etwas von Synopsys ansehen, werden Sie sehen, dass sie für solche Dinge eine Verzögerung eingebaut haben. Nur ein Gedanke.
Schauen Sie sich diesen Link an caxapa.ru/thumbs/405687/av_54019.pdf Seite 8 spricht über das Verzögern der RX-Abtastzeit.

Antworten (1)

Ich bin der TA für eine digitale Designklasse, die den MCP3001 (die Einkanalversion dieses Adc) verwendet, und habe eine Testbench zum Debuggen von Problemen, die Studenten damit haben. Ich habe es für Ihr MCP3008-Beispiel modifiziert. Bitte versuchen Sie, Ihr Design damit zu testen.

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity adc_tb is
end adc_tb;

architecture arch of adc_tb is

  -- Component declaration of the tested unit
  component main
    port(
      clk      : in  std_logic;
      sclk     : out std_logic;
      miso     : in  std_logic;
      mosi     : out std_logic;
      cs       : out std_logic
      );
  end component;

  -- Stimulus signals - signals mapped to the input and inout ports of tested entity
  signal sclk  : std_logic := '0'; -- the sample clock

  signal clk  : std_logic := '0';
  signal dout : std_logic := 'Z';
  signal din  : std_logic;

  -- Observed signals - signals mapped to the output ports of tested entity
  signal cs       : std_logic;
  signal adc_data : std_logic_vector(9 downto 0);

  -- clock period
  constant period : time := 20 ns; -- 50 MHz clock

  -- constant data set that will be sent back as the ADC data  
  constant FIXED_DATA : std_logic_vector(9 downto 0) := std_logic_vector(to_unsigned(328,10));

  -- timing parameters from the datasheet
  constant T_HI   : time := 125 ns;     -- CLK high time
  constant T_LO   : time := 125 ns;     -- CLK low time
  constant T_SUCS : time := 100 ns;     -- CS Fall to first rising CLK edge
  constant T_DO   : time := 125 ns;  -- CLK fall to output data valid ( 125ns at 5V )
  constant T_EN   : time := 125 ns;  -- CLK fall to output enable ( 125ns at 5V )
  constant T_DIS  : time := 100 ns;     -- CS Rise to output disable
  constant T_CSH  : time := 270 ns;     -- CS disable time
  constant T_R    : time := 100 ns;     -- D_OUT rise time
  constant T_F    : time := 100 ns;     -- D_OUT fall time

begin

  ---- Unit Under Test port map
  UUT : main
    port map (
      clk      => clk,
      sclk     => sclk,
      miso     => dout,
      mosi     => din,
      cs       => cs
      );

  -- generate the clock                     
  clk <= not clk after period/2;

  -- emulate what the MCP3001 ADC is doing, by sending back some test data
  -- this process uses the timing diagram (Fig. 1) from 21293C.pdf
  process
    variable differential : boolean := false;
    variable channel_sel : unsigned(2 downto 0) := "000";
  begin
    -- Set the data line to HI-Z
    dout <= 'Z';

    -- wait until the CS is brought to '0', this starts the conversion.
    -- also check for an error where there is a rising edge that happens
    -- less than 100 ns after CS is brought to '0'
    wait until falling_edge(cs);
    if sclk = '0' then
      wait for T_SUCS;
      assert sclk = '0'
        report "Timing constraint Tsucs=100ns violated, clock rising edge must come atleast 100ns after CS transitions to '0'"
        severity error;
    else
      wait for T_SUCS;
    end if;

    -- wait for the start bit
    if din = '0' then
      wait until rising_edge(din);
    end if;

    -- handle the input mode and channel select
    -- setup and hold times are not checked
    wait until falling_edge(sclk);
    wait until rising_edge(sclk);
    if din = '1' then
      differential := false;
    else
      differential := true;
    end if;
    for i in 2 downto 0 loop
      wait until rising_edge(sclk);
      channel_sel(i) := din;
    end loop;
    if differential then
      report "sampling in differential mode on channel " & integer'image(to_integer(channel_sel));
    else
      report "sampling in differential mode on channel " & integer'image(to_integer(channel_sel));
    end if;

    -- sample time...
    wait until falling_edge(sclk);
    wait until falling_edge(sclk);
    wait for T_EN; -- small delay time after falling edge from datasheet
    dout <= '0';

    -- output the converted data MSB first after every falling edge.
    -- also check for a likely problem where the CS is not held at '0' while
    -- reading all 10 bits of data.
    for i in 9 downto 0 loop
      wait until falling_edge(sclk);
      wait for T_DO; -- small delay time after falling edge from datasheet
      dout <= FIXED_DATA(i);
      assert cs = '0'
        report "CS needs to be held at '0', not all bits have been transmitted"
        severity warning;
    end loop;

    -- wait for CS to go back high then disable the output
    wait until rising_edge(cs);
    wait for T_DIS;
    dout <= 'Z';

    -- wait for the minimum delay time before the start of the next sample.
    -- also check for a likely error, where CS is only '1' for a single
    -- 320ns clock period
    wait for T_CSH-T_DIS;
    assert cs = '1'
      report "Timing Constraint Tcsh=350ns violated, CS needs to be held to '1' for atleast 350ns before transitioning to '0'"
      severity error;

  end process;

end arch;
Danke für den TB, ist er auf eine Eingangsspannung von 5 V oder 3,3 V eingestellt, da dies auch das Timing beeinflussen würde ...
Ich habe ein wenig geändert ... aber es scheint mir nicht die gewünschte Ausgabe auf der Hardware zu geben, wie die Simulation mir zeigt.
Bitte schau dir mein Update an.
Komischerweise hatte ich dieses Problem das letzte Mal, als ich das Labor gemacht habe, und ich hatte den ADC falsch verdrahtet. Prüfe das. Auch für Ihre Simulation möchten Sie vielleicht die Daten in Ihrem Schieberegister ausgeben, damit wir überprüfen können, ob es richtig funktioniert. Die Testbench, die ich Ihnen gegeben habe, hat 328 als ADC-Daten (fixed_data) zurückgegeben. Stellen Sie sicher, dass Sie diesen Wert erhalten. Es sieht so aus, als ob dies geändert wurde, da die Simulation fixed_data=1023 zeigt.
Bitte schauen Sie sich mein Update an. RX_LED zeigt die Ausgabe, wenn jedes Bit nach links verschoben wird. Ich habe Vdd = Vref = CH0 mit demselben Pin verbunden, was mir eine binäre Ausgabe von "1111111111" geben sollte ... was es manchmal tut, aber die Antwort ist nicht so stabil, wie ich dachte.
Es scheint, als hätte ich nach dem ersten Null-Bit ein 0-Bit gelesen. Dies könnte daran liegen, dass ich die Werte bei einer fallenden Flanke lese und nicht bei einer steigenden Flanke. Die Verzögerung beim Lesen von 0 anstelle des gerade eingestellten Werts wird erzeugt.
könnte es die fehlende T_suck-Zeit sein?
Ich habe das T_suck-Timing hinzugefügt. Das hat es sehr verbessert, wenn ich es mit einer Frequenz betrachte. von 3,57 ist es perfekt => LED-weise, aber wenn ich die Zeit nach unten stelle, kommt es zu einem Fehler zwischen den Konvertierungen ...
Haben Sie versucht, mit dieser Testbench eine Post-Place- und Route-Timing-Simulation durchzuführen? Es könnte Ihnen eine bessere Vorstellung davon geben, was schief läuft.
Ich bin mir nicht sicher, wie ich das machen soll?