Nachdem meine Wetterstation ihren Teststatus absolviert hatte, war ich damit konfrontiert, wie ich am dümmsten eine Art Log-Funktion einrichten könnte. Schließlich ist es zwar schön, immer die aktuellen Daten zu sehen, interessanter wäre es aber, beispielsweise den Verlauf von Temperatur, Luftfeuchtigkeit und Druck von einer gesamten Woche vor sich sehen zu können. Eine Auswertung des Daten erfolgt selbstverständlich auf dem PC.
Wie man dem Datenblatt des von mir eingesetzten ATMega32 entnehmen kann, verfügt dieser über 1KByte EEPROM sowie 2KByte SRAM. Das Datenaufkommen eines Messpunktes kann beispielsweise mit 50Byte angesetzt werden, was auch immer darin dann enthalten sein soll. Somit wird schnell ersichtlich, dass die internen Speichermöglichkeiten des AVRs viel zu gering für diese Art von Aufgabenstellung sind. Im EEPROM könnte man bspw. nur 20 Messpunkte erfassen, dann wäre dieser voll. Für die grobe Auswertung des Wetters eines Tages wäre das vielleicht noch praktikabel, allerdings für größere zeitliche Dimensionen bzw. Auflösungen wohl eher nicht.

Nun genug der Vorrede, es galt, einen geeigneten Speicher zu finden. Hierbei kann man diese erstmal prinzipiell in der Art des Anschlusses unterscheiden. Es gibt serielle und parallele. Da parallele Speicher mit ihren diversen Daten- und Adressleitungen einfach nur Pins in Massen kosten, war von vornherein klar, dass ein serieller Speicher gefunden werden muss. Dieser sollte nach Möglichkeit einfach zu handhaben (Löten!) sein und nicht die Welt kosten, sowie eine wesentliche Speichererweiterung mit sich bringen. Gelandet bin ich letztendlich bei zwei Vertretern:

  1. Seriellen CMOS EEPROM Speichern, welche 5V kompatibel und über den I²C-Bus anzusprechen sind. Um das Kind beim Namen zu nennen: AT24C512. Hierbei handelt es sich um einen 512KBit großen Speicher. Das entspricht 64KByte, was also schon einmal eine ordentliche Speichererweiterung bedeuten würde. Es können hiervon aus später genannten Gründen maximal 4 Stück an einem Bus betrieben werden. Der Speicher ist im DIL-Package erhältlich. Es werden 100.000 Schreibzyklen garantiert.
  2. Der MMC, welche bereits aus diversen tragbaren Geräten bekannt sein sollte. Deren Speicher übersteigt im Normalfall den der EEPROM-Speicher um ein vielfaches, er liegt bereits im unteren bis oberen MByte-Bereich. Angesprochen werden kann diese Speicherform über das SPI.

Zusätzlich sei hier noch die Compact-Flash Karte erwähnt. Diese lässt sich relativ einfach ansteuern, solange man nicht anfängt, kleinere Dateisysteme (FAT16) zu implementieren. Dann wird es etwas komplizierter. Allerdings sind CF-Karten ausgesprochen pinhungrig. Man wird im Normalfall bei deren Ansteuerung nicht umher kommen, einen AVR zwischenzuschalten. Dieser bekommt dann per I²C o.ä. die zu speichernden Daten. Alternativ kann man auch einen größeren MC wie den ATMega128 verwenden, welcher viele Pins bereits stellt. Als Quelle für weitere Informationen kann ich nur auf Holger Klabundes Webseite verweisen.

Wie man sieht, können beide Speicherformen über bereits im AVR integrierte Busformen angesprochen werden, was mir diesmal das lästige implementieren des Busprotokolls erspart hat, wie es bei den diversen Sensoren nötig war. Die Kosten halten sich für beide jeweils im Rahmen, wobei was das Verhältnis Speichergröße/Euro angeht, die MMC klar vorne liegt.
Gerne hätte ich auch bspw. RAMs bzw. andere Flash-Speicher genutzt, doch habe ich diese auf Anhieb nicht in geeigneten Gehäusen und Speichergrößen sowie als serielle Bausteine gefunden. Wer SMD nicht als Problem ansieht, kann natürlich auf andere Formen ausweichen.
Gemeinsam ist beiden Speicherformen, dass sie seitenweise organisiert sind, in so genannten Pages. Beim AT24Cxxx liegt deren Größe bei 128Byte, bei den MMC im Normalfall bei 512Byte, eine Ausnahme macht hier bspw. die Data Flash Card von Atmel (AT45DCB008), diese bringt es auf 264Byte/Page. Ein Unterschied zwischen EEPROM und MMC liegt nun darin, dass bei EEPROM noch ein einzelnes Ansprechen von Bytes möglich ist, bei der MMC können nur noch komplette Seiten (pages) geschrieben bzw. gelesen werden. Somit muss als der AVR die Daten zunächst in einen Puffer schaufeln (bspw. ein Array im SRAM), um dann, wenn dieses voll ist, die Daten an den Speicher zu senden. Ein großes Problem sollte dies jedoch nicht darstellen. Das Auslesen würde analog erfolgen.

