Hallo, ich möchte bei meinen Projekten (Geräte am Hausbus) zukünftig die Daten auf einer SD-Karte ablegen. Im Anhang ein Beispiel einer INI-Datei, die ich mit dem AVR lesen möchte. Gibt es eine elegante Lösung, dies ohne großen RAM-Bedarf zu bewerkstelligen? Ich habe schon gegoogelt, habe aber nix gescheites gefunden. Bei meinem Webserver Marke Eigenbau habe ich ein externes RAM drauf, da geht es recht einfach: 1. Datei von SD-Karte komplett ins externe RAM kopieren. 2. entsprechender Bereich (z.B. "[Zimmer 8]") in der Datei mit strstr() suchen. Diesen String zwischen "[Zimmer 8]" und der nächsten eckigen Klammer in ein neues Feld (String 2) im externen RAM kopieren. 3. String 2 mit dem Suchbegriff (z.B. "name=") mittels strstr() durchsuchen. Bei meinen anderen (älteren) Platinen habe ich zwar einen SD-Kartenleser drauf, aber kein externes RAM. Ein Cluster der SD-Karte hat 512 Byte. Der RAM-Verbrauch im AVR sollte z.B. 1024 Byte nicht überschreiten (je weniger, desto besser). Ich sehe ein gewisses Problem, wenn die gesuchten Daten zwischen 2 Clustern sind (der Anfang z.B. in Cluster 6, der Rest in Cluster 7). Hat jemand schon mal so etwas programmiert? Bzw. kennt einen Quellcode, der so etwas macht? Gruß Martin PS: bin eher der Hardwaremann, daher stelle ich mich diesbezüglich auch etwas "ungeschickt" an. Ich denke, wenn man mit entsprechend passenderen Fachbegriffen googelt, findet man sicherlich im Netz etwas.
:
Verschoben durch User
Hallo Martin, bist du auf dieses Konfigurationsformat festgelegt? Schätze eher nein. Dann wirf es weg! Bau dir ein Binärformat mit einer festen Struktur bei dem ein einzelner Block wenige (und festgelegt!) viele Bytes groß ist. Je näher dieses Format an der internen Repräsentation ist, desto einfacher und effektiver geht die Verarbeitung vonstatten. Auf so beschränkten Systemen musst du dich damit anfreunden, dass die Lesbarkeit von (Konfigurations)-Daten durch Menschen (meist unnötiger) Luxus ist. Wenn es dich glücklicher macht, kannst du dir ja ein kleines Programm schreiben, dass die Ini-Datei in das interne Datenformat übersetzt. mfG Markus PS: Deine Verarbeitung ist äußerst Ineffektiv: Es würde reichen, wenn du ab Beginn des Abschnittes solange einzelne Zeilen auswertest, bis du zum Beginn eines neuen Abschnittes kommst. Also nicht mehr in Abschnitten denken, sondern stattdessen Zeilenbasiert (+ ein bisschen Hintergrundinformation über den gerade aktiven/bearbeiteten Abschnitt).
Hi, Hab gerade letztens das gleiche, auch mit dem INI Format und nem AVR mit SD-Karte umgesetzt. Dabei hab ich wie markusj schreibt immer nur Zeilenweise ausgewertet, wobei auch jede Variable nur einmal vorkommt in der ganzen INI-Datei, das bedeutet, dass die einzelnen Unterabschnitte nur der Lesbarkeit dienen ... Ich hab so immer nur eine Zeile eingelesen, den ersten Teil bis zum = verglichen und den entsprechenden Wert dann gespeichert...
Hallo, vielen Dank für die Antworten. Die INI-Datei sollte schon mit einem Editor (z.B. Notepad) editierbar sein, das macht das Handling einfacher. So ist zumindest meine Meinung. Mit Binärdateien habe ich bisher nichts gemacht. Wie gesagt, bin mit der Programmierung ja nicht so fit. Das mit den Abschnitten möchte ich eigentlich auch beibehalten, da die Namen der Einträge sind oft wiederholen (z.B. "name=" kommt in jedem Abschnitt "[Zimmer x]" vor). Ich könnte auch die Abschnittkennung "[Zimmer x]" weg lassen und dann "Zimmer00_name=", "Zimmer01_name=", "Zimmer03_name=",usw. schreiben, aber dann muß man bei dem Editieren mehr auf eventuelle Fehler achten. Momentan läßt sich die Sache einfacher erweitern, indem ich den Block kopiere und nur eine neue Nummer in der Abschnittkennung eintragen muß. Zudem wird auch die Datei größer, da die Parameterkennungen länger werden Gruß Martin
Nachtrag: vielleicht war es nicht ganz klar, daß ich die SD-Karte mit dem AVR nur lese. Beschreiben tue ich diese mit dem Editor am PC. Daher muß die Datei lesbar sein (ASCII-Zeichen). Ich habe mir nochmals wegen den Clusterübergängen Gedanken gemacht. Das ist mit den Abschnitten nicht so ganz einfach. Wenn man nun doch keine Abschnitte macht, sondern die Parameterkennung eindeutig ist, würde es die Sache mit den Clusterübergängen deutlich vereinfachen. z.B.: Version A: Zeile für Zeile nacheinander in ein Array (mit z.B. 50 Bytes) kopieren und dort jeweils nach der Parameterkennung suchen. Version B: * ersten Cluster von der SD-Karte holen und in den 512 Byte des Clusters nach der Paramerennung suchen. * wird die Parameterkennung nicht gefunden, nächsten Cluster holen und dort suchen. * sind alle Cluster der Datei durchsucht worden und die Parameterkennung wurde nicht gefunden, dann muß diese an einem Clusterübergang sein. Hierzu müßte ich dann noch die Textzeilen, die zwischen den Clustern sind, in ein temporäres (kleines) Array schreiben und durchsuchen. => wäre sicherlich schneller als Variante A. PS: Daß man am Ende des Arrays ein 0x00 anhängen muß ist mir schon klar. Sonst erkennt strstr() das Ende des Arrays nicht. Gruß Martin
Hi Martin, ich hab zwar von Programmierung nicht viel Ahnung aber will trotzdem mal meinen Senf dazu geben ;-) Vielleicht lerne ich dabei ja auch noch was. Was für Lesefunktionen hast du denn bereits im AVR drin? Nur Lowlevel-Zugriff auf die Karte oder auch Funktionen für das FAT-Dateisystem? Ich gehe mal davon aus, dass Dateisystemfunktionen vorhanden sind. Sonst könntest du schlecht feststellen, dass in einem anderen Cluster noch weitere Daten liegen und welcher Cluster das ist. Welche Funktionen bietet denn der Dateisystemtreiber an? Nur Blockweise lesen/schreiben oder bei Textdateien auch Zeilenweise? In dem Fall würde ich auch (wie schon weiter oben beschreiben) die Datei zeilenweise einlesen. Wenn das erste Zeichen ein [ ist, dann geht es um einen neuen Abschnitt - Abschnittsname merken. Alle Variablen bis zur nächsten [-Zeile gehören zu diesem Abschnitt. Der RAM-Bedarf dürfte so nur der Länge der längsten zu erwartenden Zeile zzgl. der Abschnittsmerker entsprechen. Wenn das Dateisystem nicht zeilenweise einlesen kann, dann kommt noch das RAM zur Pufferung eines 512Byte Blocks dazu. Ein ganzer Cluster umfasst bei FAT ja mehrere Blöcke, der Cluster muss hoffentlich nicht komplett ins RAM - das können nämlich bis zu 32kB werden. Ob deine Daten jetzt genau auf der Grenze von einem zum nächsten Block lagen, kannst du ja anhand der ini-Syntax prüfen: - wenn die Zeile mit [ begonnen hat, dann muss sie im nächsten Block mit dem zugehörigen ] enden - wenn die Zeile mit was anderem begonnen hat, dann muss erstmal ein = kommen und dann irgendwann ein Zeilenende (CR-LF) - wenn im nächsten Block etwas unerwartetes kommt (erster Block hatte ein [, im nächsten Block kommt dann ein = oder Zeilenende), dann kannst du von einem defekten Dateisystem ausgehen. mfg Harri
Hi Harry, ich habe SD-Kartenfunktionen verwendet, die ich im Internet gefunden habe. Eine FAT.c ist auch dabei. Das Auslesen von Dateien funktioniert ja auch problemlos. Bisher verwende ich nur blockweises Lesen (je 1 Block mit 512 Byte). Es gibt in dem Sourcefile folgende Funktionen: extern unsigned char sd_card_read_byte(void); extern void sd_card_write_byte(unsigned char); extern void sd_card_read_block(unsigned char *,char *,unsigned in); extern unsigned char sd_card_init(void); extern unsigned char sd_card_read_sector (unsigned long, char *); extern unsigned char sd_card_write_sector (unsigned long,char *); extern unsigned char sd_card_write_command (unsigned char *); extern unsigned char sd_card_read_csd (char *); extern unsigned char sd_card_read_cid (char *); Ich denke nicht, daß eine davon das zeilenweises Einlesen der Datei ermöglicht... Zumindest verstehe ich dies so. Ich glaube ich habe da Cluster und Block verwechselt. Ich meinte immer so einen Datenblock mit 512 Byte, aus denen eine Datei besteht. Ich denke ich muß es zeilenweise Einlesen, sonst wird das zu aufwändig. So weit habe ich mich durch die Infos von Euch schon überzeugen lassen. Ob ich nun das mit der Abschnittkennung mache oder nicht, muß ich mal überlegen. Das macht die Sache programmtechnisch halt nicht gerade einfacher... Gruß Martin
Willst du die Datei den einmalig parsen und dir dann alle Informationen um RAM ablegen? Oder soll die Datei "immer mal wieder" geparst werden und dir dann ein bestimmtes Datum zurückliefern?
Hallo Läubi, das Ganze wird nur einmal beim Booten gelesen. Die Einträge werden im RAM abgelegt. Gruß Martin
Dann würde ich einfach direkt auf dem Rampuffer Zeichenbasiert arbeiten.
1 | while(!EOF) |
2 | buffer = readSektor(); |
3 | for i = 0; i < 512 i++ |
4 | ... tu was ... |
braucht auf jedenfall kaum zusätzlichen RAM (höchsten ein paar lokale Zustandsvariablen die am Ende wieder freigegeben werden können).
zeichenbasiert arbeiten? Du klapperst eines nach dem anderen Byte in dem Block ab? Wie macht man da sinnvoll eine Abfrage nach einer bestimmten Stringfolge wie z.B. "zimmer="? strcpy(suchbegriff, "zimmer="); if ( (feld[i] == suchbegriff[0]) && (feld[i+1] == suchbegriff[1]) && (feld[i+1] == suchbegriff[2]) && ... (feld[i+n] == suchbegriff[n-1]) && // wobei n = strlen(suchbegriff); ) { // Übereinstimmung } Geht sicherlich mit Zeigern einfacher, aber mit Zeigern hab ichs nicht so. Felder kann ich mir besser vorstellen.... Gruß Martin
Also ich würde es wie schon angedeutet Zustandsbasiert machen. Du reservierst dir einen Buffer (z.B. 30 Zeichen) Und du hast einen Zustand, und eine aktuelle Sektion, sowie einen Parameter. Dann beginnst du das parsen: - Wenn das Zeichen ein '[' ist gehst du in den Zustand SEKTION, schließt die aktuelle Sektion ab (falls eine existiert) und beginnst eine neue. - Wenn das Zeichen ']' auftaucht und du im Zustand SEKTION bist wird der Name der aktuellen Sektion auf den Inhalt des Buffers gesetzt. (bis zum \0 Zeichen und Bufferpostion danach wieder zurücksetzen) und du wechselst in den Zustand NAME - Wenn das Zeichen ein '=' ist und du im Zustand NAME bist wird der Name des aktuellen Parameters gesetzt/verglichen und du wechselst in den Zustand WERT - Wenn das aktuelle Zeichen ein Zeilenende-Zeichen ist und du im Zustand WERT bist schreibst du den aktuellen Parameter den Wert des Buffers - In allen anderen Fällen schreibst du das aktuelle Zeichen an die aktuelle Buffer Postion und an Position+1 das \0 Zeichen (ggf. auf Puferüberlauf prüfen) Hier ist jetzt natürlich nicht jeder Fall berücksichtigt (Kommentarzeilen? Leerzeilen? Ungültige Parameter) aber auf diese Weise bearbeitest du immer nur ein Zeichen, und wenn du einen "verarbeitbaren" Wert hast wird dieser genutzt und du wechselst den Zustand.
@Martin Thomas: danke für den interessanten Link. @Läubi: danke für die Tipps. Ich denke ich habe es soweit verstanden. Gruß Martin
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.