www.mikrocontroller.net

Forum: FPGA, VHDL & Co. SPI Problem, Counter zählt nicht jede Flanke


Autor: Der Daimlerfahrer (daimlerfahrer)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Morgen zusammen,

ich habe derzeit ein komisches Problem mit einer SPI Schnittstelle.
Hier erstmal der Code:
Schieberegister : process (Takteingang,SPI_Clock) 
begin
  if Takteingang'event and Takteingang='1' then
  SPI_Clock_old <= SPI_Clock; -- saving the actual values
    if SPI_Clock_old='0' and SPI_Clock='1' then -- detect the rising edge of the SPI clock
      for i in 0 to Register_width-2 loop
      temp_signal(i+1) <= temp_signal(i);
      end loop;
    Counter <= Counter + 1;
    temp_signal(0) <= SPI_Data;
    end if;
  end if;
Der SPI_Clock liegt bei 50 kHz und der "Masterclock" Takteingang bekommt 
von mit 13 MHz.
ich taste den SPI-Takt ab und zähle auf die steigende Flanke einen 
Zähler auf 16. Danach sollte das Schieberegister voll sein und die Werte 
werden übernommen.
Jetzt passiert jedoch folgendes: Der Counter kommt häufig nur bis 15 und 
damit ist das Schieberegister noch nicht voll. Die Signale am Oszi sind 
aber prima (also Clock, CS und Daten sind da und haben schöne, steile 
Flanken).
Muss ich da noch etwas grober "entprellen" oder gibts andere 
Fehlerquellen für den falschen Zähler?

Danke schonmal für eure Tipps!

