Hallo zusammen
ich möchte mit meinem STM32F4 (Discovery Board) eine SD Karte lesen
(genauer gesagt: eine MicroSD Karte).
Dazu möchte ich DMA benutzen. Um einen oder mehrere Blöcke zu lesen,
verwende ich folgenden Code:
/* initialise a data transfer with maximal timeout */
21
SDIO_DTIMER=SD_DATATIMEOUT;
22
23
/* set the data length */
24
SDIO_DLEN=NumberOfBlocks*BlockSize;
25
26
/* block size is 512 bytes, read from the card and enable dpsm */
27
SDIO_DCTRL=((9u<<4)|(1<<1)|(1<<0));
28
29
send_command(ReadAddr,SD_CMD_READ_MULT_BLOCK);
Was mich aber sehr verwirrt, ist, dass der DMA gleich loslegt, nachdem
ich das Enable-Bit gesetzt habe (DMA2_S3CR |= 1) und der S3NDTR sofort
auf 65535 springt. Was mache ich da falsch?
Die Initialisierung der Karte geht soweit problemlos und zuverlässig
auch mit verschiedenen Karten, die Kartenkapazität etc. wird zuverlässig
erkannt. Wie würde ich einen Block lesen, ohne DMA zu benutzen?
Gruss
Tobias
Tobias Plüss schrieb:> Was mich aber sehr verwirrt, ist, dass der DMA gleich loslegt, nachdem> ich das Enable-Bit gesetzt habe
Was soll er auch sonst machen?
Du musst sicher zuerst SDIO konfigurieren und dann DMA anmachen.
Ach ja: Vieeel zuviele magic numbers. Von ST gibt es Header Dateien, wo
die ganzen Bits auch mit Namen definiert sind.
Um SDIO+DMA zu verwenden musst du im DMA den Peripheral Flow Controller
aktivieren, und das NDTR Register überhaupt nicht verwenden. Der SDIO
bestimmt dann wie viele Items übertragen werden. Allerdings kannst du
auf einen DMA-Transfer nur max 2^16-1 Items übertragen.
Hallo
ich habe es jetzt versucht umzustellen, also der Read-Command wird
gesendet, bevor der DMA initialisiert wird. Das geht natürlich auch
nicht, die Karte wird einen Transfer_Error.
Kann man die Daten vorerst auch mal lesen, ohne DMA zu benutzen? ich
verstehe da einfach irgendwie nicht, wie der SDIO_FIFO funktioniert.
Hallo Kollegen
ich hänge euch mal meinen Code an.
Ich habe diesen im Netz gefunden, leider basierte er auf der absolut
grässlichen Library von ST, die ich nicht einsetzen will. Also habe ich
damit begonnen, das ganze ein wenig umzustrukturieren, dass der Code
auch ohne die Library (möglichst standalone) compilierbar ist.
Ich möchte dann den Code zusammen mit FatFS von CHAN benutzen.
Vielleicht kann mir ja einer über meine Leseroutine drüber schauen. Ich
habe es jetzt geschafft, dass mit diesem Code ein einzelner Sektor von
der Karte gelesen wird. Allerdings geht der SDIO in einen Overrun Error.
Auch würde mich sehr interessieren, wie man das hässliche Polling der
Flags weg bekommen könnte (z.B. unter Zuhilfenahme von Interrupts).
>leider basierte er auf der absolut>grässlichen Library von ST, die ich nicht einsetzen will.
Wer nicht will der hat schon und macht sich seine Probleme selber.
>Vielleicht kann mir ja einer über meine Leseroutine drüber schauen.
Wozu? Das ist viel zu komplex für "mal eben" drüberschauen.
>Ich möchte dann den Code zusammen mit FatFS von CHAN benutzen.
Hab ich mit dem Code von ST schon gemacht und es funktioniert.
Du musst das Rad nicht neu erfinden.
Ja holger, danke dir.
Das ändert allerdings nichts daran, dass die Lib von ST voll ist mit
Polling und damit eigentlich schlecht geeignet ist, um in einer Umgebung
mit RTOS und laufendem Watchdogtimer betrieben zu werden.
Aber gut. Ich werds schon auch selbst zum Laufen kriegen ;-)
Hallo Niklas
danke für deine Hilfe. Ich versuch das gleich hier nachzuvollziehen.
Hast du deinen Code auch schon mit SDHC Karten getestet? in der SD
Karten Spezifikation heisst es, dass die Blockgrösse auf 512 Bytes
festgesetzt ist bei diesen Karten und nicht verändert werden kann,
insofern würde es meiner Meinung nach dann zu einem Error führen, wenn
man andere Blocksizes als 9 verwendet.
Ja, genau, aber eben, du schreibst in deinem Kommentar, dass ...
>> Für CMD6 und CMD13 verwende blockSize=6, für ACMD13 verwende blockSize=3
was dann != 9 ist und somit einen Error ergibt, oder nicht?
Übrigens habe ich den Code jetzt so implementiert. Normale SD Karte geht
zuverlässig, meine Sektoren werden mit dem Commando "Read Multiple"
erfolgreich gelesen. SDHC Karte liest aber immer nur einen Sektor aufs
mal, wieso kann ich auch nicht verstehen :-(
Gruss
Tobias Plüss schrieb:>>> Für CMD6 und CMD13 verwende blockSize=6, für ACMD13 verwende blockSize=3>> was dann != 9 ist und somit einen Error ergibt, oder nicht?
Nein, denn CMD6, CMD13 und ACMD13 lesen keinen Block von der Karte,
sondern übertragen andere Daten/Register mit anderen Blockgrößen (siehe
SD-Spezifikation). Zum Lesen von Datenblöcken der Karte brauchst du
CMD17, und da braucht es blockSize=9 (=512 Bytes).
Tobias Plüss schrieb:> SDHC Karte liest aber immer nur einen Sektor aufs> mal, wieso kann ich auch nicht verstehen :-(
Weil mein CMD17 nur einen Block liest. Zum Lesen mehrerer Blocks
brauchst du CMD18. Siehe SD Spezifikation.
Es funkioniert so mit einer normalen SD Karte. Bei einer SDHC, die ich
hier auch noch auf dem Tisch liegen habe, wird immer maximal ein
einzelner Sektor gelesen.
Initialisierst du die SDHC Karte denn auch entsprechend? Bei den
Lese-Kommandos bei SDHC musst du die Start-Adresse als Blocknummer
angeben, und nicht als Byte-Adresse, d.h. das "sector << 9" ist
verkehrt.
CMD16 ist unnötig bei SDHC.
Hi Niklas,
was heisst ob ich sie entsprechend initialisiere?
Ich bin nach dem Flowchart auf S. 28 der Spezifikation vorgegangen. Also
zuerst CMD0, dann CMD8, dann ACMD41 mit gesetztem HCS bit. Die Karte
antwortet immer wie erwartet und meine Initialisierung klappt.
Im Falle von SDHC mache ich das "sector << 9" nicht, sondern übergebe
nur "sector" (da ist also noch ne Fallunterscheidung mit drin, ja).
Hi Niklas
so ich habe meinen Code mal gründlich überarbeitet und das ganze
Polling-Zeug raus geschmissen und verwende nun alles mit Interrupts und
Semaphoren vom OS. Jetzt funktioniert das Lesen und Schreiben!
Was ich allerdings feststelle: wenn man sehr oft hintereinander eine
Lese- oder Schreibfunktion aufruft, dann passiert es sporadisch, dass
der Vorgang fehlschlägt. Ich tippe mal darauf dass da das Wear Leveling
zuschlägt.
Im Moment mache ich es so, dass ich vor dem Schreib/Lesevorgang die
Interrupts aktiviere (SDIO_MASK). Im Interrupthandler setze ich dann ein
Semaphor. In der Schreibroutine warte ich auf das Semaphor, aber mit
einem Timeout. Das funktioniert gut, aber sporadisch werden einzelne
Sektoren nicht richtig gelesen (CRC_FAILED). Es könnte allerdings auch
an meinem Aufbau liegen (der SD Sockel ist mit Wrapdrähten am
Discoveryboard angelötet).
Tobias Plüss schrieb:> Ich tippe mal darauf dass da das Wear Leveling> zuschlägt.
Das manifestiert sich aber lediglich in langen Schreib/Erase-Zeiten,
nicht durch Fehlschlagen einer Operation.
Tobias Plüss schrieb:> Das funktioniert gut, aber sporadisch werden einzelne> Sektoren nicht richtig gelesen (CRC_FAILED). Es könnte allerdings auch> an meinem Aufbau liegen (der SD Sockel ist mit Wrapdrähten am> Discoveryboard angelötet).
Ja das klingt nach Übertragungsfehler. In dem Fall die Operation einfach
noch einmal durchführen?
Hallo Niklas
O.K. bin jetzt so weit dass ich auf normale SD Karten zuverlässig
schreiben und davon lesen kann. Initialisierung und so weiter klappt
auch sehr zuverlässig. Ich habe mittels FATFS soeben auf meinem
Discoveryboard ein paar Hello World Textdateien anlegen und am PC
auslesen können, das funktioniert jetzt also!
SDHC Karte habe ich leider nur eine hier. Und die meldet mir beim
Zugriff einen CRC Fehler. Auch wenn ich die Taktfrequenz auf 400 kHz
heruntersetze. Kann dies mit einer falschen Initialisierung
zusammenhängen, oder könnte auch einfach meine Karte defekt sein?
Den CMD23 (SET_BLOCK_COUNT) frisst meine SDHC Karte auch nicht. Da wird
ein 'illegal command' zurückgemeldet. Gemäss Spezifikation müsste die
Karte aber doch den Befehl akzeptieren?
Hallo Niklas
ich habe nun nach längerer unterbrechung das Projekt SD Karte wieder
angefangen.
Ich habe ein paar neue Karten gekauft (SDHC) und das Lesen einzelner
Sektoren funktioniert jetzt. D.h. die Sektoren werden komplett gelesen.
Das Problem war, dass ich den Stop Transfer command gesendet hatte,
bevor der DMA den Datentransfer beendet hatte.
Das einzige Problem was ich jetzt noch habe ist folgendes: wenn ich
innerhalb kurzer Zeit mehrmals Sektoren von der Karte lesen will, dann
schlägt dies nach nicht genau reproduzierbarer Zeit fehl, und die Karte
meldet beim Lesebefehl 'illegal command'. Wenn ich den Lesebefehl
wiederhole, dann bleibt es dabei - die Karte scheint sich 'aufzuhängen'.
Woran könnte dies liegen?
Gruss
Tobias
Hallo Niklas
nein, das war es nicht. Mittlerweile scheint es so, dass ich den Fehler
gefunden habe - der Abblockkondensator, den ich an meine Kabel zur
SD-Karte angelötet hatte, hatte anscheinend eine kalte Lötstelle, wo was
unterbrochen wurde. Und da die Karte beim Lesen doch ein wenig Strom
braucht, vermute ich, dass das Problem daran lag, denn jetzt, wo ich den
Kondensator wieder angelötet habe, funktioniert es :-)