Forum: Mikrocontroller und Digitale Elektronik SD Karte mit FatFs über SPI an Atmega, wie kann man die Schreibgeschwindigkeit maximieren?


von Daniel N. (natulo)


Lesenswert?

Hallo,

ich möchte mit einem Atmega32 (2kB SRAM) Daten auf eine µSD Karte 
speichern.
Der µC läuft mit 14,7456MHz, SPI läuft mit einer Endgeschwindigkeit von 
fosc/2 und zum Beschreiben der Karte benutze ich Elm Chan's FatFs 
(R0.13).

Die 8 Analogeingänge (mit 10 bit) des Atmega möchte ich mit je 1kHz 
auslesen und die Werte abspeichern, es fallen also 8000 Samples/Sekunde 
an.

Als Karte benutze ich eine Medion 4GB SDHC Class 10 die auf Fat32 
formatiert ist. Alternativ habe ich noch eine 8GB Sandisk SDHC Class 10, 
die aber nochmal rund 50% langsamer.


Ich habe den FatFs Beispielcode so weit lauffähig bekommen und testweise 
in einer Schleife mit f_write ein char-Array mit unterschiedlichen 
Größen weggeschrieben, ohne selber f_sync zu benutzen. Geschrieben wird 
in ein einziges Dokument.

Dabei habe ich zum Beispiel 2048*32 Byte in ca. 1640ms geschrieben, was 
ca. 78 kB/s ergibt. Bei 512*256 Byte in ca. 1530ms sind das ca. 84,8 
kB/s.
Also scheinen größere zusammenhängende Datenmengen schneller in den 
Puffer geschrieben zu werden. Flushen müsste er ja in beiden Fällen nach 
je 512 Byte automatisch.

Einen großen Unterschied zwischen den Konfigurationen FF_FS_TINY 0 bzw. 
1 konnte ich nicht feststellen.

Leider ist das vom Datendurchsatz nicht so hoch wie erhofft. Ideal wären 
Geschwindigkeiten von rund 250 kB/s, die erreiche ich aber aktuell bei 
weitem nicht. (Eigentlich wollte ich jeden Messwert mittels f_printf mit 
Datum, Uhrzeit, Namen und Trennzeichen auf die SD Karte schreiben, damit 
die am PC direkt in Excel eingelesen werden können, dafür würden 32 Byte 
pro Sample anfallen.)

Die ffconf.h sieht bei mir zurzeit so aus: (Vielleicht habe ich da ja 
schon etwas wichtiges übersehen.)
1
#define FFCONF_DEF 87030
2
#define FF_FS_READONLY  0
3
#define FF_FS_MINIMIZE  0 
4
#define FF_USE_STRFUNC  2
5
#define FF_USE_FIND    0
6
#define FF_USE_MKFS    0
7
#define FF_USE_FASTSEEK  0
8
#define FF_USE_EXPAND  0
9
#define FF_USE_CHMOD    0
10
#define FF_USE_LABEL    0
11
#define FF_USE_FORWARD  0
12
13
#define FF_CODE_PAGE    437
14
#define FF_USE_LFN    0
15
#define FF_MAX_LFN    128
16
#define FF_LFN_UNICODE  0
17
#define FF_STRF_ENCODE  0
18
#define FF_FS_RPATH    0 
19
20
#define FF_VOLUMES    1
21
#define FF_STR_VOLUME_ID  0
22
#define FF_VOLUME_STRS    "RAM","NAND","CF","SD","SD2","USB","USB2","USB3"
23
#define FF_MULTI_PARTITION  0
24
#define FF_MIN_SS    512
25
#define FF_MAX_SS    512
26
#define FF_USE_TRIM    0
27
#define FF_FS_NOFSINFO  0
28
29
#define FF_FS_TINY    1 
30
#define FF_FS_EXFAT    0
31
#define FF_FS_NORTC    0
32
#define FF_NORTC_MON    1
33
#define FF_NORTC_MDAY  1
34
#define FF_NORTC_YEAR  2016
35
#define FF_FS_LOCK    0
36
#define FF_FS_REENTRANT  0
37
#define FF_FS_TIMEOUT  1000
38
#define FF_SYNC_t    HANDLE

Die SPI Konfiguration in mmc_avr_spi.c sieht so aus:
1
#define  FCLK_SLOW()  SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPR1)|(1<<SPR0)
2
#define  FCLK_FAST()  SPCR = (1<<SPE)|(1<<MSTR)
3
4
[...]
5
6
static void power_on (void){
7
  DDRB  |= (1<<PB7)|(1<<PB5)|(1<<PB4);
8
  DDRB  &= ~(1<<PB6);
9
  PORTB |= (1<<PB5)|(1<<PB4);
10
  PORTB |= (1<<PB6);
11
  
12
  SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPR1)|(1<<SPR0);
13
  SPSR = (1<<SPI2X);  
14
}
15
static void power_off (void){
16
  SPCR = 0;
17
  DDRB  &= ~((1<<PB7)|(1<<PB5)|(1<<PB4));
18
  PORTB &= ~((1<<PB5)|(1<<PB4));
19
}
20
[...]
Die SPI Geschwindigkeit müsste also nach dem Initialisieren bei fosc/2 
liegen. Nachmessen kann ich es leider nicht.


Das Vormerken von Speicher (Pre-Allocation) mit f_lseek oder f_expand 
habe ich bisher noch nicht ausprobiert.
Merkt er sich dabei eigentlich immer die gleichen Speicherbereiche auf 
der Karte vor, sprich erhöht das die Abnutzung der Karte? Das soll 
soweit ich es gelesen habe die Schreibgeschwindigkeit erhöhen, was für 
Nachteile entstehen dadurch?
Und wie viel schneller ist man damit unterm Strich?


Allgemein würde ich gerne wissen:
* Was für Schreibraten kann man mit FatFs bei einem Atmega über SPI im 
Schnitt erreichen? Vielleicht kann ja der Eine oder Andere 
Erfahrungswerte mit seiner Konfiguration (MCU, Karte, Takt- und 
SPI-Frequenz) teilen.
* Mit welchen Maßnahmen man mit FatFs die besten 
Schreibgeschwindigkeiten erreichen kann.

Seien es FatFs Optionen (z.B. FF_FS_TINY 0/1), die Art und Weise, wie 
man die FatFs Funktionen benutzt (f_write oder f_printf einfach irgendwo 
im Programmcode aufrufen o.Ä.) oder wie man die zu schreibenden Daten 
bereitstellt/zuführt (was bringt z.B. ein FiFo Puffer, aus dem man 
f_write speist oder wie groß sollten die einzelnen Datenpakete je 
f_write sein).

Also was die wichtigsten Stellschrauben / Fallstricke sind, um gute 
Schreibgeschwindigkeiten mit FatFs zu erreichen.

Wäre großartig, wenn man hier etwas zusammentragen könnte. Denn langsam 
habe ich Zweifel, ob ich die von mir angepeilten 
Schreibgeschwindigkeiten mit der Hardware überhaupt erreichen kann.

Schöne Grüße

