Forum: Mikrocontroller und Digitale Elektronik STM32F4xx: Kann mal jemand die SPI testen?


von Christian J. (Gast)


Lesenswert?

Hallo,

falls jemand einen Aufbau zur Hand hat, wo ein Gerät per SPI versorgt 
wird.
Könnte er das mal bei 168Mhz SysClock, SPI Prescaler 32 oder 16 testen? 
GPIO Speed ist Fast für alle Pins.

Ob die GCC Einstellung

__attribute__((optimize("Os"))) // Optimize for Size

die SPI Kommunikation noch einwandfrei laufen lässt? Bei mir ist das 
defintiv nicht der Fall, das DR Register enthält dann immer 0x00 statt 
den Wert vom Gerät. Ich habe alle Kombinationen ausprobiert, wie man die 
Befehle anordnen kann, auch mit und ohne __DMB().

Mangels Oszi kann ich da nicht tiefer rein und am LA ist nichts zu 
sehen.

Es spielt keine Rolle, ob ich da Lib Funktionen verwende oder die 
Register direkt anspreche, der GCC Compiler optimiert das sowieso alles 
auf das Wesentliche und macht aus den Libs inline Funktionen.
1
/* ------ SPI Low Level: Ein 8 Bit Datenwort senden und holen ------- */
2
uint8_t __attribute__((optimize("O0"))) SPI_TransferByte(uint8_t data)
3
{
4
    while(!SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE));       // Warte bis TXE (SPI ist frei)
5
    SPI_I2S_SendData(SPI1, data);                                // Byte senden...
6
    while(!SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE));      // Warte bis Byte empfangen worden
7
    while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY));
8
    return SPI_I2S_ReceiveData(SPI1);
9
}

von hhhhhhhh (Gast)


Lesenswert?

Ich weiss, das hast du vermutlich schon geprüft, aber es klingt so als 
könnte irgendwo ein volatile fehlen. Wäre allerdings seltsam, die 
Funktions- und Makronamen deuten darauf hin dass du Library-Code vom 
Hersteller verwendest?

von Pete K. (pete77)


Lesenswert?

Vielleicht hilft es, wenn Du hier ein Beispiel einstellst, mit allen 
Dateien.
Code soweit wie möglich reduzieren.

von Christian J. (Gast)


Lesenswert?

Pete K. schrieb:
> Vielleicht hilft es, wenn Du hier ein Beispiel einstellst, mit allen
> Dateien.
> Code soweit wie möglich reduzieren.

Das nützt nichts, wenn jemand nicht die Hardware hat. Das ist genau der 
Codefetzen wo es Probleme gibt. Und die sind nicht "erfunden", da haut
irgendwas nicht hin. Völlig egal ob Lib Routinen oder ob da Register 
stehen.
SPIx->DR wird zu Null und das nur bei -Os Einstellung.

von chris (Gast)


Lesenswert?

Christian J. schrieb:
> Das nützt nichts, wenn jemand nicht die Hardware hat. Das ist genau der
> Codefetzen wo es Probleme gibt. Und die sind nicht "erfunden", da haut
> irgendwas nicht hin.

Ich habe aber keine Lust, mir die GPIO- und SPI-Init selbst 
zusammenzubasteln.
Dann könnte auch wieder was anderes rauskommen als bei dir.
Also mach doch bitte ein vollständiges, kompilierbares Programm.
Als Hardware reicht ja dann das STM32F4-Discovery aus.

von Marcus H. (Firma: www.harerod.de) (lungfish) Benutzerseite


Lesenswert?

Hi Hobel,
ohne Deinen Code angeschaut zu haben:

F4 Datenblatt DM00037051 Rev5 5.3.19:
SPI 42Mbps / 21Mbps bei 30pF Leitungskapazität.

Wie sieht denn Deine Bustopologie aus?

Sag jetzt bitte nicht "zweimal Breadboard verbunden mittels 2,5m 
Flachbandleitung" ;)

Gruß,
 Marcus

P.S.: spar auf ein Oszi!

P.P.S.: bis zu welchem Prescalerwert funktioniert Deine Kommunikation? 
Mach doch testweise mal ganz langsam.

P.P.P.S.: "Digilent Analog Discovery" - das liegt bei mir hinter dem 
alten Vierkanaloszi.

von Christian J. (Gast)


Lesenswert?

Marcus H. schrieb:
> ohne Deinen Code angeschaut zu haben:

Bei solchen Antworten erübrigt sich eigentlich jeder Kommentar.... da 
reagiere ich auch nicht mehr drauf.

von Marcus H. (Firma: www.harerod.de) (lungfish) Benutzerseite


Lesenswert?

