ich stehe vor einem Problem mit meinem STM32F030C6T6 Controller in Bezug auf SPI DMA-Übertragungen und hoffe auf eure Unterstützung.
Ich verwende SPI DMA, um kontinuierlich Daten vom Master an einen Slave-Controller zu senden. Die Nachricht besteht aus 12 Byte, die jeweils in 8 Bit übertragen werden. Anfangs funktioniert alles einwandfrei: Der Slave empfängt die Daten, verarbeitet sie und sendet entsprechende Antworten zurück. Dies funktioniert für einige Minuten/Sekunden bis ca. 30 min reibungslos.
Allerdings tritt dann plötzlich ein Problem auf: Die Nachricht, die der Master sendet, scheint im SPI DMA des Slaves verschoben zu sein. Obwohl die Daten vorhanden sind, stimmt ihre Reihenfolge nicht mehr. Dies führt dazu, dass mein Protokoll nicht mehr korrekt funktioniert. Dieser Fehler zieht sich dann komplett durch ich hatte schon gedacht eventuell den DMA Pointer der auf die Memory Adresse ist zurückzusetzen, aber leider ohne Erfolg.
Ich habe versucht, das Problem zu lösen, indem ich das FRXTH-Register für CR2 (FIFO Reception Threshold) setze, jedoch ohne Erfolg. Selbst wenn der Slave nur die empfangenen Daten des Masters zurücksendet, tritt das Problem nach einer gewissen Zeit auf. Es ist nur das SPI Modul initialisiert und keine anderen Interrupts die den Prozess behindern können. Die Clock Polarität ist auf dem Slave und Master auch gleich.
Ich bin bereits verzweifelt und hoffe sehr, dass jemand von euch eine Idee hat, wie ich dieses Problem lösen kann.
Anbei findet ihr eine Logic Analyzer-Datei, die zeigt, was der Master sendet, sowie eine Debug-Datei, die die Daten im Array zeigt, in dem das Problem auftritt.
Ich bedanke mich im Voraus für eure Hilfe und Unterstützung!
Vielleicht wäre es sinnvoll, sich zuerst für EINEN Transfer-Modus zu entscheiden: Entweder Ringpuffer-Modus oder Einzelschuss-Modus. Beides gleichzeitig macht keinen Sinn.
NSS wird nicht benutzt?
Die 12 da mehrfach rein zu schreiben, ist keine gute Idee. Wenn die einzelnen Nachrichten alle 12 Byte lang sind, legt man besser einen Puffer mit genau dieser Länge an und nimmt sizeof() für die Längenangaben. Oder zumindest eine Konstante oder ein #define für die "Standard"-Länge.
Ach ja, für dieses Problem natürlich egal, aber die Bits in den Registern so häppchenweise zu setzen/löschen ist schon etwas eigenartig. Man darf auch mehr als ein Bit gleichzeitig ändern: Gleich den kompletten gewünschten Wert zusammenstellen und in einem Rutsch ins Register. Ausnahme nur in Einzelfällen, wenn man wirklich insgesamt nur ein Bit ändern will.
Und für Mehrbitfelder gibt's extra die ..._Pos Makros.
Das "Per32Bit; MEM32Bit" ist doch sicher nicht richtig?!
Das IFCR ist eigentlich "write-only", ein "DMA1->IFCR |=" macht daher keinen Sinn (auch wenn ein Lesen "zufälligerweise" 0x0 ergibt). "DMA1->IFCR = DMA_IFCR_CTCIF2;" ist zielführender.
der Hardware PIN NSS wird verwendet. Mein dma_SPI_RX[] und mein dma_SPI_TX[] Array die auf MemAdresse liegen sind 12 Felder lang da ich ja 12 Bytes sende und auch gleichzeitig empfange quasi im Write/Read Modus. Der DMA RX channel läuft im Circular Mode und auf Memory Increment der Channel ist dauerhaft aktiviert weil er ja ständig auf Daten wartet. Der TX channel läuft im direction Mode und auch memory Increment. Was meinst du mit Ringpuffer? Der circular Mode?. Denkst du das Problem liegt an der Konfiguration vom SPI oder eher am DMA? Komisch ist das das Problem immer zu unterschiedlichen Zeiten auftritt manchmal nach 1 min manchmal nach 5 oder sogar manchmal auch nach 20 min erst.
Komisch
ist das das Problem immer zu unterschiedlichen Zeiten auftritt manchmal
nach 1 min manchmal nach 5 oder sogar manchmal auch nach 20 min erst.
Dann hast du wahrscheinlich ein Hardware-Problem.
Aber das wollen Softwerker (die ihre Hardware selbst bauen)
nur ungern hören.
Ich habe dann auch mal zu Testzwecken 2 developments boards von STM miteinander nur über SPI verbinden aber fliegende Leitungen ca. 10 cm. und das selbe Problem tritt auch auf. Den Bus habe ich auch schon auf 450 khz ca. gesetzt. An dem Master kann es ja eigentlich nicht liegen der schickt in 1,5 ms intervallen ca einfach die gleiche Nachricht an dem Slave. Am logic Analyzer ist auch zu sehen dass die Nachrichten rausgesendet wurden. Das Problem liegt halt nur am Slave mit SPI DMA dass die Daten irgendwann im RX Channel verschoben sind. Wenn ich 8 Bit sende dann muss das SPI_CR2_FRXTH (FIFO Reception Threshold) Bit gesetzt werden habe ich in anderen Foren gelesen und das steht auch in dem Beispiel von ST drin, aber trotzdem ohne Erfolg.
Den Bus habe ich auch schon auf
450 khz ca. gesetzt.
Dann hast du noch nicht verstanden was Macken in der Hardware
alles bewirken können. Den Takt herunter setzen hilft gegen
Hardware-Macken gar nichts. Damit zeigst du nur dass du
Probleme in Hardware noch gar nicht verstanden hast.
Allerdings verwendest Du zum Empfang ja den Circular Mode, d.h. eigentlich kann das nicht passieren, da es komplett in DMA abläuft. Du darfst natürlich in Deinem Code den Empfangs-DMA nicht neu initialisieren. Das ist nur einmal am Anfang notwendig.
der Hardware PIN NSS wird verwendet. Mein dma_SPI_RX[] und mein
Davon ist aber nichts zu sehen.
dma_SPI_TX[] Array die auf MemAdresse liegen sind 12 Felder lang da ich
ja 12 Bytes sende und auch gleichzeitig empfange quasi im Write/Read
Modus. Der DMA RX channel läuft im Circular Mode und auf Memory
Increment der Channel ist dauerhaft aktiviert weil er ja ständig auf
Daten wartet. Der TX channel läuft im direction Mode und auch memory
??? Bezieht sich das auf den Master oder den Slave? Beim Master wartet man nicht ständig auf Daten, denn der Master liefert den Takt, also ein Byte raus bedeutet genau ein Byte rein, sonst kann nichts ankommen.
Increment. Was meinst du mit Ringpuffer? Der circular Mode?. Denkst du
Ja, was sonst? Der macht hier keinen rechten Sinn, weder beim Master, noch
beim Slave, denn beide müssen ohnehin wissen, wo die "Telegramm"-Grenzen sind,
dann ein vollständiges Paket verarbeiten und können dann DMA fürs nächste
Pakt scharf machen.
Gegen ein Hardwareproblem (Störungen auf der Taktleitung) spricht, dass
der Versatz immer ein Vielfaches von 8 Bits zu sein scheint.
Nochmals: Das "Per32Bit; MEM32Bit" ist doch sicher nicht richtig?!
Allerdings verwendest Du zum Empfang ja den Circular Mode, d.h.
eigentlich kann das nicht passieren, da es komplett in DMA abläuft. Du
darfst natürlich in Deinem Code den Empfangs-DMA nicht neu
initialisieren. Das ist nur einmal am Anfang notwendig.
Hallo Markus,
ja richtig ich verwende den Circular Mode da dürfte dann nichts verloren gehen. Der master sendet aufjedenfall richtig die 12 Bytes raus das habe ich schon bereits mit dem Logic Analyzer überprüft nur irgendwann passt die Reihenfolge beim Slave SPI RX DMA nicht mehr. Die Frage ist jetzt gehen wirklich die Bytes beim Slave verloren. Der Empfangs DMA wird nur einmal initialisiert.
der Hardware PIN NSS wird verwendet. Mein dma_SPI_RX[] und mein
Davon ist aber nichts zu sehen.
dma_SPI_TX[] Array die auf MemAdresse liegen sind 12 Felder lang da ich
ja 12 Bytes sende und auch gleichzeitig empfange quasi im Write/Read
Modus. Der DMA RX channel läuft im Circular Mode und auf Memory
Increment der Channel ist dauerhaft aktiviert weil er ja ständig auf
Daten wartet. Der TX channel läuft im direction Mode und auch memory
??? Bezieht sich das auf den Master oder den Slave? Beim Master wartet
man nicht ständig auf Daten, denn der Master liefert den Takt, also ein
Byte raus bedeutet genau ein Byte rein, sonst kann nichts ankommen.
Increment. Was meinst du mit Ringpuffer? Der circular Mode?. Denkst du
Ja, was sonst? Der macht hier keinen rechten Sinn, weder beim Master,
noch
beim Slave, denn beide müssen ohnehin wissen, wo die "Telegramm"-Grenzen
sind,
dann ein vollständiges Paket verarbeiten und können dann DMA fürs
nächste
Pakt scharf machen.
den Code den ich hier gepostet habe bezieht sich komplett nur auf den Slave. Der Master sendet alle 1,5 ms die Nachricht raus beim senden gehen keine Bytes verloren das habe ich schon mit dem Logic Analyzer übprüft. Das muss irgendwie am Slave liegen. Warum ergibt der Circular Mode beim DMA keinen sinn beim Slave für den Empfangschannel für SPI?? WOher sollen denn Störungen kommen wenn ich 2 dev boards vom stm über SPI verbinde. Die Leitungen sind höchstens 10 cm. Das Per32Bit und MEM32Bit ist nicht gesetzt. stand nur noch in den kommentaren. Frage ist es jetzt ist es eher ein Software oder Hardware Problem. Hardwareproblem hätte ich jetzt eigentlich ausgeschlossen.
ja richtig ich verwende den Circular Mode da dürfte dann nichts verloren
gehen. Der master sendet aufjedenfall richtig die 12 Bytes raus das habe
ich schon bereits mit dem Logic Analyzer überprüft nur irgendwann passt
die Reihenfolge beim Slave SPI RX DMA nicht mehr. Die Frage ist jetzt
gehen wirklich die Bytes beim Slave verloren. Der Empfangs DMA wird nur
einmal initialisiert.
Nochmals: Das ist eine ganz schlechte Idee. Wenn irgendwann einmal ein Problem auftritt (hard- oder softwaremäßig), ist danach der Empfang auf ewig außer Tritt ... Ringpuffer-Modus ist nur dann sinnvoll, wenn es um einen "endlosen" Datenstrom geht, bei dem ein Wiederaufsetzen anhand inhaltlicher Analyse möglich ist. Z. B. Text, wo man beim nächsten CR oder LF neu aufsetzen kann. Dann ist zwar bei einer Störung eine oder zwei Zeilen weg, aber danach geht's weiter.
Und stillschweigend eine absolut fehlerfreie Kommunikation anzunehmen ist ziemlich blauäugig. Murphy's Gesetz ... Außerdem ist auch bei einem noch so simplen Protokoll eine Fehlerprüfung (CRC oder so) oder zumindest eine Plausibilitätskontrolle zwingend.
Übrigens: Beim SPI gibt's auch Fehler-Flags, die man prüfen könnte. Netterweise sind die auch "sticky".
Nochmals: Das ist eine ganz schlechte Idee. Wenn irgendwann einmal ein
Problem auftritt (hard- oder softwaremäßig), ist danach der Empfang auf
ewig außer Tritt ... Ringpuffer-Modus ist nur dann sinnvoll, wenn es
um einen "endlosen" Datenstrom geht, bei dem ein Wiederaufsetzen anhand
inhaltlicher Analyse möglich ist. Z. B. Text, wo man beim nächsten CR
oder LF neu aufsetzen kann. Dann ist zwar bei einer Störung eine oder
zwei Zeilen weg, aber danach geht's weiter.
OK und was würdest du dann anders konfigurieren an dem DMA? Zurzeit läuft der Empfangschannel vom DMA SPI auf Circular Mode von dem Slave was ja nicht so sinnvoll wäre wie du sagtest.
Und stillschweigend eine absolut fehlerfreie Kommunikation anzunehmen
ist ziemlich blauäugig. Murphy's Gesetz ... Außerdem ist auch bei einem
noch so simplen Protokoll eine Fehlerprüfung (CRC oder so) oder
zumindest eine Plausibilitätskontrolle zwingend.
Ja das ist aufjedenfall klar es kann ja auch mal ein Bit kippen wenn Störungen auftreten sollten. Aber mein Protokoll hat ja auch eine Plausibilitätsabfrage über einen CRC vorgesehen.
Übrigens: Beim SPI gibt's auch Fehler-Flags, die man prüfen könnte.
Netterweise sind die auch "sticky".
Ja an Fehlerflags hatte ich auch schon gedachte der DMA hat ja auch welche. Man müsste nur eine richtige Debugmethode finden, dass man das Fehlerflag nicht verpasst , wenn mehrmals hintereinander gesendet wird. Ich vermute dass bei 200 mal senden oder so es einmal kurz auftreten könnte und dann automatisch wieder resettet wird. Entweder beim DMA Fehlerflag oder SPI Fehlerflag.
Generell: bei SPI findet die Synchronisation (Byte und auch Paket/Nachricht) zwischen Master und Slave per SS statt. Ohne SS kein sync. Ein verlorener Takt führt zu einer Bitverschiebung die nie wieder einrastet. Ein Antwort mit unerwarteter Länge (wg Fehler o.ä.) zu einer Byteverschiebung. Das Error-Recovery sollte auf jeden Fall SS einmal deaktivieren, noch besser wäre es, zwischen jedem Kommando. Ob das der STM32 bei DMA beherrscht, weiß ich nicht.
OK und was würdest du dann anders konfigurieren an dem DMA? Zurzeit
läuft der Empfangschannel vom DMA SPI auf Circular Mode von dem Slave
was ja nicht so sinnvoll wäre wie du sagtest.
Beide im "normalen Modus", und beide nach jedem Telegramm neu starten.
Und immer kontrollieren, dass "transfer complete" auf beiden DMA-Kanälen angezeigt wird.
Ja an Fehlerflags hatte ich auch schon gedachte der DMA hat ja auch
welche. Man müsste nur eine richtige Debugmethode finden, dass man das
Fehlerflag nicht verpasst , wenn mehrmals hintereinander gesendet wird.
Ich vermute dass bei 200 mal senden oder so es einmal kurz auftreten
könnte und dann automatisch wieder resettet wird. Entweder beim DMA
Fehlerflag oder SPI Fehlerflag.
Deswegen das 'Netterweise sind die "sticky"'. Die werden halt ganz bewusst NICHT automatisch zurückgesetzt. Das ist doch gerade der Witz dabei ...
"TEIFx: Channel x transfer error flag (x = 1..5)
This bit is set by hardware. It is cleared by software writing 1 to the corresponding bit in the DMA_IFCR register."
Oder:
"Bit 6 OVR: Overrun flag
0: No overrun occurred
1: Overrun occurred
This flag is set by hardware and reset by a software sequence."
Beide im "normalen Modus", und beide nach jedem Telegramm neu starten.
Und immer kontrollieren, dass "transfer complete" auf beiden
DMA-Kanälen angezeigt wird.
Wir reden ja nur vom Slave richtig? Der Master ist ja erstmal egal und spielt da ja keine Rolle. Also darf ich das Bit CIRC: Circular mode beim RX Channel nicht setzen dann bin ich ja im normalen Modus quasi Einzelschussmodus. Darum muss ich dann nach jeden Telegram den DMA neustarten. Der TX Channel vom Slave ist ja im Einzelschuss Modus den Channel starte ich ja sowieso immer neu, wenn ich was übertragen will. Eigentlich muss ich doch dann nur den RX Channel vom Slave neustarten wenn ich was vom Master etwas empfangen habe oder? Das kann ich ja im DMA RX Interrupt machen. Also wenn ich was empfangen habe dann direkt neustarten oder nicht?
Deswegen das 'Netterweise sind die "sticky"'. Die werden halt ganz
bewusst NICHT automatisch zurückgesetzt. Das ist doch gerade der Witz
dabei ...
"TEIFx: Channel x transfer error flag (x = 1..5)
This bit is set by hardware. It is cleared by software writing 1 to the
corresponding bit in the DMA_IFCR register."
Oder:
"Bit 6 OVR: Overrun flag
0: No overrun occurred
1: Overrun occurred
This flag is set by hardware and reset by a software sequence."
Das habe ich auch schon mal überprüft aber das TEIF Flag tritt aufjedenfall beim DMA nicht auf. Das Error FLag beim SPI könnte ich nochmal überprüfen.