von Stefan F. (Gast)


Lesenswert?

Mit 80kB/s hast du bereits mehr erreicht, als die meisten anderen. Ich 
schätze, dass du im SPI Modus nicht viel mehr erreichen kannst.

Ärgerlicher ist hingegen, dass diese Karten sich spontan mal mit etwas 
anderem beschäftigen und plötzlich einige 100ms lang nicht mehr 
antworten. Dafür brauchst du einen ausreichend großen Puffer.

von c-hater (Gast)


Lesenswert?

Stefan U. schrieb:

> Mit 80kB/s hast du bereits mehr erreicht, als die meisten anderen. Ich
> schätze, dass du im SPI Modus nicht viel mehr erreichen kannst.

Doch, das kann man. Der begrenzende Faktor ist nicht die 
SPI-Geschwindigkeit, wie man sehr leicht ausrechnen kann. Schließlich 
bedeutet ca. 7MHz SPI-Takt einen Bruttodurchsatz von nicht weniger als 
875kB/s, also mehr als das Zehnfache des erreichten Nettodurchsatzes.

> Ärgerlicher ist hingegen, dass diese Karten sich spontan mal mit etwas
> anderem beschäftigen und plötzlich einige 100ms lang nicht mehr
> antworten. Dafür brauchst du einen ausreichend großen Puffer.

Das ist nur ein Aspekt der Sache. Aber dieser Aspekt führt genau wie 
alle anderen nur zu einer Lösung: man braucht viel mehr RAM im µC. 
ElmChan's Werk kann zumindest bei einigen der Aspekte von zusätzlichem 
RAM profitieren (mit entsprechender Konfiguration), deswegen ist die 
einfachste Lösung, den Mega32 schlicht durch einen Mega1284P zu 
ersetzen, der immerhin 16k RAM bereitstellt, ansonsten aber relativ 
weitgehend Pin- und Softwarekompatibel zum Mega32 ist. Damit kann man 
dann schon mit FatFS irgendwas um die 300kB/s (bei dem gegebenen 
SPI-Takt) erreichen.

Schneller geht's nur, wenn man sich von dieser eierlegenden Wollmilchsau 
trennt und etwas von vorherein auf "relativ viel Buffer verfügbar" 
optimiertes programmiert. Damit kann man etwa 85..90% des durch die 
SPI-Anbindung vorgegebenen theoretischen Maximums als Dauerleistung beim 
Schreiben erreichen.

Alternativ kann man auch FatFS weiter verwenden, aber eine puffernde 
Schicht zwischen dem eigentlichen FatFS und dem physischen Zugriff auf 
die Karte einfügen. Das ist performancemäßig sehr nahe an der optimalen 
Lösung, allerdings nur dann, wenn wirklich viel Puffer-RAM verfügbar 
ist. Leider deutlich zu viel auch für einen Mega1284P. Sprich: diese 
Variante ist zwar programmiertechnisch deutlich einfacher zu 
realisieren, aber auch sehr viel ineffizienter, denn selbst mit dem real 
auf einem Mega1284P verfügbaren RAM bringt sie nur sehr wenig 
zusätzlichen Gewinn. Den Aufwand nicht wert. Das wäre allenfalls eine 
Option für Megas mit externem RAM.

von holger (Gast)


Lesenswert?

Die Bremse wird das fprintf sein. Das kostet auch Zeit.
Mit einem ARM wirst du da eher glücklich.

von Dieter F. (Gast)


Lesenswert?

c-hater schrieb:
> Damit kann man
> dann schon mit FatFS irgendwas um die 300kB/s (bei dem gegebenen
> SPI-Takt) erreichen.

Zeig mal bitte :-)

Ich schaffe ähnliches - aber nur, weil ich den DMA-Modus des ATXMega in 
Verbindung mit RAW-Schreiben auf Sektor-Ebene (ohne FAT..) nutze.

von PittyJ (Gast)


Lesenswert?

Bei meinen Datensampler schreibe ich immer binär weg.
Datum, Uhrzeit das passt auch in einem 32 Bit time_t. Trennzeichen 
braucht man nicht. Immer gleiche Record-Längen und dann rein ins Flash.

Zum Auswerten später braucht man ja eh ein extra Auswerteprogramm. Das 
kann dann die Zeiten menschenlesbar machen, die Rohwerte filtern und in 
Einheiten umrechnen. Ein PC hat genug Rechenleistung dafür.

von Dieter F. (Gast)


Lesenswert?

PittyJ schrieb:
> Zum Auswerten später braucht man ja eh ein extra Auswerteprogramm. Das
> kann dann die Zeiten menschenlesbar machen,

Ich mache es umgekehrt und nutze Python :-)

von Malte _. (malte) Benutzerseite


Lesenswert?

Ich hatte mal einen Atmega mit 16MHz, SD Karte und einem Ethernet Chip 
als mp3 Player gebaut.
Über FTP hat der 120kB/s lesend und 80kB/s schreibend erreicht. Das sind 
so ungefähr deine Werte. Wobei davon natürlich signifikant viel Zeit 
fürs Netzwerk drauf ging. Ich meine die Schreibwerte hatte ich auch erst 
erreicht, nachdem ein paar Sektoren immer gebuffert wurden. Sonst muss 
der beim Schreiben jedes mal noch die FAT Tabelle holen, ändern, 
zurückschreiben. Eventuell ist da also noch was bei dir (auf Kosten von 
RAM) zu holen. Ansonsten 8000Samples/Sekunde, jeweils 16Bit binär 
kodiert -> 16KB/s -> reicht doch?
Wenn du aber alles in Dezimal umrechnest sind das ne menge Divisionen, 
und die Kosten auf nem AVR wirklich Zeit:
https://www.mikrocontroller.net/articles/ArithmetikBenchmark
Rechne dann mal mit einer Division pro Dezimalstelle. 10Bit->5 Stellen 
-> 245Takte pro Division -> 1225 Takte pro Wert * 8000 -> ~10MHz nur 
fürs dividieren.

von Peter (Gast)


Lesenswert?

Also hab nicht letztens mit dem SPI des Atmega beschäftigt. Auf der 
Seite SPI geht's zu diesem Link. Da geht's um die Optimierung des 
Quelltexts. Bzw um ein schnelleres schreiben von SPI Busses.
http://www.matuschek.net/atmega-spi/

von holger (Gast)


Lesenswert?

>Da geht's um die Optimierung des
>Quelltexts. Bzw um ein schnelleres schreiben von SPI Busses.

Richtig schnell geht es mit dem USART als
SPI Master. Der ist in Senderichtung doppelt gepuffert beim Atmega.

von Matthias M. (Firma: privat) (quadraturencoder)


Lesenswert?

Ich befürchte, dass Du ohne grosse RAM-Puffer keine zuverlässige 
Datenrate hinbekommst. Sobald ein Sektor auf der SD Karte voll ist, muss 
ja der Block Lookup neu geschrieben werden und der nächste Sektor zum 
Schreiben gefunden werden, etc. .

