Hallo, ich nutze die HAL für einen kleinen Versuchsaufbau. Ein Nucleo64 mit STM32L476 sendet alle 5 ms 74 Bytes Daten auf der SPI3 als Master mit 625 kBit/s. Chipselect wird als GPIO-Output angesteuert. Clock High im Idle, CS low active, Trigger auf die fallende Flanke. Ein zweites Nucelo64 soll diese 74 Byte empfangen, konfiguriert als Slave ohne NSS-Nutzung. ChipSelect als IRQ-Eingang mit fallender Flanke. Ich wollte das jetzt so lösen, dass ich im IRQ von der fallenden Flanke von ChipSelect ein HAL_SPI_TransmitReceive_DMA() aufrufe. CS geht einstellbar auf dem ersten Nucleo früher auf LOW als angefangen wird zu senden (hab es bis 50µs versucht). Auf dem Slave werden die Daten empfangen, aber sie sind immer um ein paar Byte versetzt. D.h. meistens sind die ersten 3-4 Bytes noch das Ende des letzten Telegrammes und dann beginnt erst das neue Telegramm. Ich hab schon alles mögliche versucht, verstehen kann ich das Verhalten aber nicht. Mit dem Logikanalyzer mitgeloggt sind die Daten ok. Hab ich vielleicht einen Denkfehler? Grüße Volker
Hilft mit deine Antwort jetzt? Bin Anfänger und hab es auch mit NSS funktioniert, da hat es auch nicht richtig funktioniert.
Volker K. schrieb: > Ein Nucleo64 mit STM32L476 sendet alle 5 ms 74 Bytes Daten auf der SPI3 > als Master mit 625 kBit/s. Chipselect wird als GPIO-Output angesteuert. > Clock High im Idle, CS low active, Trigger auf die fallende Flanke. Hilft dir nicht weiter, aber deine Einstellungen sind etwas unüblich. Normal macht man das mit: CPOL = 0 [Clock Low when Idle] CPHA = 0 [Trigger auf die steigende Flanke] 90% der Geräte (Module) funktionieren auf diese Weise.
:
Bearbeitet durch User
Danke für die Antwort, ich soll später Daten von einer HW auslesen, die ich fertig bekomme und die arbeitet mit diesen Einstellungen. Deshalb möchte ich das gerne damit zum laufen bekommen. Würde eine andere Einstellung denn etwas an der Problematik ändern? Hab es gerade mit den empfohlenen Einstellungen versucht, gleiches Verhalten.
:
Bearbeitet durch User
Volker K. schrieb: > Auf dem Slave werden die Daten empfangen, aber sie sind immer um ein > paar Byte versetzt. D.h. meistens sind die ersten 3-4 Bytes noch das > Ende des letzten Telegrammes und dann beginnt erst das neue Telegramm. Slave empfängt nur das, was ihm reingetaktet wird, d.h. dein Master hat die vorherigen Daten noch nicht vollständig rausgetaktet. Versuche es mal ohne DMA.
Ohne DMA geht es. DMA wäre besser, da die zukünftige HW nur mit 120 kBit senden kann und ich für die 74 Byte dann lange blockiert wäre wenn ich kein DMA nutze. Aber bei beiden Modi hab ich ein Problem. Wenn ich während dieser laufenden Kommunikation den Master resette, dann läuft wieder alles verschoben. Ausser ich resette danach den Slave. Wie kann ich denn beim SLAVE, wenn ich z.B. über eine Checksumme bemerke, dass das Telegramm nicht passt, quasi die SPI zurücksetzen und sofort wieder aktivieren?
das hört sich nach Problem mit dem fifo an.
Volker K. schrieb: > DMA wäre besser, da die zukünftige HW nur mit 120 kBit senden kann und > ich für die 74 Byte dann lange blockiert wäre wenn ich kein DMA nutze. Sollte das nicht auch mit einem Interrupttreiber zu erledigen sein?
Kann ich auch mal versuchen. Zum "Zurücksetzen" der verkorksten Kommunikation hab ich schon: HAL_SPI_Abort_IT() und HAL_SPI_DeInit() versucht, leider ohne Erfolg.
Volker K. schrieb: > Aber bei beiden Modi hab ich ein Problem. Wenn ich während dieser > laufenden Kommunikation den Master resette, dann läuft wieder alles > verschoben. Ausser ich resette danach den Slave. Mach es mit 74Byt * 8Takte = 592 Takte, also zur Sicherheit beim Reset erstmal 75 * 0xFF raustakten.
:
Bearbeitet durch User
Lad dir irgendwas Fertiges runter wie es gern gemacht wird :-)
@Marc: Du meinst auf dem Master? Der sendet ja das Richtige, sonst würde doch nicht der Slave, wenn ich ihn nach dem Problem alleine resette, wieder das Richtige empfangen. Irgendwie kommt der Slave in diesem Fall durcheinander, das könnte ja auch mal passieren, wenn durch ein EMV-Problem etwas gestört wird. Es gibt ne Checksumme, die ich am Slave auswerten kann, nur was wäre dann die richtige Reaktion auf eine detektierte Störung?
Volker K. schrieb: > auch mal passieren, wenn durch ein EMV-Problem etwas gestört wird. Es > gibt ne Checksumme, die ich am Slave auswerten kann, nur was wäre dann > die richtige Reaktion auf eine detektierte Störung? Wenn du am Protokoll schrauben kannst, vieles. a) Falls der Slave eine falsche Checksumme detektiert, wird in SPI Datenregister sofort eine 0x7F reingeschrieben (Beispiel). Master kriegt dann beim ersten rausgesendetem Byte (neues Telegramm) ein 0x7F anstatt 0xFF und weiss somit, dass mit dem vorigen Telegramm etwas nicht in Ordnung war. Natürlich können andere Werte auch andere Fehler bezeichnen. b) Ein Telegramm kann niemals mit 0xFF anfangen und kann nicht länger als z.B. 74 Bytes sein. Deswegen weiss der Slave wenn der Master sich z.B. beim 25-sten Byte resettet: 1) Nach weiteren 50 Bytes sollte das Telegram zu Ende sein aber es kommen weitere Bytes an (Master hat sich resettet und versucht ein neues Telegramm zu senden). 2) Slave prüft nach 73 Bytes die Checksumme und stellt fest, dass diese falsch ist. 3) Slave schreibt 0x7F ins Dataregister (einmalig oder dauernd) und wartet auf das erste Byte <> 0xFF (oder was auch immer als SOF gilt). Das ist dann auch das neue Telegramm. Oder so ähnlich (ziemlich lange her, dass ich so etwas geschrieben habe, kann natürlich versuchen, es rauszugraben).
:
Bearbeitet durch User
Setzt du bei einer neuen Übertragung im Slave den DMA komplett zurück? Wie soll der Slave wissen, dass es eine neue Übertragung ist? Der NSS ist etwas nervig bei dem STM32F ich meine mich zu erinnern, dass du über den Interrupt (EXTI) den Slave initalisieren kannst (Pointer zurück setzen DMA...) und dann die Übertragung starten. Versuche es erst einmal nur über den Interrupt und wenn das klappt bau es zu DMA um
Hallo, da ich nicht sicher bin was die richtigen Schritte zum zurücksetzen sind, mache ich das vermutlich aktuell nicht korrekt. @Marc: Unser Ablauf soll so sein: 1. Das Telegramm hat am Anfang zwei definierte Bytes als SOF und am Ende eine 16 Bit Checksumme. 2. Der Master sendet immer die kompletten 74 Byte, zyklisch alle 5 ms. Unabhängig davon was der Slave antwortet. Er prüft ebenfalls das SOF und die Checksumme. 3. Kommt ein "falsches" Paket an, dann soll das verworfen werden. Geprüft wird das im Callback TxRxCplt... Zwischen zwei Telegrammen ist ja immer 5 ms Zeit, so dass dafür genug Zeit zur Verfügung steht. Da ich aber nicht weiß, was der Grund für das falsche Paket ist, würde ich gerne in diesem Moment auch die SPI und die DMA etc. komplett zurücksetzen. DeInit/Init hat nicht geklappt, davor noch ein Abort auch nicht. Also wenn da jemand noch eine konkrete Vorgehensweise kennt, wäre das prima. Wie schon gesagt bin ich als Anfänger im Moment ein HAL-Nutzer und nicht so tief in den Register-Varianten drin.
Warum machst du es nun nicht per Interrupt (Testweise) und schwenkst dann auf DMA um. So weißt du, an welcher Stelle es hackt? Nutzt du das CRC vom SPI? oder was eigenes?
Das mit dem IRQ werde ich heute Abend mal testen. Wir nutzen ne eigene CRC weil der Master keine HW-Unterstützung für ne 32 Bit CRC hat und schon ziemlich ausgelastet ist. Deshalb ne 16 Bit CRC.
Ich hatte Folgendes versucht, wenn die Kommunikation schief lief: halRet = HAL_DMA_Abort_IT(&hdma_spi3_rx); halRet = HAL_DMA_DeInit(&hdma_spi3_rx); halRet = HAL_DMA_Abort_IT(&hdma_spi3_tx); halRet = HAL_DMA_DeInit(&hdma_spi3_tx); halRet = HAL_SPI_DeInit(&hspi3); MX_DMA_Init(); MX_SPI3_Init(); Der erste HAL_DMA_Abort_IT() liefert dann immer ein HAL_ERROR zurück. Der hdma->State ist dabei: HAL_DMA_STATE_READY Lock = HAL_UNLOCKED. Der Inhalt von hdma ist in beiden Fällen (gut/schlecht) identisch.
:
Bearbeitet durch User
Es fehlt die Erkennung des Anfangs eines Telegramms im Slave, um Master und Slave zu synchronisieren.
Volker K. schrieb: > konfiguriert als > Slave ohne NSS-Nutzung. ChipSelect als IRQ-Eingang mit fallender Flanke. Das /SS sagt dem Slave, wann ein Paket beginnt. Warum willst Du das nicht nutzen? Ein Master fängt typisch direkt nach der 1-0 Flanke an, zu senden. Da hast Du keine Zeit mehr, erst noch irgendwas zu initialisieren. Laß es also die HW machen. Oder nimm die abschließende 0-1 Flanke zum Reset des SPI.
Mit dem NSS hat es nicht richtig funktioniert, deshalb hab ich den CS mit IRQ-Flankenerkennung probiert. Der Master macht nach der fallenden Flanke von CS eine Pause von aktuell 50µs bevor das Senden los geht. Das kann ich dem Programmierer des Master vorgeben.
Ich glaub ich weiss, was mein Problem mit dem NSS war. Ich konnte nicht den NSS in der SPI nutzen und gleichzeitig einen IRQ von dem NSS-Pin bekommen. Den IRQ wollte ich ja nutzen um dort den TransmitReceive_DMA() scharf zu schalten. Aber ich glaube ich hab einen Denkfehler. Ich schaue in der TxRxCplt() nach dem DMA Status. Dort ist der natürlich OK, weil die 74 Byte ja korrekt empfangen wurden. Ich müsste im IRQ vom CS schauen, ob da der DMA noch nicht fertig ist.
Also kurz zum aktuellen Stand: Ich habe auf IRQ umgestellt und es wurde etwas besser. Allerdings ist nicht ganz zu verstehen, wie der NSS in Hardware funktioniert. Lege ich ihn komplett auf High statt Low, dann werden immer noch Daten empfangen. Da der Versatz der Daten immer noch möglich ist, wollte ich folgendes umsetzen: Parallel zum NSS werte ich die steigende Flanke von CSS aus (Dann ist ja die SPI-Kommunikation für das eine Telegramm beendet). In der IRQ-Routine hab ich dann Folgendes implementiert: if (GPIO_Pin == GPIO_PIN_3) { if (hspi3.ErrorCode != HAL_SPI_ERROR_NONE) { if (hspi3.ErrorCode == HAL_SPI_ERROR_OVR) { __HAL_SPI_CLEAR_OVRFLAG(&hspi3); } else if (hspi3.ErrorCode == HAL_SPI_ERROR_MODF) { __HAL_SPI_CLEAR_MODFFLAG(&hspi3); } else if (hspi3.ErrorCode == HAL_SPI_ERROR_FRE) { __HAL_SPI_CLEAR_FREFLAG(&hspi3); } HAL_SPI_DeInit(&hspi3); MX_SPI3_Init(); HAL_SPI_Abort_IT(&hspi3); HAL_SPIEx_FlushRxFifo(&hspi3); // Alles neu intialisiert, jetzt wieder den Empfang triggern halRet = HAL_SPI_TransmitReceive_IT(&hspi3, abSend, abReceive, 74); } else { HAL_SPI_Abort_IT(&hspi3); HAL_SPI_DeInit(&hspi3); MX_SPI3_Init(); HAL_SPIEx_FlushRxFifo(&hspi3); // Alles neu intialisiert, jetzt wieder den Empfang triggern halRet = HAL_SPI_TransmitReceive_IT(&hspi3, abSend, abReceive, 74); } } Damit hätte meiner Meinung nach am Ende jedes SPI-Telegramms die SPI wieer in den Anfangszustand versetzt werden sollen, der ja immer funktioniert. Nach einem Reset war der SPI-Empfang immer korrekt. Leider scheint da etwas zu kollidieren, weil er immer wieder im SPI_Abort an dieser Stelle hängt: /* Change Rx and Tx Irq Handler to Disable TXEIE, RXNEIE and ERRIE interrupts */ if (HAL_IS_BIT_SET(hspi->Instance->CR2, SPI_CR2_TXEIE)) { hspi->TxISR = SPI_AbortTx_ISR; while (hspi->State != HAL_SPI_STATE_ABORT); } Er kam dann aus dem while nicht mehr raus. Ich hatte mit IRQ-Prioritäten rum gespielt, aber das brachte keine Besserung. Mir wäre es am liebsten, wenn ich mit der steigenden Flanke von CS die komplette SPI zurücksetzen kann, so dass diese dann immer wieder "neu" startet. Ohne alte Daten im RX-Buffer.
probier das Ganze mal mit Bitbang-spi. Wenn es damit nicht läuft, muss es an der Hardware liegen.
Bitbang SPI: Ask your favourite search engine. Dort gibt's auch gleich entsprechenden sourcecode.
Sorry, aber warum sollte ich bei einer vorhandenen HW-SPI auf eine simulierte umsteigen? Das man die SPI zum funktionieren bekommt, davon gehe ich aus. Es fehlen sicherlich nur Detailkenntnisse an ein paar Stellen um meine Problemchen lösen zu können.
grundschüler schrieb: > probier das Ganze mal mit Bitbang-spi. Wenn es damit nicht läuft, muss > es an der Hardware liegen. Bitbang SPI geht nur im Master Mode, hier geht es AFAIK um den SPI Client.
Jim M. schrieb: > Bitbang SPI geht nur im Master Mode Halte ich für eine Mindermeinung. Volker K. schrieb: > Sorry, aber warum sollte ich bei einer vorhandenen HW-SPI auf eine > simulierte umsteigen? Weil du möglicherweise mit den fifos nicht zurechtkommst und bitbang-spi ohne fifo leichter beherrschbar bzw. zur Eingrenzung des Problems geeignet ist?
Ich glaube ich muss noch ein bisschen ausholen, warum ich das hier mache. Ich soll für einen Bekannten ein Gateway SPI->Ethernet implementieren. Die SPI (als Master mit einem PIC realisiert) funktioniert korrekt. Da das Ganze später aber auch unter EMV-Bedingungen funktionieren soll, will ich das Ganze weitestgehend wasserdicht implementieren. Ich hab hier also beide Boards (ein Nucleo 64 und das PIC-Board) nebeneinander stehen und hab für die ersten Tests beide mit 10 cm Leitungen verbunden. SPI läuft mit 250 kBit. Der PIC als Master sendet fröhlich alle 5 ms ein neues Telegramm mit einer Startsequenz und einer 16 Bit CRC am Ende. Ich empfange mit dem STM fröhlich und versuche jetzt durch Aus- und Wiedereinstecken der SPI-Kabel oder RESETs am PIC die SPI-Kommunikation zu stören. Und ich erwarte, dass Sie sich immer wieder fängt und korrekt die gesendeten Telegramme empfängt. Manchmal geht der STM sogar in HardFault, bis jetzt meist, wenn man die GND-Leitung manipuliert. Das Ganze läuft in der jetzigen Implementierung schon sehr stabil, aber in ganz seltenen Fällen bekommt man einen Zustand, wo die SPI immer Ihre korrekte Byteanzahl empfängt, aber die Bytes verschoben sind. Oft um ganze Bytes, ganz selten auch mal um ein paar Bits. Mein Ziel wäre es, dass sich die SPI nach Fehlern immer wieder fängt und ich dann wieder korrekte Telegramme empfangen kann. So viel zu meiner Intention an dieser Stelle. Die SPI-Übertragung selbst dauert knapp unter 3 ms, die restlichen 2 ms habe ich Zeit die SPI zurück zusetzen und wieder in einen empfangsfähigen Zustand zu versetzen.
Volker K. schrieb: > Ich empfange mit dem STM fröhlich und versuche jetzt durch Aus- und > Wiedereinstecken der SPI-Kabel oder RESETs am PIC die SPI-Kommunikation > zu stören. Und ich erwarte, dass Sie sich immer wieder fängt und korrekt > die gesendeten Telegramme empfängt. Huiii, das kann aber ins Auge gehen... > Manchmal geht der STM sogar in HardFault, bis jetzt meist, wenn man die > GND-Leitung manipuliert. Klar: Dann fließen eventuelle Ausgleichsströme über die GPIO Pins. Nicht so gut. Man bedenke das ohne GND die Referenz fehlt, das gibt "interessante" Effekte. Volker K. schrieb: > Das Ganze läuft in der jetzigen Implementierung schon sehr stabil, aber > in ganz seltenen Fällen bekommt man einen Zustand, wo die SPI immer Ihre > korrekte Byteanzahl empfängt, aber die Bytes verschoben sind. Oft um > ganze Bytes, ganz selten auch mal um ein paar Bits. Das ist der Erwartungswert, wenn kein NSS benutzt wird. Mitten in einer Übertragung angesteckt empfängt er ein zufälliges Bit als Erstes. Volker K. schrieb: > Die SPI-Übertragung selbst dauert knapp unter 3 ms, die restlichen 2 ms > habe ich Zeit die SPI zurück zusetzen und wieder in einen > empfangsfähigen Zustand zu versetzen. Dann mach nach der Übertragung den NSS hoch. Auf dem Slave triggert das die Auswertung + Neuinitialiserung, um wieder bei fallender Flanke für die nächste Übertragung bereit zu sein. So ist beim Anstecken nur die 1. Übertragung kaputt. Braucht man übrigens nicht unbedingt mit an-und Abstecken testen - einfach die SPI Ports im Master zwischendurch mal auf GPIO umstellen und Bits rauskloppen ginge auch.
NSS brachte an dieser Stelle keine Verbesserung. Empfangen wird auch wenn der NSS-Pin auf HIGH bleibt. Das war ja die grundsätzliche Idee, die man bei Nutzung der CS-Leitung hat. Ohne NSS auf LOW sollte die SPI nichts empfangen und am Ende, wenn NSS wieder auf High geht, dann soll der Empfang aufhören. Pustekuchen. Ich hab dann parallel zu NSS noch einen Pin als IRQ mit steigender Flanke konfiguriert und den NSS da drauf gebrückt. Da wollte ich dann in der IRQ-Routine einfach immer der SPI zurücksetzen (mit SPI_ABORT, FlushFifo etc.). Hat aber nicht immer funktioniert, ab und zu scheint die SPI noch nicht ganz fertig gewesen zu sein, wenn der IRQ aufgerufen wurde. Dann hing er in der Funktion SPI_Abort() in einer while()-Schleife fest. Also jetzt wieder ohne NSS und nur mit einem Software-CS mit dem ich selbst bestimmen kann was bei welcher Flanke getan wird. Das ist der aktuelle Stand. Heute noch auf DMA erweitert und es läuft gar nicht schlecht. Erklären kann ich mir nur nicht, dass er beim Abziehen/Einstecken der GND-Verbindung der SPI ab und an im HardFault_Handler() landet. Und zwar meist mit dem ErrorCode NOCOP "No Coprozessor".
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.