AVR FAT32

Aus der Mikrocontroller.net Artikelsammlung, mit Beiträgen verschiedener Autoren (siehe Versionsgeschichte)
Wechseln zu: Navigation, Suche

Dies ist eine freie FAT 16/32 Bibliothek. Die Bibliothek ist modular und besteht aus folgenden Modulen:

File-Modul
Bietet high-level Datei-Operationen, wie man sie von Dateizugriffen kennt, z. B. ffopen/ffclose um eine Datei zu öffnen/schließen.
FAT16/32-Modul
Bietet die grundlegenden Funktionen für den FAT-Zugriff und die Initialisierung der FAT. Es dient als Middleware zwischen low-level Karten-Zugriff und high-level Datei-Operationen. Der größte Teil der des FAT-Protokolls ist dort implementiert.
MMC/SD-Modul
Dafür zuständig, die Kommunikation mit MMC- und SD-Karten zu managen. Wie z. B. Initialisierung der Karte oder low-level Routinen zum Schreiben auf die Karte. Es ist das einzige hardwareabhängige Modul.

Es gibt die Möglichkeit über Makros in der config.h den Umfang der Lib zu bestimmen, dies wirkt sich auch stark auf die Codegröße aus.

Schichtmodell der FAT-Implementierung

Die Module sind C99 Standard-konform.

Der Status

  • Aktuelle Version: AVR Version 0.6.4
  • Bekannte Probleme: Bei software SPI läuft die Initialisierung nicht mit 400 KHz, das gibt bei einigen Karten Probleme. Bei der Aktuellen Version ist Multi-block ungetestet.
  • Problem melden, oder sonstige Anfrage: hier Thread.

Funktionalität

Code einbinden

Die Module werden über die *.h Dateien bekannt gemacht. Zudem müssen noch die dazugehörigen *.c Dateien eingebunden werden. Es gibt mehrere Möglichkeiten dies zu tun. Einmal im Makefile im Bereich der "c source files" oder bei IDEs über einen Dialog der das Gleiche macht.

Die Funktionalität

In der Datei config.h gibt es Möglichkeiten die Bibliothek zu konfigurieren.
Folgende Funktionen werden unterstützt (FAT16/32):

  • Lesen, Schreiben und Überschreiben von Dateien.
  • Vor- und Rückspulen in Dateien.
  • Anlegen von Ordnern
  • Wechseln von Verzeichnissen
  • Löschen von Dateien und Ordnern, bei Ordnern Rekursiv.
  • Ermitteln der freien Bytes auf der Karte
  • Schnelleres Schreiben und Lesen durch Multi-Block Operationen.
  • Kartenunterstützung für MMC/SD/SDHC.
  • Falls eine RTC vorhanden ist, kann bei den Dateioperationen die Zeitstempel Funktionalität genutzt werden.
  • Anbindung der Karte über Software SPI oder Hardware SPI

Die Schalter

Hier ein Auszug aus der config.h mit den wichtigsten Parametern:

#define MMC_SMALL_FILE_SYSTEM 	TRUE
#define MMC_WRITE		TRUE	
#define MMC_OVER_WRITE 		FALSE
#define MMC_MULTI_BLOCK 	FALSE
#define MMC_SDHC_SUPPORT	TRUE
#define MMC_TIME_STAMP 		FALSE
 
#define MMC_MAX_SPEED 		TRUE

#define MMC_SOFT_SPI 		FALSE

#define MMC_MAX_CLUSTERS_IN_ROW 256

Bedeutung der Schalter

