Forum: FPGA, VHDL & Co. SPI in VHDL, unerklärliches Verhalten


von Md M. (Firma: Potilatormanufaktur) (mdma)



Lesenswert?

Hallo,

ich möchte einen ADC vom Typ MCP3008 per SPI ansprechen. Dazu benutze 
ich folgenden Prozess, um eine Kommunikation zu erreichen, wie sie in 
FIGURE 6.1 auf Seite 21 des Datenblatts zu sehen ist:
1
  process(fxclk_signal)
2
    variable delay_counter0 : integer := 0; 
3
  begin
4
    if rising_edge(fxclk_signal) then
5
      case state is        
6
7
        when 0 =>
8
          TX_Data0_signal                 <= "000000011000000000000000";
9
          TX_Start0_signal                <= '1';
10
          lsi_in_strobe_signal            <= '0';
11
          if TX_Done0_signal = '1' then
12
            state                         <= 1;
13
          end if;
14
15
        when 1 =>
16
          TX_Start0_signal                <= '0';
17
          state                           <= 2;
18
19
        when 2 =>
20
          TX_Start0_signal                <= '1';
21
          if TX_Done0_signal = '1' then
22
            state                         <= 3;
23
          end if;
24
25
        when 3 =>
26
          TX_Start0_signal                <= '0';
27
          state                           <= 4;
28
29
        when 4 =>
30
          TX_Start0_signal                <= '1';
31
          if TX_Done0_signal = '1' then
32
            state                         <= 5;
33
          end if;
34
35
        when 5 =>
36
          led2(9 downto 0)                <= RX_Data0_signal(9 downto 0);
37
          lsi_out_data_signal(9 downto 0) <= RX_Data0_signal(9 downto 0);
38
          lsi_out_adress_signal           <= std_logic_vector(to_unsigned(0, 8));
39
          --lsi_in_strobe_signal            <= '1';
40
          TX_Start0_signal                <= '0';
41
          state                           <= 6;
42
43
        -- delay
44
        when 6 =>
45
          if delay_counter0 = delay0 then
46
            delay_counter0                := 0;
47
            state                         <= 0;
48
          else
49
            delay_counter0                := delay_counter0 + 1;
50
          end if;
51
52
        when others =>
53
54
      end case;
55
    end if;
56
  end process;

Ich habe noch nicht ganz begriffen, wie genau die SPI-Schnittstelle des 
MCP3008 arbeitet, insbesondere, weil es anscheinend einen Modus gibt, 
bei dem eine Abfrage 24 Takte braucht und einen, bei dem es 16 sind. 
Egal, ich habe das mit 24 Takten versucht: Hier wird also 
"000000011000000000000000" zum MCP3008 geschoben. Die erste '1' ist das 
Startbit. Die folgenden Bits '1000' bewirkt, dass Channel 0 single-ended 
abgerufen wird. Das funktioniert auch alles einigermaßen. Allerdings ist 
folgendes Problem aufgetreten: Ich habe versucht, in den Prozess direkt 
dahinter eine weitere Abfrage eines anderen Channels einzubauen, also 
die Anzahl der states verdoppelt und beim zweiten Durchlauf einfach die 
Bits angepasst. Es sollte also immer nacheinander ...Ch0-Ch1-Ch0-Ch1... 
abgefragt werden. Komischerweise kam dabei aber die Abfragesequenz 
...Ch0-Ch0-Ch1-Ch0-Ch0-Ch1... raus. Ich hab absolut nicht verstanden, 
wie das sein kann. Deshalb habe ich das ganze wieder auf eine einzige 
sich wiederholdende Abfrage reduziert und hinter die Abfrage mit einem 
counter ein delay gesetzt. Auch hier zeigt sich ein Fehler. Er ist aus 
den angehängten screenshots vom LA ersichtlich: Auf dem ersten Bild ist 
eine Abfrage zu sehen. Das Funktioniert grundsätzlich. Auf dem zweiten 
Bild habe ich etwas herausgezoomt. Man man sieht, dass nach jedem 
zweiten Durchlauf der delay nicht greift. Das kann ich mir nicht 
erklären, aber ich vermute, dass es die selbe Ursache hat wie mein 
Problem bei zwei Abfragen hintereinander. Was mach ich falsch?

VG

: Verschoben durch Moderator
von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Md M. schrieb:
> Er ist aus den angehängten screenshots vom LA ersichtlich
Was sagt denn die Simulation? Zeigt die den selben Effekt?

von Md M. (Firma: Potilatormanufaktur) (mdma)



Lesenswert?

Oh je, wie peinlich. Die genaue Ursache hab ich zwar nicht gefunden, 
aber die Simulation hat mich zumindest veranlasst, die ganzen 
überflüssigen states zu entfernen, die ich ursprünglich zum debuggen 
drin hatte. Ohne die läuft es jetzt wie es soll. Auf die Idee, den Code 
zu entrümpeln hätte ich natürlich auch vorher schonmal kommen können.
1
  process(fxclk_signal)
2
    variable delay_counter0 : integer := 0; 
3
  begin
4
    if rising_edge(fxclk_signal) then
5
      case state is        
6
7
        when 0 =>
8
          TX_Data0_signal                 <= "000000011000000000000000";
