Forum: Mikrocontroller und Digitale Elektronik Speicherkarten-Daten auslesen zu langsam


von Hirbel (Gast)


Lesenswert?

Hi,

ich hab ein (kleines) Problem bei der Ansteuerung meiner SD Karte. Das 
Auslesen von Daten aus einer Datei usw. klappt wunderbar, aber es ist 
mir zu langsam.

Ich steuere die Karte via SPI an, mein CPU Takt beträgt 16 MHz...
Bei einem Prescaler von 8 (2 MHz) funktionierts noch, beim Prescaler 4 
(4 MHz) nicht mehr, woran kann das liegen?
Ich dachte die SD Karten kommen mit einem Takt von 20 MHz klar (laut 
Datenblatt)??

Danke schonmal im Voraus.

von Benedikt K. (benedikt)


Lesenswert?

Falsches Timing, zu hochohmige Spannungsteiler (falls der µC mit 5V 
läuft) usw.
Ich hatte mit 15MHz SPI Takt bisher noch nie Probleme.

von Hirbel (Gast)


Lesenswert?

Danke für die schnelle Antwort.

Ich benutze ein Pegelwandler (74LVC245) mit einer "Propagation delay 
time" von maximal 7 ns, das dürfte doch locker für 8 MHz Takt reichen??

von Benedikt K. (benedikt)


Lesenswert?

Ja, das sollte reichen. Und in der anderen Richtung?

von Hirbel (Gast)


Lesenswert?

Andere Richtung? also du meinst SDO von der SD-Karte aus?
Na die Leitung hängt an einem Eingang vom 245er chip, der Ausgang geht 
dann zum MISO pin am µC (dazwischen noch ein 4,7K Widerstand)..

von Benedikt K. (benedikt)


Lesenswert?

Kommt der µC mit den 3,3V Pegel zurecht? Da die Pegel vermutlich recht 
knapp als high erkannt werden, kann es sein, dass dadurch nochmal ein 
wenig Verzögerung hinzukommt, vor allem mit dem 4,7k Widerstand. Lass 
den mal weg.

von Hirbel (Gast)


Lesenswert?

im Datenblatt vom ATmega128 steht nur die Output Spannung

Output Low Voltage
(Ports A,B,C,D, E, F, G) :     0,7 V

Output High Voltage
(Ports A,B,C,D, E, F, G) :     4.2 V


Aber beim TWI Steht bei Input High: 0,7*Vcc das wären 3,5 V und 3,3 V 
wären dann schlecht...
die Frage ist dann nur, warum es bei 2 MHz überhaupt funktioniert?


Gibt es ne schnelle Möglichkeit den Pegel anzuheben ohne noch so ein IC 
zu verwenden und ohne die Spannung des µC zu verändern?

von Benedikt K. (benedikt)


Lesenswert?

Die Ausgangsspannungen interessieren hier nicht, die Eingangsspannungen 
sind wichtig:
Input High Voltage except XTAL1 and RESET pins: 0.6*VCC, also 3V bei 5V.
Bei 3,3V Spannung sind das nur 0,3V mehr, was eben zusammen mit dem 4,7k 
Widerstand eine zusätzliche Verzögerung bewirkt.
Versuch es mal ohne den Widerstand.

Die beste Lösung wäre ein HCT245 oder irgendwas in der Art, denn dieser 
hat Schaltschwellen bei etwa 0,8V bzw. 2V. Am Ausgang liefert er 0V/5V.

von Hirbel (Gast)


Angehängte Dateien:

Lesenswert?

naja aber ich kann den LVC245 nicht durch den HCT ersetzen sondern 
brauch wieder ne extra schaltung...mist, hätt ich mir vorher überlegen 
sollen..


Ohne den Widerstand gehts garnicht, nicht mal bei nem Takt von 125 KHz, 
mit dagegen einwandfrei, wieder bis 2 MHz...

von Hirbel (Gast)


Lesenswert?

Hab mal die schaltung (oben) mitgeschickt

von Benedikt K. (benedikt)


Lesenswert?

Ich meinte den HCT125 zusätzlich zum LVC245 für die andere Richtung.

Hast du den richtigen SPI Modus gewählt?
Und häng mal einen Pullup (irgendwas im Bereich 10-100k) an den Ausgang 
des SD Karte.

von Hirbel (Gast)


Lesenswert?

So sieht mein SPI Init aus:

SPI_DIR |= (1 << MOSI) | (1 << SCK) | (1 << SS);
SPI_PORT |= (1 << MOSI) | (1 << SCK) | (1 << SS);

SPI_DIR &= ~(1 << MISO);