MMC_SMALL_FILE_SYSTEM
TRUE, oder FALSE. Bestimmt den Funktionsumfang der Lib. Es fallen folgende Funktionalitäten raus: ffcd(), fat_str(), ffls(), ffcdLower(), ffmkdir() und ffrm() der Teil mit Ordnern rekursiv löschen. (FALSE = Komplette Unterstützung)
MMC_WRITE
TRUE oder FALSE. Bestimmt, ob Schreibunterstützung einkompiliert wird oder nicht: TRUE = Write an
MMC_OVER_WRITE
TRUE oder FALSE. Bestimmt, ob ffwrite() mit Überschreiben-Funktionalität compiliert wird oder nicht. Überschreiben von Dateien ist nicht so performant. Siehe auch: Interne Technik. TRUE = Dateien überschreiben
MMC_MULTI_BLOCK
TRUE oder FALSE. Legt fest, ob mit MultiBlock Read/Write Unterstützung compiliert wird oder nicht. Geht nur, wenn OVER_WRITE FALSE ist! TRUE = MultiBlock Operation
MMC_MAX_SPEED
TRUE oder FALSE. Legt fest, ob nach der Initialisierung der MMC/SD-Karte mit maximalem Speed geschrieben/gelesen wird oder nicht. Zum Testen, wenn man nicht sicher ist, ob die Hardware die maximale Geschwindigkeit mitmacht, empfiehlt sich FALSE als Wert.
MMC_MAX_CLUSTERS_IN_ROW
1-500. Gibt an, wie viele Cluster, leer oder verkettet die zusammenhängen, gesucht werden sollen. Hier muss man den Overhead des Suchens abwägen, gegen die Zeit, die z. B. benötigt wird, 500 Cluster in der FAT zu verketten. Siehe auch: Interne Technik
MMC_SDHC_SUPPORT
TRUE oder FALSE. Legt fest, ob mit SDHC-Unterstützung compiliert wird oder ohne. Der SD Standard 2.0 wird damit unterstützt, also Karten bis 32 GB Größe.
MMC_TIME_STAMP
TRUE oder FALSE. Legt fest, ob die Zeitstempel-Unterstützung mit compiliert wird oder nicht. Wenn TRUE, dann wird das Erstelldatum und die Erstellzeit eingetragen. Bei weiteren Schreibzugriffen auf die Datei wird dann auch das Zugriffsdatum und die Zugriffszeit eingetragen. Es müssen dafür auch 2 Funktionen in der fat.c mit Code gefüllt werden, damit diese das Datum und die Zeit liefern.
MMC_SOFT_SPI
TRUE oder FALSE. Legt fest, ob mit Software-SPI Unterstützung compiliert wird oder nicht. Wenn TRUE, muss in der mmc.h noch eingetragen werden, welche Pins als SPI-Interface benutzt werden sollen. Es werden nur Pins des selben Ports unterstützt. Also z. B. nur Pins von Port B.

Die C-Funktionen

Das Modul FILE bietet in der Standard-Konfiguration folgende für den Nutzer interessante Funktionen:

unsigned char ffread(void)
void          ffwrite(unsigned char c)
void          ffwrites(unsigned char *s)
unsigned char ffopen(unsigned char name[])
unsigned char ffclose(void)
void          ffseek(unsigned long int offset)
unsigned char ffcd(unsigned char name[])
void          ffls(fptr uputs_ptr)
unsigned char ffcdLower(void)
unsigned char ffrm(unsigned char name[])
unsigned char ffmkdir(unsigned char name[])

Das Modul MMC/SD bietet für den Nutzer direkt nur eine interessante Funktion:

unsigned char mmc_init(void)

Das Modul FAT bietet für den Nutzer direkt nur zwei interessante Funktionen:

unsigned char fat_loadFatData(void)
unsigned long long int fat_getFreeBytes(void)

Für die Anwendungen der Funktionen gibt es Beispiele, Kommentare und Dokumentation in den Sourcen. Ein einfaches Beispiel gibt es im Abschnitt Einfaches Code-Beispiel.

Alle Funktionen, die mit FAT-Namen in Zusammenhang stehen (Datei- und Ordner-Namen), müssen folgende Konvention erfüllen:

  • Die FAT-Namenskonvention ist immer 8.3, das heißt, ein Datei-Name mit maximal 8 Zeichen und eine Datei-Endung maximal 3 Zeichen lang.
  • Ein Dateiname "test.txt" muss in "TEST    TXT" gewandelt werden! Noch ein Beispiel: "MAIN    C  " = "main.c"

Nur vFAT unterstützt lange Datei-Namen.

Interne Technik

In der Regel wird das Schicht-Modell eingehalten. Das heißt, jedes Modul nutzt nur die Funktionen aus der Schicht darunter. Es gibt allerdings Ausnahmen aus Gründen der Code-Größe. Beispiel: ffwrite nutzt direkt die Funktion mmc_write_sector(sector) aus dem Modul mmc.c. Aufgrund der Namenskonvention ist aber leicht zu erkennen, woher die Funktion kommt.