Christian J. schrieb:
> Marcus H. schrieb:
>> ohne Deinen Code angeschaut zu haben:
>
> Bei solchen Antworten erübrigt sich eigentlich jeder Kommentar.... da
> reagiere ich auch nicht mehr drauf.

Das find ich jetzt aber schade...

von chris (Gast)


Lesenswert?

Christian J. schrieb:
> Bei solchen Antworten erübrigt sich eigentlich jeder Kommentar.... da
> reagiere ich auch nicht mehr drauf.

Ich dachte du wolltest Hilfe?
Komischerweise hast du dauernd irgendwelche Probleme mit dem STM32, die 
sonst niemand hat.
Dann ist es ja wohl auch nicht zu viel verlangt, wenn du ein 
kompilierbares Testprogramm hochlädst.

von Klaus Skibowski (Gast)


Lesenswert?

Hast Du alle notwendigen Taktquellen aktiviert ?

von Patrick B. (p51d)


Lesenswert?

Mhm, Kristallkugel mal wieder entstauben....
Wäre echt nett, wenn du den ganzen Code zum SPI inklusive 
Initialisierung hochladen würdest. Weil so kann man nicht wirklich 
helfen.

Also folgende Punkte wären Hilfreich:
- Kompletter Code des SPI
- Hardwarebeschaltung
- Exakte Fehlerbeschreibung

Als Grundsatz würd ich mal die Optimierung ausschalten. Vieleicht hilft 
das ja schon. Und bei SPI wird meistens ein CS oder SS verwendet. Bei 
dir nicht ersichtlich.

Ich verweise hier mal auf ein gutes Tutorial:
http://diller-technologies.de/stm32.html#spi

Oder das hier:
http://www.lxtronic.com/index.php/basic-spi-simple-read-write

Da kannst du ja deinen Code vergleichen.

: Bearbeitet durch User
von Christian J. (Gast)


Lesenswert?

Patrick B. schrieb:
> Als Grundsatz würd ich mal die Optimierung ausschalten.

Duuu.... das stand schon im ersten Post. Ohne Optiemierung läuft der 
nämlich aber MIT nicht. Also ist er fehlerhaft. Was soll ich zu 
Beiträgen sagen, wo ich merke, dass wie zwei drüber der Antworter meinen 
Beitrag gar nicht erst gelesen hat? Nochmal das wiederholen was schon 
geschrieben steht?

Und völlig egal bei welchem SPI Gerät, es ist immer das gleiche.  Ich 
kann da ein 74HCT165 Serial-Shift-Input dran setzten oder wie hier ein 
NRF24L01+.

Das DR liefert 0x00. Und der Code steht oben auch schon. Wer den 
verwendet haut sich Bugs rein.

Code um Steuerpins gekürzt. Baudrate : 1.333 Mhz
1
    GPIO_InitTypeDef GPIO_InitStruct;
2
    SPI_InitTypeDef SPI_InitStruct;
3
4
    // PA7 = MOSI, PA6 = MISO, PA5 = SCK
5
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);
6
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
7
8
    /*-------- SCK, MOSI, MISO: Pinpack 1 --------*/
9
    GPIO_StructInit (&GPIO_InitStruct);
10
    GPIO_InitStruct.GPIO_Pin    = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
11
    GPIO_InitStruct.GPIO_Mode   = GPIO_Mode_AF;
12
    GPIO_InitStruct.GPIO_Speed  = GPIO_Fast_Speed;
13
    GPIO_InitStruct.GPIO_OType  = GPIO_OType_PP;
14
    GPIO_InitStruct.GPIO_PuPd   = GPIO_PuPd_NOPULL;
15
    GPIO_Init(GPIOA, &GPIO_InitStruct);
16
17
    /* SPI1 die Alternate Pins zuordnen */
18
    GPIO_PinAFConfig(GPIOA, MOSI_AF, GPIO_AF_SPI1);
19
    GPIO_PinAFConfig(GPIOA, MISO_AF, GPIO_AF_SPI1);
20
    GPIO_PinAFConfig(GPIOA, SCK_AF, GPIO_AF_SPI1);
21
22
    /* Die SPI1 einstellen: Master, 2 Lines, 8 Bit, Mode 0 */
23
    SPI_StructInit(&SPI_InitStruct);
24
    SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
25
    SPI_InitStruct.SPI_Mode      = SPI_Mode_Master;
26
    SPI_InitStruct.SPI_DataSize  = SPI_DataSize_8b;
27
    SPI_InitStruct.SPI_CPOL      = SPI_CPOL_Low;
28
    SPI_InitStruct.SPI_CPHA      = SPI_CPHA_1Edge;
29
    SPI_InitStruct.SPI_NSS       = SPI_NSS_Soft;
30
    SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_64;
