Forum: Compiler & IDEs FAT Directory scrollen


von ruepel (Gast)


Lesenswert?

Servus,

der RAM von meinem ATMega1284p bereitet mir allmählich Sorgen, beim 
derzeitigen Projekt zeigt mir avrgcc knapp 70% Ramverbrauch an.

Das ist auch kein Wunder, es hängt eine SD-Karte dran und eine 
halbseidene Lösung für das Scrollen durch ein Verzeichnis.

Hardware-Setup ist im Groben ein DOG-Display, ein Joystick, und eine 
SD-Karte. Das DOG (128x64) frißt 1kB RAM-Speicher, weil ich auch 
Listings ausgebe (-> nach oben scrolle).

Für das Inhaltsverzeichnis habe ich eine maximale Dateinamenlänge von 32 
Zeichen reserviert (LFN). Jeder Dateiname im jeweiligen Verzeichnis wird 
zudem im RAM gespeichert; ich benutze kein malloc, reserviere also den 
Speicher vorab (Einstellung max. 250 Dateinamen).
Macht 250*32 Byte = 8kByte, die für das Verzeichnis draufgehen.


Vorteil: Man kann nach vorwärts UND rückwärts durch das Verzeichnis 
scrollen - mit guter Geschwindigkeit.

Prinzipiell ist FAT konstruktionsbedingt nicht geeignet, rückwärts zu 
scrollen, da der Datenzeiger zwar nach vorwärts den nächsten Cluster 
findet, jedoch kein entsprechender Eintrag für den vorhergehenden 
Cluster existiert (was eigentlich ziemlich dämlich ist, ein paar Bytes 
mehr spielen doch kaum eine Rolle?).


Frage: Wie kann man das Scrollen nach oben innerhalb eines 
Verzeichnisses besser lösen?
Man kann zwar jedes Mal beim nach-oben-scrollen (also in Richtung erster 
Eintrag) das FAT-Dir resetten und wieder von vorne einlesen, aber das 
geht mir etwas zu langsam. Auch malloc will ich nicht gern verwenden, da 
es Verzeichnisse gibt, die tatsächlich nahe an 200 Einträge kommen.
Ist da was anderes machbar? Danke im voraus für Meinungen...

Gruß Klaus

: Verschoben durch User
von Svenska (Gast)


Lesenswert?

Du könntest seitenweise scrollen, dann brauchst du nur die paar Einträge 
speicher, die du gleichzeitig siehst.

Oder du speicherst immer 4 oder 8 solcher Seiten gleichzeitig. Wenn du 
dann scrollst, kannst du die Anzeige sofort vornehmen und im Hintergrund 
den Cache in größeren Blöcken aktualisieren.

von D. V. (mazze69)


Lesenswert?

ruepel schrieb:
> der RAM von meinem ATMega1284p bereitet mir allmählich Sorgen, beim
> derzeitigen Projekt zeigt mir avrgcc knapp 70% Ramverbrauch an.

Sorry, aber das impliziert auch mir zu dieser fortgeschrittenen Stunde 
den Hinweis auf: einen µC mit zumindest größerem Speicher als die 16k 
des mega1284. Möglicherweise benötigst du im weiteren Verlauf noch mehr 
(temporären) Speicher.

Bevor man einen passenden µC auswählt, macht man sich Gedanken über die 
Resourcen, die man für ein Projekt benötigt und wählt dann den passenden 
Käfer aus.

Den von dir benötigten Speicher kannst du (z.B. mit alten Cache-RAMs von 
486er Boards realisieren, falls du bei den 8-Bittern von Atmel bleiben 
willst, dann aber solltest du zu den AVR-Typen greifen, die eben 
external RAM zulassen).

von ruepel (Gast)


Lesenswert?

Svenska schrieb:
> Oder du speicherst immer 4 oder 8 solcher Seiten gleichzeitig. Wenn du
> dann scrollst, kannst du die Anzeige sofort vornehmen und im Hintergrund
> den Cache in größeren Blöcken aktualisieren.
Das wäre eine Möglichkeit. Wenn man an eine Blockgrenze stößt, muß der 
Hintergrunddienst dann schon erledigt sein. Das wäre machbar und hätte 
auch den Vorteil, daß man beliebig (also was FAT32 hergibt) viele Seiten 
= Einträge haben kann.