Ein echter Zeitfresser ist auch printf in all seinen varianten, weil der 
Controller den Formatierstring erst byte für byte interpretieren muss. 
Ausserdem ist ASCII natürlich immer auch extrem verschwenderisch, da ja 
im Prinzip nur 5bit von 8bit überhaupt signifikant sind.

Schau doch mal so genau wie möglich, wo der Flaschenhals ist. Ist das 
wirklich der SPI? Oder eher der FatFs? Oder warten alle auf prinf()?

Gefühlsmässig wäre mein Ansatz, die Daten stark zu komprimieren. Der 
Timestamp ist fast komplett überflüussig, wenn du mit einer festen Rate 
samplest. Oder schreib halt nur den Delta-Wert in 1000tel Sekunden zum 
vorherigen Sample. Ändern sich die Analogwerte wirklich alle 1000 mal 
pro Sekunde, oder muss man nur die wegschreiben, die sich wirklich 
ändern? Dann noch alles binär wegschreiben mit ner simplem Runlength 
Encoding Kompression, und Du kommst plötzlich mit der Datenrate hin - 
und Deine SD Karte lebt auch viel länger.

von grundschüler (Gast)


Lesenswert?

holger schrieb:
> Richtig schnell geht es mit dem USART als
> SPI Master. Der ist in Senderichtung doppelt gepuffert beim Atmega.

wirklich schneller wirds damit aber nicht. Hab das mit einem spi-tft 
ausprobiert.

Es gibt die Möglichkeit ohne fat direkt auf die Karte zu schreiben:
Beitrag "Re: Geschwindigkeitsfrage AVR auf SD"

Wenn es auf Geschwindigkeit ankommt, sollte man auf stm32/sdio 
umsteigen.

von Dieter F. (Gast)


Lesenswert?

grundschüler schrieb:
> Wenn es auf Geschwindigkeit ankommt, sollte man auf stm32/sdio
> umsteigen.

Zeig mal :-) (analog C-Hater, der sich ja nicht zurück gemeldet hat)

von grundschüler (Gast)


Lesenswert?


von Dieter F. (Gast)


Lesenswert?

grundschüler schrieb:
>> Zeig mal
>
> Beitrag "Re: sd-card SDIO Initialisierung"

Witzig - hat Du immer noch nicht übersetzt :-)

von Stefan F. (Gast)


Lesenswert?

>> Mit 80kB/s hast du bereits mehr erreicht, als die meisten anderen. Ich
>> schätze, dass du im SPI Modus nicht viel mehr erreichen kannst.

> Der begrenzende Faktor ist nicht die SPI-Geschwindigkeit

Das wollte ich damit auch nicht sagen. Dass die SPI Schnittstelle viel 
höhere Datenraten unterstützt, ist ja offensichtlich. Ich denke eher, 
dass der Knackpunkt die "Denkpausen" der SD Karte sind, und dass sie im 
SPI Modus eher nicht auf Performance optimiert arbeiten.

von Volker S. (vloki)


Lesenswert?

Stefan U. schrieb:
> Ärgerlicher ist hingegen, dass diese Karten sich spontan mal mit etwas
> anderem beschäftigen und plötzlich einige 100ms lang nicht mehr
> antworten. Dafür brauchst du einen ausreichend großen Puffer.

Mit dem Problem beschäftige ich mich auch im Moment.
(PIC18F27J53, FatFS, verschiedene Sensoren mit 5kHz-100Hz-33Hz 
Abtastrate und 16bit Daten)
Dafür zwei Buffer mit 352Bytes, die in wechselweise in 30ms gefüllt und 
an FatFS weitergereicht werden.
Bei den ersten Versuchen mit einer älteren microSD Karte kam die große 
Ernüchterung. Regelmäßig 250ms und mehr in denen kein Schreiben auf die 
Karte möglich ist.

Ich habe inzwischen verschiedene microSDHC Karten ausprobiert, von denen 
bisher nur eine, auch bei stundenlangen Aufzeichnungen, nie eine größere 
Auszeit nahm als 25ms. Die kann ich dann leicht mit einem größeren 
Buffer oder mehr Buffern überbrücken.

Die "gute" Karte war eine Intenso microSDHC 8GB Class 10 (3413460)

Durchgefallen (mit großen Ausfallzeiten) sind:
- PLATINUM microSDHC 4GB Class 4 (177315)
- PHILIPS microSDHC 4GB Class 6 (FM04MD35B)
- Samsung Evo 16GB Class 10 / U1
- 2GB Standard microSD Karten

Hat jemand vielleicht schon weitere gute oder auch schlechte Erfahrungen 
mit anderen Karten gesammelt? z.B.:
- KINGSTON 32GB microSDHC/SDXC UHS-I Class U3 (SDCA3/32GBSP)
- KINGSTON 16GB microSDHC UHS-I Class U3 (SDCG/16GB)

: Bearbeitet durch User
von Markus F. (mfro)


Lesenswert?

Stefan U. schrieb:
> Ich denke eher,
> dass der Knackpunkt die "Denkpausen" der SD Karte sind, und dass sie im
> SPI Modus eher nicht auf Performance optimiert arbeiten.

Das denke ich nicht. Auch der SPI-Modus kann recht flott sein.

Ich beschreibe SD-Karten im SPI-Modus mit bis zu 5-600 KB/s. Allerdings 
mit einem ColdFire (22 MHz SPI-CLK).

von Volker S. (vloki)


Lesenswert?

Markus F. schrieb:
> Ich beschreibe SD-Karten im SPI-Modus mit bis zu 5-600 KB/s.

Bisher keine "Denkpausen" aufgefallen, oder einfach nur genügend 
Zwischenspeicher vorgesehen?
Welche Karte, wäre auch noch interessant...

von Markus F. (mfro)


Lesenswert?

Volker S. schrieb:
> Markus F. schrieb:
>> Ich beschreibe SD-Karten im SPI-Modus mit bis zu 5-600 KB/s.
>
> Bisher keine "Denkpausen" aufgefallen, oder einfach nur genügend
> Zwischenspeicher vorgesehen?
> Welche Karte, wäre auch noch interessant...

