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!!!
Schaltplan, Foto des Aufbaus, Quellcode...
DMA testweise aus der Gleichung nehmen. Da kein RTOS läuft und du verzweifelt bist, ist eh alles egal.
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?
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.
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.
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.
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...
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.
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...
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.
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
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.