31
    SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB;
32
    SPI_Init(SPI1, &SPI_InitStruct);

von Peter D. (peda)


Lesenswert?

Christian J. schrieb:
> Und der Code steht oben auch schon.

Wo?
Ich sehe nirgends einen compilierbaren und linkbaren Code.
Auch das Make und die Includes sind nirgends zu sehen.

Meinst Du im Ernst, da setzt sich jemand hin und bastelt aus den 
Schnipselchen erstmal ein Programm?
Da kannst Du aber lange warten.

Murphy’s Law:
Programmierfehler sind immer grundsätzlich in dem nicht geposteten Code.

: Bearbeitet durch User
von Markus F. (mfro)


Lesenswert?

Ich hab' das auch schon in deinem anderen Thread geschrieben, aber 
entweder hast Du's nicht gelesen oder nicht ernst genommen: Du machst 
hier  (wie dort) keinerlei Fehlerüberprüfung (zumindest habe ich keine 
gesehen).

Wenn eins der Error-Flags gesetzt ist, kriegst Du's schlicht nicht mit, 
wie's aussieht.

Nicht gerade das, was ich unter defensiver Programmierung verstehen 
würde. Dafür braucht man kein Oszi.

von chris (Gast)


Lesenswert?

Christian J. schrieb:
> Code um Steuerpins gekürzt. Baudrate : 1.333 Mhz


Peter D. schrieb:
> Wo?
> Ich sehe nirgends einen compilierbaren und linkbaren Code.
> Auch das Make und die Includes sind nirgends zu sehen.


Wie viele Leute müssens dir eigentlich noch sagen?
Lad doch endlich mal ein kompilierbares vollständiges Beispielprogramm 
hoch.
Dann teste ich es auch gerne an meinem STM32F4 Discovery.

von Patrick B. (p51d)


Lesenswert?

Christian J. schrieb:
> Duuu.... das stand schon im ersten Post. Ohne Optiemierung läuft der
> nämlich aber MIT nicht.

Falsch. Im ersten Post steht absolut nichts davon, dass du mit der 
Optimierung experimentiert hast. Es heist lediglich, dass man mit einer 
Optimierungseinstellung den Code testen soll.

Christian J. schrieb:
> SPIx->DR wird zu Null und das nur bei -Os Einstellung.

Hier erahnt man, dass du etwas mit Optimierungen experimentiert hast. 
Ist diese Optimierung zwingend nötig? Andere Variante möglich?

Christian J. schrieb:
> Was soll ich zu
> Beiträgen sagen, wo ich merke, dass wie zwei drüber der Antworter meinen
> Beitrag gar nicht erst gelesen hat? Nochmal das wiederholen was schon
> geschrieben steht?

Die Leseschwäche kann man bei dir aber auch vorwerfen: Mehr als die 
Hälfte der hilfsbereiten Leute hier hat dir gesagt, dass du ein 
KOMPLETTES Beispiel hochladen sollst. Anhand von deinem Code sieht man, 
dass du defines benutzt, welche nicht ersichtlich sind. Und du 
verwendets softwarmässiges Slaveselect... nicht ersichtlich wie das 
gesetzt oder gelöscht wird.

Wie sieht die Hardware aus? Ist noch etwas am SPI1 angeschlossen 
(Discovery-Bord...)? Hast du eine Daisy-Chain aufgebaut?

Ich wage einmal eine Vermutung: Sofern in deinem nächsten Post nicht ein 
Zip mit dem kompletten C-Projekt (kannst die "geheimen" Teile ja 
löschen, aber es sollte direkt compilierbar sein), Angaben über 
verwendete IDE und Schaltplänen sowie Beschreibung der Tests hochlädst, 
wird dir hier niemand mehr helfen.

Falls du das komplette Projekt hochladen würdest, wäre ich wohl bereit 
das auf einem Discovery zu testen, aber so sicher nicht.

von Christian J. (Gast)


Lesenswert?

Patrick B. schrieb:

> Falls du das komplette Projekt hochladen würdest, wäre ich wohl bereit
> das auf einem Discovery zu testen, aber so sicher nicht.

Da ich mit einer IDE arbeite, die hier ohnehin niemand hat und wo 
sämtliche Einstellungen in GUI's gemacht werden, wo es keine Make Files 
gibt und zu deren Betrieb dutzende Libs, CMSIS etc eingebunden werden 
die auch niemand genauso hat wie ich sie liegen habe bzw in genau der 
Version, hat das keinen Sinn, das Projekt kann niemand laden und 
Portierbarkeit ist von der IDE auch nicht vorgesehen. Und ohne 
zusätzliche Hardware, die Daten liefert geht es auch nicht.