D. V. schrieb:
> Bevor man einen passenden µC auswählt, macht man sich Gedanken über die
> Resourcen, die man für ein Projekt benötigt und wählt dann den passenden
> Käfer aus.
Sagen wir so: An SD-Karten schätze ich die Anbindung mit nur 4 IOs, mit 
der man auch in kleinen Anwendungen schnell Speicher anhängen kann. 
Einen XMEM-AVR zu verwenden, wäre da öfter ein ziemlicher Overkill 
(braucht 19 IOs allein für die Anbindung des Speichers). Aus ähnlichem 
Grund möchte ich keine größeren Prozessoren von vornherein bei 
SD-Karten-Anwendungen vorsehen.



Vor längerer Zeit hatte ich mir mal die FAT16/32 von Roland Riegel 
deswegen genauer angeschaut. Dabei bin ich auf eine Variable vom Typ 
cluster_t gestoßen, die auch den Start jedes neuen Verzeichniseintrags 
enthält (soweit ich das durchdrungen habe). Diese ist ein uint64_t-Typ. 
Wenn man nur diesen ins Ram ablegt, hätte man bei beliebig langen 
Verzeichnisnamen (LFN) nur 8 Byte pro Eintrag abzulegen, womit man sehr 
gut leben kann. Schnell wäre es auch.
Soweit ich weiß, konnte ich diese Startcluster auch im RAM speichern, 
aber beim Zurückschreiben ging was schief, was auf die Schnelle nicht zu 
lösen war.

Wahrscheinlich muß ich mich doch in FAT bzw. die Lib noch besser 
einarbeiten sfz
Ich fand dazu bisher wenig hier oder im Netz zu dem rückwärts-Scrollen; 
obwohl man spätestens bei MP3 auf dieses Problem stößt. Wahrscheinlich 
resetten die meisten das Directory.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

ruepel schrieb:
> Prinzipiell ist FAT konstruktionsbedingt nicht geeignet, rückwärts zu
> scrollen, da der Datenzeiger zwar nach vorwärts den nächsten Cluster
> findet, jedoch kein entsprechender Eintrag für den vorhergehenden
> Cluster existiert (was eigentlich ziemlich dämlich ist, ein paar Bytes
> mehr spielen doch kaum eine Rolle?).

Du könntest in Deiner "lies den nächsten Cluster"-Routine Dir einen 
Zeiger auf den letzten davor gelesenen aufheben ... oder auch auf die 
letzten n davor gelesenen. Bei der üblichen Clustergröße von 4 kiB 
würdest Du mit 16 Clusterzeigern, also 64 Byte Speicherbedarf, immerhin 
über 2000 8.3-Verzeichniseinträge erreichen können.

 Das verbraucht zwar etwas RAM, aber es dürfte immer noch erheblich 
schneller sein, als das Verzeichnis ständig neu von vorne einzulesen.

von c-hater (Gast)


Lesenswert?

ruepel schrieb:

> Prinzipiell ist FAT konstruktionsbedingt nicht geeignet, rückwärts zu
> scrollen, da der Datenzeiger zwar nach vorwärts den nächsten Cluster
> findet, jedoch kein entsprechender Eintrag für den vorhergehenden
> Cluster existiert (was eigentlich ziemlich dämlich ist, ein paar Bytes
> mehr spielen doch kaum eine Rolle?).
>
> Frage: Wie kann man das Scrollen nach oben innerhalb eines
> Verzeichnisses besser lösen?

Oben, wo du dich über die Schwächen von FAT ausläßt, beschreibst du doch 
implizit bereits den sinnvollsten Ansatz zur Lösung des Problems.

Nämlich: du cachest nicht das Directory selber im RAM, sondern nur eine 
lineare Liste der Directory-Cluster. Die kannst du dann problemlos in 
beiden Richtungen durchlaufen und genauso problemlos die eigentlichen 
Directory-Inhalte vom Medium nachladen.

von MaWin (Gast)


Lesenswert?

> Frage: Wie kann man das Scrollen nach oben innerhalb eines
> Verzeichnisses besser lösen?

