Kampis Elektroecke

AVR mit einer SD-Karte erweitern – Teil 2

Im ersten Teil des AVR SD-Karten Tutorials habe ich gezeigt, wie die SD-Karte mit einem Mikrocontroller verbunden wird und wie die Kommunikation mit einer SD-Karte aufgebaut ist. Anschließend habe ich gezeigt, wie die Karte initialisiert wird, sodass der Mikrocontroller mit ihr kommunizieren kann. Dieser Teil des Tutorials soll sich nun etwas tiefer mit der Kommunikation der SD-Karte beschäftigen. Es wird gezeigt, wie die Hardwareinformationen der SD-Karte gelesen, bzw. wie einzelne Blöcke oder Sektoren der Karte gelesen und beschrieben werden können, wodurch es dem Mikrocontroller ermöglichen werden soll, auf das Dateisystem und die Dateien, die auf der SD-Karte gespeichert sind, zuzugreifen und diese zu bearbeiten.

Die Realisierung des Zugriffs auf das Dateisystem wird im nächsten Teil vorgestellt. Ausgangsbasis dafür ist die FatFs-Bibliothek von ElmChan. Diese Bibliothek stellt eine allgemeine Schnittstelle für ein FAT-Dateisystem mit Support für verschiedene Plattformen bereit. Alles was der Anwender entwickeln muss, ist die Schnittstelle zum Speichermedium (hier die SD-Karte).

Bevor Daten von der SD-Karte ausgelesen werden können, muss zuerst ein entsprechendes Kommando (wie z. B. CMD9) an die Karte gesendet werden. Die SD-Karte bestätigt dieses Kommando mit einer R1-Antwort. Anschließend beginnt die Karte mit der Übertragung der angeforderten Daten.

Das gesendete Datenpaket besteht aus einem Token, einen bis zu 2048 Byte langen Datenblock und einer 2 Byte langen CRC.

Token Beschreibung
0xFC Token für CMD17/18/24
0xFD Token für CMD25
0xFE Stop Token für CMD25

Aus dem gezeigten Ablauf ergibt sich die folgende Empfangsroutine:

Über diese Funktion kann nun z. B. das STATUS-Register der SD-Karte ausgelesen werden. Dazu wird das Kommando ACMD13 an die Karte gesendet. Die SD-Karte antwortet auf diesen Befehl mit einem 64 Byte großen Datenpaket, welches die Statusinformationen der SD-Karte beinhaltet.

Aus Gründen der Übersicht habe ich die Statusinformationen in einer Struktur hinterlegt, sodass die empfangenen Daten direkt identifiziert werden können.

Es ergibt sich damit der folgende Aufruf:

In den Statusinformationen ist z. B. der Kartentyp oder die Klasse der SD-Karte kodiert.

SpeedClass Beschreibung
0x00 Klasse 0
0x01 Klasse 1
0x02 Klasse 2
0x03 Klasse 3

Auf ähnliche Weise kann nun z. B. das CID oder das CSD-Register der SD-Karte ausgelesen werden.

Über das CID-Register kann die Karte identifiziert werden. Das Register beinhaltet u. a. eine Hersteller- und eine OEM-ID, sowie die Seriennummer der SD-Karte.

Damit das Dateisystem Daten auf die SD-Karte schreiben, bzw. Daten von der SD-Karte lesen kann, werden Funktionen benötigt, die einzelne Datenblöcke lesen, bzw. schreiben. Die SD-Karte bietet dafür die folgenden Befehle an:

Befehl Funktion
CMD17 Lese einzelnen Block
CMD18 Lese n Blöcke
CMD24 Schreibe einzelnen Block
CMD25 Schreibe n Blöcke

Ein Lese- bzw. Schreibzugriff auf einen einzelnen Datenblock sieht dabei folgendermaßen aus.

Für einen lesenden Datenzugriff kann die bereits vorgestellte ReadBlock-Funktion verwendet werden. Die Schreibfunktion sieht dann folgenermaßen aus:

Zusammen mit der SendCommand-Funktion werden aus der ReadBlock– und WriteBlock-Funktion die vollständigen Funktionen zum Lesen, bzw. Schreiben eines Datenblocks.

