Moin Leute,
irgendwie kriege ich bei meinem STM32F4-Discovery-Board das SPI nicht
ans Laufen.
Konkret: Es werden keine Daten "rausgetaktet" und die while-Schleife,
welche darauf wartet, dass das TXE-Bit gesetzt wird nie verlassen.
Wo liegt mein Fehler bzw. mein Brett vor meinem Kopf?
Hallo, du musst auf jeden fall warten, bis die SPI nicht mehr busy ist,
wenn du direkt nach dem das TXE bit wieder kommt die SPI disablest,
kommt nichts raus.
Es gibt da ein Buffer, sobald du was in DR Register schreibst, geht
gleich danach das TXE bit wieder an.
Das BSY Bit braucht man für normales SPI nicht. Senden bei SPI bedeutet
IMMER auch empfangen - und wenn es nur Nullen sind.
Nach dem Schreiben von SPIx->DR = DATA; musst du warten, bis das RXNE
bit gesetzt ist, was bedeutet, dass Daten empfangen wurden (eben das
Byte bei 8bit).
Initialisieren und Senden/Empfangen tu ich folgendermaßen:
Martin schrieb:> Hallo, du musst auf jeden fall warten, bis die SPI nicht mehr busy ist,> wenn du direkt nach dem das TXE bit wieder kommt die SPI disablest,> kommt nichts raus.
Naja aber das mache ich doch hier:
stm32-anfänger schrieb:
1
while(SPI1->SR&SPI_SR_BSY);
2
SPI1->DR=0x53;
3
while(!(SPI1->SR&SPI_SR_TXE));
Nils N. schrieb:> Nach dem Schreiben von SPIx->DR = DATA; musst du warten, bis das RXNE> bit gesetzt ist, was bedeutet, dass Daten empfangen wurden (eben das> Byte bei 8bit).
Naja sollte das Ergebnis nicht das gleiche sein? - Wenn der TX-Buffer
leer ist, dann muss das Byte gesendet worden sein. Das Empfangen geht
mit dem Senden bei SPI natürlich einher.
Aber wie dem auch sei, es wird ja an und für sich überhaupt NICHTS
gesendet. Keine Aktivitäten bei NSS, MOSI oder CLK...
Der Transmitbuffer kann theoretisch schon leer sein. Du schreibst Daten
rein, diese gehen in das Shift-Register und werden werden rausgeschoben.
Damit hast du quasi eine 1-byte FIFO. Das byte welches du in DR
reinkopierst geht sofort in das Shiftregister, wird rausgeschicktet und
DR ist sofort wieder leer.
stm32-anfänger schrieb:> Aber wie dem auch sei, es wird ja an und für sich überhaupt NICHTS> gesendet. Keine Aktivitäten bei NSS, MOSI oder CLK...
Dann überprüfe drei und vierfach, ob du die CLKs, GPIOs etc. richtig
eingestellt hast. Schreibe für das Setzen der GPIOs Makros, die deinen
Code lesbarer machen. Z.B:
Nils N. schrieb:> Dann überprüfe drei und vierfach, ob du die CLKs, GPIOs etc. richtig> eingestellt hast. Schreibe für das Setzen der GPIOs Makros, die deinen> Code lesbarer machen. Z.B:
Das ist der eine Punkt, der andere: Das ständige Aktivieren/Deaktivieren
des SPI ist überflüssig. Solange man keine weiteren Daten in Datenreg.
schreibt, bleibt das SPI sowieso still. Außerdem:
"Bit 6 SPE: SPI enable
0: Peripheral disabled
1: Peripheral enabled
Note: This bit is not used in I 2 S mode.
When disabling the SPI, follow the procedure described in Section
25.3.10: Procedure for disabling the SPI."
Wenn man nicht aufpasst, gerät das BSY-Bit da durcheinander ...
Und: Diese Spaghetti-Initialisierung ist schon arg hässlich. die Bits
nacheinander einzeln zu setzen, erhöht die Übersicht nicht gerade.
(Außerdem wird dabei stillschweigend vorausgesetzt, dass das Register
vorher gleich 0x0 war.)
Besser: Register lesen, alle Bits, die man verändern bzw. in
bestimmten Zustand haben will ausmaskieren, und dann alle, die gesetzt
werden sollen, setzen. Dann diesen Wert komplett ins Register
schreiben.
Ist 1) effizienter, 2) geht auch bei
*Neu*initialisierung/Umkonfiguration, 3) vermeidet
unzulässige/unerwünschte "Zwischenzustände".
A. B. schrieb:> Und: Diese Spaghetti-Initialisierung ist schon arg hässlich. die Bits> nacheinander einzeln zu setzen, erhöht die Übersicht nicht gerade.> (Außerdem wird dabei stillschweigend vorausgesetzt, dass das Register> vorher gleich 0x0 war.)> Besser: Register lesen, alle Bits, die man verändern bzw. in> bestimmten Zustand haben will ausmaskieren, und dann alle, die gesetzt> werden sollen, setzen. Dann diesen Wert komplett ins Register> schreiben.> Ist 1) effizienter, 2) geht auch bei> *Neu*initialisierung/Umkonfiguration, 3) vermeidet> unzulässige/unerwünschte "Zwischenzustände".
Dadrüber kann man streiten. Ich habe z.b. untereinander eine Liste mit
allen Änderungen, zeile für Zeile mit Kommentar am Ende. Finde ich
persönlich sehr übersichtlich.
Beim Start ist JTAG immer aktiviert (paar Pins auf GPIOA und GPIOB
vorkonfiguriert), da muss man aufpassen, sonst sollte alles "0" sein,
sofern man keinen Defaultcode von der IDE mit übersetzt. Ein Blick ins
Datenblatt spricht da Bände und klärt auf. Meine Markos sind z.B. nur
ein Oder, bestehendes bleibt bestehen.
Hallo zusammen, danke für eure Hilfe!
Ich versuche mal allen Beiträgen gerecht zu werden:
Nils N. schrieb:> Dann überprüfe drei und vierfach, ob du die CLKs, GPIOs etc. richtig> eingestellt hast. Schreibe für das Setzen der GPIOs Makros, die deinen> Code lesbarer machen. Z.B:
Naja, das habe ich gemacht - ihr seht es ja oben. Und das Blinken der
LEDs mittels Systick-Handler funktioniert auch wie erwartet, daher nehme
ich an, dass die Takterzeugung mittels Quarz und PLL funktioniert wie
sie soll.
Und laut dem Clock-Tree hängt das SPI1 an dem APB2 - und den Takt für
SPI1 meine ich eigentlich ja mit der Zeile
1
RCC->APB2ENR|=RCC_APB2ENR_SPI1EN;
abgefrühstückt zu haben.
Daher habe ich gehofft, dass ich einfach nur einen ganzen Lattenzaun vor
dem Kopf hatte und ich einfach etwas absolut offensichtliches übersehen
habe - bzw. falsch verstanden habe.
A. B. schrieb:> Das ist der eine Punkt, der andere: Das ständige Aktivieren/Deaktivieren> des SPI ist überflüssig. Solange man keine weiteren Daten in Datenreg.> schreibt, bleibt das SPI sowieso still.
Das ist ein richtiger Punkt. Das werde ich in Zukunft berücksichtigen -
aber mein Problem hat es immer noch nicht behoben :(
A. B. schrieb:> Diese Spaghetti-Initialisierung ist schon arg hässlich. die Bits> nacheinander einzeln zu setzen, erhöht die Übersicht nicht gerade.Nils N. schrieb:> Dadrüber kann man streiten. Ich habe z.b. untereinander eine Liste mit> allen Änderungen, zeile für Zeile mit Kommentar am Ende. Finde ich> persönlich sehr übersichtlich.
Ich finde es so auch übersichtlicher - zumal ich mit dem DaBla bzw.
Reference-Manual nebenbei einfach von oben nach unten die Register
durchgehe und dann Bitweise das Setzen mache.
Aber du hast Recht mit folgendem Punkt:
A. B. schrieb:> (Außerdem wird dabei stillschweigend vorausgesetzt, dass das Register> vorher gleich 0x0 war.)
Normalerweise setze ich meist (nicht immer) das entsprechende Register
ohnehin immer gesamt auf Null bevor ich anfange die entsprechenden Bits
zu setzen. Hier ist es diversen Löschereien/Umschreibaktionen zum Opfer
gefallen.
stm32-anfänger schrieb:> Normalerweise setze ich meist (nicht immer) das entsprechende Register> ohnehin immer gesamt auf Null bevor ich anfange die entsprechenden Bits> zu setzen. Hier ist es diversen Löschereien/Umschreibaktionen zum Opfer> gefallen.
Dann kann es aber passieren, dass du dir deine Debug-Schnittstelle
deaktivierst. Stupide alles auf "0" setzen sollte man nicht, aber das
weißt du bestimmt.
Die arbeit nimmt dir wohl keiner ab, aber:
1. Richtige SPI Clk aktiviert?
3. Richtigen GPIO Clk aktiviert?
3. GPIOs Pins als Alternate?
4. Richtige Alternate Funktion?
6. Sind GPIO Pins ggf. vorkonfiguriert?
7. Ggf. trotzdem Ausgänge auf Push-Pull setzen
8. Speed setzen habe ich bis 1Mhz oft nicht gemacht und funktioniert
dennoch.
Anschließend probiere evtl. mal meine Zeilen von oben, hier nochmal:
PS: Effizienz bei Initialisierung ist doch in der Regel egal. Da mach
ich leiber Zeile für Zeile die entsprechenden Konfigurationen mit
Kommentar. Womöglich optimiert der Compiler das sowieso weg. Pin für
Pin, zeile für Zeile. Aber das kann jeder machen, wie man will :).
Nils N. schrieb:> PS: Effizienz bei Initialisierung ist doch in der Regel egal. Da mach> ich leiber Zeile für Zeile die entsprechenden Konfigurationen mit> Kommentar. Womöglich optimiert der Compiler das sowieso weg. Pin für> Pin, zeile für Zeile. Aber das kann jeder machen, wie man will :).
Das geht doch genauso übersichtlich Zeile für Zeile in effizient, z.B.
so
1
SPI1->CR1|=(
2
SPI_CR1_MSTR| \
3
SPI_CR1_SPE| \
4
SPI_CR1_SSI| \
5
SPI_CR1_SSM
6
);
Also warum unnötig, Speicherplatz, Rechenkapazität und Strom
verschwenden?
A. B. schrieb:> Das ist der eine Punkt, der andere: Das ständige Aktivieren/Deaktivieren> des SPI ist überflüssig. Solange man keine weiteren Daten in Datenreg.> schreibt, bleibt das SPI sowieso still. Außerdem:
Das macht er wohl, weil er erwartet, dass NSS automatisch beim Setzen
und Löschen von SPE aktiviert bzw. deaktiviert wird.
stm32-anfänger schrieb:> Naja sollte das Ergebnis nicht das gleiche sein? - Wenn der TX-Buffer> leer ist, dann muss das Byte gesendet worden sein. Das Empfangen geht> mit dem Senden bei SPI natürlich einher.> Aber wie dem auch sei, es wird ja an und für sich überhaupt NICHTS> gesendet. Keine Aktivitäten bei NSS, MOSI oder CLK...
Du hast einen Vorteiler von 256 eingestellt. Wie schnell der APB
getaktet ist, läßt sich aus Deinem Beispiel nicht ersehen. Auf jeden
Fall hat der mindestens einen Vorteiler von 2.
Aber auch so ist klar, dass das Byte niemals vor dem Abschalten der
SPI-Schnittstelle rausgetaktet werden kann.
Daher: Nur wenn Du genau die Taktzyklen gezählt hast, auf das Abfragen
des BSY-Flags verzichten. Als Anfänger solltest Du das besser immer
machen.
Mein Vorschlag ist, das Ganze erstmal ein bisschen simpler zu machen.
Nimm also den NSS-Kram raus, den hast Du sicherlich nicht verstanden.
Mache den Chipselect ganz einfach per GPIO.
Ich bin sicher, dass Du damit die Daten rausgetaktet bekommst.
Anschliessend liest Du Dir mal das entsprechende Kapitel im Reference
Manual gründlich durch, idealerweise mehr als einmal. Einmal reicht
nämlich für Anfänger nicht.
Wenn Dir die GPIO-CS-Lösung dann nicht gefällt, kannst Du von da aus
weitere Änderungen vornehmen.
Nils N. schrieb:> stm32-anfänger schrieb:>> Normalerweise setze ich meist (nicht immer) das entsprechende Register>> ohnehin immer gesamt auf Null bevor ich anfange die entsprechenden Bits>> zu setzen. Hier ist es diversen Löschereien/Umschreibaktionen zum Opfer>> gefallen.>> Dann kann es aber passieren, dass du dir deine Debug-Schnittstelle> deaktivierst. Stupide alles auf "0" setzen sollte man nicht, aber das> weißt du bestimmt.
Das ist mir bewusst. Das ist ein Fall von "Nicht immer". Bevor ich ein
Register "radikal" auf Null setze, mache ich mir natürlich schon ein
Paar Gedanken, ob das ohne Probleme möglich/zulässig ist. Das "meist"
bezog sich eher auf "0815"-Register a la SPI, wo soetwas problemlos
möglich ist.
Nils N. schrieb:> 1. Richtige SPI Clk aktiviert?
Check?!
Nils N. schrieb:> 6. Sind GPIO Pins ggf. vorkonfiguriert?
Ja, Port A hat ein paar Bits nach dem Reset gesetzt (Default Für
Mode-Register ist 0xA800 0000, das sollte aber mich nicht tangieren).
Nils N. schrieb:> 7. Ggf. trotzdem Ausgänge auf Push-Pull setzen
Check!
Nils N. schrieb:> 8. Speed setzen habe ich bis 1Mhz oft nicht gemacht und funktioniert> dennoch.
1
GPIOA->OSPEEDR|=GPIO_OSPEEDER_OSPEEDR4_1|GPIO_OSPEEDER_OSPEEDR5_1|GPIO_OSPEEDER_OSPEEDR6_1|GPIO_OSPEEDER_OSPEEDR7_1;//OSPEEDR[1:0] = 10 -> High Speed
Check!
Deinen Code hier habe ich auch probiert, hat allerdings auch nicht
funktioniert:
Nils N. schrieb:> void SPI_Init(SPI_TypeDef * oSPI, uint8_t ucPrescaler){> oSPI->CR1 |= SPI_CR1_CPHA;> oSPI->CR1 |= SPI_CR1_CPOL;> oSPI->CR1 |= SPI_CR1_MSTR;> oSPI->CR1 |= (ucPrescaler << 3);> oSPI->CR2 |= SPI_CR2_SSOE;> oSPI->CR1 |= SPI_CR1_SPE;> }>> uint8_t SPI_Send(SPI_TypeDef * oSPI, uint8_t cVal){> oSPI->DR = cVal;> while(!(oSPI->SR & SPI_SR_RXNE));>> return oSPI->DR;> }
Konkret sieht es so aus, dass der Controller wieder bei der Abfrage
hängen bleibt, ob das RXNE-Bit gesetzt wurde. Da weder Daten
herausgetaktet wurden, konnten auch keine Daten eingelesen werden -
daher kann das Bit auch nie gesetzt werden.
Doctor What schrieb:> Du hast einen Vorteiler von 256 eingestellt. Wie schnell der APB> getaktet ist, läßt sich aus Deinem Beispiel nicht ersehen. Auf jeden> Fall hat der mindestens einen Vorteiler von 2.
Ich habe ein Sysclock von 84 MHz und daher ein APB2-Peripheral-Clock von
21MHz. Daher sollte der Takt bei ca. 82 kHz liegen.
Doctor What schrieb:> Aber auch so ist klar, dass das Byte niemals vor dem Abschalten der> SPI-Schnittstelle rausgetaktet werden kann.> Daher: Nur wenn Du genau die Taktzyklen gezählt hast, auf das Abfragen> des BSY-Flags verzichten. Als Anfänger solltest Du das besser immer> machen.
Hm. Dann habe ich das BSY-Flag wohl nicht richtig verstanden. Ich habe
es so verstanden, dass es so lange gesetzt ist, wie er noch am
Raustakten ist. Sprich erst, wenn die vollen 8 Bit gesendet (bzw.
empfangen) wurden, dass dann das BSY-Flag gelöscht wird.
Doctor What schrieb:> A. B. schrieb:>> Das ist der eine Punkt, der andere: Das ständige Aktivieren/Deaktivieren>> des SPI ist überflüssig. Solange man keine weiteren Daten in Datenreg.>> schreibt, bleibt das SPI sowieso still. Außerdem:>> Das macht er wohl, weil er erwartet, dass NSS automatisch beim Setzen> und Löschen von SPE aktiviert bzw. deaktiviert wird.
Nein, dass das NSS entweder hardwareseitig (zu Beginn eines
Transmit/Receive-Zyklus) oder softwareseitig (sprich manuell) gelöscht
bzw. gesetzt. So zumindest mein Verständnis.
Doctor What schrieb:> Ich bin sicher, dass Du damit die Daten rausgetaktet bekommst.
Mein Saleae-Logic-Analyer, mein Oszi (Agilent DSO 2000) und meine LEDs
sind anderer Meinung. Ich habe sie extra an den Beginn der
SPISendTest-Funktion gesetzt, um zu sehen, ob die Funktion brav zyklisch
angesprungen wird. Da sie aber in einer Endlosschleife (die
While-Schleife, welche auf das TXE bzw. RXNE-Bit wartet) festhängt.
Doctor What schrieb:> Anschliessend liest Du Dir mal das entsprechende Kapitel im Reference> Manual gründlich durch, idealerweise mehr als einmal. Einmal reicht> nämlich für Anfänger nicht.
Das Kapitel habe ich gefühlt schon oft genug gelesen - daher habe ich
die Vermutung, dass ich mehr einfach ein großes Brett vor dem Kopf habe
und meinen Fehler/Missverständnis selber einfach nicht mehr sehe, da ich
für meine Fehler betriebsblind geworden bin...
Das NSS ist auch (noch) nicht mein Problem - es findet einfach absolut
keine Tätigkeit seitens des SPI1 statt - und das irritiert mich.
Nils N. schrieb:> GPIOA->AFR[0] |= 5U<<16 | 5U<<20 | 5U<<24 | 5U<<28;>> Setze da mal Klammern. Meckert da der Compiler nicht?>> Bei mir hieße das:AFUNC(GPIOA, 4, 5);> AFUNC(GPIOA, 5, 5);> AFUNC(GPIOA, 6, 5);> AFUNC(GPIOA, 7, 5);>> Lesbarer oder?
Dem kann ich nicht widersprechen - keine Frage. Das werde ich auch für
die Zukunft so beibehalten, danke für den Tipp! Nach einer
übersichtlicheren Lösung habe ich schon länger gesucht bzw. wollte mir
selbst etwas leserlicheres schreiben.
Aber mein Problem hat es trotzdem nicht gelöst :(
Doctor What schrieb:> Lass malSPI1->CR1 |= SPI_CR1_BIDIOE;> weg.
Check! - Kein Unterschied.
Doctor What schrieb:> Stell mal Deinen kompletten Source ein. Da fehlt doch was. Vermutlich> liegt da der Fehler.
Das ist alles, was ich habe. Nicht mehr und nicht weniger.
Das einzige, was ich noch hochladen kann ist meine stm32f4.h bzw.
system_stm32f4.c und .h
Ich habe mittlerweile den Sysclock auf 168 MHz erhöht und der APB2-Clock
sollte jetzt bei 42 MHz liegen.
Doctor What schrieb:> Welcher Controller ist das?> Und ein Makefile und ein Linkerscript hast Du bestimmt ;)
Es handelt sich hier um einen STM32F407VG auf einem
STM32F4-Discovery-Board.
Und da ich die Coocox CoIDE nutze kann ich mit beidem nicht dienen.
Doctor What schrieb:> Schalt mal den Takt für AFIO in APB2ENR ein.
Und den scheint es laut Reference-Manual bzw. Datenblatt bei meinem
Controller nicht zu geben.
Allerdings schalte ich den Takt für das SPI1 mit folgender Zeile ein:
stm32-anfänger schrieb:> Nils N. schrieb:>> 1. Richtige SPI Clk aktiviert?RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;> Check!
stm32-anfänger schrieb:> Und da ich die Coocox CoIDE nutze kann ich mit beidem nicht dienen.
Das sollte man erstmal ändern. Benutze die STM32 Workbench oder Atollic
für STM32.
Ich habe schnell mal frei Schnauze was zusammengekloppt.
Hab leider kein Board hier zum Testen.
Aber Ausprobieren sollte Dich maximal zwei Minuten kosten.
Schau, ob mit dem Programm was aus SPI1 rauskommt.
>Schau, ob mit dem Programm was aus SPI1 rauskommt.
Das sind aber gemütliche 164 kHz.
>>Und da ich die Coocox CoIDE nutze ...>Das sollte man erstmal ändern.>Benutze die STM32 Workbench oder Atollic für STM32.
Da bin ich auch für.
Darum auch ein SW4STM32 Projekt passend zur BIN. Macht 41 MHz auf SPI1
und ein bisschen LED Blinky. Mit CubeMX zusammengeklickt. Ja, ich weiss,
ist lame aber funzt.
Wer die harte Tour mag kann ja sich aus dem CubeMX Code was abgucken und
nach Coocox kopieren.
>>Das sind aber gemütliche 164 kHz.>So war seine Vorgabe... ;)
Wollte nur nicht, das jemand sagt, CubeMX macht den uC/SPI lahm ;)
Ist in CubeMX ja nur ein Mausklick entfernt, den Prescaler
hochzustellen.
Nils N. schrieb:> stm32-anfänger schrieb:>> Und da ich die Coocox CoIDE nutze kann ich mit beidem nicht dienen.>> Das sollte man erstmal ändern. Benutze die STM32 Workbench oder Atollic> für STM32.
Ich habe mich jetzt mal in STM32 Workbench ein wenig eingearbeitet und
bin durchaus positiv überrascht. Ich habe eine Zeit lang versucht das
reine Eclipse ans Laufen zu kriegen und es ist mir auch nach Tagen nicht
vernünftig gelungen.
Beim Debuggen habe ich mir mal die Register genauer angeschaut - und
offensichtlich kriege ich permanent einen "Master mode fault MODF" - was
dazu führt, dass auch das MSTR-Bit gelöscht wird.
Das Reference-Manual sagt dazu folgendes:
1
Master mode fault (MODF)
2
Master mode fault occurs when the master
3
device has its NSS pin pulled low (in NSS
4
hardware mode) or SSI bit low
5
(in NSS software mode), this
6
automatically sets the MODF
7
bit. Master mode fault affects the SPI peripheral in the following ways:
8
• The MODF bit is set and an SPI interrupt is generated if the ERRIE bit is set.
9
• The SPE bit is cleared. This blocks all output from the device and disables the SPI interface.
10
• The MSTR bit is cleared, thus forcing the device into slave mode. Use the following software sequence to clear the MODF bit: 1.
11
Make a read or write access to the SPI_SR register while the MODF bit is set.
12
2. Then write to the SPI_CR1 register.
13
To avoid any multiple slave conflicts in a
14
system comprising several MCUs, the NSS pin
15
must be pulled high during the MODF bit clearing sequence. The SPE and MSTR bits can
16
be restored to their original state after this clearing sequence.
17
As a security, hardware does not allow the setting of the SPE and MSTR bits while the
18
MODF bit is set.
19
In a slave device the MODF bit cannot be set.
20
However, in a multimaster configuration, the
21
device can be in slave mode with this MODF bi
22
t set. In this case, th
23
e MODF bit indicates
24
that there might have been a multimaster conflic
25
t for system control. An interrupt routine can
26
be used to recover cleanly from this state by performing a reset or returning to a default
27
state
Doctor What schrieb:> Ich habe schnell mal frei Schnauze was zusammengekloppt.> Hab leider kein Board hier zum Testen.> Aber Ausprobieren sollte Dich maximal zwei Minuten kosten.> Schau, ob mit dem Programm was aus SPI1 rauskommt.
Danke! - Das File sorgt in der Tat für eine periodische Ausgabe auf dem
SPI1! Genau so, wie ich es auch testweise haben wollte.
Also muss bei mir der Fehler liegen (wie es nicht anders zu erwarten war
;) )
Jetzt bleibt nur noch die Preisfrage: WO ist mein Fehler?
Doctor What schrieb:> dasrotemopped schrieb:>> Das sind aber gemütliche 164 kHz.>> So war seine Vorgabe... ;)> SPI1->CR1 |= SPI_CR1_BR_0 | SPI_CR1_BR_1 | SPI_CR1_BR_2; //Clock-Divider> = 256dasrotemopped schrieb:>>>Und da ich die Coocox CoIDE nutze ...>>Das sollte man erstmal ändern.>>Benutze die STM32 Workbench oder Atollic für STM32.>> Da bin ich auch für.
s.o.
dasrotemopped schrieb:> Wer die harte Tour mag kann ja sich aus dem CubeMX Code was abgucken und> nach Coocox kopieren.
Ja, so habe ich es in der Tat auch versucht, nachdem es die ersten
Versuche nicht geklappt hat.
Hier mal mein Code. Ist nicht ausgefeilt und verbesserungswürdig, also
nicht meckern. Aber vielleicht als Startpunkt für Dich, wenn Du bei
Deinem Ansatz bleiben willst und nicht die ST-Bloatware verwendest
(damit sparst Du auch gleich die stm32f4xx_conf.h, system_stm32f4xx.c
und system_stm32f4xx.h...).
Init.c, ausgerufen aus dem startup vor Sprung in main():
So. Einen großen Spaziergang und Hausputz später habe ich es ans Laufen
bekommen.
Offenbar muss ich mir die ganze Geschichte mit dem Software-NSS und
Hardware-NSS mal ganz ganz ganz genau durchlesen, denn offenbar lag da
das Problem. Nachdem alles, was damit zu tun hatte auskommentiert war
hat es zumindest nicht mehr diesen Fehler gegeben und es wird zyklisch
über das SPI etwas herausgetaktet.
Ich hatte es zwar schon vorher so versucht auszukommentieren aber da
muss ich wohl irgendwas übersehen haben, denn jetzt funktioniert es auf
einmal.
Wie dem auch sei, ein großes Danke an alle, die sich an diesem Thread
beteiligt haben und ihre Zeit investiert haben!
Wenn noch weitere Probleme auftreten werde ich mich nochmal melden :)
stm32-anfänger schrieb:> Offenbar muss ich mir die ganze Geschichte mit dem Software-NSS und> Hardware-NSS mal ganz ganz ganz genau durchlesen, denn offenbar lag da> das Problem. Nachdem alles, was damit zu tun hatte auskommentiert war> hat es zumindest nicht mehr diesen Fehler gegeben und es wird zyklisch> über das SPI etwas herausgetaktet.
Hättest Du mal gleich auf mich gehört ;)
Doctor What schrieb:> Mein Vorschlag ist, das Ganze erstmal ein bisschen simpler zu machen.> Nimm also den NSS-Kram raus, den hast Du sicherlich nicht verstanden.> Mache den Chipselect ganz einfach per GPIO.
Doctor What schrieb:> stm32-anfänger schrieb:>> Offenbar muss ich mir die ganze Geschichte mit dem Software-NSS und>> Hardware-NSS mal ganz ganz ganz genau durchlesen, denn offenbar lag da>> das Problem. Nachdem alles, was damit zu tun hatte auskommentiert war>> hat es zumindest nicht mehr diesen Fehler gegeben und es wird zyklisch>> über das SPI etwas herausgetaktet.>> Hättest Du mal gleich auf mich gehört ;)>> Doctor What schrieb:>> Mein Vorschlag ist, das Ganze erstmal ein bisschen simpler zu machen.>> Nimm also den NSS-Kram raus, den hast Du sicherlich nicht verstanden.>> Mache den Chipselect ganz einfach per GPIO.
Ja Papa ;)
Nein, im Ernst - du hattest Recht. Ich hatte aber auch mal eine kleine
Pause notwendig gehabt und irgendwie hatte ich mich da etwas verrant.
Wie dem auch sei, jetzt funktioniert alles wie erwartet und ich lese
mich noch ein wenig zum NSS ein.
Mal eine Verständnisfrage zu folgendem Ausschnitt:
1
while(!(SPI1->SR&SPI_SR_TXE))
2
asm("nop");
3
while(SPI1->SR&SPI_SR_BSY)
4
asm("nop");
Das ist genau die Umsetzung von
1
1.
2
Enable the SPI by setting the SPE bit to 1.
3
2. Write the first data item to send into
4
the SPI_DR register (this clears the TXE bit).
5
3. Wait until TXE=1 and write the next data item
6
to be transmitted. Repeat this step for
7
each data item to be transmitted.
8
4. After writing the last data
9
item into the SPI_DR register, wa
10
it until TXE=1, then wait until
11
BSY=0, this indicates that
12
the transmission of the
13
last data is complete.
Sprich für Dummies und stm32-anfänger: Die erste While-Schleife wird so
lange durchlaufen, wie das TXE-Bit als 0 gelesen wird. Die zweite wird
so lange durchlaufen, wie das BSY-Bit gesetzt ist (was sich ja wiederum
mit der Anleitung im dem Reference-Manual decken würde)?!
Doctor What schrieb:> Wobei das in meinem Beispiel nicht nötig wäre, das war extra für Dich> als vollständiges Anschauungs-Beispiel gedacht :)
Hm.
Warum wäre es in deinem Beispiel nicht nötig gewesen? - du meinst, weil
ein simples "Delay" (pöhse, ich weiß) ausgereicht hätte?
Ich habe nämlich erst ein wenig mit dem Weglassen dieser Schleifen
herumexperimentiert und - oh Wunder - das ganze hat dann natürlich nicht
mehr vernünftig funktioniert. Dann habe ich das RefManual nochmals
konsultiert und bin auf diese Beschreibung gestoßen, was das ganze
rückwirkend logisch erklärte.
stm32-anfänger schrieb:> Warum wäre es in deinem Beispiel nicht nötig gewesen? - du meinst, weil> ein simples "Delay" (pöhse, ich weiß) ausgereicht hätte?
Weil ich nur ein Byte übertrage. Der Test auf TXE ist damit unnötig.
Und je nach SPI-Takt / CPU-Takt ist auch der BSY-Test unnötig, da der
Delay ausreichend lang ist.
Im Übrigen ist ein simpley Delay (ein bzw. mehrere "nop") nicht "pöhse",
im Gegenteil, da Du die Frequenzen kennst, wird Dein Programm damit
schneller.
Ist halt nur nicht portabel, wenn sich mal die Frequenz ändern soll.
Doctor What schrieb:> Im Übrigen ist ein simpley Delay (ein bzw. mehrere "nop") nicht "pöhse",
Ist doch pöhse.
Braucht nur ein anderes Hardware Teil deines STM32 mal dazwischen
funken und dein sensibles Wait/Delay-Gefüge aus dem Rhythmus
bringen, und schon hängt die SPI Maschine wieder.
Kann ja sein dass du schon Delay machst während die SPI noch
darauf wartet überhaupt starten und übertragen zu können ....
Doctor What schrieb:> Im Übrigen ist ein simpley Delay (ein bzw. mehrere "nop") nicht "pöhse",> im Gegenteil, da Du die Frequenzen kennst, wird Dein Programm damit> schneller.
Es war auch mehr ironisch gemeint; das habe ich mir schon so gedacht,
dass du explizit auf das Taktezählen hinauswolltest (ansonsten hätte ich
schon "böse" geschrieben ;) ).
Ich werde es für die nähere Zukunft aber zunächst datenblattgetreu
machen, da habe ich schon genug damit zu tun. Daher war dein Beispiel
mit den beiden Schleifen schon genau richtig!
Wenn ich dann mit den STM32ern etwas sattelfester bin, dann werden auch
solche händischen Optimierungen unter Umständen eingesetzt - natürlich,
wie immer mit Bedacht und mit Köpfchen.
Und der Vollständigkeit halber eine Ergänzung für andere Leser, die über
diesen Thread bzw. dieses Problem stolpern: Folgende Lösung zum Senden
ist auch funktional:
1
uint32_tSPI_SendTest(void){
2
uint32_tspi_byte_received=0;
3
GPIOD->ODR^=(GPIO_ODR_ODR_15|GPIO_ODR_ODR_14|GPIO_ODR_ODR_13|GPIO_ODR_ODR_12);//Toggle LED for each function-call
4
GPIOE->ODR|=GPIO_ODR_ODR_7;//Set the SS-Pin
5
GPIOE->ODR&=~GPIO_ODR_ODR_7;//Clear the SS-Pin
6
SPI1->DR=0x93;//Send a dummy-byte
7
while(!(SPI1->SR&SPI_SR_RXNE));//wait until the RXNE-Flag is set by hardware
8
spi_byte_received=SPI1->DR;//Read the SPI-DataRegister and by reading this register the SPI_SR_RXNE-Flag will be cleared.
9
GPIOE->ODR|=GPIO_ODR_ODR_7;//After the transmission is complete, the SS-Pin will be set again
10
returnspi_byte_received;//Retun the value that was received via the SPI
Korinthen Kacker schrieb:> Doctor What schrieb:>> Im Übrigen ist ein simpley Delay (ein bzw. mehrere "nop") nicht "pöhse",>> Ist doch pöhse.>> Braucht nur ein anderes Hardware Teil deines STM32 mal dazwischen> funken und dein sensibles Wait/Delay-Gefüge aus dem Rhythmus> bringen, und schon hängt die SPI Maschine wieder.>> Kann ja sein dass du schon Delay machst während die SPI noch> darauf wartet überhaupt starten und übertragen zu können ....
Nö, SPI ist eine State Machine. Ab Transfer ins DR-Register läuft die
unabhängig von der anderen Hardware deterministisch.
Und worauf soll denn das SPI-Modul warten???
stm32-anfänger schrieb:> Offenbar muss ich mir die ganze Geschichte mit dem Software-NSS und> Hardware-NSS mal ganz ganz ganz genau durchlesen, denn offenbar lag da> das Problem. Nachdem alles, was damit zu tun hatte auskommentiert war> hat es zumindest nicht mehr diesen Fehler gegeben und es wird zyklisch> über das SPI etwas herausgetaktet.
tldr;
STM32 Spi-Master -> Software-NSS nutzen
Da ich damit auch schonmal auf die Nase gefallen bin, hier noch ein Tip
von mir:
Lass das mit dem Hardware-NSS so lange du deinen STM32 als SPI-Master
betreibst am besten komplett bleiben und nutze Software-NSS. Selbst ST
macht das in deren Demo-Code so und das hat definitiv seine Gründe.
Keine Ahnung was sich ST bei der Implementierung gedacht hat aber man
kann da die ein oder andere böse Überraschung erleben, z.B. das mit
Hardware-NSS bei einem DMA-Transfer grundsätzlich nach jedem Byte die
CS-Leitung getogglet wird, was man viel zu oft wahrscheinlich gar nicht
möchte. Das ist aber zum Glück alles kein Problem, weil man - selbst bei
Nutzung von DMA - ganz einfach jeden beliebigen GPIO-Pin als
Software-NSS nutzen kann.
Ich habe jetzt auch das ursprüngliche Problem lösen können! Es liegt
quasi auf der Hand. Da ich nicht die korrekte Reihenfolge der Abfragen
eingehalten hatte.
Ich hatte ursprünglich nur auf das BSY-Flag gewartet, dann nur auf das
TXE-Flag. Und schlussendlich nur auf das RXNE-Flag, ohne zu beachten,
dass man es nur löschen kann, indem man das DataRegister liest.
Dadurch, dass ich mich nicht an die vorgeschriebene Reihenfolge gehalten
habe wurde noch während das Byte aus dem Buffer in das Datenregister zum
Senden geladen wurde an dem SS-Pin herumgepfuscht, was der Controller
gar nicht gut fand.
Jetzt funktioniert es auch mit dem manuellen Setzen des eigentlichen
NSS-Pins und morgen teste ich mal das automatische Setzen/Löschen.
Wenn ich Zeit habe lade ich zur Vollständigkeit auch mal ein Mitschnitt
vom LA hoch, wo man genau sieht, dass bei fehlerhafter Reihenfolge der
SS-Pin nur ganz kurz auf Low geht, noch bevor die eigentliche
Übertragung gestartet wurde. Und genau DAS hatte mir letztlich das
Genick gebrochen.