Die Frage wäre wohl eher: Wie kann man insgesant besser programmieren
unter den Anforderungen eines uC, also wenig Speicher.

Du hast eine SD-Karte, also ein schnelles Medium.

Du kannst eine Routine bauen
SkipXFilenamesAndReturnNFilenames(X,N,buffer,sizeof(buffer))
die JEDESMAL aufgerufen wird, wenn du nach oben (N--) oder
unten (N++) scrollst.

Und das DOG hat einen eigneen Graphikspeicher, das braucht nicht im RAM 
dupliziert werden.

Ja, die Reaktion des Programms wird langsamer, und es kann lästig 
langsam werden wenn hunderte von Dateien im Verzeichnis sind. Dann kann 
man immer noch beschleunigen, z.B. durch gemerkete Startposition von 
Datei 96.

von Falk B. (falk)


Lesenswert?

Man kann auch anstatt der Dateinamen einfach einen Filepointer auf die 
Datei speichern, der braucht nur 4 Byte. Oder eine Art HASH-Tabelle. Die 
muss man nur einmal aufbauen, ggf. sortierten. Damit braucht man 
deutlich weniger Speicher und ist dennoch schnell beim scrollen.

von ruepel (Gast)


Lesenswert?

Rufus Τ. Firefly schrieb:

> Du könntest in Deiner "lies den nächsten Cluster"-Routine Dir einen
> Zeiger auf den letzten davor gelesenen aufheben ...
Manchmal sieht man den Baum vor lauter Wäldern nicht - genauso wird es 
gemacht. Danke, das ist genau das, wonach ich gesucht habe.

c-hater schrieb:
> Oben, wo du dich über die Schwächen von FAT ausläßt, beschreibst du doch
> implizit bereits den sinnvollsten Ansatz zur Lösung des Problems.
>
> Nämlich: du cachest nicht das Directory selber im RAM, sondern nur eine
> lineare Liste der Directory-Cluster. Die kannst du dann problemlos in
> beiden Richtungen durchlaufen und genauso problemlos die eigentlichen
> Directory-Inhalte vom Medium nachladen.
Ja, manchmal klemmt's ein bißchen im Gehörn ;)
Rufus' hat eine saubere Lösung aufgezeigt. Gefällt mir.

MaWin schrieb:
>> Frage: Wie kann man das Scrollen nach oben innerhalb eines
>> Verzeichnisses besser lösen?
>
> Die Frage wäre wohl eher: Wie kann man insgesant besser programmieren
> unter den Anforderungen eines uC, also wenig Speicher.
Ist schon klar, und daß man effizienter programmieren kann, zeigen ja 
die Ideen hier. Die Methode, das komplette Verzeichnis im RAM 
mitzuschleifen, war eine kurzfristige Lösung (ein MP3-Player braucht ja 
sonst nix an RAM). Daß die blöd ist, war insoweit egal, als daß ich 
schon froh war, daß die Lib von Roland einwandfrei lief. Ich mach nur ab 
und zu was (nichtberuflich) mit MCs, und finde manche meiner Routinen 
zweckmäßig, über die ein E-Freak die Hände über dem Kopf 
zusammenschlagen würde ;)

> Und das DOG hat einen eigneen Graphikspeicher, das braucht nicht im RAM
> dupliziert werden.
Für Scrollen fiel mir auf die Schnelle nichts anderes ein, weil man die 
Pixel nicht zurücklesen kann. Inzwischen kann ich die DOGs aber eh nicht 
mehr leiden, seit ich auf TFTs umgestiegen bin. Hätte ich schon viel 
früher machen sollen, die sind in mancher Hinsicht viel einfacher zu 
handeln.

Falk Brunner schrieb:
> Man kann auch anstatt der Dateinamen einfach einen Filepointer auf
> die
> Datei speichern, der braucht nur 4 Byte. Oder eine Art HASH-Tabelle. Die
> muss man nur einmal aufbauen, ggf. sortierten. Damit braucht man
> deutlich weniger Speicher und ist dennoch schnell beim scrollen.
Hallo Falk, ich schau mir das mal in der Lib von Roland an, wo der 
Filepointer gesetzt wird. Wäre evtl. verlockend, nur 4 Byte pro Datei zu 
verschwenden. Muß allerdings gestehen, daß das für mich hartes Brot ist, 
durch die Lib durchzusteigen; denn wenn ich den Filepointer habe, müßte 
ich mir wahrscheinlich eine separate Routine basteln, um die LFN zu 
bauen. Rolands Lib ist das sehr kompakt - ändert man nur bißchen was am 
inneren Bereich, hagelt es Compilerwarnings g - trotzdem gefällt sie 
mir sehr gut.

Vielen Dank euch! Ich fang erstmal mir Rufus' Strategie an. Melde mich 
dann Jahre später wieder ;)

von Svenska (Gast)


Lesenswert?

Hallo,

>> Und das DOG hat einen eigneen Graphikspeicher, das braucht nicht im RAM
>> dupliziert werden.
> Für Scrollen fiel mir auf die Schnelle nichts anderes ein, weil man die
> Pixel nicht zurücklesen kann.

Statt des Bildes kannst du auch den Inhalt scrollen. Irgendwo werden die 
anzuzeigenden Dateinamen schon im Speicher stehen - die Pixel kannst du 
daraus beim Scrollen jederzeit neu erzeugen, wegspeichern musst du die 
eher nicht.

> Inzwischen kann ich die DOGs aber eh nicht
> mehr leiden, seit ich auf TFTs umgestiegen bin. Hätte ich schon viel
> früher machen sollen, die sind in mancher Hinsicht viel einfacher zu
> handeln.

Ich habe vor TFTs eher Respekt, weil die mit 'nem AVR so schlecht 
ansprechbar sind... oder hast du welche mit Controller?

Gruß,
Svenska

von ruepel (Gast)


Lesenswert?

Svenska schrieb:
> Hallo,
>
>>> Und das DOG hat einen eigneen Graphikspeicher, das braucht nicht im RAM
>>> dupliziert werden.
>> Für Scrollen fiel mir auf die Schnelle nichts anderes ein, weil man die
>> Pixel nicht zurücklesen kann.
>
> Statt des Bildes kannst du auch den Inhalt scrollen. Irgendwo werden die
> anzuzeigenden Dateinamen schon im Speicher stehen - die Pixel kannst du
> daraus beim Scrollen jederzeit neu erzeugen, wegspeichern musst du die
> eher nicht.

Ah ok, ja die Scrollfunktion ist mir mal über den Weg gelaufen. Der 
1kB-Speicher ist halt auch nicht schlecht, wenn man Grafik über Text 
malt mit XOR oder so.

> Ich habe vor TFTs eher Respekt, weil die mit 'nem AVR so schlecht
> ansprechbar sind... oder hast du welche mit Controller?
Nur mit Controller, SSD1963. Ich hatte aber trotz Controller Respekt vor 
denen - jetzt nicht mehr. Wobei der AVR bei den 800x480-Typen schon 
ziemlich alt aussieht. Trotzdem sind die gegenüber den monochromen so 
hübsch anzuschauen, kann mich gar nicht losreißen ;)
Ohne Controller muß man ja alles geschickt verschachteln, das würde ich 
nicht schaffen.
Ich habe gerade mein erstes "Blinky"-Projekt mit dem STM32F4 
abgeschlossen. Die Installation ging gut und der blinkt jetzt vor sich 
hin; jetzt hoffe ich, in den nächsten Monaten dazuzulernen.

von J. -. (Gast)


Lesenswert?

Hallo ruepel g,
diese Scrollerei ist schon schon eine Sache für sich. Besonders 
rückwärts ;)
In der ff.c von Chan muß man die Routine 'f_readdir' um einen 
Indexpointer erweitern, der entweder den aktuellen Index zurückliefert 
oder diesen schreibt.
In Chans FatFS wird jeder Directory-Eintrag - also ein File mit 
entsprechenden FILINFO-members - mit einem Index vom Typ UINT (16bit) 
abgespeichert.
Damit kann man alle FILINFO-Parameter auslesen. Auch die langen 
Filenamen ;)
 Man braucht beim Scrollen selbst nur einen Puffer, der die Indizes 
gespeichert hat (2 Byte * Zahl der Dateien im Directory).