Keine, die mich stören würden. Die Karte spielt dabei eher eine 
untergeordnete Rolle. In meinem Fundus habe ich nur eine (eine der 
ersten Micro-SD's mit Adapter) die merklich lahmt und nur etwas mehr als 
die Hälfte schafft. Der Rest ist (100 KB/s hin oder her) fast 
vergleichbar.

Wenn Du mit einem geringen SPI-Takt drangehst, werden die Denkpausen 
naturgemäß auch länger, denke ich. Wie hoch kannst Du gehen?

von Volker S. (vloki)


Lesenswert?

Markus F. schrieb:
> Wenn Du mit einem geringen SPI-Takt drangehst, werden die Denkpausen
> naturgemäß auch länger, denke ich. Wie hoch kannst Du gehen?

Ich habe bisher nicht getestet, wie sich der SPI Takt auf die Pausen 
auswirkt. Bisher habe ich wenig Erfahrung mit SD Karten. Ich kann mir im 
Moment nicht so recht vorstellen, wie sich der SPI Takt auf etwas 
auswirken soll das in der Karte vorgeht (zwischengespeicherten Sektor 
ins Flasch schreiben oder was auch immer...), wäre aber für jeden 
Hinweis dankbar!

Die Hardware stammt aus einer Bachelor-Aschluss-Arbeit und wegen eines 
kleinen Designfehlers wird momentan Software-SPI verwendet.
Der SPI-Clock ist ~2MHz. Das Schreiben von 512 Bytes dauert ~4,3ms.
Wenn ich kontinuierlich schreiben könnte/würde, dann sollten das etwas 
über 100kByte/s sein. Daten zum Schreiben habe ich im Moment ~12kByte/s. 
Das ist also im Zeitlichen Mittel kein Problem.

Blöd sind nur die langen Pausen, in denen kein Schreiben möglich zu sein 
scheint und dass auf so einem (mickrigen ;-) 8Bitter nicht eben viel RAM 
zum Zwischenspeichern verfügbar ist.
Jetzt stellt sich die Frage nach "besseren Karten", oder eben einem 
anderen Controller. Andere Karte einstecken wird natürlich erst mal 
bevorzugt ;-)

von Volker S. (vloki)


Lesenswert?

Volker S. schrieb:
> Ich habe inzwischen verschiedene microSDHC Karten ausprobiert, von denen
> bisher nur eine, auch bei stundenlangen Aufzeichnungen, nie eine größere
> Auszeit nahm als 25ms. Die kann ich dann leicht mit einem größeren
> Buffer oder mehr Buffern überbrücken.
>
> Die "gute" Karte war eine Intenso microSDHC 8GB Class 10 (3413460)

Korrektur - die Freude war leider nur von beschränkter Dauer. Nach 
einigen Stunden/einigen 100MB zeigt die Karte ähnliches Verhalten wie 
die anderen. (>100ms kein Schreibzugriff)

: Bearbeitet durch User
von Christopher J. (christopher_j23)


Lesenswert?

Volker S. schrieb:
> Stefan U. schrieb:
>> Ärgerlicher ist hingegen, dass diese Karten sich spontan mal mit etwas
>> anderem beschäftigen und plötzlich einige 100ms lang nicht mehr
>> antworten. Dafür brauchst du einen ausreichend großen Puffer.
>
> Mit dem Problem beschäftige ich mich auch im Moment.
> (PIC18F27J53, FatFS, verschiedene Sensoren mit 5kHz-100Hz-33Hz
> Abtastrate und 16bit Daten)

Das Problem ist, dass SD-Karten ebenso wie beispielsweise SSDs keine 
transparenten Schreibzugriffe ermöglichen, weil da eben noch ein kleiner 
Controller zwischen deinem uC und dem Flash sitzt, der sich 
beispielsweise um Wear-Leveling kümmert. Die SD-Spec sieht 
Schreiblatenzen von bis zu 500ms(!) vor. D.h. dein Buffer muss 
mindestens so groß sein, dass du diese Zeit überbrücken kannst, also für 
deine Anwendung in etwa 5kB, womit der PIC18F27J53 prinzipiell nicht 
dienen kann.

von Volker S. (vloki)


Lesenswert?

Christopher J. schrieb:
> Die SD-Spec sieht
> Schreiblatenzen von bis zu 500ms(!) vor. D.h. dein Buffer muss
> mindestens so groß sein, dass du diese Zeit überbrücken kannst...

Sieht im Allgemeinen wohl ganz so aus.

Für unsere spezielle Anwendung kann es eventuell erst mal tolerierbar 
sein, kurze Aussetzer in der Aufzeichnung zu haben.
Das darf dann aber "relativ" selten passieren und wir müssen die 
Information mit abspeichern wie lange die Pause war.

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

@Michael K. (natulo)

>Die 8 Analogeingänge (mit 10 bit) des Atmega möchte ich mit je 1kHz
>auslesen und die Werte abspeichern, es fallen also 8000 Samples/Sekunde
>an.

Macht 16kB/s. Schafft man locker.

>Als Karte benutze ich eine Medion 4GB SDHC Class 10 die auf Fat32
>formatiert ist. Alternativ habe ich noch eine 8GB Sandisk SDHC Class 10,
>die aber nochmal rund 50% langsamer.

Interessiert hier nicht, mit SPI und AVR kriegst du die nicht mal 
ansatzweise ausgelastet.

>Dabei habe ich zum Beispiel 2048*32 Byte in ca. 1640ms geschrieben, was
>ca. 78 kB/s ergibt. Bei 512*256 Byte in ca. 1530ms sind das ca. 84,8
>kB/s.

Lahm.

Beitrag "Re: Geschwindigkeitsfrage AVR auf SD"

Ich hab mal einen DMX-Rekorder programmiert, dort wurden 22kB/s von 
SD-Karte gelesen/schreiben und per UART empfangen/gesendet. Die mittlere 
CPU-Last lag bei ~30% @16MHz.

Beitrag "Re: Atemega328p - schaffst du das?"

: Bearbeitet durch User
von c-hater (Gast)


Lesenswert?

Volker S. schrieb:

> Für unsere spezielle Anwendung kann es eventuell erst mal tolerierbar
> sein, kurze Aussetzer in der Aufzeichnung zu haben.

Wenn du 500ms für "kurz" hältst, dann sollte es eigentlich niemals 
irgendwelche Probleme geben.

> Das darf dann aber "relativ" selten passieren.

Das kann (im Grenzfall) bei jedem verschissenen write passieren. Hint: 
die Specs haben de facto allein die Hersteller der Karten geschrieben, 
nicht die Käufer/Benutzer. Dementsprechend schwammig ist die 
Beschreibung dessen, was so eine Karte tatsächlich leisten können 
muss....

Der Trick ist: mache es der Karte (bzw. dem ziemlich dummen 
Flashcontroller darin) so leicht, wie irgend möglich. Dann (und leider 
nur dann) wird sie die versprochenen Schreibraten auch dann noch 
erreichen, wenn sie z.B. bereits mehrfach vollgeschrieben war. Alle dazu 
nötigen Informationen lassen sich in den Daten finden, die der 
Controller der Karte liefert. Um sie sinnvoll zu nutzen, braucht man:

1) Viel Buffer (leider sehr viel mehr als auf kleinen µC typischerweise
   verfügbar ist)
2) Das Verständnis, wie man diesen Buffer unter Berücksichtigung der vom
   Controller gelieferten Informationen sinnvoll einsetzt, um den
   Controller die Arbeit maximal zu erleichtern.

De facto also alles Scheiß, der durch den Flashcontroller für die 
Anwendung eigentlich irrelevant gemacht werden sollte. Genau das ist die 
große Lüge des SD-Card-Konsortiums. Tatsache ist: er macht das eben 
leider nicht überflüssig.

Damit das nicht so auffällt, spielt er eine Weile gut mit (so lange der 
Spare reicht), dann schwächelt er performancemäßig, dann stirbt er ganz. 
Der Unterschied der Mafiosi aus der einen oder anderen Konzernzentrale 
besteht dann nur noch darin, ob sie dir erlauben, wenigstens noch 
erlauben, ein Image zu ziehen oder ob sie dich stumpf komplett von 
deinen Daten trennen...