Mit der SD_ReadDataBlock-Funktion können einzelne Sektoren, wie z. B. der erste Sektor, der SD-Karte ausgelesen werden.

Bei einem FAT-Dateisystem handelt es sich bei dem ersten Sektor immer um den Bootsektor für das Betriebssystem. Die gelesenen Daten können überprüft werden, indem die letzten beiden Stellen des Arrays betrachtet werden. Bei einem gültigen FAT-Dateisystem sind dort die Werte 0x55 und 0xAA gespeichert.


Info:

Bei einem FAT-Dateisystem ist der erste Sektor des Speichermediums immer 512 Byte groß, egal was für ein Speichermedium genutzt wird. Dieser Sektor wird vom BIOS eingelesen und der Code des Bootloaders ausgeführt, der dann das Betriebssystem lädt.


Für große Datenmengen ist es sinnvoll, sämtliche Daten in einem einzigen Durchlauf zu übertragen. Speziell für die Übertragung von mehreren Datenblöcken besitzen SD-Karten die Befehle CMD18 und CMD25, mit denen mehrere Blöcke gelesen oder geschrieben werden können.

Einmal eingeleitet sendet, bzw. empfängt die SD-Karte so lange Daten bis der Host über ein Stop-Signal die Übertragung beendet. Dies geschieht bei einem Lesevorgang über das Kommando CMD12 und bei einem Schreibvorgang durch ein Stop-Token (siehe Tabelle) in einem leeren Datenpaket.

Zum Senden, bzw. Empfangen der einzelnen Datenblöcke können die bereits erstellten Funktionen genutzt werden, sodass die Funktionen zum Senden, bzw. Empfangen von mehreren Datenblöcken leicht programmiert werden können.

Die grundlegenden Schreib- und Lesefunktionen sind nun fertig programmiert. Wir sind damit in der Lage Daten zwischen der SD-Karte und dem Mikrocontroller auszutauschen und die Daten permanent auf der SD-Karte zu speichern.

Im nächsten Teil soll es dann darum gehen, wie die Unterstützung eines FAT-Dateisystems umgesetzt wird. Das komplette Beispielprojekt kann in meinem GitLab-Repository heruntergeladen werden.

Viel Spaß beim Ausprobieren und vielen Grüße
Daniel

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

Offset Length Description
0x00 3 bytes Part of the bootstrap program.
0x03 8 bytes Optional manufacturer description.
0x0b 2 bytes Number of bytes per block (almost always 512).
0x0d 1 byte Number of blocks per allocation unit.
0x0e 2 bytes Number of reserved blocks. This is the number of blocks on the disk that are not actually part of the file system; in most cases this is exactly 1, being the allowance for the boot block.
0x10 1 byte Number of File Allocation Tables.
0x11 2 bytes Number of root directory entries (including unused ones).
0x13 2 bytes Total number of blocks in the entire disk. If the disk size is larger than 65535 blocks (and thus will not fit in these two bytes), this value is set to zero, and the true size is stored at offset 0x20.
0x15 1 byte Media Descriptor. This is rarely used, but still exists. .
0x16 2 bytes The number of blocks occupied by one copy of the File Allocation Table.
0x18 2 bytes The number of blocks per track. This information is present primarily for the use of the bootstrap program, and need not concern us further here.
0x1a 2 bytes The number of heads (disk surfaces). This information is present primarily for the use of the bootstrap program, and need not concern us further here.
0x1c 4 bytes The number of hidden blocks. The use of this is largely historical, and it is nearly always set to 0; thus it can be ignored.
0x20 4 bytes Total number of blocks in the entire disk (see also offset 0x13).
0x24 2 bytes Physical drive number. This information is present primarily for the use of the bootstrap program, and need not concern us further here.
0x26 1 byte Extended Boot Record Signature This information is present primarily for the use of the bootstrap program, and need not concern us further here.
0x27 4 bytes Volume Serial Number. Unique number used for identification of a particular disk.
0x2b 11 bytes Volume Label. This is a string of characters for human-readable identification of the disk (padded with spaces if shorter); it is selected when the disk is formatted.
0x36 8 bytes File system identifier (padded at the end with spaces if shorter).
0x3e 0x1c0 bytes The remainder of the bootstrap program.
0x1fe 2 bytes Boot block 'signature' (0x55 followed by 0xaa).