Ich schliesse das das daher hier mal ab. Zudem habe ich die Vermutung, 
dass die while Schleifen einfach überrannt werden, weil die Hardware zu 
schnell/langsam ist.

von Jojo S. (Gast)


Lesenswert?

Müsste der MOSI Pin nicht als Output definiert werden?

von Christian J. (Gast)


Lesenswert?

Christian J. schrieb:

Jojo S. schrieb:
> Müsste der MOSI Pin nicht als Output definiert werden?

Nö. Diese Zuweisung übernimmt das ja, klemmt die Pins an das SPI Modul.
Nur die Steuerpins (habe ich gelöscht, wegen Übersichtlichkeit) bekommen 
Richtungen.

> /* SPI1 die Alternate Pins zuordnen */
>     GPIO_PinAFConfig(GPIOA, MOSI_AF, GPIO_AF_SPI1);
>     GPIO_PinAFConfig(GPIOA, MISO_AF, GPIO_AF_SPI1);
>     GPIO_PinAFConfig(GPIOA, SCK_AF, GPIO_AF_SPI1);

Ich habe gestern 1h damit verbracht. Es geht hier um taktgenaue Abfragen 
der Flags . Und ohne Optimierung werden die Zeiten eingehalten, mit 
Optimierung aber nicht mehr. Setze ich zb 5 "volatile" NOPs zwischen die 
befehle geht es auch mit Opmtierung.

von chris (Gast)


Lesenswert?

Christian J. schrieb:
> Da ich mit einer IDE arbeite, die hier ohnehin niemand hat und wo

CooCox?
Benutze ich auch.


Christian J. schrieb:
> CMSIS etc eingebunden werden
> die auch niemand genauso hat wie ich sie liegen habe bzw in genau der
> Version,

Einfach den ganzen Projektordner in ein zip und Hochladen.
So einfach ist das.


Christian J. schrieb:
> Portierbarkeit ist von der IDE auch nicht vorgesehen.

CooCox Projektordner kopieren und fertig. Lässt sich wunderbar überall 
öffnen.


Christian J. schrieb:
> Und ohne
> zusätzliche Hardware, die Daten liefert geht es auch nicht.

Ich dachte das Register liefert immer 0x00?
Dann reicht doch ein einfaches Loopback zum Testen ob es funktioniert.
Wozu externe Hardware? (außer einem Jumperkabel)


Christian J. schrieb:
> Ich schliesse das das daher hier mal ab.

Auch eine Möglichkeit. Aber ob es sinnvoll ist, Fehler einfach zu 
ignorieren?


Christian J. schrieb:
> Zudem habe ich die Vermutung,
> dass die while Schleifen einfach überrannt werden, weil die Hardware zu
> schnell/langsam ist.

Dann darf im Datenregister nicht 0 stehen, sondern normalerweise der 
alte Wert aus der Transmission vorher.
Ich vermute einen Programmfehler irgendwo in deinen nicht geposteten 
Zeilen.

von Christian J. (Gast)


Lesenswert?

chris schrieb:
> CooCox Projektordner kopieren und fertig. Lässt sich wunderbar überall
> öffnen.

EmBitz Ver 0.42! Projektordner ist ca 150mB gross, nur das Template. 
Aber vielleicht reicht auch eine Main Funktion wo alles drin ist, die 
lässt sich auch in CooCox und alles andere kopieren.

Es würde auch reichen ein Kabel einfach an 3.3V zu legenn für 1 und GND
für 0.

Gestern war hier noch ein Fred, der fast genau das gleiche Problem 
hatte. Aber seine Lösung war dann noch falscher. Da hätten auch NOPs 
gereicht.

Bei den Ic2 Routinen, die alle laufen und daher erstmal "richtig" sind 
das gleiche Problem: Nur ohne Optimierung, mit geht es nicht mehr, weil 
da "Events" verpasst werden bzw. nicht erkannt.

von Jojo S. (Gast)


Lesenswert?

ich hatte die Anregung hierher genommen:
http://stackoverflow.com/questions/6922584/spi-is-reading-data-as-zero-in-stm32f103ze

Oder den PullUp einschalten? Wenn der MISO nicht terminiert ist könnten 
auch geschwindigkeitsabhängige Probleme auftreten.

Ich habe F4 CubeMX Libs da ist einfaches SPI gar nicht mehr drin, nur 
Interrupt oder DMA getrieben. Vielleicht kriegt ST das selber nicht 
hin...

von CAN-Fan (Gast)


Lesenswert?

Jojo S. schrieb:
> Ich habe F4 CubeMX Libs da ist einfaches SPI gar nicht mehr drin, nur
> Interrupt oder DMA getrieben. Vielleicht kriegt ST das selber nicht
> hin...

Also ich hab die schon noch drinnen.

HAL_StatusTypeDef HAL_SPI_TransmitReceive(...);

Wenn ich mich richtig erinnere, pollt die intern selbst. Bei UART hatte 
ich aber das Problem, dass das Pollen nicht funktioniert hat, über den 
DMA ging es direkt. Vll lag es auch an den Compiler-Parameter.

Ansonsten @Topic:
Ich bin noch nicht ganz schlau was da wie nicht funktioniert.
Können Fehler in der HW definitiv ausgeschlossen werden? V.a. geht es 
bei langsamen Frequenzen? Ich hatte mal das Problem, dass bei einem 
SPI-Baustein die Zeiten im Datenblatt nicht gepasst haben. Sprich nach 
dem CS hat er deutlich länger gebraucht. mit NOPs dazwischen gints, als 
der Kompiler es optimiert hat ging es natürlich wieder nicht.

Ansonsten kann der Ausgangstreiber bei zu großer Last Probleme bekommen. 
Aber was genau da dran hängt sehe ich auch nicht.

Ich hätte auch mal zur aktuellen HAL geraten, mit Cube ist in paar min 
ein Projekt erstellt, dann einfach SPI testen. Läuft es dann, liegt es 
wohl an der anderen Software.

So wird keiner helfen können. Es ist nicht mal ersichtlich, ob ein 
Betriebssystem oder andere Interrupts verwendet werden etc.

von Jojo S. (Gast)


Lesenswert?

CAN-Fan schrieb:
> Also ich hab die schon noch drinnen.
>
> HAL_StatusTypeDef HAL_SPI_TransmitReceive(...);

stimmt, habe ich auch, ich habe die private_functions im source 
Kommentar falsch interpretiert.

Mit dem CubeMX kann man aber leicht einen Testcode generieren lassen 
(wie CAN-Fan auch schon vorgeschlagen hat), da ist dann ja alles incl. 
Init drin. Wenn die SPI damit funktioniert sollte auch die HW ok sein. 
Dann die paar Zeilen für SPI_TransferByte() von Christian dazupacken und 
gucken was dann passiert.

Könnte ich heute abend auch machen, ich spiele gerade aber lieber mit 
dem F469-Disco und dem schönen Display.

von chris (Gast)


Lesenswert?

Christian J. schrieb:
> Aber vielleicht reicht auch eine Main Funktion wo alles drin ist, die
> lässt sich auch in CooCox und alles andere kopieren.

Ja dann nichts wie her damit ;)

von CAN-Fan (Gast)


Lesenswert?

Jojo S. schrieb:
> Dann die paar Zeilen für SPI_TransferByte() von Christian dazupacken und
> gucken was dann passiert.

Ich glaube die HAL_SPI_TransmitReceive macht exakt das. Sendet x Byte 
über SPI raus und liest x Byte zurück. So würde ich den originalen code 
oben auch interpretieren.

Sogar mit Timeout und für n-Bytes. Grundsätzlich bin ich kein Fan von 
HALs, aber in dem Fall würde ich entweder die HAL-Funktion selbst 
nachprogrammieren, oder mit Assembler/C (ohne HAL) mehr Performance 
erreichen.

Daher kann ich die gleich vom Hersteller verwenden, hat dann weniger 
Fehler und ist getestet und wird gepflegt.

von Jojo S. (Gast)


Lesenswert?

Die Zeilen von Christian (die ja lt. Doku und anderen Beispielen reichen 
sollten) verbraten vielleicht ein paar CPU Takte weniger, aber für mehr 
Performance würde ich dann auch den DMA Treiber nehmen. Wenn es denn 
nötig ist, der nrF wird den F4 wohl kaum an die Grenzen treiben. Es geht 
scheinbar mehr um das Prinzip.

von Matthias (Gast)


Lesenswert?

>Daher kann ich die gleich vom Hersteller verwenden, hat dann weniger
>Fehler und ist getestet und wird gepflegt.

Böser Fehler sowas zu unterstellen!

Auch die Leute, die das Zeug geschrieben haben, kochen nur mit Wasser 
und ich hab schon zu oft code gesehen, der "zufällig" funktioniert hat, 
bis
eine Kleinigkeit (z.B. Mainloop-Zykluszeit, ...) anders war, als beim 
Test.

Ich kann nur empfehlen für sowas einen Logik-Analyzer zu verwenden.
Dann sieht man was beim SPI an Signalen vorliegt und hat wenigstens
eine Chance zw. HW- und SW-Fehlern zu unterscheiden. Man kann auch eine 
Kombination aus beidem haben und sucht sich dann einen Wolf.