SPCR = (1 << MSTR) | (1 << SPE);

SPCR |= (1 << SPR0) | (1 << SPR1);                                SPSR 
|= (1<<SPI2X);


kann kein pullup mehr dazwischen klemmen ohne die schaltung zu 
zerstören, ist schon fertig geätzt :(

von Benedikt K. (benedikt)


Lesenswert?

Hirbel wrote:

> kann kein pullup mehr dazwischen klemmen ohne die schaltung zu
> zerstören, ist schon fertig geätzt :(

Schlecht, der ist aber notwendig, siehe hier:
Beitrag "Re: Adressierung der Daten auf einer SD-Karte?"

Ansonsten passt der SPI Modus.

von Hirbel (Gast)


Lesenswert?

also auf dem Board, das vom Thread-Teilnehmer benutz wird,
ist auch nirgends ein Pullup widerstand dran:

http://heldt-intern.dyndns.org/uploads/pdf/Experimentierboard-Schematic.pdf

von Benedikt K. (benedikt)


Lesenswert?

Vermutlich hat er den internen Pullup aktiviert, was bei dir nicht geht, 
da du den LVC245 dazwischen hast.

von Hirbel (Gast)


Lesenswert?

Warum hab ich den eigentlich dazwischen??
ich versuch mal ne umleitung direkt zum µC pin zu bauen und aktiviere 
ebenfalls den internen pullup..

von Hirbel (Gast)


Lesenswert?

Okay, jetzt läufts mit 8 MHz....mehr ist ja nicht drin beim Hardware SPI 
(16 MHz CPU Takt) oder??

von Benedikt K. (benedikt)


Lesenswert?

Ja, 8MHz ist das maximale.
Lag es nur an dem Pullup?

von Hirbel (Gast)


Lesenswert?

Ja es lag daran und an den LVC, keine ahnung warum ich die Outputdaten 
von der SD Karte noch dadurch jagen wollte....

ich habe jetzt eine Rate von 512 Bytes in 7 µs
das sind doch knapp 70 MB/s, kann das stimmen?

von Knut B. (Firma: TravelRec.) (travelrec) Benutzerseite


Lesenswert?

Nein.

von Hirbel (Gast)


Lesenswert?

dachte ich mir,
bitte einmal vorrechnen was nun wirklich rauskommt :)

von Knut B. (Firma: TravelRec.) (travelrec) Benutzerseite


Lesenswert?

Die 512Byte pro 7µs zweifele ich hiermit öffentlich an.

von Hirbel (Gast)


Lesenswert?

meine messung habe ich wie folgt vorgenommen:

Sd Karte initialisiert.

Datei geladen.

Timer wie folgt initialisiert:

TCCR1A=0x00;
TCCR1B=0x01;         //Prescaler 1 -> 16 MHz
TCNT1=0x10000 - 16;  //---> 1 MHz -> jede µs
ICR1H=0x00;
ICR1L=0x00;
OCR1AH=0x00;
OCR1AL=0x00;
OCR1BH=0x00;
OCR1BL=0x00;

Timer gestartet (mit TIMSK=0x04)
-> Timerinterrupt jede µs->zähler wird erhöht, TCNT1 wird zurückgesetzt

Ein Block (512 Bytes) angefordert

Timer gestoppt (mit TIMSK=0x04)

Ausgabe der 512 Bytes (sind korrekt)
und ausgabe des Zählers (7 µs)


wo ist der Fehler?? Kann ja nur beim timer sein

von Benedikt K. (benedikt)


Lesenswert?

Hirbel wrote:

> Timer gestartet (mit TIMSK=0x04)
> -> Timerinterrupt jede µs->zähler wird erhöht, TCNT1 wird zurückgesetzt

Alle 16 Takte ein Interrupt? Vergiss es!

> Ausgabe der 512 Bytes (sind korrekt)
> und ausgabe des Zählers (7 µs)

Mit ein wenig Nachdenken hättest du eigentlich selbst drauf kommen 
können, dass dies nicht sein kann:
SPI hat 8MHz, also 8MBit/s. 512Byte sind 4kBit. Wenn du für 8bit 1µs 
brauchst, dann kannst du in 7µs maximal 56Bit übertragen, aber nie 
4kBit.

von Hirbel (Gast)


Lesenswert?

na ich habe ja glaube geschrieben das es nicht sein kann...
habe den fehler gefunden...beim zurücksetzen von TCNT1 stand ein alter 
wert drin....
so komme ich auf 7 ms und grad mal 71 KByte/s
...langsam....

von Knut B. (Firma: TravelRec.) (travelrec) Benutzerseite


Lesenswert?

Ahaaa. Rein theoretisch sind 512Bytes gut einer halben ms drin, wenn 
Dein Controller nichts anderes mehr machen muß. Also macht Dein 
Controller die anderen 6 1/2 ms noch irgendwelchen anderen Kram. 
Vielleicht kannst Du da noch optimieren. Oder aber der Controller wartet 
auf ein Busy von der Karte. Da könnte dann nur noch das Kommando 
"MultiBlockRead" etwas helfen. Ich erreiche bei meinen Karten bei 16Mhz 
SPI (XMega@32Mhz) etwas über 400kByte/s beim Single Block lesen. Du 
kannst natürlich auch Deinen Controller übertakten. 25Mhz machen die 
meisten ATMEGAs problemlos mit.

von Benedikt K. (benedikt)


Lesenswert?

Travel Rec. wrote:
> Ahaaa. Rein theoretisch sind 512Bytes gut einer halben ms drin, wenn
> Dein Controller nichts anderes mehr machen muß. Also macht Dein
> Controller die anderen 6 1/2 ms noch irgendwelchen anderen Kram.

Ja, nämlich das hier:

> TCCR1B=0x01;         //Prescaler 1 -> 16 MHz
> TCNT1=0x10000 - 16;  //---> 1 MHz -> jede µs
> ICR1H=0x00;
> ICR1L=0x00;
> OCR1AH=0x00;
> OCR1AL=0x00;
> OCR1BH=0x00;
> OCR1BL=0x00;
>
> Timer gestartet (mit TIMSK=0x04)
> -> Timerinterrupt jede µs->zähler wird erhöht, TCNT1 wird zurückgesetzt

Wenn der AVR jede µs einen Interrupt ausführt, dann geht da fast die 
ganze Rechenleistung drauf.
So eine Zeitmessung macht man anders:
Den Timer auf 0 setzen und starten. Im Overflow Interrupt eine weitere 
8/16bit Variable erhöhen. Nun hast du zusammen mit dem 16bit Timer einen 
24/32bit Zähler mit 62,5ns Auflösung und einem Messbereich bis 1s/256s.
Wenn du fertig bist, stoppst du den Timer mit TCCR1B=0;
Nun kannst du das Timer Register und die Variable auslesen und ausgeben.

Ich wette dann bist du mindestens doppelt so schnell.
Beim Lesen sind die SD Karten in der Regel sehr schnell, so dass fast 
nur der SPI Takt + Overhead zwischen den Bytes eine Rolle spielen. Ich 
schreibe sogar mit rund 100kByte/s mit einem mega8 @10MHz...

von Hirbel (Gast)


Lesenswert?

@Benedikt:

Kannst du das nochmal genau erklären mit dem timer und der Zeitmessung?
Hab ich grad nich wirklich verstanden....

von Benedikt K. (benedikt)


Lesenswert?

Du benutzt den Timer nicht als Timer zur Erzeugung eines Zähltaktes, 
sondern lässt den Timer direkt bis 65535 zählen. Bei 16MHz Takt gibt es 
so nur mit 244Hz einen Overflow Interrupt. Im Interrupt erhöhst du eine 
weitere Variable, falls ein Messbereich bis 4ms nicht reicht.

von Hirbel (Gast)


Lesenswert?

Also mit den Timereinstellungen komme ich auf folgendes:

16bit Zähler = 1 --> 4,096 ms

TCNT1 = 41860  -->  2,61625 ms

= 6,71225 ms

Das ist keine wirkliche Verbesserung...was mache ich falsch??

von Benedikt K. (benedikt)


Lesenswert?

Irgendwie kommt mir das etwas komisch vor:
Wenn du vorher wirklich den Timer alle 1µs einen Interrupt hast 
ausführen lassen, dann wäre diese zu (4 Takte Interrupteintritt, 4 Takte 
um Register zu retten, und das ganze nochmal beim Verlassen, also 16 
Takte insgesamt, dann nochmal 10 Takte um eine 16bit Variable zu 
erhöhen, macht also mindestens mal 26 Takte. Deine erste Messung war 
vermutlich also komplett falsch.

Da deine zweite Messung fast dasselbe Ergebnis liefert, und bei der 
ersten Messung der µC eigentlich die ganze Zeit im Interrupt verbringen 
müsste, kann da irgendwas nicht passen.

von Hirbel (Gast)


Lesenswert?

meine interrupt routine sieht so aus:

_timer1_overflow:
  ST   -Y,R30
  ST   -Y,R31
  IN   R30,SREG
  ST   -Y,R30
  MOVW R30,R6
  ADIW R30,1
  MOVW R6,R30
  LD   R30,Y+
  OUT  SREG,R30
  LD   R31,Y+
  LD   R30,Y+
  RETI

Sind genau 22 Takte (Codevision compiler)...
also ich weiß nich wo der fehler liegt :(

von Knut B. (Firma: TravelRec.) (travelrec) Benutzerseite


Lesenswert?

Poete mal den Rest vom Code.

von Hirbel (Gast)


Lesenswert?

welchen rest denn?
die ganzen sd-karten routinen für die ansteuerung?
neee das ist zu viel...

wenn bei beiden Messungen 7 ms rauskommt, warum sollte es da einen 
Fehler geben, ich versteh das nich so ganz...

von Benedikt K. (benedikt)


Lesenswert?

Hirbel wrote:
> meine interrupt routine sieht so aus:
>
> _timer1_overflow:
>   ST   -Y,R30
>   ST   -Y,R31
>   IN   R30,SREG
>   ST   -Y,R30
>   MOVW R30,R6
>   ADIW R30,1
>   MOVW R6,R30
>   LD   R30,Y+
>   OUT  SREG,R30
>   LD   R31,Y+
>   LD   R30,Y+
>   RETI
>
> Sind genau 22 Takte (Codevision compiler)...
> also ich weiß nich wo der fehler liegt :(

Wenn der Code (bzw. etwas ähnliches das du vorher hattest) 22 Takte 
braucht, er aber alle 16 Takte ausgeführt wurde, dann kann da was nicht 
passen.

Wenn du es nun anders machst, und immer noch das selbe rauskommt, dann 
kann da was nicht passen.

von Hirbel (Gast)


Lesenswert?

na das mit den 22 Takten bekomme ich ja nun nicht kleiner hin...

ich könnte den timer langsamer laufen lassen, so das ich garkeine 
Intterupt routine brauche, sonder TCNT1 ausreicht, also nie der overflow 
erreicht wird...
aber ich wette da kommt dann das gleich raus

von Knut B. (Firma: TravelRec.) (travelrec) Benutzerseite


Lesenswert?

>wenn bei beiden Messungen 7 ms rauskommt, warum sollte es da einen
>Fehler geben, ich versteh das nich so ganz...

Weil 7ms das 7-fache davon ist, was der Controller eigentlich an Zeit 
braucht, um die Daten auf der Karte abzulegen. Wenn Kartenschreibzeit 
und andere Routinen im zeitlichen Verhältnis von etwa 1:1 vorliegen 
würden, würde ich ja gar nichts sagen. Aber so vertrödelst Du einfach 
irgendwo Zeit, die Du vielleicht andernorts brauchen könntest.

von Benedikt K. (benedikt)


Lesenswert?

Hirbel wrote:
> na das mit den 22 Takten bekomme ich ja nun nicht kleiner hin...

Darum gehts ja auch nicht.

Ich machs mal an einem einfachen Beispiel:
Du liest ein Buch mit 512 Seiten.
Du liest nur 1h pro Tag, da du die restliche Zeit mit etwas anderem 
zubringst.
Jetzt liest du das Buch nochmal, diesmal liest du aber 23h pro Tag. 
Trotzdem brauchst du 512 Tage.
Wenn du beidesmal genausoschnell gelesen hast, dann muss irgendwo ein 
Fehler sein, denn dann kann das nicht stimmen.

Wenn du alle 1µs, also alle 16 Takte den Timer Interrupt ausführst, 
dieser aber 22 Takte braucht, dann
- passt die Zeitmessung nicht
- ist der Prozessor zu 22/23, also 96% mit dem Timer Interrupt 
beschäftigt

Wenn dein Code hierbei genausolange braucht, als wenn er nur z.B. 1% im 
Timerinterrupt verbringt, also 99% der Rechenleistung zum Lesen hat, 
dann kann irgendwas nicht stimmen.

Zeig also mein deinen ganzen Code!

von Hirbel (Gast)


Lesenswert?

sooo der timer lief jetzt mit 2MHz.
TCNT1 hatte den Wert 13423, das sind wieder 6,7115 ms
die Messung ist also korrekt oder nich?

es stimmt schon, das ich zeit vertrödele, in dem ich auf die SD karte 
warte (busy wait) aber das geht doch nun mal nich anders ??

von Benedikt K. (benedikt)


Lesenswert?

Wie alt ist denn die SD Karte?
70kByte/s ist schnarchlahm. Die Zugriffszeit beim Lesen ist eigentlich 
vernachlässigbar klein (meist irgendwas im 2 stelligen µs Bereich). 
Zumindest hatte ich noch keine Karte, die länger gebraucht hat.

von Hirbel (Gast)


Angehängte Dateien:

Lesenswert?

ja die ist schon entwas älter und sieht sehr mitgenommen aus...

anbei mal die sd.c, fat32.c (nicht alles auf meinem mist gewachsen) und 
die main.c (steht auch anderes zeug drin -> nicht beachten).

also ein wenig zeit geht auch drauf, weil er zu einer bestimmten stelle 
in einer datei springt...aber das ist nicht viel (hab ich schonmal 
auskommentiert)

von Hirbel (Gast)


Lesenswert?

hab mal ne neuere Karte genommen, da sinds jetzt immerhin "nur" noch
6,6555 ms ;)

kann es evtl. sein, das es an der takt oder datenleitung liegt?
hab dafür nur ein 0,2 mm kupferlackdraht genommen und alle leitungen 
zusammengedreht....???

von Benedikt K. (benedikt)


Lesenswert?

Achso, du liest nicht nur die Daten, sondern suchst erst noch den Sektor 
aus der Cluster Chain usw.
Das erklärt einiges.

Les mal z.B. 16x 512Bytes und mess dabei mal die Zeit.
Dann sollten deutlich >100kByte/s rauskommen.

Meine Erfahrung war bisher, das die Datenrate bei bis zu rund der Hälte 
des der SPI Frequenz liegt, also bei dir bei rund 4MBit/s=500kByte/s.
Hängt natürlich auch von der Geschwindigkeit des Prozessors usw. ab.

In der sd.c könntest du bei der Lesefunktion noch
*(buffer + i) = card_sendbyte(0xFF);
durch
*buffer++ = card_sendbyte(0xFF);
ersetzen, das sollte der Compiler deutlich besser optimieren können.

Und die card_sendbyte Funktion selbst, die solltest du als inline 
deklarieren, denn der Sprung + Rücksprung dauert fast so lange wie das 
Lesen selbst.
Und die Timeoutabfrage in der Funktion ist eigentlich auch unnötigt. 
Nach 16 Takte ist der SPI Transfer fertig, wenn nicht dann ist irgendwo 
was gewaltig schief gelaufen, da hilft dann auch keine Abfrage mehr.

Ansonsten sieht der Code gut aus. Wenn du mehrere Blöcke liest, und den 
Code wie angegeben etwas optimiert, dann sollten definitiv >100kByte/s 
rauskommen.

von Hirbel (Gast)


Lesenswert?

okay, ich hab das mit dem buffer abgeändert und 10 mal die gleichen 512 
bytes gelesen (ohne Seek, also vom anfang der datei)...

und man glaubt es kaum, aber ich komme auf 7,067 ms für 512 bytes :(

von Benedikt K. (benedikt)


Lesenswert?

Versuch mal die Änderungen in die Software einzubauen, vor allem das mit 
dem inline sollte eigentlich rund 50% mehr Geschwindigkeit bewirken.

von P. S. (Gast)


Lesenswert?

Am Besten liest du mal 10.000 Bloecke und stopst mit der Uhr ;-)

von Hirbel (Gast)


Lesenswert?

ach naja ich lass das jetzt erstmal so, alles in asm umzuschreiben, da 
hab ich jetzt keine lust zu 70KByte sind ja 560 kbit/s das dürfte für 
mp3 anwendungen ausreichen?

Kannst mir aber auch deine routinen zur verfügen stellen, wenn du willst 
:)


Vielen Dank aber erstmal für doe Hilfe.

von Benedikt K. (benedikt)


Lesenswert?

Hirbel wrote:
> ach naja ich lass das jetzt erstmal so, alles in asm umzuschreiben, da
> hab ich jetzt keine lust zu

Musst du auch nicht.
Mit dem Code von ELM-Chan erreiche ich >200kByte/s (alles in C).
http://elm-chan.org/fsw/ff/00index_e.html

> 70KByte sind ja 560 kbit/s das dürfte für mp3 anwendungen ausreichen?

Ja, sollten eigentlich. Mehr als 320kBit/s gibt es bei den normalen mp3s 
ja nicht.

von Hirbel (Gast)


Lesenswert?

okay, werd den code mal testen..

wie sieht es eigentlich mit SDHC karten aus??
funktionieren die auch (mit dem gleichen code für normale SD karten) 
oder funktionieren die anders oder garnicht?

von Benedikt K. (benedikt)


Lesenswert?

Die Routinen von ELM Chan können glaube ich SDHC ausprobiert habe ich es 
noch nicht.

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.