Moin Moin zusammen,
ich stehe mal wieder vor einer Hürde, bei der mir die
STM32F103-Reference und StdPeriph-Bibliothek nicht weiterhelfen. Ich
versuche mich an DMA, um damit Datenpuffer in SPI-RAM zu schreiben. Laut
Doku, Beispielcode im Netz und so weiter ein Fingerschnipp. Funktioniert
augenscheinlich erst mal auch ganz gut und fix. Nur bekomme ich als
Daten immer nur "0" anstatt des gesendeten Puffer-Inhalts zurück.
Weitere Info: Wenn ich auf Transfer Complete warte, bleibt der µC
stehen, das kommt nicht.
Hier mal zusammengekürzt mein Code, der nicht richtig tutet:
Dirk K. schrieb:> DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)buffer1_rx[0];
Autsch... Du musst die Adresse des Arrays in das Register schreiben und
nicht den Inhalt des Arrays an Index 0...
Hallo Simon,
aua, Danke sehr :)
Wir kommen der Sache näher. Ich habe jetzt wieder einen Pointer auf die
Puffer verwendet - beim Herumprobieren ist dieser Fehler reingekommen
und stehengeblieben.
Zudem habe ich Interrupts und Bearbeitungsroutinen hinzugefügt. Mit
letzterem läuft der Code dann sogar. (Interessanterweise ohne
NVIC-Initialisierung für den tx-Interrupt.)
Das Ergebnis bleibt gleich/vrgleichbar, wenn ich die "spixbase" anstatt
SPI1->DR für 8bit-Transfers verwende (beim F103 nötig.) Das scheinen die
Daten zu sein, die ich im vorherigen Durchgang am Ende tatsächlich per
Polling hineingeschrieben habe; das DMA-tx (auch mit NVIC-Init) scheint
nicht zu klappen?
Immerhin keine Nullen :) Aber leider noch nicht richtig ...
Dirk K. schrieb:> DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&(buffer1_rx);
Auch nicht viel richtiger. Entweder
(uint32_t)&buffer1_rx[0];
oder
(uint32_t)buffer1_rx;
aber nicht
(uint32_t)&buffer1_rx;
Ok, selbst, wenn ich das korrigiere - bekomme ich Quatsch zurück.
Grade wieder 0 oder 255 (wenn ich "spixbase" als Ziel verwende, sonst
das, was ich vorher per Polling reingeschrieben habe); das ist zum
Mäusemelken.
Ich komme ein wenig voran, aber so wirklich erbaulich ist das noch
nicht. Die RAM-Test-Routine ist jetzt reines DMA, ohne Polling. Die
Ergebnisse lassen vermuten, dass da irgendwas geschrieben wird -
allerdings halt quatsch:
Was nicht ganz der Reihe 0 1 2 3 4 5 6 7 8 9 entspricht, was dort aber
ein wenig versteckt vorkommt. Und nichts mit den Werten gemein hat, die
ich per Polling im vorherigen Durchgang reingeschrieben habe. Ich komme
nicht drauf, wieso das nicht will :(
Dank für den Tipp Holger,
ich dachte, ich hätte das mit dem SPI-BSY-while-loop abgegolten. Habe
eine Stützvariable "transfer" eingeführt, die bei den
DMA-Transfer-Complete-Interrupts zurückgesetzt und beim Start gesetzt
wird.
1
voidspi1_dma_tx(intsize){
2
while(transfer);// DMA transfer ready
3
while(!(SPI1->SR&SPI_SR_TXE));// Wait for bus free
Das Warten auf SPI-BSY hat offenbar denselben Effekt und (zugegeben,
unpräzise und von hinten durch die Brust ins Auge) das Ende des
DMA-Transfers angezeigt. :-/
>Dank für den Tipp Holger,
Hat nur nicht viel geholfen und du nichts verstanden.
Sieh dir z.B. mal diese Stelle an:
spi1_dma_tx(2);
chip_deselect();
Du ziehst dem SPI die Chipselect Leitung
unterm Arsch weg während der DMA gerade erst
angefangen hat zu laufen. Also viel zu früh!
Ah - das habe ich wirklich nicht gesehen, Danke!
Edit: Ändert am Ergebnis aber gar nichts. Ausgabe bleibt gleich. Auch
bei langsameren SPI-Takt. Nur schneller geht immer noch nicht, will
nicht mit >18 MHz ;)
Bitte schau dir einmal an - am besten mit Logic Analyzer, 2Kanal-oszi
geht auch - was über den Bus geht. Damit kann man 90% solcher Probleme
aus der Welt schaffen. Entweder es überträgt nicht richtig, dann hast du
was in der Initialisierung falsch, oder es überträgt richtig, dann
stimmt was mit der Abspeicherung/Auswertung nicht.
Sieh dir mit einem Debugger mal die ganzen Puffer und Register an.
Was steht zu welchem Zeitpunkt wo.
Mir kommt es so vor als ob du noch nicht einmal weißt wo in deinem
ganzen Datenfluß das Problem steckt.
Ohne das zu wissen ist es nur stochern im Nebel.
Die Feststellung ist vollkommen korrekt, ich stochere im Nebel. Das ist
das erste Mal, das ich mich mit DMA programmiertechnisch
auseinandersetze. Habe mir dazu die Datenblätter und AppNotes von STM
bis kurz vorm Umfallen zu Gemüte geführt, die Samples der STM-Library
viele Male untersucht, aber solch einen einfachen "Standardfall"
nirgends gefunden. Da werden in der Regel einmalig um die 20 Bytes
irgendwo herumgeschoben, DMA via Funktionsaufruf habe ich etwa noch
nirgends gesehen. Von daher taste ich mich langsam vor, bis es mal
läuft.
Klappt eigentlich ganz gut, eine solche Herangehensweise - so konnte ich
SPI, I2C und USART gut auseinanderpflücken und lernen. Nur der DMA wehrt
sich grad mächtig. Wie schon geschrieben, Logic Analyzer ist in Kürze
einsatzbereit, dann kann ich gucken, was tatsächlich auf dem Bus
passiert. Ohne das geht es glaube ich nicht sinnvoll weiter.
Hatte halt auf Tipps wie von Holger gesetzt, dass ich irgendwo einen
dummen Fehler im Ablauf übersehe (etwa wie dem DMA-Transfer die Luft
abzuschnüren, indem ich CS frühzeitig HIGH setze).
Es sieht ja soweit schonmal fast gut aus, nur ein wenig so, als ob meine
Transfers einen 8-Bit-Wert gerne mal eine Adresse zu weit schreiben (die
Zahlen dort sind ja schon halb-korrekt aufsteigend).
Dirk K. schrieb:> 8-Bit-Wert gerne mal eine Adresse zu weit
schau dir im Handbuch mal an, wie der Datentransfer abläüft. Es werden
immer 32 bits übertragen.
über burstsize, srcwidth,dstwidth und endian kann man einstellen, wie
diese 32bit in src und dest aufgeteilt werden.
Nimm notfalls das LPC-Manual UM10360. Auf Seite 589 ist dazu eine
einigermaßen verständliche Übersicht.
> Es werden immer 32 bits übertragen.
Nein, es werden immer
DMA_PeripheralDataSize * DataNumber
bzw.
DMA_MemoryDataSize * DataNumber
(je nach Richtung)
übertragen.
> Nimm notfalls das LPC-Manual UM10360. Auf Seite 589 ist dazu eine
einigermaßen verständliche Übersicht.
Es geht hier um ST, nicht um NXP
Phantomix Ximotnahp schrieb:>> Es werden immer 32 bits übertragen.>> Nein, es werden immer
Mit jedem DMA-Takt werden 32 bits übertragen. Das ist mit einiger
Sicherheit bei allen Cortex-M3-32Bittern das gleiche.
Wenn die Erklärung des Cortex-M3-DMA im ST-Manual nicht verständlich
sein sollte, kann man "notfalls" im LPC manual nachlesen, weil dort das
Cortex-M3 System verständlich erklärt wird.
Grundschüler schrieb:> Mit jedem DMA-Takt werden 32 bits übertragen. Das ist mit einiger> Sicherheit bei allen Cortex-M3-32Bittern das gleiche.
Der DMA Controller ist nicht Teil des Cortex M3 Core.
Achso. Das war ein wenig missverständlich und klang so, als könnte man
über den SPI-Bus mit DMA nur 32bit-Worte schieben. Das wär aber für NXP
auch ein Armutszeugnis ;-)
A. K. schrieb:> Der DMA Controller ist nicht Teil des Cortex M3 Core.
thx. da habe ich auch grad intern gezweifelt...
Grundschüler schrieb:> Nimm notfalls das LPC-Manual UM10360. Auf Seite 589 ist dazu eine> einigermaßen verständliche Übersicht.
Seitenangaben ändern sich schneller als Kapitel/Abschnitt/... In der
aktuellen Version lande ich bei Seite 589 im ADC.
Grundschüler schrieb:> über burstsize, srcwidth,dstwidth und endian kann man einstellen, wie> diese 32bit in src und dest aufgeteilt werden.
Wenn du mir jetzt noch zeigst, wo man im STM32F10x die burst size
einstellt... Der LPC kann wohl 4 Bytes nacheinander von der Peripherie
lesen und akkumuliert als 1 Wort ins RAM schreiben, der STM32F10x kann
das jedoch nicht.
STM32F10x: 13.3.4 Programmable data width, data alignment and endians
LPC: 31.4.1.6.2 Endian behavior
Naja, dem STM32F103 kannste auch WORD, HALFWORD oder BYTE als Größe
mitgeben. Wenn PeriphSize != MemSize, bricht der DMA automatisch um. Von
fix 32Bit steht da jedoch nichts. Wäre in meinem Fall nur beim Senden
des SEQUENTIAL_MODE von Belang, die anderen Fälle sind alle 4-Byte
(=WORD)-Aligned - selbst, wenn der SPI-Transfer als Byte läuft.
Dirk K. schrieb:> Naja, dem STM32F103 kannste auch WORD, HALFWORD oder BYTE als Größe> mitgeben. Wenn PeriphSize != MemSize, bricht der DMA automatisch um.
Wenn man beim STM32F10x aus der UART byteweise n Bytes liest und als
Zielgrösse Worte angibt, dann kriegt man am Ziel 4*n Bytes. Ein Byte pro
Wort. Das ist wohl selten das, was man haben will. Der LPC hingegen kann
offenbar akkumulieren und schreibt ggf. 1 Wort mit allen 4 Bytes am
Stück - byteweise kann er aber auch.
Der DMA Controller des LPC sieht nach der ARM PrimeCell PL080 aus:
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0417a/index.html
Vielleicht hat "Grundschüler" deshalb angenommen, die seien alle gleich.
Allerdings hat ST diese PrimeCell nicht verwendet.
A. K. schrieb:> der STM32F10x kann> das jedoch nicht.
du hast sicher recht, dass das Stm-DMA etwas anders aufgebaut ist als
bei NXP.
die Stm-Tabelle 13.3.4 Programmable data width, data alignment and
endians
scheint der von mir angeführten LPC-Tabelle aber zumindest ähnlich zu
sein. Es gibt dort verschiedene Modi, die zum Vertauschen der
Byte-Reihenfolge führen, was mit dem von Dirk angesprochenen Problem in
Verbindung stehen könnte.
Grundschüler schrieb:> scheint der von mir angeführten LPC-Tabelle aber zumindest ähnlich zu> sein.
Klar. Beides sind DMA-Controller in einem 32-Bit System mit 3
verschiedenen Transferbreiten pro Seite. Da stellt sich diese Frage
zwangsläufig. Nur ist der Inhalt völlig verschieden, wenn source-width
!= dest-width.
> Es gibt dort verschiedene Modi, die zum Vertauschen der> Byte-Reihenfolge führen
Kann ich darin nicht wiederfinden, zumal er das DMA im ersten Beitrag
auf byte-to-byte konfiguriert hat, was zu steng bytesequentieller
Operation auf Peripherie und Speicher führt. Eine Vertauschung durch den
DMA-Controller scheint mir folglich ausgeschlossen zu sein. Bei
source-width != dest-width sähe die Sache anders aus.
PS/@Dirk: Ich habe nicht alle Aspekte der Konfiguration untersucht.
Nicht zuletzt weil ich kein Freund der StdPerLib bin.
So, ich habe den LA erfolgreich in Betrieb genommen. Geht leider nur bis
16 MHz, aber scheint zu reichen.
Von außen betrachtet sieht das eigentlich richtig aus: CLK rennt los,
MISO/MOSI laufen richtig, hinten erkennt man schön das langsame Polling
und die 10 Byte, die ich ganz am Schluss zurücklese.
Muss noch herausfinden, wie ich mit dem SPI-Dissector da wieder
menschenlesbare Daten draus mache, also ob ich 0..1..2..3.... übertrage
da am Anfang (zweites Bild, Zoom.png).
Edit: kann irgendwie keine Bilder löschen, habe enen korrigierten
Mitschnitt bei 9MHz SPI noch angehängt.
Dirk K. schrieb:> Geht leider nur bis 16 MHz, aber scheint zu reichen.
Eher nicht. Wenn du einen 9MHz SPI Takt mit einem 16MHz LA abtastest,
dann fehlt ab und zu schon mal was. Siehe Taktsignal.
Geh testweise mit den SPI Takt deutlich runter, wenn du das Zeug
dekodiert kriegen willst.
Etwa mit Prescaler 64 wird die "Trennschärfe" besser. Man kann gut
erkennen, dass auf MOSI korrekt 0..1..2..3..4..5.... und so weiter
rausgehen. (SCK zählen und dann die Flanken auf MOSI zählen, ist das
Bitmuster.)
Allerdings passiert beim DMA auf MISO gar nichts mehr. Bleibt auf
HIGH-Pegel. Erst ganz am Ende beim Polling läuft auch das wieder. Oder
eben bei höherem SPI-Takt.
Kontrolliere vorsichshalber mal, was exakt in den SPI und DMA Registern
drin steht. Und genau im Manual nachlesen, was das bedeutet. Nur um
sicher zu gehen, dass die Hardware auch das sieht, was du per StdPerLib
zu tun hoffst.
Wie sieht es mit der Taktversorgung aus? Haben alle Busse den zulässigen
Takt. Einer der Peripheriebusse ist u.U. auf halben Takt begrenzt.
Ohje, jetzt JTAG noch fertig basteln bin ich schon zu durch für, morgen
mal da ran setzen (WUA, JTAG musste ich deaktivieren, damit ich die
PB-Pins nutzen kann...). Habe mir jetzt auch das MISO im Zoom angesehen
- die Quatsch-Werte, die ich in der Ausgabe sehe, zeigt auch der LA an,
das bekomme ich auf der Leitung wirklich zurück.
Meine Takte sind System-Standard; das einzig angepasste ist der SPI mit
dem Prescaler.
Da MISO erst ab irgendwo 4MHz losläuft, vermute ich da irgendwo einen
Fehler. Das mit den Registern prüfen scheint eine gute Idee!
Ich habe jetzt noch mal angepasst, die zu übertragende Datenmenge mit
"-1" anzugeben, also zählt der DMA doch von 0 bis "zu übertragende
Bytes"! :)
Was mit den 4 Bytes am Anfang jetzt noch falsch ist, muss ich noch
rausfinden. Aber ich komme der Lösung näher :)
Noch ein Nachtrag: Ich lasse mir jetzt alle fehlhaften Speicherstellen
ausgeben.
Edit: Blödsinn gelöscht ...
Ein wenig hat das noch mit dem Warten zwischen DMA-Übertragung und neu
Beschreiben der Buffer zu tun; die Transfer-Größe ist mit (buffer_size)
ohne ein -1 schon korrekt.
Warte ich jetzt nach dem read+Adresse und write+Adresse auf das Ende der
Übetragung, bevor ich die Puffer fülle, komme ich schon auf dieses
Ergebnis - 1 Byte ist falsch: