Forum: Mikrocontroller und Digitale Elektronik STM32 SPI Master<->Slave Kommunikation periodische Ausfälle


von Oz z. (ozzy)


Lesenswert?

Moin,

   ich habe hier ein Problem, bei dem ich gerade absolut nicht mehr 
weiterkomme und hoffe, dass Ihr noch Ideen habt: ich habe zwei STM32F4, 
die über SPI miteinander kommunizieren. Die Prozessoren laufen beide auf 
168MHz, SPI Prescaler ist 16, auf beiden läuft FreeRTOS. Der Master 
sendet jede ms (über Timer, nicht Task) 9 Words an den Slave (per 
HAL_SPI_TransmitReceive_DMA), der Slave hat einen externen Interrupt auf 
die CS-Leitung und "antwortet" dann per HAL_SPI_TransmitReceive. Die 
Sendedaten von Master und Slave sind so aufgebaut, dass am Anfang ein 
Paketcounter ist, und am Ende eine Checksumme. Die Checksumme wird dann 
nach der Übertragung überprüft und das Paket als valide oder invalide 
markiert. Das ganze funktioniert auch "eigentlich" ganz gut, aber: auf 
einmal sind für ungefähr 1s-1,5s sehr viele Pakete auf der Master-Seite 
invalide, der Checksummencheck schlägt also fehl. Danach läuft die 
Kommunikation wieder normal weiter, bis sie nach 66s wieder für 1s 
fehlschlägt. Und das wiederholt sich dann alle 66s.
Wenn ich genauer in die aufgezeichneten Daten sehe, dann fällt auf, dass 
in dem Zeitraum nicht alle Pakete fehlerhaft sind, und dass es zwischen 
den Fehlerhaften Paketen immer mal wieder valide Pakete gibt. In einem 
Intervall (erstes-letztes invalides Paket) der Länge 1416 sind es z.B. 
918 invalide Pakete.

Daraufhin habe ich nacheinander folgendes gemacht:
- alle Tasks auf dem Slave deaktiviert
- alle Tasks auf dem Master deaktiviert
- Slave Paket ist statisch, Paketcounter ist fest, Payload = 0, 
Checksumme statisch
- Sendeintervall auf 2ms
Allerdings sehe ich bei keiner der Änderungen einen wirklichen Einfluss. 
Beim letzten Versuch sehe ich eben, dass es nur noch ca. 500 invalide 
Pakete pro "Sprung" sind, was ja aber wieder ca. 1s entspricht.

Ich bin gerade mit meinem Latein am Ende; habt Ihr Ideen, was ich noch 
ausprobieren könnte um den Fehler einzugrenzen? Ich bin über jeden Tipp 
dankbar!!!

von John Doe (Gast)


Lesenswert?

Schaltplan, Foto des Aufbaus, Quellcode...

von NameHierEinfügen (Gast)


Lesenswert?

DMA testweise aus der Gleichung nehmen. Da kein RTOS läuft und du 
verzweifelt bist, ist eh alles egal.

von pegel (Gast)


Lesenswert?

Oz z. schrieb:
> sendet jede ms

Oz z. schrieb:
> Und das wiederholt sich dann alle 66s.

Kann es sein, dass da irgendwo eine 16Bit Variable oder ein 16Bit Puffer 
überläuft?

von NameHierEinfügen (Gast)


Lesenswert?

pegel schrieb:
> Kann es sein, dass da irgendwo eine 16Bit Variable oder ein 16Bit Puffer
> überläuft?

Jetzt wo du es sagst, würde ich fast Geld drauf wetten, dass da 
irgendwas überläuft.
Klingt genau danach.

von Peter D. (peda)


Lesenswert?

SPI-Slave ist fast immer ein Alptraum, da der Master blind sendet und 
hofft, der Slave möge bereit sein.
DMA kann das entschärfen, der Slave muß dann mit der High-Flanke auf /CS 
des vorherigen Pakets das DMA neu aufsetzen. Bei der Low-Flanke hat er 
keine Zeit, der Master sendet ja sofort los.

UART oder I2C ist da bedeutend sicherer, wenn die Datenrate ausreicht.
UART hat auf fast allen MCs eine FIFO und I2C hat ein Handshake 
(Clock-Stretching) falls der Slave gerade busy ist.
Z.B. 400kBit I2C sollte reichen.

von Mw E. (Firma: fritzler-avr.de) (fritzler)


Lesenswert?

Peter D. schrieb:
> und I2C hat ein Handshake
> (Clock-Stretching) falls der Slave gerade busy ist.
> Z.B. 400kBit I2C sollte reichen.