> und wir müssen die
> Information mit abspeichern wie lange die Pause war.

Das ist simpel: Du speicherst einfach Zeitstempel, die auf die Gewinnung 
der Werte bezogen sind, nicht auf das Wegschreiben selbiger.

Das sollte aber sowieso der Grundansatz sein, völlig unabhängig vom 
Speichermedium. Alles andere wäre schlechtes Softwaredesign. Und zwar 
deiner eigenen Software. Das zumindest könntest du also nicht der 
SD-Card-Mafia anhängen, so finster die auch ist...

von Dieter F. (Gast)


Lesenswert?

Eigentlich ist es mir vollkommen Wurscht, was hier abgeht - aber
 ich weiß, das die SD-Karten einen eigenen Prozessor haben, der sich 
entsprechend durchsetzt.

Dagegen kann sich auch niemand wehren. Die Spezifikationen kann man sich 
im Netz herunterladen. Natürlich braucht der SD-Karten-Prozessor Zeit um 
seine  Verwaltung der Daten zur organisieren. Diese stört halt ab und an 
die Kommunikation.

von Brue W. (brue)


Lesenswert?

SD Karten verwenden einen NAND Flash. Auf den NAND Flash kann man leider 
nicht so einfach zugreiffen wie auf die SD Karte selbst, der Controller 
in der Karte muss sich darum kümmern die Zugriffe des Hosts auf den 
Flash abzulegen und ihn entsprechend anzusteuern. Je nachdem wie die 
Daten vom Host kommen kann dies mehr oder weniger Effizient passieren.
Ein NAND Flash kann beispielsweise nur in Einheiten sogenannter Pages 
Programmiert werden, bevor eine Page Programmiert werden kann muss sie 
gelöscht sein. Das Löschen passiert in Einheiten von Flash Blöcken. Eine 
Page ist bei MLC Flashes 4, 8, 16kiB gross, ein Block kann 64, 128 256 
Pages beinhalten. Die Werte sind als Grössenordnungen zu verstehen und 
hängen von der konkreten Architektur und Flash Type ab.
Wenn der Host nun nur einen Sektor (512 Bytes) oder sogar noch weniger 
(nur bei SDSC möglich) auf die Karte schreibt, muss die Karte intern 
trotzdem mindestens eine ganze Flash Page programmieren. Je nach dem ist 
intern ein Read-Modify-Write notwendig, weil vielleicht ein Block 
gelöscht werden muss der teilweise noch gültige Daten beinhaltet. Das 
ganze ist recht komplex.

Der Datendurchsatz der Karte selbst wird umso schneller sein, desto mehr 
Daten der Host auf einmal schreibt. Mikrocontroller benutzen oft 
ausschliesslich Sektorzugriffe, die sehr langsam sind.
Das SD Protokoll hat zwei Schreibkommandos: WRITE_BLOCK für das 
Schreiben von einzelnen Sektoren und das WRITE_MULTIPLE_BLOCK um mehrere 
Sektoren zu schreiben. Der Host sollte wenn möglich WRITE_MULTIPLE_BLOCK 
benutzen und möglichst viele Sektoren auf einmal schreiben.

Gruss
brue

von Daniel N. (natulo)


Lesenswert?

Schonmal Danke für all die Antworten!

Um die Datenmenge zu reduzieren habe ich überlegt, die einzelnen 
Teildaten eines Messpunktes per Bitshifting in eine 64 Bit Variable 
abzulegen und die dann hinterher am PC wieder aufzudröseln.
Bei den Zeitstempeln einen Offset zu benutzen wäre auch noch eine Idee, 
z.B. sich langsam ändernde Werte wie das Datum nur ab und zu in größeren 
Intervallen zu schreiben. Allerdings wüsste ich spontan nicht, wie ich 
Daten mit dynamischer Länge (z.B. mal 64 Bit, dann nur 32 Bit oder etwas 
dawischen, wenn etwa das Datum wegfällt) in einem FIFO zwischenspeichern 
könnte.

Ich werde auch mal einen pinkompatiblen AVR mit mehr SRAM raussuchen, 
wobei es da ja eh nur den 644PA mit 4kB oder den 1284P mit 16kB gibt.


Falk B. schrieb:
> Lahm.
>
> Beitrag "Re: Geschwindigkeitsfrage AVR auf SD"
>
> Ich hab mal einen DMX-Rekorder programmiert, dort wurden 22kB/s von
> SD-Karte gelesen/schreiben und per UART empfangen/gesendet. Die mittlere
> CPU-Last lag bei ~30% @16MHz.
>
> Beitrag "Re: Atemega328p - schaffst du das?"

Dass das "lahm" ist, sehe ich. Daher war ja auch der Sinn des Threads in 
Erfahrung zu bringen, wie man das schneller machen kann.

Die von dir im ersten verlinkten Beitrag angehängte Datei hatte ich auch 
schon gesehen. Bei 100 Byte Blöcken (also 100 Byte an f_write 
übergeben?) schon Schreibwerte von 250kB/s zu erreichen finde ich 
beachtlich.

