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 ------- */
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?
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.
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.
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.
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.
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...
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.
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.
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
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.
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.
Christian J. schrieb:> Code um Steuerpins gekürzt. Baudrate : 1.333 MhzPeter 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.
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.
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.
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.
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.
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.
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...
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.
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.
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 ;)
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.
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.
>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
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_tSPI_TransferByte(uint8_tdata)
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
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.
--
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?
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_tSPI_TransferByte(uint8_tdata)
3
{
4
while(!SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE));// Warte bis TXE (SPI ist frei)
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.
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.
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.
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
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