Im Modul fat gibt es "Daten-Ketten". Die Aufgaben sind atomar aufgeteilt. Zum Lesen von Daten sind beispielsweise die Funktionalitäten, Lesen eines Sektors, Auswerten der Information und weitere Entscheidung (nächsten Sektor Laden usw.) nötig. Die Idee dabei ist, die Funktionen der atomaren Aufgabe nach zu implementieren.

Beispielkette:

fat_loadSector -> fat_loadRowOfSector -> fat_loadFileDataFromCluster -> fat_loadFileDataFromDir
Funktionsübersicht der FAT-Imple­men­tierung. Viele der Funktionen enden in Schreib­opera­tionen, weil das Diagramm mit der Option MMC_OVER_WRITE TRUE erstellt wurde.

Diese Kette wird beispielsweise beim Öffnen einer Datei durchlaufen. Um die Datei zu öffnen, muss man wissen, ob es die Datei im aktuellen Verzeichnis gibt. Um das herauszufinden, muss man den ersten Sektor des Verzeichnisses laden. Dann muss man die Reihen (immer 32 Byte am Stück), also Datei/Ordner-Einträge des Sektors prüfen. Da ein Cluster aus verschiedenen Sektoren bestehen kann, müssen diese auch geprüft werden. Als letzte logische Einheit müssen jetzt noch die verketteten Cluster geprüft werden, also das ganze Verzeichnis. Ist diese Kette so durchlaufen, weiß man, ob die Datei da ist oder nicht.

Lesen

Beim Lesen einer Datei wird in der FAT nach verketteten Clustern gesucht. Bei einer unfragmentierten FAT liegen im Idealfall alle Cluster in einer Reihe. Dies wird ausgenutzt:

Es wird der erste Cluster der Datei gesucht und solange gesucht, bis MAX_CLUSTERS_IN_ROW am Stück gefunden wurden oder ein Abbruch der Kette erkannt wird. Wird ein Abbruch erkannt, wird das bekannte Teilstück der kompletten Kette gelesen, danach wird das nächste Teilstück gesucht, bis die ganze Kette durchlaufen wurde.

Bei einer unfragmentierten FAT kann man oft die ganze Datei lesen ohne einen weiteren FAT-Lookup. Das ist sehr effizient.

Schreiben

Beim Schreiben einer Datei wird in der FAT nach leeren Clustern gesucht. Bei einer unfragmentierten FAT liegen diese im Idealfall alle nebeneinander. Das wird ausgenutzt:

  1. Es werden ab Cluster Nr. 2, MAX_CLUSTERS_IN_ROW am Stück gesucht, oder so viele, wie es freie am Stück gibt.
  2. Diese (also die Sektoren die mit diesen Clusternummern in Zusammenhang stehen) werden beschrieben. Sind genügend freie Cluster für die Datei-Daten bekannt, werden diese verkettet und man ist fertig.
  3. Sind nicht genügend freie Cluster bekannt, wird die erste Teilkette verkettet und die letzte Cluster-Nummer dieser Kette gesichert (file.lastCluster). Danach werden neue Cluster gesucht.
  4. Sind immer noch nicht genügend freie Cluster bekannt, werden die beiden Teilketten verkettet (wo die alte aufhört weiß man durch file.lastCluster). Weiter bei 2.

Bei beiden Methoden kann es zu größerem Overhead kommen, wenn die FAT fragmentiert ist. Im Extremfall ist immer nur ein Cluster am Stück frei bzw. verkettet.

Die Richtlinien

Allgemein:

Es ist immer darauf zu achten, dass es sich bei MMC/SD-Karten um Flash-Speicher handelt, dieser ist NICHT unendlich oft beschreibbar. Die Schreiben/Anhängen- und Löschen- Funktionalitäten sollten so selten wie möglich benutzt werden!
Eine SD Karte besitzt einen eigenen Controller in der Karte, der auf den eigentlichen Flash-Speicher schreibt. So können die logischen von den physikalischen Sektoren getrennt werden (Wear Leveling). Also gibt es nicht so viele Schreib Vorgänge auf dem gleichen Sektor, weil der Karten Controller den logischen Sektor auf einen anderen physikalischen schreibt.

Siehe Auch: Wikipedia SD Karten , und MMC- und SD-Karten.

Richtlinien stable-mmc-0.5.x:

Wenn eine Datei zum schreiben geöffnet ist, aber gerade keine Daten zum schreiben vorliegen, bringt es keinen Vorteil (Strom oder Geschwindigkeit) die Datei zu schließen, wenn später noch Daten geschrieben werden sollen. Bei jedem ffclose, wird der Datei Eintrag aktualisiert, also dieser Sektor geschrieben (möglicherweise zusätzlich sogar ein FAT-Sektor).

Oftmaliges anlegen und löschen einer Datei schreibt immer die selben (logischen) Sektoren. Besser nicht löschen und einfach neu anlegen, mit anderem Namen, oder überschreiben, da dabei die schon verketteten FAT-Sektoren nicht nochmals beschrieben werden, sondern nur die Daten Sektoren überschrieben werden.

Beispiel-Beschaltung

Schaltplan

Schaltplan

MMC/SD-Karten brauchen 3,3 Volt. Wenn der Controller mit 5 Volt arbeitet, muss man die Spannungspegel anpassen. Arbeitet der Controller auch mit 3,3 Volt, kann man alle Leitungen direkt mit der Karte verbinden.

Als Beispiel für die mögliche Frequenz des hier verwendeten HC4050 zur Pegelwandlung: bei +125 °C und VCC von 2 V wird das Propagationdelay mit Max 130 ns angegeben. Das macht ungefähr 7,6 MHz maximal mögliche Frequenz. Bis +85 °C und VCC von 2 V geht es bis 9,5 MHz. Da aber hier 3,3 Volt anliegen, wird er in allen Bereichen etwas schneller sein. Siehe Datenblatt für mehr Infos oder Pegelwandler.

Benutzt wird der SPI-Port des Controllers (bis auf Slave Select, hier PB1). Es wäre aber auch Software-basiertes SPI möglich, siehe hierfür auch Abschnitt Die Funktionalität.

Die Kondensatoren C1 und C2 (100nF) dienen nur zum Entstören (möglichst nah an ICs)und sollten an jedes digitale IC in einer Schaltung. Die MMC/SD-Leiste kann eine richtige Halterung sein. Was auch sehr gut passt ist ein Stück ISA-Bus Buchse eines alten Mainboards (nur MMC-Karten). Die Abstände passen gut. Einfach vom Board flexen und dann kleine Kabel an den Stecker löten (da wo man unten jetzt das Metall sieht)

Das HC4050 IC ist ein "high to low" Level-Shifter. Es sorgt dafür, dass die 3 abgehenden Signale des µC auf 3.3 Volt gebracht werden. Das abgehende Signal der Karte zum Controller wird auch so als logisch high erkannt.

Achtung
Die Anschlussart mit einem normalen Pegelwandler wie im Bild rechts birgt jedoch die Gefahr der Zerstörung der SD Karte. Nämlich genau dann, wenn einmal (z.B. falsche Portdeklaration) versehentlich die MISO-Leitung an 5V liegt oder wenn noch ein weiterer 5V-Teilnehmer am SPI-Bus hängt und 5V Signale einspeist. Dann liegen am Datenausgang der SD-Karte nämlich plötzlich 5V an, und das ist fatal. Besser und professioneller sind sog. birektionale Pegelwandler, die alle Datensignale in beide Richtungen von 5V ↔ 3,3V transferieren. Eine Bezugsquelle für ein fertiges Kartenmodul inkl. birektionalem Pegelwandler ist z.B. Display3000. Tipp: ganz an das Ende scrollen, dort gibt es ein paar anschauliche Informationen.

Pinbelegung MMC/SD/SDHC

Pinout MMC-Karte
Pinout SD/SDHC-Karte
Pin-Nr.
Karte
Pin
Karte
Funktion Pin-Nr.
µC
Pin
µC
Funktion
1 CS Chip Select 15 PB1 Cusom Slave Select
2 DI Data In 17 PB3 (MOSI) Master Out, Slave In
3 GND
4 VCC
5 CLK Clock 19 PB5 (SCK) Clock
6 GND
7 DO Data Out 18 PB4 (MISO) Master In, Slave Out

Der Code

Sourcen

  • Hier immer die Aktuellste Version mit Informationen dazu FAT16/32.
  • Achtung! Nur noch alte Version im SVN.

Sourcecode mit Linux Makefile und als AvrStudio-Projekt auf: Alte Versionen SVN AVR-Fat32

Einfaches Code-Beispiel

Ein Beispiel um in eine Datei zu schreiben/anhängen und wieder zu lesen:

#include <stdlib.h>