9
          TX_Start0_signal                <= '1';
10
          if TX_Done0_signal = '1' then
11
            TX_Start0_signal              <= '0';
12
            state                         <= 1;
13
          end if;
14
15
        when 1 =>
16
          led2(9 downto 0)                <= RX_Data0_signal(9 downto 0);
17
          lsi_out_data_signal(9 downto 0) <= RX_Data0_signal(9 downto 0);
18
          lsi_out_adress_signal           <= std_logic_vector(to_unsigned(0, 8));
19
          TX_Start0_signal                <= '0';
20
          if delay_counter0 = delay0 then
21
            delay_counter0                := 0;
22
            state                         <= 2;
23
          else
24
            delay_counter0                := delay_counter0 + 1;
25
          end if;
26
27
        when 2 =>
28
          TX_Data0_signal                 <= "000000011001000000000000";
29
          TX_Start0_signal                <= '1';
30
          if TX_Done0_signal = '1' then
31
            TX_Start0_signal              <= '0';
32
            state                         <= 3;
33
          end if;
34
35
        when 3 =>
36
          led2(19 downto 10)              <= RX_Data0_signal(9 downto 0);
37
          lsi_out_data_signal(9 downto 0) <= RX_Data0_signal(9 downto 0);
38
          lsi_out_adress_signal           <= std_logic_vector(to_unsigned(0, 8));
39
          TX_Start0_signal                <= '0';
40
          if delay_counter0 = delay0 then
41
            delay_counter0                := 0;
42
            state                         <= 0;
43
          else
44
            delay_counter0                := delay_counter0 + 1;
45
          end if;
46
47
        when others =>
48
49
      end case;
50
    end if;
51
  end process;

von Ingo S. (logikneuling)


Lesenswert?

Hallo!

Du scheinst dein Problem ja schon gelöst zu haben, daher kommt das 
vielleicht etwas zu spät... aber vielleicht hilft Dir das trotzdem noch.

Ich habe mich vor ein paar Wochen auch selbst an einer SPI Schnittstelle 
zum MCP3008 in VHDL versucht (und im Endeffekt eher Probleme mit dem 
Analogteil gehabt: siehe Beitrag "SAR ADC erzeugt Spikes während AD Wandlung?" 
und Beitrag "Hilfe Auslegung low-pass Filter und Verbindung zu ADC"), aber irgendwie doch 
ungewollt immer ein oder zwei überflüssige States eingebaut. Schließlich 
hab ich mir dann doch mal den Referenz-Code zu meinem Board angesehen 
und nach ausführlichem Studium jeder einzelnen Code-Zeile bin ich zu dem 
Schluss gekommen, dass ich es wohl nicht smarter und kürzer selbst 
aufschreiben kann (vielleicht kann Lothar es doch, er scheint ja ein 
Meister darin zu sein ;)).

Beispielweise bin ich eingangs nicht selbst auf so etwas simples wie 
dies hier, obwohl es natürlich nur eine klassische 
Shiftregister-Beschreibung ist (quasi bis zum vollständigen DAC Wert 
"ungueltige" Werte weiter zu schieben, aber den momentanen adc_miso Wert 
direkt in Q abzulegen, so dass dann mit dem letzten Hineintakt-State, 
ohne im Anschluss eine weitere Kopie von Q irgendwo "abzulegen", in 
einem anderen Prozess Q direkt übernommen werden kann, ...), da hätte 
ich wieder extra-states verschenkt:
1
  -- MISO shift register (rising edge)
2
  shift_in : process(adc_clock)
3
  begin
4
    if adc_clock'event and adc_clock = '1' then
5
      if state = '1' then
6
        Q(0)          <= adc_miso;
7
        Q(9 downto 1) <= Q(8 downto 0);
8
      end if;
9
    end if;
10
  end process;

Oder beispielweise auch, das invertierte "state"-Bit direkt als CS 
Ausgangssignal zu benutzen...

Vermutlich muss ich mir noch mehr solcher Implementationen ansehen, um 
solche Konstrukte in Zukunft wie ein Template einfach aus dem Kopf holen 
zu können.

Der vollständige MCP3008/SPI VHDL Code ist zu finden hier: 
https://www.micro-nova.com/resources/ unter "Tutorials & Example Code", 
"On-board ADC Reference Design" (ich wollte die ZIP-Datei jetzt nicht 
einfach so verlinken).

Viele Grüße,
Ingo

: Bearbeitet durch User
von Md M. (Firma: Potilatormanufaktur) (mdma)


Lesenswert?

Hallo Ingo,

ja, der Codeschnipsel sieht in der Tat elegant aus. Das merk ich mir auf 
jeden Fall. Wobei ich aber eigentlich ohnehin nie abschätzen kann, was 
das Synthesetool am Ende aus meinem Code macht. Da fehlt mir definitiv 
die Erfahrung. Deine beiden threads hab ich übrigens schon gesehen und 
gebookmarked. Die wollte ich auch durchgehen, wenn ich beim 
Antialiasing-Filter angekommen bin. Ich rechne mit einigen Problemen, 
über die du dann zu meinen Glück vielleicht schon gestolpert bist.

VG

: Bearbeitet durch User
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.