Der hier ist vom Preis her recht günstig (~150..250€) und hat mir bisher 
immer gute Dienste geleistet. Man kann sich auch eigene Plugins 
schreiben, wenn man was exotischeres als I2C, SPI, bzw. gängigen 
Bussen/Protokollen hat.

www.saleae.com

von Christian J. (Gast)


Angehängte Dateien:

Lesenswert?

Hi,

Mal etwas gespielt auf "Optimize for Size". Es funktioniert genau dann, 
wenn exakt 4 NOPs eingefügt werden zwischen Abfrage des RXNE Bits und 
dem Auslesen des DR Registers. Die Abfrage des Buys Flags erfüllt den 
gleichen Zweck aber auch nur als "Füllmaterial", dann reicht ein NOP.

Laut Datenbuch ist DR gültig, sobald RXNE High geht. BSY braucht man 
nicht abfragen, da es synchron mit RXNE low geht. Habe Slave Mode 
ngehängt, sieht aber im Full Duplex Master Mode genauso aus.

Also schliesse ich daraus, dass da eine kurze Verzögerung drin ist, 
bevor DR wirklich gültig ist. In diesem Fall 4 Ticks + etwas Overhead.

Und da findet sich auch was im Datenbuch, siehe Snipped. Da der APB1 mit 
1/2 Sysclock läuft sind das ergo 4 Befehle. Falls sich BSY auch auf RXNE 
bezieht.

Ich benutze SPI noch mit DMA für ein Display, da ist das Problem nicht, 
und die läuft mit vollen 20 Mhz durch.
1
uint8_t   SPI_TransferByte(uint8_t data)
2
{
3
    while(!SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE));       // Warte bis TXE (SPI ist frei)
4
    SPI_I2S_SendData(SPI1, data);                                // Byte senden...
5
    while(!SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE));      // Warte bis Byte empfangen worden
6
    //while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY));
7
    __NOP();
8
    __NOP();
9
    __NOP();
10
    __NOP();
11
    return SPI_I2S_ReceiveData(SPI1);
12
}

von Adib (Gast)


Lesenswert?

Hallo Christian,

wenn du uns nicht deinen ganzen Quelltext offenlegen willst:
- erzeuge ein neues Projekt
- schreibe genausoviel Code, bis dein Problem auftritt
- beschreibe wie wir das Problem identifizieren können.
- packe den ganzen Code in ein zip
- mach das zip verfügbar - hier im Forum oder bei pastebin.com
- eventuell noch mit der Angabe deiner Umgebung: Compiler, IDE, 
Bertiebssystem

Nur so können wir wirklich nachvollziehen, was bei dir abgeht und dir 
seriöse Antworten geben. Du willst doch Hilfe?

Grüße, Adib.
--

von Jojo S. (Gast)


Lesenswert?

In dem CubeMX generierten Code wird GPIO mit Low Speed initialisiert:
GPIO_InitStruct.Speed = GPIO_SPEED_LOW;
macht das einen Unterschied bei der Anzahl der nötigen NOPs? Die SPI 
HW-Implementierung scheint ja wirklich nicht so toll zu sein, ich frage 
mich ob die NOPs reichen wenn der APB durch DMA Opterationen belegt ist?

von Christian J. (Gast)


Lesenswert?

Adib schrieb:
> Nur so können wir wirklich nachvollziehen, was bei dir abgeht und dir
> seriöse Antworten geben. Du willst doch Hilfe?

Lassen wir das, ich habe die Antwort ja schon gefunden: Das RXNE Bit 
scheint verzögert zu reagieren bzw scheint der Wert eben nach RXNE ein 
paar Take später im DR zu stehen, er wird ja aus dem Shifter in das 
Schattenregister geladen. Nur ist da normalerweise so viel Code 
zwischen, dass es eben ausreicht, nicht aber wenn ich maximale 
Optimierung setze und BSY nicht mehr abfrage. Die 4 NOPs beheben das 
Problem vollständig. Es trat bei jeder SPI Geschwindigkeit gleichermaßen 
auf.

Altenativ geht es auch so, dass behebt das Problem ebenfalls:
1
/* ------ SPI Low Level: Ein 8 Bit Datenwort senden und holen ------- */
2
uint8_t   SPI_TransferByte(uint8_t data)
3
{
4
    while(!SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE));       // Warte bis TXE (SPI ist frei)
5
    SPI_I2S_SendData(SPI1, data);                                // Byte senden...
6
    while(!SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE));
7
    while(!SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE));      // Warte bis Byte empfangen worden
8
    while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY));
9
    return SPI_I2S_ReceiveData(SPI1);
10
}