Die Erläuterung des EEPROMs kann in der Rubrik ICs & Co gefunden werden.

Die MMC

Für alle, die mehr Speicher benötigen, nun ein paar Infos zu MMC. Das diese nicht im DIL-Package verfügbar ist, sollte jedem klar sein. Wie man also deren frei liegende Kontakte mit der restlichen Schaltung verbindet, bleibt dem Bastelgeschick des Einzelnen überlassen.

Wichtige Vorzüge der MMC sind ihre kleine Bauform, die Speichergröße sowie der Fakt, dass sie über ein im AVR bereits in Hardware existierendes Interface angeschlossen werden kann, das SPI.
Als Nachteile seien der Spannungsbereich gängiger MMCs (2,7-3,6V) sowie die benötigten 4 Leitungen (MISO, MOSI, SCK, /CS) erwähnt.

Pin # Pin-Name Beschreibung
1 /CS Chip Select
2 SI Serieller Eingang (MOSI)
3 GND Masse
4 Vcc Betriebsspannung
5 SCK Taktleitung
6 NC -
7 SO Serieller Ausgang (MISO)

Wie man sieht, ist der eigentlich Anschluss an den MC auf dem Papier nicht unbedingt schwierig. Man sollte jedoch nicht die Spannungspegel vergessen. Für die Spannungsversorgung gibt es verschiedene Varianten. Die sauberste wäre die Verwendung eines Spannungsreglers für 3,3V. Man findet jedoch auch Varianten, bei denen einfach mittels einer Reihenschaltung von zwei oder drei Dioden die Spannung erzeugt wird (5V-2*0,7V=3,6V). Wie man die Pegel der Busleitungen anpasst, ist auch Sache des Anwenders. Man kann entweder speziell dafür vorgesehene Schaltkreise verwenden oder einfach einen Spannungsteiler. Dieser würde für /CS, SI und SCK benötigt werden. SO kann direkt an den MC angeschlossen werden, da dieser ab 2,0V H-Pegel erkennt. Das L-Pegel zwischen 0...0,4V definiert ist, ist hier unkritisch. Wem gänzlich unklar ist, was ein Spannungsteiler ist bzw. wie man einen solchen dimensioniert, den möchte ich hiermit auf elektronik-kompendium.de verweisen. In meinem Fall ändern sich die Pegel wie folgt:

U = 5V*(10K/(4,7K+10K)) = 3,4V
Also liegen an der MMC bei H-Pegel am AVR statt 5V nur noch 3,4V an. Wer einen 3,3V Spannungsregler verwendet, kann den unten dargestellten Spannungsteiler nicht mehr verwenden. Die Widerstände müssen dann anders dimensioniert werden. Vcc muss immer größer oder gleich der Signalspannung sein.

Die man die Pads der Karte mit der restlichen Schaltung verdrahtet, hängt vom jeweiligen Nutzer ab. Die Variante, die Pads zu löten, habe ich lieber nicht ausprobiert. Es gibt auch fertige MMC-Aufnehmer mit ausgeführten Kontakten zum Löten zu kaufen. Allerdings lassen einen hier im Normalfall die großen Elektronikversender im Stich. Eine Bastellösung ist es, einen alten ISA-Slot zu verwenden. Diesen kann man noch zurechtsägen. Das Rastermaß der Kontakte passt genau (2,54mm), aber man hat keinen Schutz gegen Verpolung oder ähnliches. Da das Ganze aber soundso relativ statischen Charakter haben sollte, habe ich die letztere Variante benutzt.