// In mmc_config.h werden alle nötigen Konfigurationen vorgenommen
#include "mmc_config.h"
#include "file.h"
#include "fat.h"

// Hardwareabhängig
#include "mmc.h"

// Hardwareabhängig, es kann auch eine eigene eingebunden werden !		
#include "uart.h"


//**************************************************************************

int main (void)
{
	// Uart-Initialisierung zu Ausgabe von Daten.
	uinit();

	uputs ("\nBoot");

	// Versuch Karte zu Initialisieren, bis es klappt.
	// Unbedingt so, weil die Initialisierung nicht immer
	// auf Anhieb klappt.
	while (FALSE == mmc_init())
	{
		nop();
	}

	uputs ("...");

	// Fat initialisieren. Nur wenn das klappt sind weitere
	// Aktionen sinnvoll, sonst endet das Programm.
	if (!fat_loadFatData())
  	    return -1;

	// Wenn auf dem terminal "Boot... OK" zu lesen ist, ist Init OK.
	// Jetzt kann man Schreiben/Anhängen/Lesen.
	uputs ("Ok\n");

	// Dateinamen muessen in diesem Format sein
	// Man beachte die Größe des Arrays und die Großbuchstaben!
	unsigned char file_name[] = "test.txt";

	// String zum in die Datei schreiben.
	char str[] = "Hallo Datei!";

	// Wenn Datei nicht existiert
	if (FALSE == ffileExsists(file_name))
	{
            ffopen(file_name, 'c'); //Datei existiert nicht, also anlegen
	    // Schreibt String auf Karte.
	    // Nur richtige Strings koennen mit ffwrites geschrieben werden.
	    ffwrites (str);

	    // Neue Zeile in der Datei.
            // Schreibt Zeilenumbruch in die Text Datei.
	    ffwrite ('\n');
	    // Für MS-Windows Terminal '\r' anhängen.
	    ffwrite ('\r');

	    // Schließt Datei.
	    ffclose();
	}

	// Datei existiert, also anhaengen !
	if (TRUE == ffileExsists(file_name))
	{
            ffopen (file_name, 'c');
	    // Spult bis zum Dateiende vor um anzuhaengen.
	    // Geht auch ohne Option MMC_OVER_WRITE
	    ffseek (file.length);

	    // Schreibt String.
	    ffwrites(str);

	    // Neue Zeile in der Datei.
            // Schreibt Zeilenumbruch in die Textdatei.
	    ffwrite ('\n');
	    // Für MS-Windows Terminal '\r' anhängen.
	    ffwrite ('\r');

	    // Schließt Datei.
	    ffclose();
	}

	// Datei existiert, also lesen.
	// Gerade angelegt und beschrieben, oder war schon vorhanden
	// und es wurde was angehaengt?
	if (TRUE == ffileExsists(file_name))
	{
            ffopen (file_name, 'r');
	    // Setzen einer Variable und dann runterzählen geht
	    // am schnellsten
	    unsigned long int seek = file.length;

	    // Lesen eines chars und Ausgabe des chars.
	    // Solange bis komplette Datei gelesen wurde.
	    do {
	        uputc (ffread());
	    } while (--seek);

	    ffclose();
	}

	return 0;
}

FAT 32 Grundlegendes

Grundlegende FAT32-Struktur

Ein Dateisystem des Typs FAT besteht aus mehreren Bereichen. FAT ist immer little Endian. Ein Dateisystem wie FAT32 ist ein Verwaltungsinstrument. Der FAT16/32 Standard beschreibt die Art und Weise wie die Daten Verwaltet werden.

Im Folgenden werden die Schritte, die nötig sind, um Daten aus dem System zu lesen und in das System zu schreiben, erläutert. Die Zahlenbeispiele und Bilder sollen ein praktisches Beispiel geben. Zum Verständnis empfiehlt es sich, das ganze Kapitel FAT32 durchzulesen.

  • Der erste Teil ist der Sektor 0 oder LBA. In dem Sektor stehen die nötigen Informationen, um mit dem Auslesen beginnen zu können.
  • Dann folgt die FAT selbst, also die Dateizuordnungstabelle.
  • Der dritte Teil ist der Daten-Bereich, in dem beispielsweise Dateieinträge/Ordnereinträge oder Daten stehen.