Oder eleganter:
1
#define SPI_IS_BUSY(SPIx) (((SPIx)->SR & (SPI_SR_TXE | SPI_SR_RXNE)) == 0 || ((SPIx)->SR & SPI_SR_BSY))
2
#define SPI_WAIT(SPIx)            while (SPI_IS_BUSY(SPIx))

womit dann
1
/* ------ SPI Low Level: Ein 8 Bit Datenwort senden und holen ------- */
2
uint8_t   SPI_TransferByte(uint8_t data)
3
{
4
    while(!SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE));       // Warte bis TXE (SPI ist frei)
5
    SPI_I2S_SendData(SPI1, data);                                // Byte senden...
6
    SPI_WAIT(SPI1);
7
    return SPI_I2S_ReceiveData(SPI1);
8
}

von Jojo S. (Gast)


Angehängte Dateien:

Lesenswert?

ich habe auf meinem F469 jetzt auch die SPI aus der mbed lib laufen. 
Initialisiert wird über die Cube HAL, ich habe zusätzlich noch die 
Funktionen aus der SPL in einen Test reinkopiert.
Auf der SPI gebe ich 0x55 über die mbed Instanz aus und danach 0x33 über 
die SPL Funktion. Der MISO Pin ist auf '1' gelegt, es muss also immer 
0xff zurückkommen. Das funktioniert auch wie es soll, beide Funktionen 
sind nahezu gleich schnell, mbed ist 120 ns schneller. Einen Unterschied 
zwischen -Os und Debug sehe ich allerdings nicht.
1
void IOTask(void* params)
2
{
3
  DigitalOut led4(LED4);            // Test Blinky
4
5
  SPI  spi(PA_7, PA_6, PA_5);          // use SPI1
6
  spi.frequency(1.333E6);            // Bitrate 1.333 MHz
7
  DigitalOut cs(PA_2);
8
9
  volatile bool bRun = true;
10
11
  while(bRun)
12
  {
13
    vTaskDelay(100 / portTICK_RATE_MS);
14
15
    cs = 0;
16
    spi.write(0x55);
17
    cs = 1;
18
19
    cs = 0;
20
    SPI_TransferByte(0x33);
21
    cs = 1;
22
23
    led4 = !led4;
24
  }
25
}

von Jojo S. (Gast)



Lesenswert?

Ich hätte noch ein Update. Habe mir die halbe Nacht mit einem falschen 
Fehler um die Ohren gehauen: Atollic lädt mit dem Debug Button immer die 
debug build configuration, auch wenn man release als aktive config 
eingestellt hat. Das erklärt warum ich keine Unterschiede in 
debug/release gesehen hatte.
Damit habe ich den SPI nochmal wiederholt und Aufnahmen mit CPHA 0 und 1 
gemacht sowie warten auf BSY oder nicht warten. Zum überprüfen frage ich 
den SPI gelesenen Wert auf 0xff ab. Der muss 0xff sein weil sein MISO 
fix auf '1' liegt. Wenn wie bei Christian beschrieben eine 0 zurückkommt 
wird der error Ausgang gesetzt.
Das konnte ich aber in keiner Kombination provozieren, auch ohne warten 
kommt immer 0xff zurück.
@Christian:
wenn dieser Test deiner Umgebung entspricht sind vielleicht Unterschiede 
in der Initialisierung oder de F469 verhält sich anders.
Compiler im Atollic ist der gcc 4.8.3.

von Christian J. (Gast)


Lesenswert?

Jojo S. schrieb:
> Christian:
> wenn dieser Test deiner Umgebung entspricht sind vielleicht Unterschiede
> in der Initialisierung oder de F469 verhält sich anders.
> Compiler im Atollic ist der gcc 4.8.3.

Hi,

irgend wo dran wird es liegen, ich habe GCC 5. irgendwas und einen 
STM32F407.
Nur suche ich da jetzt nicht weiter, weil es mit den NOPs ja 
funktioniert.
Trotzdem, nett, dass Du Dir mal die Mühe gemacht hast. BSY braucht man 
nicht.

von Adib (Gast)


Angehängte Dateien:

Lesenswert?

Hallo Christian,

ich habe nun mal mittels CubeMx für das Olimex-STM32-E407 ein kleines 
Beispiel gemacht:

per MOSI gehts raus,
per mit einem Jumper sind MOSI und MISO verbunden
per MISO kommt also genau das gleiche rein

das eine Bild ist mit -O0 das andere mit -Os

verwendet habe ich einen STM32F407ZGT, 
gcc-arm-none-eabi-5_2-2015q4-20151219-win32 unter winarm eclipse Mars

die CubeMx Libraries sind STM32F4 1.11 und Codegenerator CubeMx 4.13
1
  while (1)
2
  {
3
    HAL_Delay(1000);
4
    HAL_GPIO_WritePin(SPI_CS_GPIO_Port, SPI_CS_Pin, GPIO_PIN_RESET);
5
    HAL_SPI_TransmitReceive(&hspi2, buffer_tx, buffer_rx, 2, 1000);
6
    HAL_GPIO_WritePin(SPI_CS_GPIO_Port, SPI_CS_Pin, GPIO_PIN_SET);
7
    HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
8
    if(memcmp(buffer_rx, buffer_tx, 2) != 0) {
9
      HAL_GPIO_WritePin(SPI_CS_GPIO_Port, SPI_CS_Pin, GPIO_PIN_RESET);
10
      HAL_Delay(100);
11
      HAL_GPIO_WritePin(SPI_CS_GPIO_Port, SPI_CS_Pin, GPIO_PIN_SET);
12
    }
13
  /* USER CODE END WHILE */
14
  /* USER CODE BEGIN 3 */
15
16
  }

von Adib (Gast)


Angehängte Dateien:

Lesenswert?

hier jetzt mein selbs geschriebener Code:

man erkennt die Lücke zwischen den Bytes, die bei der Lib variante nicht 
sind.
Funktioniert aber.
1
void trx(uint8_t *buffer_tx, uint8_t *buffer_rx, uint16_t len)
2
{
3
  SPI_TypeDef *spi = hspi2.Instance;
4
5
  // wait spi finished
6
  while((spi->SR & SPI_SR_RXNE) != 0) spi->DR;
7
  while((spi->SR & SPI_SR_TXE) == 0) ;
8
9
  do {
10
    spi->DR = *buffer_tx++;
11
    while((spi->SR & SPI_SR_TXE) == 0) ;
12
    while((spi->SR & SPI_SR_RXNE) == 0) ;
13
    *buffer_rx++ = spi->DR;
14
  }while(--len > 0);
15
16
  while((spi->SR & SPI_SR_BSY) != 0) ;
17
18
}

von Adib (Gast)


Angehängte Dateien:

Lesenswert?

Hallo Christian,
PS: ich habe immer Bauschschmerzen, mit "bei 4x NOP ist alles in 
Ordnung" ...

im Anhang die Clock und SPI Konfiguration
das ganze noch im Disassembler:

r4 ist also die Addresse von der SPI
1
 66         SPI_TypeDef *spi = hspi2.Instance;
2
          trx:
3
08000188:   ldr     r3, [pc, #64]   ; (0x80001cc <trx+68>)
4
 65       {
5
0800018a:   push    {r4, lr}
6
 66         SPI_TypeDef *spi = hspi2.Instance;
7
0800018c:   ldr     r4, [r3, #0]
8
 69         while((spi->SR & SPI_SR_RXNE) != 0) spi->DR;
9
0800018e:   ldr     r3, [r4, #8]
10
08000190:   lsls    r3, r3, #31
11
08000192:   bpl.n   0x8000198 <trx+16>
12
08000194:   ldr     r3, [r4, #12]
13
08000196:   b.n     0x800018e <trx+6>
14
 70         while((spi->SR & SPI_SR_TXE) == 0) ;
15
08000198:   ldr     r3, [r4, #8]
16
0800019a:   lsls    r3, r3, #30
17
0800019c:   bpl.n   0x8000198 <trx+16>
18
0800019e:   subs    r3, r2, #1
19
080001a0:   uxth    r3, r3
20
080001a2:   adds    r3, #1
21
080001a4:   add     r3, r0
22
 73           spi->DR = *buffer_tx++;
23
080001a6:   ldrb.w  r2, [r0], #1
24
080001aa:   str     r2, [r4, #12]
25
 74           while((spi->SR & SPI_SR_TXE) == 0) ;
26
080001ac:   ldr     r2, [r4, #8]
27
080001ae:   lsls    r2, r2, #30
28
080001b0:   bpl.n   0x80001ac <trx+36>
29
 75           while((spi->SR & SPI_SR_RXNE) == 0) ;
30
080001b2:   ldr     r2, [r4, #8]
31
080001b4:   lsls    r2, r2, #31
32
080001b6:   bpl.n   0x80001b2 <trx+42>
33
 76           *buffer_rx++ = spi->DR;
34
080001b8:   ldr     r2, [r4, #12]
35
080001ba:   strb.w  r2, [r1], #1
36
 77         }while(--len > 0);
37
080001be:   cmp     r0, r3
38
080001c0:   bne.n   0x80001a6 <trx+30>
39
 79         while((spi->SR & SPI_SR_BSY) != 0) ;
40
080001c2:   ldr     r3, [r4, #8]
41
080001c4:   lsls    r3, r3, #24
42
080001c6:   bmi.n   0x80001c2 <trx+58>
43
 81       }

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.