Eine bessere Variante hat mir Ulrich Radig, welcher auch mir als Ideengeber diente, bereit gestellt. Der komplette Schaltplan kann über den folgenden Link herunter geladen werden.

Schaltplan für MMC

Wie die exakte Kommunikation mit der MMC abzulaufen hat, kann vom Hersteller der MMC abhängen, auch wenn es eigentlich eine zentrale MMC-Spezifikation gibt. Ich stelle hier einfach den Sachverhalt dar, der bei mir funktioniert hat.

Zunächst muss man wissen, dass es zur Kommunikation mit MMCs prinzipiell zwei Wege gibt: den MMC Modus sowie den SPI Modus. Für uns ist nur letzterer interessant, weshalb ich keine weiteren Informationen zum MMC Modus geben möchte. Dessen Protokoll sollte man aber auch von Hand nachbilden können. Allerdings würde dann wahrscheinlich die Performance stark absinken, was den Vorteil seiner Nutzung egalisiert.

Aktiviert wird der SPI-Modus durch das gegen Masse ziehen von Pin 1 (/CS) sowie das Senden von CMD0. Es wird empfohlen, vorausgesetzt ist es nur diese eine Karte am SPI-Bus angeschlossen, Pin 1 während der gesamten Programmlaufzeit auch auf Masse zu lassen.

Die MMC verfügt über vier Register. Hiervon sind einem die folgenden drei noch im SPI-Modus zugänglich:

Name Größe (Bytes) Beschreibung
OCR 4 enthält zulässigen Spannungsbereich (nur lesbar)
CID 16 Karten ID (Identifier), wird bei Herstellung festgelegt (nur lesbar)
CSD 16 enthält Karten spezifische Daten (Arbeitsbedingungen usw.)

Wen die Interpretation des Inhaltes der Register interessiert, den verweise ich hiermit auf das Datenblatt sein MMC. Diese sind für die folgenden Betrachtungen relativ unwichtig, weshalb ich sie hier nicht weiter erörtern möchte.
In meinem Programm ist natürlich eine entsprechende Routine zum Auswerten der Register vorgesehen.

Um nach dem Einschalten der MMC klar zu machen, dass der SPI Modus genutzt werden soll, muss CMD0 an sie gesendet werden. Zunächst sollte man ihr jedoch 1ms Zeit zum "Erwachen" geben. Laut Empfehlung des Datenblattes kann man auch einfach mind. 74 Einsen senden, was ich für praktikabler halte. Doch was heißt CMD0. Das CMD für command steht, ist ja noch relativ klar. Man muss wissen, dass sämtliche Kommandos an die MMC aus 6 Bytes bestehen. D.h. es müssen insgesamt für einen Befehl 6*8 = 48 Bit übertragen werden.

Normalerweise erfolgt die Datenübertragung zur MMC blockweise. Das letzte Byte beinhaltet die CRC-Summe der vorherigen Bytes. Im SPI Modus gibt es die Möglichkeit, den CRC zu deaktivieren. Das Byte wird zwar trotzdem gesendet bzw. muss gesendet werden, jedoch kann es ignoriert werden. Standardmäßig wird bei der Initialisierung des SPI der CRC deaktiviert. Dessen zu trotz muss für CMD0 eine Checksumme gesendet werden, für alle folgenden Blöcke jedoch nicht mehr. Der Aufbau eines Befehls sieht allgemein wie folgt aus:

Bit Position [47] [46] [45:40] [39:8] [7:1] [0]
Anzahl der Bits 1 1 6 32 7 1
Wert '0' '1' x x x '1'
Beschreibung Start-Bit Transmission-Bit Befehls-Code Argument Checksumme End-Bit

Da für die Initialisierung kein Argument benötigt wird, können diese Bits Nullen bleiben. Der Befehls-Code ergibt sich direkt aus der Befehlsbezeichnung. Für CMD0 ist er "000000", für CMD39 bspw. "100111".

Somit ergibt sich für die Befehlssequenz nach dem Einschalten die folgende Bytefolge:

0x40    0x00    0x00    0x00    0x00    0x95

Auf die Berechnung der CRC Summe möchte ich hier nicht weiter eingehen, da wir sie in Zukunft nicht mehr benötigen werden. Wen es interessiert, der kann das Datenblatt seiner MMC dazu konsultieren.