von J. -. (Gast)


Lesenswert?

D. V. schrieb:
> Sorry, aber das impliziert auch mir zu dieser fortgeschrittenen Stunde
> den Hinweis auf: einen µC mit zumindest größerem Speicher als die 16k
> des mega1284. Möglicherweise benötigst du im weiteren Verlauf noch mehr
> (temporären) Speicher.
>
> Bevor man einen passenden µC auswählt, macht man sich Gedanken über die
> Resourcen, die man für ein Projekt benötigt und wählt dann den passenden
> Käfer aus.
>
> Den von dir benötigten Speicher kannst du (z.B. mit alten Cache-RAMs von
> 486er Boards realisieren, falls du bei den 8-Bittern von Atmel bleiben
> willst, dann aber solltest du zu den AVR-Typen greifen, die eben
> external RAM zulassen).
"Sorry"? Ah, das ist scheint's ein Zeiger auf arroganten, nicht 
tragfähigen Bullshit.
Ebenfalls sorry für den zu fortgeschrittener Stunde erfolgten Hinweis. 
Aber man erkennt ja so die Luftblasenmacheniker allmählich.
Die Fat-Up-Down-Story hier erfordet jedenfalls kein solche 
Gemächtgewedel, sondern ist recht uptodate (ohne alte 486-er Geschichten 
etc., die einen Haufen Lötarbeit für RAM erfordern würden).
Man kann sie anders lösen als mit Luftblasen und arrogantem Geschwätz.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Jürgen S. schrieb:
> "Sorry"? Ah, das ist scheint's ein Zeiger auf arroganten, nicht
> tragfähigen Bullshit.

Du hast schon mitbekommen, von wann dieser Beitrag war? Genau: Vor einem 
halben Jahr.

von J. -. (Gast)


Lesenswert?

Rufus Τ. Firefly schrieb:
> Jürgen S. schrieb:
>> "Sorry"? Ah, das ist scheint's ein Zeiger auf arroganten, nicht
>> tragfähigen Bullshit.
>
> Du hast schon mitbekommen, von wann dieser Beitrag war? Genau: Vor einem
> halben Jahr.

Hast Du schon mitbekommen, daß Roland Riegels Dateisystem in der 
öffentlichen Diskussion bei den kleinen embedded-Filesystemen leider 
nicht das Rennen macht, sondern FatFS?

Und nenne mir nun bitte einen einzigen Beitrag, der AUFWÄRTSSCROLLEN in 
der  FAT/FAT32 ohne exzessiven Speicherverbrauch zum Thema hat.

Genau. Dieser hier.

Und deswegen mag ich auch kein Geschwafel, denn auch ein STM32F407VGT6 
wäre mit den Speicheranforderungen des Threadopeners überfordert.

Es geht auch anders, mit einem mittleren ATMega - nichts anderes wollte 
ich damit ausdrücken.
Auch wenn der Beitrag schon älter ist.

von J. -. (Gast)


Lesenswert?

To keep it simple, und ohne Bluffs.
Hier die modifizierte f_readdir von Chan:

1
FRESULT f_readdir (
2
  DIR* dp,      // Pointer to the open directory object
3
  FILINFO* fno,    // Pointer to file information to return
4
  UINT *idx      //pointer to the dir.index, if 0x8000->get index, else set index
5
)
6
{
7
  FRESULT res;
8
  DEF_NAMEBUF;
9
10
11
  res = validate(dp);            // Check validity of the object
12
  if (res == FR_OK) {
13
// KJS ----------------------------------------------------------------------------
14
    if (!(*idx & GETENTRY))  res = dir_sdi(dp, *idx);//set actual index for readout
15
// /KJS ----------------------------------------------------------------------------
16
    if (!fno) {
17
      res = dir_sdi(dp, 0);      // Rewind the directory object
18
    } else {
19
      INIT_BUF(*dp);
20
// KJS ----------------------------------------------------------------------------
21
      if (*idx & GETENTRY) *idx = dp->index;  //get actual index before incrementing
22
// /KJS ----------------------------------------------------------------------------
23
      res = dir_read(dp, 0);      // Read an item
24
      if (res == FR_NO_FILE) {    // Reached end of directory
25
        dp->sect = 0;
26
        res = FR_OK;
27
      }
28
      if (res == FR_OK) {        // A valid entry is found
29
        get_fileinfo(dp, fno);    // Get the object information
30
        res = dir_next(dp, 0);    // Increment index for next
31
        if (res == FR_NO_FILE) {
32
          dp->sect = 0;
33
          res = FR_OK;
34
        }
35
      }
36
      FREE_BUF();
37
    }
38
  }
39
40
  LEAVE_FF(dp->fs, res);
41
}

Es sind die 2 Zeilen Änderung by KJS (=myname), plus zusätzlichem 
Indexpointer in den Funktionsargumenten notwendig.
Wobei gilt :#define GETENTRY 0x8000
Bedingt durch die Indizierung können sowieso nicht mehr als 0x8000 
Verzeichniseinträge bei FatFS incl. LFN erfaßt werden.

f_readdir wird sonst von keiner anderen Funktion aufgerufen, außer vom 
User selbst.


Mit 0x8000 gesetzt will man den Index holen, ohne selbigen setzen.
Diesen Index (16 bit) muß man sich in einem Array merken, es gibt einen 
Index pro File im Directory.

Ruft man f_readdir mit *idx (ohne 0x8000) auf, setzt man den Index.
Mit FILINFO holt man sich dann die zum File gehörigen Infos ab.

Geht ganz einfach, wirklich ganz ohne blödes Geschwätz bezüglich mehr 
Speicher und genau geplanten Projektanforderungen etc..



P.s. Chan selbst holt sich in seinen Examples zum LPC den kompletten 
FILINFO zu jedem File ab -> Speicherwasting(?).

Ganz abgesehen davon, daß die Einträge bis dahin noch nichtmal sortiert 
sind ;)

von Takao K. (takao_k) Benutzerseite


Lesenswert?

Nekroposting.

Im uebrigen, serielles RAM IC verwenden, kein Drama.

von J. -. (Gast)


Lesenswert?

> Im uebrigen, serielles RAM IC verwenden, kein Drama.
Wow, tolle Idee.
Wahrscheinlich selbst auch ein begnadeter Speicherwaster bei bestehenden 
Projekten.

von Nik D. (y2kicn)


Lesenswert?

Die SD Karten dürften längst schnell genug sein, um die Filenamen 
jeweils bei Bedarf erneut auszulesen. Ich habe vor Ewigkeiten meinen 
ersten MP3 Player mit einem ATmega16 (afaik 1KB Ram) gebaut und habe mir 
da jeweils einen Cluster + Sektorpointer + Index das erste Element des 
Filelistings im Ram gespeichert. Daraus lassen sich (jedenfalls bei 8.3 
Filenames) die Start- und Endadressen sehr schnell berechnen und die 
entsprechenden Eintraege auffinden.
Auch hilft es, sich eine Linkedlist der naechsten 2 oder 3 Cluster des 
aktuellen Directories zu speichern, damit man die Adressen deren direkt 
ausrechnen kann, ohen immer wieder die FAT auslesen zu muessen.

Ich habe damit locker durch ein Vereichnis mit LFN File descriptors 
durchscrollen koennen. (Zugegebenermassen allerdings mit einem up/down 
button, nicht mit Mausrad oder ähnlichem, da wäre definitiv eine höhere 
Performance von Nöten)

von J. -. (Gast)


Lesenswert?

Nik D. schrieb:
> Die SD Karten dürften längst schnell genug sein, um die Filenamen
> jeweils bei Bedarf erneut auszulesen.
Endlich jemand, der es versteht (abgesehen von den ganz frühen 
Einträgen) :p
Mit dem Index s.o. aus der FatFS ist man mit 2Byte pro File im Directory 
dabei, das ist wahrscheinlich noch etwas effizienter (wenn das Directory 
offen ist)
Weil man in den meisten Anwendungen erst ein bestimmtes Directory 
öffnet, um zu sehen, was drin ist, ist das praktisch. Wenn man bei 
Öffnen schon die Indizes mitnotiert, ist das Scrollen danach ganz 
einfach. Und eben auch schnell - ganz ohne zusätzliches Ram oder 
Ähnliches.

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.