Wenn ich das richtig rauslese, benutzt du da generell Pre-Allocation mit 
f_lseek und hast bei den "mit CLMT" Werten zusätzlich FF_USE_FASTSEEK 1 
benutzt? (http://elm-chan.org/fsw/ff/doc/lseek.html letztes Beispiel)
Der Unterschied zwischen Fastseek 1 und 0 ist in deinem Beispiel ja 
nicht so groß.

Ich habe das mit der Preallocation (ohne Fastseek und die CLMT) mal 
ausprobiert, sofern ich es richtig verstanden habe:

1
xprintf(PSTR("open1 rc=%d\n"), f_open(&file1,"test.txt",FA_WRITE|FA_OPEN_ALWAYS));
2
uint16_t written;
3
4
xprintf(PSTR("1. f_size nach fopen %lu\n"),f_size(&file1));
5
xprintf(PSTR("1. f_tell nach fopen %lu\n"),f_tell(&file1));
6
7
f_write(&file1, &day, sizeof(day),&written); //uint8_t day = 97
8
f_write(&file1,"Test Text",9,&written); // <+^ Zusammen 10 Byte
9
10
unsigned char charArray[256];
11
for(int i=0;i<(sizeof(charArray)/sizeof(char));i++){
12
  charArray[i]=86;
13
};
14
15
16
xprintf(PSTR("f_sync1 rc=%d\n"), f_sync(&file1) );
17
18
19
Timer = 0; // Wird im 10ms Interrupt inkrementiert
20
xprintf(PSTR("Timer start %d\n"), Timer*10 );
21
22
xprintf(PSTR("1. f_size nach 10 byte %lu\n"),f_size(&file1));
23
xprintf(PSTR("1. f_tell nach 10 byte %lu\n"),f_tell(&file1));
24
25
f_lseek(&file1,f_size(&file1));  // Pointer zum Ende der Datei
26
FSIZE_t endoffile = f_size(&file1);  // "eof", Speicher das Ende der Datei vor der Erweiterung
27
28
xprintf(PSTR("2. f_size start %lu\n"),f_size(&file1)); // Wie groß ist die Datei?
29
xprintf(PSTR("2. f_tell start %lu\n"),f_tell(&file1)); // Wo liegt der Filepointer?
30
  
31
xprintf(PSTR("lseek expand %d\n"),f_lseek(&file1,1000) ); // Verschiebe Filepointer und erweiter die Datei, Wert wird im Test verändert
32
  
33
xprintf(PSTR("3. f_size nach alloc %lu\n"),f_size(&file1)); // Größe und Position nach Erweiterung
34
xprintf(PSTR("3. f_tell nach alloc %lu\n"),f_tell(&file1));
35
36
xprintf(PSTR("lseek eof %d\n"),f_lseek(&file1,endoffile) ); // Setze den Filepointer zurück zum Anfangspunkt der Erweiterung
37
  
38
xprintf(PSTR("4. f_size nach eof %lu\n"),f_size(&file1));
39
xprintf(PSTR("4. f_tell nach eof %lu\n"),f_tell(&file1));
40
41
42
for(int j=0;j<500;j++){
43
  f_write(&file1, charArray, (sizeof(charArray)/sizeof(char)), &written); // Schreibe 500*256 = 128000 byte
44
}
45
46
xprintf(PSTR("5. f_size vor truncate %lu\n"),f_size(&file1));
47
xprintf(PSTR("5. f_tell vor truncate %lu\n"),f_tell(&file1));
48
49
50
xprintf(PSTR("truncate %d\n"),f_truncate(&file1) ); // Entferne Dateiüberhang
51
52
xprintf(PSTR("6. f_size nach truncate %lu\n"),f_size(&file1));
53
xprintf(PSTR("6. f_tell nach truncate %lu\n"),f_tell(&file1));
54
55
xprintf(PSTR("Timer ende%d\n"), Timer*10 );
56
57
xprintf(PSTR("f_sync2 rc=%d\n"), f_sync(&file1) ); 
58
f_close(&file1);
59
xprintf(PSTR("Unmount rc=%d\n"), f_mount(0,"",0));

Für den Test habe ich die zu schreibenden Daten gleich gelassen und nur 
die Größe der Zuweisung geändert. Die Datei auf der Speicherkarte wurde 
vor dem Ändern der Zuweisungsgröße gelöscht.
Nach dem ersten Durchlauf wurde der µC aus- und wieder eingeschaltet.
1
f_lseek(&file1,128100):         |  f_lseek(&file1,128000+eof):     |  f_lseek(&file1,1000):
2
                                |                                  |                                        
3
1. Durchlauf:                   |  1. Durchlauf:                   |  1. Durchlauf:
4
             size:     pointer: |               size:     pointer: |               size:     pointer:
5
Start:        10          10    |  Start:        10          10    |  Start:        10          10   
6
Alloc:       128100     128100  |  Alloc:       128010     128010  |  Alloc:       1000        1000
7
EOF:         128100       10    |  EOF:         128010       10    |  EOF:         1000         10
8
Vor Trunc:   128100     128010  |  Vor Trunc:   128010     128010  |  Vor Trunc:   128010     128010
9
Nach Trunc:  128010     128010  |  Nach Trunc:  128010     128010  |  Nach Trunc:  128010     128010
10
Zeit:           ca. 1500ms      |  Zeit:           ca. 1500ms      |  Zeit:           ca. 900ms
11
                                |                                  |
12
2. Durchlauf:                   |  2. Durchlauf:                   |  2. Durchlauf:
13
             size:     pointer: |               size:     pointer: |               size:     pointer:
14
Start:       128010       10    |  Start:       128010       10    |  Start:       128010       10
15
Alloc:       128100     128010  |  Alloc:       256010     256010  |  Alloc:       128010      1000
16
EOF:         128100     128010  |  EOF:         256010     128010  |  EOF:         128010     128010
17
Vor Trunc:   256010     256010  |  Vor Trunc:   256010     256010  |  Vor Trunc:   256010     256010
18
Nach Trunc:  256010     256010  |  Nach Trunc:  256010     256010  |  Nach Trunc:  256010     256010
19
Zeit:           ca. 900ms       |  Zeit:           ca. 1500ms      |  Zeit:           ca. 900ms

Beobachtung:
*Selbst mit dem Erweitern über f_lseek komme ich nur auf ca. 139kB/s, 
verglichen mit deinen 250kB/s ist das nach wie vor langsam.

*Obwohl die zugewiesene Größe eigentlich ausreichen müsste 
(128100/128010 beim ersten Durchlauf), schreibt die Karte in diesen 
Fällen langsam. Wenn sie aber augenscheinlich zu klein ist, wird der 
Schreibvorgang schneller.

Hat jemand eine Idee, wieso sich die Karte so verhält?
Benutze ich die f_lseek Funktion falsch? Falls ja, könnte jemand ein 
Beispiel geben wie man sie richtig nutzt?

@Falk Brunner (falk)
Könntest du bitte näher ausführen, eventuell mit einem kleinen Beispiel 
zum Ablauf/groben Programmaufbau, wie du deine Schreibraten erreicht 
hast? Wäre nett, danke.

von Falk B. (falk)


Lesenswert?

@Michael K. (natulo)

>Ich werde auch mal einen pinkompatiblen AVR mit mehr SRAM raussuchen,
>wobei es da ja eh nur den 644PA mit 4kB oder den 1284P mit 16kB gibt.

Tu das. 16kb sind in diesem Kontext RIESIG!

>Wenn ich das richtig rauslese, benutzt du da generell Pre-Allocation mit
>f_lseek und hast bei den "mit CLMT" Werten zusätzlich FF_USE_FASTSEEK 1
>benutzt? (http://elm-chan.org/fsw/ff/doc/lseek.html letztes Beispiel)

Ja.

>Der Unterschied zwischen Fastseek 1 und 0 ist in deinem Beispiel ja
>nicht so groß.

Was wohl daran lag, daß die Karte neu und leer war ;-) Erstell mal 
einfach so eine fragmentierte SD-Karte 8-0

>*Selbst mit dem Erweitern über f_lseek komme ich nur auf ca. 139kB/s,
>verglichen mit deinen 250kB/s ist das nach wie vor langsam.

Läuft dein SPI WIRKLICH mit F_CPU/2? Hast du das mal gemessen? Nicht daß 
du aus Versehen den falschen Takt konfiguriert hast.

>Hat jemand eine Idee, wieso sich die Karte so verhält?
>Benutze ich die f_lseek Funktion falsch? Falls ja, könnte jemand ein
>Beispiel geben wie man sie richtig nutzt?

Hmm, ehrlich gesagt verstehe ich dein Testprogramm nicht so ganz.

>f_lseek(&file1,f_size(&file1));  // Pointer zum Ende der Datei

Das kann nicht stimmen, denn f_size(&file1) ist zu dem Zeitpunkt NULL, 
denn es wurde ja bisher noch nichts reingeschrieben und nach dem Öffnen 
ist die Dateigröße Null.

>Könntest du bitte näher ausführen, eventuell mit einem kleinen Beispiel
>zum Ablauf/groben Programmaufbau, wie du deine Schreibraten erreicht
>hast? Wäre nett, danke.

Hmm, eigentlich nicht viel, ich hab mich ganz simpel am Beispiel vom 
Meister Chan orientiert.

http://elm-chan.org/fsw/ff/doc/lseek.html

Man muss natürlich beachten, daß PRE_SIZE ein #define ist, welches die 
Größe der Datei definiert

#define PRE_SIZE 128100

>xprintf(PSTR("lseek expand %d\n"),f_lseek(&file1,1000) ); // Verschiebe 
>Filepointer und erweiter die Datei, Wert wird im Test verändert

Hier legst du nur 1000 Byte in der Datei an, schreibst dann aber 128k. 
Das wird so nix. Man muss vorher die GESAMTE Dateilänge reservieren.

von Falk B. (falk)


Angehängte Dateien:

Lesenswert?

Ich hab noch den Quelltext vom damaligen SD-Card Test gefunden.

von Volker S. (vloki)


Angehängte Dateien:

Lesenswert?

Falk B. schrieb:
> Das wird so nix. Man muss vorher die GESAMTE Dateilänge reservieren.

Ist schon etwas angestaubt und trägt auch nicht unbedingt was zur Lösung 
des zu-wenig-RAM-Speicher-Problems bei, aber wenn ich die zu füllende 
Datei mit f_lseek() oder f_expand() erweitere, dann wird das Schreiben 
bei mir sogar langsamer. Ich hänge mal zwei entsprechende Screenshots 
an. Das obere (hellblaue) Signal zeigt den /CS des SPI, darunter kommt 
SDO und ganz unten SDI des µC.

Das Schreiben selbst, scheint bei Preallocation schneller einzusetzen, 
aber danach kommt irgendwie noch was anderes.
Das (zusätzliche) geht so lange, bis die voreingestellte Dateilänge 
erreicht ist. Dann läuft es wieder so, wie wenn die Datei nicht 
künstlich erweitert wurde.

Irgendjemand eine Idee was da passiert? (wie schon erwähnt, bin SDcard 
Anfänger ;-)
(((FatFS ist nicht die neueste Version, sondern R0.12c)))

Code ist eine einfache Schleife:
1
#define BUFFER_SIZE 352
2
...
3
        for(unsigned long i = 0; i<1000000; i++){
4
            fileResult = f_write(&file, &buffer[0][0], BUFFER_SIZE, &bytesWritten);
5
            if(mGET_BTN()){
6
                while(mGET_BTN()){;}
7
                break;
8
            }
9
        }

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

@Volker SK (vloki)

>Datei mit f_lseek() oder f_expand() erweitere, dann wird das Schreiben
>bei mir sogar langsamer.

Sehr merkwürdig. Hast du vielleicht eine komische Konfiguration beim 
FatFS eingestellt? Oder ist die SD-Karte "exotisch"? Fragmentiert? Hast 
du mal einen neue, frisch formatierte probiert?

von Jim M. (turboj)


Lesenswert?

> #define BUFFER_SIZE 352

GANZ BLÖDE IDEE.

Wenn man kein Vielfaches von 512 Byte schreibt, muss er den Block vorher 
einlesen.

Bei Vielfachen von 512 fällt das Lesen weg, das ist viel schneller.

Wer richtig schnell schreiben will nimmt das Multi-Block-Write Kommando. 
Dann muss man aber bei FatFS auf Innereien (Blockaddresse) zurück 
greifen, und mehrstufiges Schreiben (z.B. jeweils 1 Block pro 
Funktionsaufruf)  braucht auch etwas Hinschmalz, weil nicht 
out-of-the-box vorhanden.

Out-of-the-box gibt es nur das Multi-Block-write mit großem Puffer, das 
kann man als Vorlage verwenden.

von Volker S. (vloki)


Lesenswert?

Jim M. schrieb:
>> #define BUFFER_SIZE 352
>
> GANZ BLÖDE IDEE.
>
> Wenn man kein Vielfaches von 512 Byte schreibt, muss er den Block vorher
> einlesen.

Das habe ich bisher nicht beobachten können. Mir schien , ohne f_sync() 
wird einfach erst auf die Karte geschrieben, wenn 512 Bytes zum 
Schreiben da sind.
(oder wenn das File geschlossen wird)

Wo kann man das in meinen Screenshots erkennen, dass da viel gelesen 
würde?

von Volker S. (vloki)


Lesenswert?

Falk B. schrieb:
> Sehr merkwürdig. Hast du vielleicht eine komische Konfiguration beim
> FatFS eingestellt? Oder ist die SD-Karte "exotisch"? Fragmentiert? Hast
> du mal einen neue, frisch formatierte probiert?

Mehrere neue Karten, verschiedener Hersteller, oder auch frisch 
formatiert. Ich sehe da keinen großen Unterschied.
(Intenso, Kingston, Samsung, Platinum, Philips, und Transcend, von 
2..32GB)

Ich hatte ja anfangs die Hoffnung eine Karte,mit möglichst kurzen 
"Denkpausen" zu finden, damit ich mit meinem "kleinen" RAM auskomme. 
(keine gefunden ;-)

Wenn ich wieder etwas mehr Zeit finde, werde ich mir mal ein hier noch 
rum liegendes PIC32 Board mit Kartenslot und das MCHP "Harmony" Zeugs 
anschauen. Ausreichend RAM, zum Überbrücken der Denkpausen, sollte dann 
jedenfalls nicht mehr das Problem sein...

<edit> Auch die neuere FatFS v0.13 könnte/sollte ich noch testen. Habe 
allerdings noch nicht genauer nachgeschaut, ob man die einfach so 
austauschen kann.

: Bearbeitet durch User
von Volker S. (vloki)


Angehängte Dateien:

Lesenswert?

Falk B. schrieb:
> @Volker SK (vloki)
>
>>Datei mit f_lseek() oder f_expand() erweitere, dann wird das Schreiben
>>bei mir sogar langsamer.
>
> Sehr merkwürdig. Hast du vielleicht eine komische Konfiguration beim
> FatFS eingestellt?

Ach ja Konfiguration hatte ich vergessen. Kurzform:
1
/*---------------------------------------------------------------------------/
2
/  FatFs - FAT file system module configuration file
3
/---------------------------------------------------------------------------*/
4
5
#define FF_FS_READONLY  0
6
7
#define FF_FS_MINIMIZE  2
8
9
#define  FF_USE_STRFUNC  0
10
11
#define FF_USE_FIND    0
12
13
#define  FF_USE_MKFS    0
14
15
#define  FF_USE_FASTSEEK  1
16
17
#define  FF_USE_EXPAND    1
18
19
#define FF_USE_CHMOD    0
20
21
#define FF_USE_LABEL    0
22
23
#define  FF_USE_FORWARD  0
24
25
#define FF_CODE_PAGE  850
26
27
#define  FF_USE_LFN  0
28
#define  FF_MAX_LFN  255
29
30
#define  FF_LFN_UNICODE  0
31
32
#define FF_STRF_ENCODE  3
33
34
#define FF_FS_RPATH  0
35
36
#define FF_VOLUMES  1
37
38
#define FF_STR_VOLUME_ID  0
39
#define FF_VOLUME_STRS  "RAM","NAND","CF","SD","SD2","USB","USB2","USB3"
40
41
#define  FF_MULTI_PARTITION  0
42
43
#define  FF_MIN_SS    512
44
#define  FF_MAX_SS    512
45
46
#define  FF_USE_TRIM  0
47
48
#define FF_FS_NOFSINFO  0
49
50
#define  FF_FS_TINY  0
51
52
#define FF_FS_EXFAT  0
53
54
#define FF_FS_NORTC  1
55
#define FF_NORTC_MON  1
56
#define FF_NORTC_MDAY  1
57
#define FF_NORTC_YEAR  2018
58
59
#define  FF_FS_LOCK  0
60
61
#define FF_FS_REENTRANT  0
62
#define FF_FS_TIMEOUT    1000
63
#define  FF_SYNC_t      HANDLE

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

@Jim Meba (turboj)

>> #define BUFFER_SIZE 352

>GANZ BLÖDE IDEE.

Nö. Nur ungewöhnlich.

>Wenn man kein Vielfaches von 512 Byte schreibt, muss er den Block vorher
>einlesen.

Nö, nur wenn man FatFs auf die Minimalkonfiguration eingestellt hat. Im 
Normalfall hat FatFS einen 512 Byte Sektorpuffer. Da muss rein gar 
nichts doppelt gelesen werden.

>Wer richtig schnell schreiben will nimmt das Multi-Block-Write Kommando.

Das macht FatFS schon selber, wenn möglich.

>Dann muss man aber bei FatFS auf Innereien (Blockaddresse) zurück
>greifen, und mehrstufiges Schreiben (z.B. jeweils 1 Block pro
>Funktionsaufruf)  braucht auch etwas Hinschmalz, weil nicht
>out-of-the-box vorhanden.

Wirklich? Hab ich anders in Erinnerung.

von Falk B. (falk)


Lesenswert?

Ich würde mal

#define FF_FS_MINIMIZE  0

nutzen. Sollte eigentlich egal sein, aber manchmal ist es komisch.
Der Rest paßt.

von Volker S. (vloki)


Lesenswert?

Falk B. schrieb:
> Ich würde mal
>
> #define FF_FS_MINIMIZE  0
>
> nutzen.

Kein Unterschied. Nicht mal im Speicherverbrauch.
Unbenutzte Funktionen scheint der Linker selber weg zu lassen.

von Falk B. (falk)


Lesenswert?

>Code ist eine einfache Schleife:
>#define BUFFER_SIZE 352
>...
>        for(unsigned long i = 0; i<1000000; i++){
>            fileResult = f_write(&file, &buffer[0][0], BUFFER_SIZE, 
&bytesWritten);
>            if(mGET_BTN()){
>                while(mGET_BTN()){;}
>                break;
>            }
>        }

Was soll dieses mGET_BTN() dort drin?

Zeig mal deinen VOLLSTÄNDIGEN Code.

von Volker S. (vloki)


Lesenswert?

Das ist nur die Abfrage eines "Buttons". Wenn ich da drauf drücke, 
möchte ich, dass die ansonsten doch sehr lang laufende Schleife 
abgebrochen wird.
1
void __init(void);
2
void newFile(void);
3
4
FATFS fatFs;
5
FIL file;
6
UINT bytesWritten, bytesRead;
7
FRESULT fileResult;
8
9
unsigned char   buffer[NR_BUFFERS][BUFFER_SIZE];
10
unsigned char * ptrBuffer;
11
unsigned short  idxData;
12
13
14
/*
15
Main application
16
 */
17
void main(void) {
18
    __init();
19
    
20
    while(1){
21
        while(!mGET_BTN()){;}
22
        while(mGET_BTN()){;}
23
        newFile();
24
        if(fileResult != FR_OK){
25
            mLED_RD_ON();
26
            continue; //TODO(sheeg): Error-handling
27
        }
28
        __delay_ms(100 );
29
        mLED_GN_ON();  mLED_RD_OFF();
30
31
#if defined(WRITESPEED)
32
        for(unsigned long i = 0; i<1000000; i++){
33
            fileResult = f_write(&file, &buffer[0][0], BUFFER_SIZE, &bytesWritten);
34
            if(mGET_BTN()){
35
                while(mGET_BTN()){;}
36
                break;
37
            }
38
        }
39
#else
40
...
41
//        f_truncate(&file);
42
#endif
43
        f_close(&file);
44
        f_mount(0, "", 0);
45
        mLED_GN_OFF();
46
    }//while(1)
47
}
48
49
50
void newFile(void)
51
{
52
    unsigned char filename[] = "MESS_000.dat";
53
54
    fileResult = f_mount(&fatFs, "", 1);
55
    if(fileResult != FR_OK) return;
56
57
//read a header for new file
58
    fileResult = f_open(&file, "header.txt", FA_READ);
59
    if(fileResult != FR_OK) return;
60
61
    fileResult = f_read(&file, &buffer[0][0], BUFFER_SIZE, &bytesRead);
62
    f_close(&file);
63
    if(fileResult != FR_OK) return;
64
    bytesRead = ((bytesRead + 15)/16)*16;    // ensure 16byte alignement
65
    buffer[0][bytesRead] = 0x0D;
66
    
67
//create a new destination file (first find existing ones...)
68
    while(fileResult == FR_OK){
69
        fileResult = f_open(&file, filename, FA_OPEN_EXISTING);
70
        if(fileResult == FR_OK){
71
            f_close(&file);
72
            if(++filename[7] > '9'){
73
                filename[7] = '0';
74
                if(++filename[6] > '9'){
75
                    filename[6] = '0';
76
                    if(++filename[5] > '9'){
77
                        filename[5] = '0';
78
    }   }   }   }   }
79
    if(fileResult == FR_NO_FILE){
80
        fileResult = f_open(&file, filename, FA_WRITE | FA_OPEN_ALWAYS);
81
    }
82
//TEST    
83
    if(PORTAbits.RA1 == 0){
84
        fileResult = f_lseek(&file, 10485760); // 100MB 104857600
85
        fileResult = f_lseek(&file, 0);
86
    }
87
//TEST
88
    else if(PORTAbits.RA2 == 0){
89
        fileResult = f_expand(&file, 5048576, 1);
90
    }
91
    
92
//write header to new file
93
    if (fileResult == FR_OK) {
94
        fileResult = f_write(&file, &buffer[0][0], bytesRead, &bytesWritten);
95
    }
96
//force write to card
97
    if (fileResult == FR_OK) f_sync(&file);
98
    
99
    INIT_BUFFER_0();
100
    return;
101
}

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

Hmm, der Quelltext sieht OK aus.

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.