FAT bedeutet File Allocation Table (Dateizuordnungstabelle). Dateizuordnungstabelle beschreibt schon das Prinzip der FAT: Kern des Dateisystems ist eine einfach verkettete Liste, in der festgehalten wird, wo sich die Daten befinden: die Dateizuordnungstabelle. In dieser Tabelle werden Cluster verkettet. Diese verketteten Cluster können Dateien oder Ordner bilden. Dazu später mehr.

Der Unterschied zwischen FAT32 und FAT16 besteht darin, dass ein FAT16-Dateisystem noch einen speziellen Teil hat. Das Root-Dir ist nicht einfach ein weiterer Ordner wie bei FAT32, sondern ein festgelegter Teil, der Platz für 512 Einträge bietet. FAT-Einträge bei FAT16 sind nur 2 Byte groß. Der Rest ist gleich.

Sektor 0 oder LBA

Bild 1: Sektor 0 (512 Byte) bei einer unpartitionierten Karte

Um das Dateisystem lesen zu können, muss man wissen, wo was steht. Die Informationen sind:

  • Sektoren pro Cluster (rot)
  • Reservierte Sektoren nach Sektor 0 (orange)
  • Anzahl der FATs (gelb)
  • Wieviele Sektoren von einer FAT belegt werden (grün)
  • Der Root-Dir Cluster (blau)

Aus diesen Daten ergibt sich demnach:

  • 1. Sektor der 1. FAT ist Sektor 32 (Bild 2), also der erste Sektor nach der Anzahl der Reservierten (orange).
  • Der erste Daten Cluster ist 1003, weil die Anzahl der FATs 1 ist, also einmal die Sektoren, die von einer FAT belegt werden (grün) (00003cb=971) 971 + 32 (orange) = 1003.
  • Das Root-Dir ist Cluster 2, also Sektor 1003.
  • Die Anzahl der Sektoren pro Cluster ist 4 (2048 Bytes/Cluster).

Damit sind alle nötigen Daten vorhanden.

Man weiß jetzt, wo das Root-Dir beginnt. Ab da spannt sich der Verzeichnisbaum auf. So kann man alle Dateieinträge lesen. Es sind also alle Informationen, mit der man das Lesen einer Datei beginnen kann, bekannt, bzw. man weiß, wo man beim Schreiben einen Dateieintrag machen muss. Um eine Datei aber vollständig lesen zu können, muss man alle Cluster kennen, die zu der Datei gehören. Da kommt die FAT-Tabelle ins Spiel, von der man die 1. Sektornummer kennt. Dort stehen ab dem 1. Cluster der Datei, verkettet alle Folgecluster.

Bei FAT-Dateisystemen können mehrere Sektoren (512 Bytes) logisch zu Clustern zusammengeschlossen werden. Ein Cluster ist für eine Datei die kleinste Speichereinheit. Das hat zur Folge, dass eine 100 Byte große Datei eventuell 2048 Bytes im Dateisystem belegt. Die rote Zahl gibt an, wieviele Sektoren einen Cluster bilden. Der Datenbereich wird immer in Clustern angegeben, das macht die Umrechnung von Sektoren zu Clustern nötig. In diesem Fall ist Cluster 2 der absolute Sektor 1003.

Die FAT

Folgendes Bild zeigt den ersten FAT-Sektor (512 Bytes) eines FAT32 Dateisystems (nicht den ersten Sektor der Karte). Eingetragen sind: Root-Dir und zwei darin enthaltene Ordner sowie eine Datei mit 60.000 Bytes Größe.

Bild 2: 1. FAT-Sektor, hier Sektor 32

Die Einträge 0-7 sind immer reserviert (Quasi Cluster 0 und 1)! Also beginnt die FAT ab 8 (rot, Cluster Nummer 2). Der erste Eintrag (rot) ist 4 Byte groß und zu lesen von Stelle 8 zu Stelle 11 weil little Endian, also umgedrehte Wertigkeit. 8 niedrigste Wertigkeit, dann 9 eins höher, 10 noch eine höher und 11 höchste (dazu später noch ein Beispiel). Wie von Microsoft empfohlen ist der erste mögliche Cluster das Root-Dir (rot). Welches hier nur einen Cluster lang ist.