Nachdem nun die Karte sozusagen betriebsbereit am SPI ist, kann man mit ihrer Initialisierung beginnen. Dies geschieht durch das Senden von CMD1. Der Vollständigkeit halber hier die entsprechende Bytefolge:

0x41    0x00    0x00    0x00    0x00    0xFF

Wie man auf 0x41 sollte anhand der obigen Beschreibungen klar sein. Dass als Checksumme + End-Bit 0xFF gesendet wird ist fakultativ, man hätte genauso auch nur 0x01 senden können, da die MMC standardmäßig ab jetzt die Checksumme ignoriert.

Wenn bis hierhin alles geklappt hat, ist die Initialisierung der MMC abgeschlossen. Doch nun zum eigentlichen Schreiben und Lesen von der Karte. Hierfür ist es wichtig zu wissen, wie der Speicher organisiert ist. Die Organisation ist ähnlich der des zu Beginn beschriebenen EEPROMs, nur das sie hier anstatt aus Pages mit 128 Byte aus Blöcken zu je 512 Byte besteht. Es muss jedes mal mindestens ein Block gelesen bzw. geschrieben werden. Je nach verwendeter Karte gibt es jedoch Möglichkeiten, die Blockgröße und viele andere Einstellungen der MMC zu manipulieren. Wer das vor hat, sollte sich genauer mit dem CSD-Register auseinandersetzen.

Lesen:

Wie man sich fast denken kann, gibt es im wesentlichen zwei Lesemodi. Entweder man liest nur einen Block, oder man liest eine bestimmte Anzahl von Blöcken. Genutzt werden die Befehle CMD17 sowie CMD18. Beim Lesen mehrerer Blöcke kann noch unterschieden werden, ob eine bestimmte Anzahl gesendet werden soll oder ob die MMC so lange senden soll, bis der MC das Ganze durch ein STOPP beendet. Um eine definierte Anzahl von Bytes zu lesen, muss CM18 ein CMD23 vorangestellt werden. Natürlich kann auch das Lesen einer definierten Anzahl von Blöcken jederzeit durch ein STOPP unterbunden werden. Es gilt noch zu erwähnen, dass die MMC auf jede Befehlssequenz mit einem so genannten Token antwortet. Dieser kann zwischen einem und zwei Bytes lang sein. Die Auswertung desselben bleibt der Software des Anwenders überlassen.

Schreiben:

Die Schreibemodi sind identisch mit den vom Lesen bekannten. Es werden die Befehle CMD24 und CMD25 genutzt. Soll eine definierte Anzahl von Blöcken geschrieben werden, so ist CMD25 analog zum Lesen ein CMD23 voran zu stellen. Solange die Karte einen Block intern speichert, sendet sie so genannte Busy-Token, um dem Anwender zu signalisieren, dass sie nicht bereits ist. Sollen mehrere Blöcke geschrieben werden, so muss jedem einzelnen Datenblock ein Start-Token vorangestellt werden. Beendet wird die Transmission mittels eines Stop-Token.

Wann man als MC-Programmierer (AVRs) in den Genuss kommt, bei deren begrenzten internen Speichern, mehrere Blöcke lesen oder schreiben zu wollen, möchte ich hier mal offen lassen.

Das soll es nun auch an Informationen zu MMCs gewesen sein. Wen eine komplette Auflistung aller möglichen Kommandos und Error-Codes usw. interessiert, der kann die entsprechende Literatur (Datenblätter) konsultieren.

Analog zum zu Beginn beschriebenen EEPROM werde ich auch hier keine weiteren Codeschnipsel einfügen, da diese im Prinzip nur einfache SPI-Funktionen nutzen. Diese können der Rubrik SPI entnommen werden. Wer sich damit noch nicht beschäftigt hat, der sollte das jetzt schleunigst nachholen :-)
Natürlich gibt es dazu auch eine komplette Beispielapplikation, an der die Verwendung der beigelegten Funktionsbibliothek demonstriert wird. An dieser wird im Moment allerdings noch gearbeitet. Bis dahin kann ich nur auf die Seite von Ulrich Radig verweisen, der sich mit diesem Thema auch schon beschäftigt hat und eine Bibliothek für diverse Kartentypen bereit stellt. Die aktuellste Version kann man entweder von seiner Homepage oder aus der Codesammlung von mikrocontroller.net bekommen.

 

Zurück zur Startseite.