Autor: Duke Scarring (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hast Du das ganze schonmal simuliert?
Wo/wann wird Counter zurückgesetzt?

Duke

Autor: SeriousSam (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wieso taucht SPI_Clock in der sensitivity list auf? Sind die 
Eingangssignale synchronisiert?

Autor: Marcus Harnisch (mharnisch) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Also zunächst würde ich mal SPI_Clock aus der Sensitivity Liste 
herausnehmen und durch einen ordentlichen Reset Eingang ersetzen 
(welchen Wert hat SPI_Clock_old nach dem Einschalten?).

Dann muss SPI_Clock natürlich auf Takteingang synchronisiert werden 
bevor es verwendet wird. Am besten nach der klassischen Methode zweier 
hintereinander geschalteter Register. Wie man Schieberegister 
implementiert weißt Du ja schon.

Den Rest kann man aus diesem Fragment nicht erkennen.

In jedem Fall schließe ich mich Duke's Meinung bezüglich einer 
Simulation an.

Gruß
Marcus

Autor: Der Daimlerfahrer (daimlerfahrer)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Der Counter wird mit der steigenden Enable Flanke auf 16 geprüft und 
zurückgesetzt. Wenn 16 erreicht wird ausgegeben, zurückgesetzt wird 
immer.

Simulation tut...

Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ist SPI_Clock direkt der Pin?
Falls ja: dann solltest du noch (mindestens) 1 FF dazwischenschalten.
Stichwort hier wieder: Metastabilität.

SPI_Data muß übrigens nicht synchronisiert werden, das ist zu dem 
Zeitpunkt garantiert stabil. tsu und th werden nicht verletzt.

Wenn schon generisch programmiert, dann doch nicht so verschleift. Das 
hier sieht doch viel kompakter aus (und ist genauso generisch):
Schieberegister : process (Takteingang,SPI_Clock) 
begin
  if Takteingang'event and Takteingang='1' then
  SPI_Clock_old <= SPI_Clock; -- saving the actual values
    if SPI_Clock_old='0' and SPI_Clock='1' then -- detect the rising edge of the SPI clock
       temp_signal <= temp_signal(temp_signal'length-1 downto 0) &  SPI_Data;
       Counter <= Counter + 1;
    end if;
  end if;

Autor: Der Daimlerfahrer (daimlerfahrer)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke schonmal für eure Tipps.
Das mit der sens. Liste gibts natürlich keinen Sinn - da habt ihr 
vollkommen recht!

Zum Thema Synchronisieren habe ich noch eine Frage - ich war der Meinung 
ich müsste die beiden Takte bei dieser Methode nicht synchronisieren, 
wenn ich auf den Master-Clock den Zustand vergleiche und so die Flanke 
detektiere.
Ich dachte schlimmstenfalls erhalte ich dabei einen Jitter von meinen 13 
MHz bei der "Abtastung" - oder wo liegt bei den nicht synchronisierten 
Signalen das Problem?

Das Eingangs-FF werde ich der Metastabilität wegen implementieren (ich 
erinnere mich an eine Vorlesung zu dem Thema... da war was, ja irgenwas 
war da :P)

Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> oder wo liegt bei den nicht synchronisierten Signalen das Problem?
>> Stichwort hier wieder: Metastabilität.

Auf gut Deutsch heißt das:
mal geht es und mal geht es nicht.
In der Simulation funktioniert es aber immer.

EDIT:
Was macht die Simulation, wenn beide Flanken gleichzeitig kommen?
Ja: immer das selbe.

Und was macht deine reale Schaltung?
"Gleichzeitig" gibt es hier nicht. Du hast aber auf jeden Fall eine tsu 
(Setup) und th (Hold) Verletzung, und deine FFs machen, was sie wollen.

Autor: Der Daimlerfahrer (daimlerfahrer)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Ich hab hier mal den kompletten Code angehängt.
Mein Problem habe ich soweit eingrenzen können:
- wenn ich auf die SPI-Flanke triggere, dann tut meine Schnittstelle, 
ich kann jedoch nicht mit dem Counter prüfen ob tatsächlich alle Bits 
geschoben wurden.
- wenn ich den Vergleich von Signal zu Signal_old als event nutze, 
erhalte ich nicht nachvollziehbares verhalten, obgleich die Simulation 
funktioniert

Ist mein erstr Code zu SPI - demnach bin ich über jegliche Tipps dankbar 
:D

Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dein SPI_Enable ist komplett asynchron, und wird einfach mit dem Takt 
abgefragt. Das geht schief, ab und zu.
    if SPI_Enable='1' then
       counter <= "00000";
    end if;
    
    if SPI_Enable='0' then


Mach das mal auch so hübsch synchron wie
  SPI_Clock <= SPI_Clock_ext;
  SPI_Clock_old <= SPI_Clock; -- saving the actual values

Aber i.A. solltest du das mit jedem Signal, das von Aussen kommt, 
machen. Ab und zu brauchst du es nicht, das muß aber gut überlegt sein.

Schöner sieht das übrigens aus, wenn du ein Schieberegister nimmst:
:
signal SCLK_sr: std_logic_vector (1 downto 0);
signal SS_sr: std_logic_vector (1 downto 0);
:
:
process (Takteingang) is 
begin
  if rising_edge (Takteingang) then
    SS_sr   <= SS_sr(0) & SS;      -- Slave-Select
    SCLK_sr <= SCLK_sr(0) & SCLK;  -- SPI Clock
    :
    if (SCLK_sr="01") then         -- steigende Flanke
    :
    if (SS_sr="10") then           -- fallende Flanke
    :
  end if;
end process;
:

Autor: Der Daimlerfahrer (daimlerfahrer)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Okay Danke Lothar,

ich habe nun alle Eingänge synchronisiert und damit treten deutlich 
seltener Aussetzer auf.
Leider kommt dennoch nicht jedes SPI-Packet am Ziel an.
Ist es denn üblich, den SPI-Takt mit einem Masterclock "abzustasten" und 
mit den geänderten Zuständen zu arbeiten?

Grüße

Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> und damit treten deutlich seltener Aussetzer auf.
Dann ist immer noch irgendwo so ein Bock drin ;-)

> Ist es denn üblich, den SPI-Takt mit einem Masterclock "abzustasten" und
> mit den geänderten Zuständen zu arbeiten?
Nein, ich würde das etwas anders machen. Zum Eintakten wird der SCLK 
verwendet, und wichtig ist nur, dass die Daten bei der Übernahme in 
die Masterclock-Domäne stabil sind.
Falk Brunner hat es hier mal schön aufgezeigt: 
Beitrag "Re: SPI im CPLD mit State Machine - geht das so?"

Autor: Tom (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
    if SPI_Enable='1' then
    counter <= "00000";
    end if;


    if SPI_Enable_old='0' and SPI_Enable='1' then
      if counter = "10000" then -- detect the rising edge of the Enable signal
      OK_Programm_Signal <= '1';
      else
      OK_Programm_Signal <= '0';
      end if


Wird die Abfrage des counters überhaupt erreicht, ich denke nicht, da er 
sobald SPI_Enable '1' wird auf Null zurückgesetzt wird.

Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Prinzipiell ist es sogar noch krasser. Zeilen wie
      if counter = "10000" then 
           OK_Programm_Signal <= '1';
      :
sind komplett unnötig, wenn man sich das SPI-Protokoll mal genau 
durchliest.

In dem Protokoll wird nämlich nicht eine bestimmte Anzahl eingeschobener 
Bits zur Datenübernahme spezifiziert, sondern das Deselektieren des 
Bausteins. Die Datenübernahme erfolgt also per Definition mit der 
steigenden Flanke des SS-Signals. Und damit werden alle Zähler, die 
irgendwelche eingetaktete Bits zählen, unnötig. Ich könnte hier also 
z.B. 123 Bits eintakten, verwendet würden die letzten 16 davon.

Und der andere Vorteil bei der Datenübernahme per SS ist der, dass 
mehrere Slaves einfach hintereinander geschaltet werden können, und 
diese Diasy-Chain mit 1 SS bedient werden kann. Siehe dazu z.B. 
http://www.maxim-ic.com/appnotes.cfm/an_pk/3947

Allerdings gibt es durchaus kommerzielle Bausteine, bei denen explizit 
im DB steht, dass nicht die letzten, sondern die ersten eingeschobenen 
Bits intern weiterverwendet werden. Das deutet wieder auf einen Zähler 
oder ein nicht nur vom SCLK gesteuertes Schieberegister hin.

Autor: Der Daimlerfahrer (daimlerfahrer)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Okay ich hab das ganze nun nochmal ohne irgendwelche Zähler-Abfragen 
implementiert.
Recht nahe angelehnt an den verlinkten Code von Falk Brunner. Leider 
bekomme ich auch weiterhin einige Bits falsch detektiert. Verstehen will 
ich es nicht mehr so recht, da das SPI Signal wirklich keinen Fehler 
aufweist, und im FPGA dennoch falsch übergeben wird...
Also herzlichen Dank für eure Hilfe und wenn ihr weitere Tipps habe 
freue ich mich natürlich. Werde jetzt mal versuchen noch einige 
Sicherheiten einzubauen, dass ein falsches Ergebnis nicht übernommen 
wird. Das wäre bei meiner Anwendung nämlich ziemlich blöd - lieber sende 
ich die Daten nochmal.

Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Verstehen will ich es nicht mehr so recht, da das SPI Signal
> wirklich keinen Fehler aufweist, und im FPGA dennoch falsch
> übergeben wird...
Es ist ohne weiteres möglich, auf beide angesprochenen Arten (alles 
einsynchronisieren  bzw. lokaler SPI-Takt) das SPI-Protokoll auf einem 
FPGA zu implementieren. Und dazu ist keine Fehlerbehandlung nötig.

Ich habe so eine Fehlerbehandlung bei mir mal implementiert, nur, um 
nach mehreren 10000 Maschinenjahren (5000 Maschinen * 6 Jahre) 
festzustellen, daß diese Fehlerbehandlung unnötig ist.

Wenn du meinst, deine Beschreibung wäre ok, dann kommt als nächstes:
Hardware?
Layout?
Masseführung?
Blockkondensatoren?

Autor: Volker (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Poste halt noch mal deinen Code.

Autor: Der Daimlerfahrer (daimlerfahrer)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Okay ich hab das ganze heute nochmal geprüft. Das problem ist nicht mehr 
der SPI - der klappt. Ich habe ihn wie oben beschrieben ohne Zähler und 
detektiere bisher alles fehlerfrei.
Das Problem das ausftrat, hatte mit meinem auswertenden Code zu tun und 
nicht mit dem SPI.
Also vielen Dank euch allen - mein SPI-Problem ist gelöst :D

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [vhdl]VHDL-Code[/vhdl]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.