An den gelben Einträgen wird die einfach verkettete Liste der FAT deutlich. Der erste gelbe Eintrag steht an der Stelle des Clusters Nummer 5 und hat als Inhalt eine 6. Das bedeutet: Cluster 5 und 6 gehören zusammen. Würde an der Stelle des Clusters Nummer 5 ein FFFFFF0F stehen (little Endian) ist nur der Cluster 5 mit Daten belegt (wie bei rot, blau und grün). So tastet man sich vom ersten Cluster einer Datei bis zum Letzten Cluster einer Datei vor. Das ist der einzige Zweck der FAT (mit FAT ist hier die eigentliche Tabelle gemeint).

Woher bekommt man aber den 1. Cluster einer Datei?

Daten

Bild 3: Inhalt eines Ordners/Directories, hier Cluster 4

Ein Dateieintrag besteht aus 32 Bytes und liegt in einem Ordner/Dir im Datenbereich des Dateisystems ( 32 Bytes = eine Zeile).

Dies ist der 1. Cluster eines Ordners. Hier sieht man den "." bzw ".." Eintrag in Zeile 1 bzw. 2. Diese beiden Einträge sind in jedem ersten Cluster eines Ordners vorhanden. Außer im Root-Dir, von dort spannt sich der Verzeichnis Baum ja erst auf. Der "." Eintrag hat als 1. Cluster Eintrag den eigenen Cluster, hier 4. Der ".." Eintrag ist interessanter, weil er den 1. Cluster des Übergeordneten Ordners enthält, hier 3. Ist der Übergeordnete Ordner das Root-Dir, ist der 1. Cluster Eintrag 0. Diese beiden Einträge werden in der Lib genutzt um rekursiv zu löschen.

Die dritte Zeile des Bilds zeigt eine Datei namens TEST3.TXT (grün) mit 60.000 Bytes Größe (blau) und dem 1. Cluster 5,damit ist immer der 1. Daten Cluster gemeint (rot, Folgecluster siehe Bild 2). Speziell ist hier, dass die 4 Bytes des ersten Clusters aufgeteilt sind auf 2 unterschiedliche "Orte" im 32 Byte Eintrag (Wertigkeit: 26:27:20:21).

Um nun die Daten der Datei lesen zu können, muss man die Daten Cluster kennen. Der erste Daten Cluster steht im Datei Eintrag, hier Cluster 5. Für die Folgenden sieht man in der FAT nach. Dort stehen die Folgecluster der Datei. An der Stelle des ersten Clusters der Datei Cluster 5 steht eine 6, d.h. der nächste Cluster ist 6. In dieser Form ist die FAT verkettet. Wenn in der FAT der letzte Cluster erreicht ist, inhalt des Clusters ist FFFFFF0F, liest man den letzten Sektor bis man insgesammt die Größe der Datei (blau) gelesen hat und ist fertig.

Root Dir

Bild 4: Root des Dateisystems, hier Cluster 2

Die Sektornummer des Root-Dirs, bei FAT32, wird aus Sektor 0 oder dem LBA ausgelesen (siehe Bild 1). Zu Sehen ist hier in der ersten Zeile ein 32 Byte Eintrag eines Ordners. Zu erkennen ist dieser am Datei Attribut 0x10 an Stelle 11 (rot). Für den Ordnernamen ist das gleiche Feld da wie für einen Dateinamen (grün). Der 1. Cluster des Ordners ist 3 (blau, Inhalt des Ordners ist Bild 3). Bei Ordnern werden die Felder für die Größe genullt (orange). Das Root-Dir hat keinen "." bzw. ".." Eintrag. Der erste Cluster des Dirs ist ja über Sektor 0 bekannt und ab dem Root-Dir spannt sich der Verzeichnisbaum erst auf. Das Root-Dir bei FAT32 kann beliebig groß werden.

Zusammenfassung

Für Dateien und Ordner sind nur zwei Bereiche eines FAT32 Dateisystems wichtig, nämlich die FAT selbst und der Datenbereich. Eine Datei wird aufgesplittet, in einen Datei-Eintrag und in die Nutzdaten der Datei (alles im Datenbereich). Ein Ordner ist vom Eintrag im Dateisystem einer Datei sehr ähnlich. Wo bei einer Datei über den 1. Cluster eine Cluster-Chain für die eigentlichen Daten der Datei beginnt, wird bei einem Ordner so eine Cluster-Chain für weitere Datei/Ordner Einträge verkettet. Ein Ordner bietet pro Sektor maximal 16 Einträge (16*32=512).

Siehe auch

Weblinks

Weitere Projekte mit FAT MMC/SD Karten: