Hallo,
ich nutze die SPI-Schnittstelle eines STM32F405 mit HAL und STM32CubeMX
und möchte mit 20 kHz einen Sensor auslesen.
Um das Auslesen zu starten nutze ich einen Timer, in dessen
PeriodElapsedCallback ich die Funktion HAL_SPI_Receive_IT(...) aufrufe.
Das NSS-Signal wird durch einen GPIO-Pin realisiert, der im
Timer-Callback auf LOW gesetzt wird und im SPI-Callback auf HIGH gesetzt
wird.
Das funktioniert soweit ganz gut. Gelegentlich läuft die SPI-Clock
jedoch nach setzen des NSS-Pins auf High einfach weiter. Im darauf
folgenden Zyklus werden dann nur 8 der gewünschten 16 bit vom Sensor
geesen und die Daten sind unbenutzbar.
Weiß jemand wie das kommen kann? Wie kann man das eleganter lösen?
Ich kenne das.
Traubensaft .. schrieb:> Gelegentlich läuft die SPI-Clock> jedoch nach setzen des NSS-Pins auf High einfach weiter.
Nein. Ich kann dir grob die Ursache nennen, aber nicht für
deinen (CubeMX-)Fall die böse Stelle im Code:
Der NSS Pin wird zu früh auf HIGH gezogen da irgendwo im
Ablauf nicht darauf gewartet wird dass die SPI Maschine
noch im Busy-Zustand ist.
Die SPI Maschine kann noch busy sein obwohl das TX empty Flag
bereits gesetzt ist. Das liegt an der gepufferten Verarbeitung
von Schreibvorgängen auf die Peripherie.
Zur Erläuterung siehe:
Beitrag "[STM32F4xx] SPI Optimierung"
Vielleicht solltest du doch mal dazu übergehen den HAL-Sch....
wegzulassen und selbst Hand anzulegen.
Traubensaft .. schrieb:> der im> Timer-Callback auf LOW gesetzt wird und im SPI-Callback auf HIGH gesetzt> wird.
Das tut man nicht. Das ist Spaghetti Code.
STMApprentice schrieb:> Die SPI Maschine kann noch busy sein obwohl das TX empty Flag> bereits gesetzt ist.
Ist es in meinem Fall das RX flag?
In dem verlinkten Beitrag fügst du eine Zeile hinzu um abzuwarten, bis
das BSY bit im SR wirklich 0 ist. Ich habe mir daher die "HAL-Version"
überlegt. Leider funktioniert das so nicht. (Portierbarkeit und "Magic
numbers" stören mich erstmal nicht. Ich versuche nur die Funktion
herszustellen.)
STMApprentice schrieb:> Das tut man nicht. Das ist Spaghetti Code.
Das hat schon beim Programmieren weh getan. Bei dieser
Interrup-basierten Variante fällt mir leider keine schönere Möglichkeit
ein. Gibt's da was?
>Die SPI Maschine kann noch busy sein obwohl das TX empty Flag>bereits gesetzt ist. Das liegt an der gepufferten Verarbeitung>von Schreibvorgängen auf die Peripherie.
korrekt
>Vielleicht solltest du doch mal dazu übergehen den HAL-Sch....>wegzulassen und selbst Hand anzulegen.
falsch, den HAL richtig benutzen. Das CS per GPIO setzen ist
überhaupt nicht nötig und wird auf Wunsch vom SPI Controller
übernommen. Dann passt das Timing auch.
Funktioniert übrigens nicht bei mehreren SPI Devices an einem SPI Kanal
parallel. Als DaisyChain aber dann doch wieder.
Jede Lösung hat halt das passende Problem.
Gruß,
dasrotemopped.
Das CS ist dem Fall doch nur ein beliebiger GPIO, das SPI im Controller
weiss nix davon und damit kann das Problem auch nicht am CS liegen. Das
Clock Signal wird beim Master doch anhand der Anzahl Bytes * Wortbreite
generiert, unabhängig vom CS.
Johannes S. schrieb:> Das CS ist dem Fall doch nur ein beliebiger GPIO
Käse. In diesem Fall ist CS (=NSS) ein hart von der SPI-
Maschine gesteuerter Pin. Das ist auch konfigurierbar.
ich sehe in dem Code nur einen Softwaregesteuerten CS. Und ob PA4 den
Output vom Slave hochohmig oder irgendeine LED anmacht ist doch egal.
Wenn die gelbe Kurve im Oszillogramm das CS Signal ist kommt es ja auch
richtig. Nur der Controller meint er müsse noch ein paar Takte
raushauen.
dasrotemopped schrieb:> HAL richtig benutzen. Das CS per GPIO setzen ist> überhaupt nicht nötig und wird auf Wunsch vom SPI Controller> übernommen.
Ich möchte gerne einen "Receive Only Master" betreiben. Das Thema
Hardware-NSS habe ich gestern ausgiebig gehabt. Es hat für Baudraten >=
1 MBaud auch prima funktioniert. Wenn ich jedoch nur den Prescaler für
SPI im CubeMX (ohne Änderungen am User-Code!) so gewählt habe dass die
Baudrate kleiner als 1 MBaud war, wurde NSS überhaupt nicht mehr
angesprochen.
grundschüler schrieb:> while (!(hspi3.Instance->SR & (1<<0)));
Ich will doch prüfen ob bit 7 noch gesetzt ist oder? Was ist mit meiner
Variante
1
while(hspi3.Instance->SR&(1<<7)!=0);
denn falsch?
Johannes S. schrieb:> ich sehe in dem Code nur einen Softwaregesteuerten CS.
Eben. Weil ich für das Verhalten von Hardware-NSS bisher noch keine
Erklärung gefunden habe.
Johannes S. schrieb:> ommt es ja auch> richtig. Nur der Controller meint er müsse noch ein paar Takte> raushauen.
Genau das ist der Kern meines aktuellen Problems :)
Es sieht irgendwie asynchron aus, als ob schon wieder oder noch ein
Receive ansteht wenn der Complete Callback kommt und das CS rücksetzt.
HAL_SPI_Receive_IT() liefert doch einen Status zurück, den vielleicht
mal überwachen ob da nicht ein Busy oder Error kommt?
Johannes S. schrieb:> als ob schon wieder oder noch ein> Receive ansteht
Wie ist das zu verstehen? HAL_SPI_Receive_IT() wird nur aufgerufen wenn
NSS runter gezogen wurde. Und auf dem Scope sehe ich, dass die Frequenz
von NSS konstant ist.
Johannes S. schrieb:> liefert doch einen Status zurück
Gute Idee. Der ist leider permanent HAL_OK.
grundschüler schrieb:> probiers aus
Habe ich. Die Schleife läuft keine einzige Iteration. Heißt also BSY ist
immer 0?
mein letzter Versuch
Die Warteroutine stimmt nicht. statt 16bit rutschen 24bit durch und
anschließend dann nur 8bit? Das wäre genau das gleiche Phänomen wie bei
meinem Spi2-Problem.
aus der stm32f4xx_hal_tim.c Den Zusammenhang sehe ich aber noch nicht.
grundschüler schrieb:> statt 16bit rutschen 24bit durch und anschließend dann nur 8bit?
Genau. Aber auch diese Schleife läuft bei mir nicht. Und das Phänomen
bleibt erhalten.
Traubensaft .. schrieb:> Den Zusammenhang sehe ich aber noch nicht.
Wenn das offensichtliche richtig ist muss man weitere Kreise ziehen, das
können schon irgendwelche Seiteneffekte sein die noch versteckt sind. So
schlecht sieht der Code ja nicht aus.
Ist das Ganze mit Debug übersetzt und sind die Asserts aktiv?
grundschüler schrieb:> while (hspi3.Instance->SR & (1<<0) == 0);
ist das Problem nicht auf SPI1 ? Und hast du auch schon die nicht direkt
am Problem beteiligten Komponenten abgeschaltet?
Johannes S. schrieb:> Ist das Ganze mit Debug übersetzt und sind die Asserts aktiv?
Jap, läuft im Debugging von IAR.
Johannes S. schrieb:> grundschüler schrieb:>> while (hspi3.Instance->SR & (1<<0) == 0);> ist das Problem nicht auf SPI1 ? Und hast du auch schon die nicht direkt> am Problem beteiligten Komponenten abgeschaltet?
Da hat er schon recht. Ich nutze beide SPIs mit gleichartigen Sensoren.
Darum war vorhin ein wechsel drin. Um Überschneidungseffekte
auszuschließen habe ich eine neue Projektmappe angelegt.
Traubensaft .. schrieb:
doch noch ein Versuch:
Setz mal hinter das bsy-wait noch ein delay von ca. 100us. Dann hat der
Sensor genug Zeit, die gesendeten 16bit zu verarbeiten. Wenn das klappt,
das delay reduzieren.
grundschüler schrieb:> Dann hat der Sensor genug Zeit, die gesendeten 16bit zu verarbeiten.
Mhm, ich hätte die Signale mal beschriften sollen. Oben im
Scope-Screenshot sehen wir MISO. Meine SPIs sind als "Receive Only
Master" konfiguriert. Das Problem tritt beim Empfangen der Sensordaten
auf.
Moin,
also ich wuerde eher erwarten, dass die SPI zu spaet disabled wird, und
daher weiter clocks generiert.
Im RefManual steht zu RX Only Mode:
1. Set the RXONLY bit in the SPI_CR2 register.
2. Enable the SPI by setting the SPE bit to 1:
a) In master mode, this immediately activates the generation of the SCK
clock, and data are serially received until the SPI is disabled
(SPE=0)....
Nun muss ich sagen, dass ich weder HAL noch RX Only mode nutze.
Die Clock generierung laesst sich da ganz einfach ueber die TX Buffer
writes kontrollieren.
Ich frage mich nur, ob die Suche hier ueberhaupt sinnvoll ist.
Wennn CubeMX fuer >1Mbit korrekt alles macht, incl. HW NSSI dann sollte
man doch versuchen da von Hand nur den Prescaler zu "Patchen" um die
gewuenschte Bitrate zu erhalten, oder?
Traubensaft .. schrieb:> Das Problem tritt beim Empfangen der Sensordaten auf.
auch das master-rx setzt ein master-tx voraus. Der Master liefert den
clk- Takt und sendet damit Daten auf Mosi, die aber nicht interessieren.
der slave sendet mit dem gleichen Takt auf miso Daten zurück. Dann wird
gewartet bis die Verarbeitung rxne/txe/bsy im master beendet ist.
An dieser Stelle liegt wahrscheinlich der Fehler, weil - warum auch
immer - nicht ausreichend gewartet wird.
Hier müsste also das delay hin.
Da es ein rx-Vorgang ist scheint mir das Warten auf tx unlogisch. Da rx
und tx eigene Flags haben, kommt es hier vermutlich zu
Zeitunterschieden. Dann scheint mir das Abwarten des zeitlich späteren
rxne eigentlich zwingend. Wieso rxne bei dir nicht funktioniert
erschließt sich mir noch nicht.
Moin,
> auch das master-rx setzt ein master-tx voraus. Der Master liefert den> clk- Takt und sendet damit Daten auf Mosi, die aber nicht interessieren.
Nein, er konfiguriert RXONLY mode, da gibt es laut RefManual kein TX.
Ich benutze immer bidirektional, und habe dann auf MOSI gleich einen
Byte/Word counter fuers Oszi. Weil mir das so besser gefaellt, und ich
den
Pin uebrig habe. Aber er halt nicht. Und der HAL scheint das ja zu
supporten.
Es gibt halt aber manchmal ein Problem mit dem Transfer-Ende.
Wenn man das untersuchen will, sollte man die Zugriffe aufs DR mit ein
Paar Debug Pins visualisieren und ggf. zeitlich verschieben, um die
Effekte in
der SPI zu beobachten.
Moin,
hoppala:
> ... Zugriffe aufs DR mit ein Paar Debug Pins visualisieren...
Meinte natuerlich CR bzw. CR1, weil da ja das enable bit drin ist.
Das DR sollte beim RXONLY mode nicht so sonderlich viel Einfluss haben.
Ausser, dass man da die Daten einliesst oder es bleiben laesst und sie
verloren gehen.
Hi,
ich lese gerade Seite 898 im Referece Manual RM0090. Dort ist das
korrekte Disabling des SPI beschrieben.
Mein Fall sollte dieser sein:
> In master unidirectional receive-only mode (MSTR=1, BIDIMODE=0, RXONLY=1)
Dort steht geschrieben:
> This case must be managed in a particular way to ensure that the SPI> does not initiate a new transfer.> 1. Wait for the second to last occurrence of RXNE=1 (n–1)> 2. Then wait for one SPI clock cycle (using a software loop) before> disabling the SPI (SPE=0)> 3. Then wait for the last RXNE=1 before entering the Halt mode> (or disabling the peripheral clock)
"ensure that the SPI does not initiate a new transfer" klingt ziemlich
vielversprechend.
Bei Schritt 1: das vorletzte RXNE=1 bekomme ich beim ersten meiner zwei
Bytes (richtig?)
Dann warte ich ein bisschen bis er anfängt das zwei Byte rauszuclocken
und disable das SPI (SPE=0) während er noch dabei ist das zweite Byte zu
holen.
Meine Frage ist nun: wie ziehe ich das mit Interrupts auf? Wenn ich die
ganze Zeit auf Flags warte, verballere ich Rechenzeit ohne dass etwas
passiert...
Eine Idee wäre nach HAL_SPI_Receive_IT(...) ein paar µs zu warten bis er
sicher beim zweiten Byte ist und dann SPE=0. Das hat aber nicht
funktioniert.
Hört sich kompliziert an und verlangt wohl Eingriffe in den HAL. Poste
das Problem doch mal im ST Forum.
Und zum Test erstmal nicht den RxOnly Mode nutzen?
Moin,
also da du die HAL_SPI_Receive_IT() ausm CallBack im Interrupt context
aufrufst, ist busy wait wohl keine gute Idee.
Ich wuerde ebenfalls empfehlen, diriektional zu konfigurieren. Dann wird
mit dummy transmit bytes nur genau die Anzahl Clocks generiert, die man
möchte.
Wenn das absolut nicht geht, und du den HAL code nicht patchen magst,
wuerde ich einen weiteren Timer empfehlen, der zeitversetzt einen IRQ
ausloest. Das sollte natuerlich an den Start-Timer synchronisiert sein.
Ich glaube da mal was gelesen zu haben, dass das geht. Kann ich aber
ausm Kopf nicht runterbeten (naja, fuer Bolero eMIOS ginge es, aber beim
M4 hab ichs nicht parat). Die Timer duerfen ja nie auseinander laufen.
Wenn beide von der selben Clock gespeist werden kann das ja nicht
passieren, aber den exakten versatz kennt man nicht, wenn man die
einfach nacheinander startet.
Wenn das am Anfang passiert wenn noch keine Ints akriv sind sollte das
aber keine allzugrossen Probleme bereiten (solange sich im System nix
aendert).
Traubensaft .. schrieb:> Wenn ich die ganze Zeit auf Flags warte, verballere ich Rechenzeit
Bevor es mit standard-spi nicht ordentlich funktioniert, brauchst du dir
darüber keine Gedanken zu machen. Ich würde es auch erstmal im 2x 8bit
modus probieren, bevor auf 16bit umgestellt wird. Bis es ordentlich
läuft, so einfach wie möglich.
grundschüler schrieb:> Bevor es mit standard-spi nicht ordentlich funktioniert, brauchst du dir> darüber keine Gedanken zu machen. Ich würde es auch erstmal im 2x 8bit> modus probieren, bevor auf 16bit umgestellt wird. Bis es ordentlich> läuft, so einfach wie möglich.
Aber der 16bit mode ist doch standard. Und es reduziert die
Komplexitaet. Dann kann nach dem HAL_SPI_Receive_IT() nach einem
SPI clock cycle das SPI enable auf 0 gesetzt werden. Bei 2x8Bit
muss ein IRQ abgewartet werden und dann 1 SPI Clock spaeter das
SPI enable auf 0 gesetzt werden.
Dazu sind dann noch 7 Clockcycles - der IRQ delay (Systemlast,
andere IRQs, Interruptsperren) uebrig. Beim 16Bit betrieb sind
es 15 Clockcycles. Das Fenster, dass zum SPI disable genutzt
werden muss ist bei 8bit nicht mal halb so gross.
Moin,
ein weitere Timer ist eigentlich voelliger Qautsch. Der "Start" Timer
wird einfach auf halbes Zeitintervall aufgezogen. In der Timer CB
Funktion dann 2 Phasen.
Phase 1: HAL_SPI_Receive_IT() mit 16bit Betrieb, -> Phase 2
Phase 2: SPI Enable auf 0 setzen, -> Phase 1
Aber das SPI enable bit Hardcoremaessig auf 0 zu setzen wenn man
eigentlich streng nur den ungepatchten HAL nutzen will ist auch
irgendwie unschoen.
Hey,
ich danke euch allen vielmals für die Unterstützung! Mit
HAL_SPI_TransmitReceive_IT(...) und GPIO als NSS funktioniert es.
Vieleicht bastel' ich mir in der vorlesungsfreien Zeit dann mal eine
HAL-frei Variante.