Oder der Slave nacked direkt, wie bei EEPROMs.

von Oz z. (ozzy)


Lesenswert?

Hallo,

   erst einmal vielen Dank für Eure Antworten und Inspirationen! Leider 
ist der SPI hardwaremäßig gesetzt, da komme ich nicht rum. Was ich 
gemacht habe, ist, die HAL_SPI_TransmitReceive auf dem Slave zu 
entschlacken, das hat schon einmal gefühlt ein wenig gebracht. Trotzdem 
blieben die meisten Fehler. Ich habe dann nach vielen Experimenten noch 
einmal das Pattern geändert, das ich rüber sende (das hätte ich schon 
viel früher machen sollen) auf
1, 2, 3, 4, 5, 6, 7, 8, CS

Wenn jetzt ein Fehler auftritt, lasse ich mir die empfangenen Daten 
ausgeben, und das ist das, was raus kommt:

1, 2, 3, 4, 5, 6, 7, 8, 8

Das kommt dann ein paar mal, dann geht es wieder ein paar mal gut, dann 
kommen wieder Fehler und das Pattern ändert sich zu:

1, 2, 3, 4, 5, 6, 7, 7, 8

Und so läuft nach und nach das doppelte Word nach vorne, bis es wieder 
sehr lange gut geht. Man muss dazu sagen, dass ich den TX-Buffer des 
Slaves zur Laufzeit gar nicht verändere, der wird so initialisiert und 
bleibt die ganze Laufzeit gleich! Es scheint also ein Timing-Problem zu 
sein, der eigentlich nur noch in diesem Teil des Codes stecken kann:
1
  while (((hspi->TxXferCount > 0U) || (hspi->RxXferCount > 0U)) && (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_4) == GPIO_PIN_RESET))
2
  {
3
    /* Check TXE flag */
4
    if ((__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_TXE)) && (hspi->TxXferCount > 0U) && (txallowed == 1U))
5
    {
6
      hspi->Instance->DR = *((uint16_t*) hspi->pTxBuffPtr);
7
      hspi->pTxBuffPtr += sizeof(uint16_t);
8
      hspi->TxXferCount--;
9
      /* Next Data is a reception (Rx). Tx not allowed */
10
      txallowed = 0U;
11
    }
12
13
    /* Check RXNE flag */
14
    if ((__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_RXNE)) && (hspi->RxXferCount > 0U))
15
    {
16
      *((uint16_t*) hspi->pRxBuffPtr) = (uint16_t) hspi->Instance->DR;
17
      hspi->pRxBuffPtr += sizeof(uint16_t);
18
      hspi->RxXferCount--;
19
      /* Next Data is a Transmission (Tx). Tx is allowed */
20
      txallowed = 1U;
21
    }
22
  }
Aus irgendwelchen Gründen wird hier kein neues Datum in das DR geladen. 
Auf der Slave-Seite habe ich allerdings keinen Lesefehler.
Ich habe jetzt die Clock weiter reduziert (1/32), das sieht schon einmal 
besser aus, allerdings wundert mich das schon ziemlich, dass das so 
einen Fehler ergibt...

von Peter D. (peda)


Lesenswert?

Klingt sehr danach, als hätte das SPI nicht mal einen einstufigen 
Sendepuffer oder er wird nicht benutzt. SPI_FLAG_TXE klingt eher nach 
Schieberegister leer und nicht nach Puffer frei.
Setze mal den SPI-Interrupt auf allerhöchste Priorität, also höher als 
das RTOS.

Oz z. schrieb:
> Es scheint also ein Timing-Problem zu
> sein, der eigentlich nur noch in diesem Teil des Codes stecken kann:

Mit Schnipselchen kann niemand was anfangen. Fehler sind in der Regel 
nie da, wo der Fragesteller sie vermutet.
In welchem Kontext wird denn das Schnipselchen ausgeführt?
Ich sehe auch nichts mit DMA.

von Oz z. (ozzy)


Lesenswert?

Moin,

   stimmt, da ist auch DMA. Wie oben geschrieben sendet der Master mit 
DMA, der Slave allerdings verwendet das ganz normale SPI über Polling. 
Da auf den beiden Prozessoren nichts mehr läuft als diese Kommunikation 
konnte ich den Fehler relativ gut lokalisieren. Der Code oben ist das 
innere der Senden/Empfangen Funktion, wie sie original beim Cube IDE 
Studio für STM erzeugt wird. Es scheint ja so zu sein, als würde der Tx 
buffer einmal zu wenig weitergeschaltet zu werden...

von Peter D. (peda)


Lesenswert?

Oz z. schrieb:
> der Slave allerdings verwendet das ganz normale SPI über Polling.

Womöglich niederpriorisiert über das RTOS, das ist dann der Supergau.
Der Master braucht kein DMA, denn der gibt den Takt vor. Der Slave ist 
es, der jeden Zeitverzug vermeiden muß!

Oz z. schrieb:
> SPI Prescaler ist 16

Damit hat der Slave 8 CPU-Takte Zeit, das nächste Byte in das 
Senderegister zu stellen. Ohne DMA quasi nicht zu schaffen.

von FloMann (Gast)


Lesenswert?

Servus,
Du betreibst am Slave also Polling des Spi.
Oben erwähnst du das die CS Leitung des Slave als Ext.Interrupt fungiert 
also
führst du den obigen Code Ausschnitt zum senden und empfangen in dieser 
context
Ebene (also in der Ext. int Routine) durch?

Neben dem RTOS sind weitere Interrupts die höher priorisiert sind 
nebenher aktiv?
Falls ja, wie stellst du sicher das durch zusätzliche Laufzeiten der 
Interrupt Routinen
so im Spi Tx Register immer gültige neue Daten zum entsprechenden 
Zeitpunkt
vorhanden sind. Der Master gibt ja den takt unabhängig vor.

Allgemein das TxAllowed Holzhammer Methoden Flag verhindert das man
vorhandenen Spi Buffer/Fifo sinnvoll nutzen kann. In deinem Fall mit 
Nutzung als
Slave bleibt nur wenig Zeit nach dem Lesen aus dem Rx und setzen des 
Flag
damit der Slave jetzt ein neues Tx Frame an den Spi Buffer übergeben 
kann.
Wenn der Master Gapless sendet dann vergeht bis zur nächsten takt Flanke
und damit quasi der start des neuen Spi Frame eine halbe Bitzeit.
In der Zeit sollte was sinnvolles im TxBuffer(konkret TxShift Register) 
des
Slave Spi stehen. Stm32 ist nicht mein spezial gebiet sprich ich kenn
deren Spi Modul nicht wirklich.

Mit Spi Slave Implementierung hatte ich auch bisher selten zu tun.
Bei dem Controller mit dem dies realisiert wurde hatte das Spi Modul
für so ein Fall noch ein Flag. Dies wurde gesetzt wenn z.b. im Slave
Modus das Frame durch den Takt vom master initiiert wurde aber kein
gültiges neues Datum im Tx Buffer zu dem Zeitpunkt vorhanden war.
(Ebenso im Master Mode mit der AutoTx Funktion ohne neue daten).
Dieser Controller sendete dann übrigens auch immer den letzten
Bit Status, sprich es wurde nicht das letzte komplette Frame wiederholt
sondern nur dessen letztes Bit.
Wenn der Stm hier auch ein solches Flag hat kannst du damit
prüfen ob es an diesem Timing liegt.

Du hast den Master aus deiner Fehlerbetrachtung schon rausgenommen?
Bist du dir sicher das du am Master immer nur gültige neue RxDaten 
holst?
Mehrfach Lesen des RxBuffer obwohl aktuell nix neues drin führt bei den
mir bekannten Controllern dazu das man jedesmal das letzte wirklich
gültige Frame wiederholt ausließt.

Nochmal zum TxAllowed Flag, ich will damit nicht sagen das ist nicht 
nötig, keinesfalls
nur die so gegebene Implementierung lässt immer nur ein Frame zu und in 
der
Regel dürfte zumindest für min. ein weiteres  Frame ein Buffer vor dem 
eigentlichen
Shift Register vorhanden sein. Wenn nicht gut dann geschenkt, wie gesagt 
kenne Stm32
nicht im Detail, aber die sind ja schon moderner und würde es erwarten.

Gruß
FloMann

von Tilo (Gast)


Lesenswert?

Hast du einen Logic Analyzer?
Damit kannst du prüfen, ob die Daten korrekt übertragen werden.

Es kann sein, dass der Slave nicht alle Daten verarbeiten kann, da er 
beschäftigt ist.

Bei meiner SPI Slave Implementierung habe ich:
- Eine feste Paketgrösse verwendet. Dadurch konnte der Empfang per DMA 
realisiert werden. Ausserdem kann man den FIFO nutzen. Selbst wenn ein 
paar Bytes ungenutzt bleiben, ist es letztendlich effizienter.
- Die CRC Kontrolle in HW verwendet.
- Zusätzliche Steuerleitungen. Mit einer Ready Leitung weiß der Master, 
ob der Slave empfangsbereit ist.

VG Tilo

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.