Hallo zusammen! In den letzten Tagen habe ich mein MMC/SD-Interface für AVR-Controller fertiggestellt. Dem ein oder anderen wird es inzwischen vielleicht schon ein bisschen zuviel, schließlich gibt es dafür schon eine ganze Menge Alternativen, aber ich wollt es eben selber machen. Implementiert habe ich den low-level-MMC-Zugriff, den Zugriff auf die Partitionstabelle und FAT16 Lese- und Schreibunterstützung. Auf http://www.roland-riegel.de/sd-reader/ findet Ihr meine Testschaltung und den ausführlich dokumentierten Quellcode. Ich habe das aber auch an diesen Beitrag angehängt. Testen konnte ich nur mit einer einzigen 128MB-Karte, es könnte also sein, dass es mit anderen Karten noch Probleme gibt. Vielen Dank noch an Ulrich Radig, von dem ich mir die einfache, aber gut funktionierende MMC-Anbindung mittels Spannungsteiler abgeschaut habe. Über Anregungen und Kommentare würde ich mich freuen. Gruß, Roland
Igitt was haben denn deine *.c fürne komische formatierung. Schade, nix mit Editor anschaun, törnt mich grad ab das reinzuziehen.
@Senf: Sieht bei mir okay aus. Deine Wortwahl und Schreibstil lassen eher vermuten, daß Kiffen doch gesundheitsschädlich ist ;)
@Senf Ich habe die Anwendung unter Linux entwickelt, daher haben die Textdateien die Unix-Zeilenende-Markierung. Wenn Du Sie aber mit einem vernünftigen Editor öffnest und nicht grad mit dem Windows-Einfachst-Editor, dann ist alles ok. Sogar Wordpad schafft das.
@Roland Da du ja ohne FAT-Buffer etc. arbeitest, interessiert mich die Geschwindigkeit die du erreichst. Hast du schon mal was gemessen?
@SuperUser Ich habe jetzt mal mit einer 32kB-Datei gemessen. Der Durchsatz liegt beim Lesen von 8Byte-Blöcken nur bei ca. 400B/s. Das überrascht mich selbst ein bisschen, aber bei genauerer Betrachtung kommt das schon hin. Denn für den ersten Cluster (also bis 16kB) braucht er nur ein bis zwei Sekunden. Der hauptsächliche Grund hierfür dürfte sein: Im Dateideskriptor speichere ich den Cluster der aktuellen Dateiposition nicht mit ab, sondern suche mir diesen immer neu. Solange ich beim Lesen innerhalb des ersten Clusters bleibe, ist er verhältnismäßig schnell, da er jeden 512Byte-Block wirklich nur einmal liest. Nach dem ersten Cluster bricht er dann ein, da der Block-Puffer durch das zwischenzeitliche Suchen des Clusters nicht mehr greift. Ursprünglich hatte ich sogar den Cluster vom vorangehenden Lesevorgang abgespeichert, hatte dann aber Probleme bei Dateien, die genau auf einer Clustergrenze enden. Sonstiges: - Sehr geringe Blockgröße pro Lese-Aufruf. - Wie Du schon gesagt hattest: kein FAT-Puffer. Der wäre aber schon bei meiner 128MB-Karte 15kB groß, man kann sich das also nur mit externem RAM leisten. - SPI läuft mit 250kHz, also brutto max. ca. 30kB/s. Ich denke, ich muss den Cluster doch zwischenspeichern. Das ist mir jetzt klar geworden. Danke :)
Die schlechte Lesegeschwindigkeit durch die fehlende Zwischenspeicherung der Clusternummer habe ich nun korrigiert. Im Anhang findet ihr den neuen Quelltext. Das Auslesen einer 1MB großen Datei dauert nun 52 Sekunden, das entspricht also ca. 20kB/s. Über weitere Beiträge würde ich mich freuen.
Sehr interessant :) überlegst du eigentlich auch ein Programm zum schreiben von daten zu entwickeln? Ich hatte mal folgende Idee: Von einer SD Karte daten lesen und diese dann auf einer Festplatte abspeichern...(speziell für Fotos) ich weiss nicht mit wieviel aufwand das zu realisieren ist.
@Fabian > überlegst du eigentlich auch ein Programm zum schreiben von daten > zu entwickeln? Was meinst Du damit? FAT16 Schreibunterstützung? Die ist doch schon implementiert?
Wie zu Beginn gesagt, das war die Idee von Ulrich Radig. Natürlich ist das recht einfach, aber wenn man eine Schaltung nur für Testzwecke entwirft, brauchts nicht mehr. Ansonsten kann man ja einen 3,3V-Spannungsregler verwenden, der dann oft ohnehin noch von anderen Komponenten benötigt wird.
@Roland ja sorry habe mich etwas unsauber ausgedrückt: Schreiben ist zwar möglich aber nur mit einer sehr geringen Geschwindigkeit, oder habe ich da etwas falsch verstanden? Weisst du zufällig wie man diese Geschwindigkeit erhöhen könnte auf z.b. 1MB/s ?
Nein, solche Geschwindigkeiten sind wohl nicht möglich. Soweit ich mich erinnere, unterstützen die Karten im verwendeten SPI-Mode nur max. 400kHz, also brutto 50kB/s. Für 1MB/s wären demnach über 8MHz nötig, was sowohl die Karte als auch den AVR weit überfordert.
MMC karten unterstützen bis zu 20 Mhz SPI takt. steht auch in den datenblättern.
@Lupin, Gast Danke für die Info. Meine Datenblätter sind ziemlich knapp und an einigen Stellen stark gekürzt. Ich habe den SPI-Takt nun auf das Maximum von 8MHz bei 16MHz CPU-Takt gestellt. Statt 52 Sekunden braucht der uC nun nur noch 13 Sekunden, um 1MB auszulesen. Das entspricht 78kB/s. Hmm, doch nicht so viel schneller... @Pete Von Reichelt, CONNECTOR SD 21 oder 22, leider sehr teuer.
Ich meine mal auf wikipedia gelesen zu haben das SD oder MMC maximal 2mb/s kann... ich denke mal es liegt aber einfach an der Ansteuerung mit SPI und die meisten karten erreichen den wert nicht wirklich. 78kB/s ist aber doch schon recht langsam, wenn ich mit der Geschwindigkeit am PC zugreifen müsste würde ich irgendwann durch drehen (wenn man nur mal daran denkt eine 4gb karte zu füllen). Ich wusste gar nicht das die reichelt connectoren einen auswurf mechanismus haben...
Nun ja, es ist ja ein AVR und kein PC... Es ginge wahrscheinlich um vieles schneller wenn der FAT16-Overhead nicht wäre. Gerade auf den AVRs mit ihren 8Bit-Registern ist das nicht zu vernachlässigen, da FAT16 viele 16Bit- und 32Bit-Operationen benötigt. Ich vermute, das es auf einem MSP430, ARM oder auch AVR32 wesentlich besser liefe. Werde es auch mal mit größeren Blockgrößen versuchen. Im Moment lese ich ja immer nur 8Byte pro read-Aufruf.
@Pete Es gibt auch wesentlich billiger Sockets. Ich weiss gerade keinen Shop in DE, der das zu diesem Preis schafft, aber diese hier (CH) können das: http://www.compona.ch (~2CHF) http://www.farnell.ch (2-19CHF, je nach Ausführung) Es kommt einfach darauf an, was für Ansprüche du an den connector hast. Wenn du nicht einen suuuper duuuper Connector mit Schirmung und Schleudersitz brauchst, dann bekommst du einen für weniger als zwei Euronen. Gruss, Matthias
Habe jetzt nochmal verschiedene Puffergrößen ausprobiert, die ich fat16_read_file() übergebe. Das hat, wie sich gezeigt hat, entscheidenden Einfluss: Puffer (B) | Zeit (s) | Geschw. (kb/s) ------------------------------------------ 8 | 13 | 78 16 | 9 | 114 32 | 6 | 171 64 | 5 | 205 Also eine Steigerung um das 2,5-fache.
also mit so kleinen reads ist das auch echt ineffizient... warum liest du nicht 512 byte pro read command? Wie machst du das denn bei der MMC funktion? Führst du partial reads aus? Im Normalfall wirst du immer 512 byte blöcke von der karte lesen müssen, die restlichen bytes "weg zu schmeißen" kostet natürlich viel Zeit. Wenn es tatsächlich um hohe Transferaten geht, dann wird ja meistens auch in großen Blöcken gelesen, logisch oder? :)
Na wahrscheindlich geht ihm das RAM aus.... leider immer das selbe, irgendeinmal trifft man auf die Grenze der AVRchen.
Richtig. Ich habe nur 1 kB RAM, die Hälfte davon nutze ich als Schreib-/Lesepuffer für die low-level-Lesefunktionen. Den Rest brauche ich für Dateisystem-, Datei- und Verzeichnis-Deskriptoren und natürlich für den Stack. Ich wollte den Kartenzugriff so flexibel wie möglich halten. Die höheren Schichten (also Partitionshandling sowie FAT16) wissen überhaupt nichts davon, dass sie von einer MMC/SD-Karte lesen, geschweige denn dass dies für gewöhnlich in Einheiten von 512 Byte geschieht. Die MMC/SD-Funktionen bieten "nach oben" die Möglichkeit an, Daten beliebiger Länge von einem beliebigen Offset anzufordern oder dorthin zu schreiben, egal ob das von der Karte selbst schon unterstützt wird oder nicht. Das ist vorteilhaft, weil ich dann z.B. zum Lesen eines FAT16 Verzeichniseintrags, der nur 32 Byte groß ist, mich nicht um die Eigenheiten des Speichermediums kümmern muss und so dieser Code Hardware-unabhängig wird. Es ist also Aufgabe der Abstraktion, sich um die niederen Dinge wie Einschränkungen der Hardware zu kümmern. Im extremen Fall, dass man sich die 512 Byte Puffer sparen will, bringt dies natürlich Einbußen in der Performance mit. Dafür ist der Code dann aber auch extrem klein und genügsam. Benutzt man aber diesen Puffer, erhält man bessere Performance und es genügt beim sequentiellen Lesen ein einfaches memcpy, um die Anfrage auszuführen. Ein weiteres Kapitel ist natürlich der Schreibzugriff. Wenn ich auch dies von beliebigen Offsets aus zulassen will, benötige ich zwingend einen Puffer, um den Teil des Blocks, der nicht überschrieben wird, mit den neuen Daten zusammenzufügen. Die Tatsache, dass dies von den MMC/SD-Routinen übernommen wird, vereinfacht wiederum den Code in den höheren Ebenen. Denn, um beim Beispiel zu bleiben, der FAT16-Code muss sich dann eben nicht mehr darum kümmern, die 32 Byte für einen neuen Verzeichniseintrag mit dem vorhandenen 512 Byte-Block auf der Karte zusammenzufügen. Aus seiner Sicht überschreibt er einfach die alten 32 Byte mit den neuen 32 Byte. Und wenn es um hohe Transferraten geht, dann spendiere ich ein bisschen mehr RAM, und schon kann ich z.B. 4 kB auf einen Schlag lesen, und die MMC/SD-Funktionen kümmern sich um die Aufteilung in einzelne 512 Byte-Anfragen an die Karte.
Hallo, in den Thread hat sich zwar schon ne weils nichts getan hoffe aber trotzdem auf ne Antwort. Ich bin auch grad dabei ne SD-Karte über SPI anzusteuern. Als erstes sende ich an die Karte 10mal 0xff und dann das Kommando 0x40,0x00,0x00,0x00,0x00,0x95 (CMD 0) alles in 8bit Blöcken. Danach polle ich mit 0xff um die Antwort von der SD-Karte zu bekommen. Normalerweise müste ich doch nun nach dem CRC irgendwann eine 0x01 von der Karte zurückbekommen. Komisch ist das die SD-Karte schon ab dem vierten 0x00 Block des Kommandos,immer mit 0xFE und gleich dannach mit 0x03 antwortet. Dann bekomme ich 2-3 Blöcke garnichts und dann die erwartete 0x01. Kann jemand die Signale deuten? Kann ich die 0xFE und 0x03 ignorieren oder ist das ein Error Code? Danke im vorraus!! Mfg Peter Baumann
Hallo Peter, Auf Anhieb kann ich mit dem von Dir beobachteten Verhalten auch nichts anfangen. Wichtig ist, dass Du der Karte nach dem Anlegen der Spannung ein paar Takte Zeit gibst, um sich selbst zu initialisieren. Das kannst Du machen, indem Du beispielsweise 10x 0xff sendest. Erst dann sendest Du CMD0. Wie bei jedem R1-Befehl ist das erste Byte, das ungleich 0xff zurückkommt, Deine Antwort. Danach solltest Du der Karte nochmal ein paar Takte Zeit geben, um intern weiterarbeiten zu können. Ich polle dann ein paar Mal das Busy-Bit in der Antwort auf CMD1, um zu warten, bis die Karte fertig ist. Schau Dir doch mal meinen Code an, den ich auf meiner Homepage oder hier in diesem Thread gepostet habe. In der Datei sd_raw.c steht eigentlich alles drin, was Du brauchst. Den Code habe ich allerdings noch nicht mit vielen Karten testen können. Mit meiner funktioniert er aber. Gruß, Roland
Hi zusammen, Ich habe eine neue Version auf http://www.roland-riegel.de/sd-reader/ zur Verfügung gestellt. Die ZIP-Datei habe ich auch an dieses Posting angehängt. Verbesserungen sind unter anderem: - sehr viel zuverlässigere Karteninitialisierung - viele Geschwindigkeitsverbesserungen - Superfloppy-Unterstützung - bessere Überprüfung des FAT16-Dateisystems beim Öffnen - Fehler beim Vergrößern von Dateien behoben - Fehler beim Anlegen einer Datei >31 Buchstaben beseitigt - viele andere Dinge Viele Grüße, Roland
Kann ich damit schnell an das Ende einer Datei springen. Ich möchte MP3 tags auslesen, und die stehen am Ende der Datei (und die haben 200MB)
Hallo Gernot, Ja das sollte möglich sein. Bzgl. der Geschwindigkeit müsstest Du das einfach mal ausprobieren, 200MB sind ja viele tausend Cluster und das könnte schon einige Zeit in Anspruch nehmen. Ich selbst habe das noch nicht ausprobiert. Gruß, Roland
Muss ich testen. Ich am besten einen ATmega32 mit 16MHz, oder? Kannst Du einen Ausdruck von der Platine dazulegen - als png oder so? Ich hab das Programm nicht, mit dem Du das gemacht hast.
So, bitteschön. Siehe Anhang. Du musst Dir aber zum Testen nicht gleich eine eigene Platine herstellen. Die Schaltung kannst Du auch auf einem Steckbrett aufbauen. Beachte aber bitte, dass die einfachen Spannungsteiler und die zwei Dioden zum Bereitstellen der Betriebsspannung für die Karte keine optimale Lösung sind, durch die einzelne Karten eventuell nicht zuverlässig arbeiten oder sogar beschädigt werden könnten. Besser wäre ein eigener Spannungsregler sowie vernünftige Pegelkonverter für den SPI-Bus. Roland
Ach so, zur MCU. Ja klar, ein ATmega16 ist ok. Mit dem kannst Du aber nicht meine Platine nutzen. Für die brauchst Du einen ATmega8 oder pinkompatiblen, also einen von ATmega8/48/88/168. Roland
Hallo, ich versuche gerade, den sd-reader mit einem ATmega32 zu betreiben. Muss ich dann in SD_RAW_CONFIG.H Änderungen vornehmen, da hier MOSI, MISO etc. an anderen Pins liegen? Ich bin mir nicht ganz sicher, weil irgenwo steht, es würde genügen im Makefile eine entsprechende Änderung vorzunehmen. In SD_RAW_CONFIG.H steht aber explizit zB. #define configure_pin_mosi() DDRB |= (1 << DDB3). Beim Mega32 ist MOSI aber an Pin 5. Bitte um Aufklärung für einen Anfänger. Malte
Hallo Malte, Wenn Du die neue Version des sd-reader von vor zwei Tagen verwendest, sollten die Pins für den SPI-Bus bereits stimmen. Daneben solltest Du aber noch je nach Möglichkeiten Deines Karten-Slots die folgenden #defines ändern: #define configure_pin_available() DDRC &= ~(1 << DDC4) #define configure_pin_locked() DDRC &= ~(1 << DDC5) #define get_pin_available() ((PINC >> PC4) & 0x01) #define get_pin_locked() ((PINC >> PC5) & 0x01) Wenn Dein Slot keine Pins zum Detektieren einer Karte bzw. der Position des Schreibschutzschalters hat, änderst Du diese ab zu: #define configure_pin_available() #define configure_pin_locked() #define get_pin_available() 0 #define get_pin_locked() 0 Im Makefile änderst Du folgende Zeilen passend ab: MCU := atmega168 MCU_AVRDUDE := m168 MCU_FREQ := 16000000UL Das sollte dann alles sein. Roland
Hallo Roland, ok, alles klar, ich hatte die alte Version. Danke Malte
Hallo, ich krige die SD Karte nicht zum laufen. Bereits beim card-reset in sd_raw_init() kommt ein Abbruch. Kann es daran liegen, dass ich eine miniSD Karte (mit Adapter) verwende? Ansonsten habe ich einen 3.3V Spannungsregler und Widerstände für die Pegelanpassung. Noch eine Frage: Kann ich den AVRISPmkII Programmieradapter eigentlich beim Testen dranlassen, oder stört er die SPI Kommunikation? Ist sonst noch was zu beachten, spezielle Fuses oder so? Gruß Malte
Malte Auch ich habe eine Mega32, bei mir läuft's. Der Programmier-Adapter stört bei mir bei einigen SD-Karten, nicht bei allen. Und beim Flashen des Mega ziehe ich immer vorsichtshalber die Karte raus. Eine MiniSD-Karte sollte eigentlich kein Problem sein und der 3,3V-Regler ist das beste was Du machen konntest. Die Dioden-Lösung ist nach meiner Erfahrung sehr unzuverläßig. Baue doch einfach einige Debug-Meldungen in den Code ein. Man kann sonst immer so schlecht sehen was wirklich passiert. z.B. mit einfachen Meldungen die Du dann auf der seriellen Schnittstelle sehen kannst. So etwa in sd_raw.c: if(!sd_raw_available()) { printf_P(PSTR("Error: sd_raw_available\n")); return 0; } usw. Platz hast Du ja im Mega32 vorerst reichlich zur Fehlersuche. Spezielle Fuse verwende ich nicht, außer für den ext. Quarz. Torsten
Gernot >..schnell an das Ende einer Datei springen. Nachdem Roland das Buffering überarbeitet hat ist das kein Problem mehr und blitzschnell. Mit der eingebauten Mini-Shell kann man das sehr elegant selbst überprüfen, z.B. so: Datei anlegen: touch 1.txt Datei auf 100MB erweitern und ein paar Zeichen reinschreiben: write 1.txt 100000000 <abcd Oder einfach mitten rein: write 1.txt 50000000 <1234 Beim drücken von Enter spüre ich keine Verzögerung. Das gilt für Schreiben, Lesen sollte schneller möglich sein. Mein Test-Aufbau: Mega32 @ 14,7456MHz, NoName 1GB SD-Karte, 115kB am Uart. OT: MP3's mit 200MB, uff. Das sind keine Songs mehr, etwa Hörbücher o.ä.? Torsten
@ Malte Zusätzlich zu den Antworten von Torsten noch drei Anmerkungen: - Die Karte kann während der Programmierung im Slot steckenbleiben, falls Du das /SS-Signal über den Mikroprozessor steuerst. Hast Du das Signal jedoch fest auf Masse gelegt, wird während der Programmierung gleichzeitig die SD-Karte angesprochen, die das wohl nicht mag. Zudem dürfte sie durch vermeintliche Antworten auch aktiv den Programmiervorgang stören. - Da Du einen 3,3V-Regler verwendest, solltest Du drauf achten, dass die Spannungsteiler bei logischem High-Pegel auf keinen Fall mehr, besser etwas weniger als 3,3V an die Karte anlegen. Ich weiß nicht, wie die Karte auf Spannungspegel reagiert, die höher als die Betriebsspannung liegen. - Achte darauf für die MISO-Leitung keinen Spannungsteiler zu verwenden, da dies ein Eingang am Mikrocontroller ist. Die 3,3V der Karte werden bei 5V Betriebsspannung des ATmega noch als High-Pegel erkannt. Gruß, Roland
@ Torsten, Roland danke für die Tips. Ich bin gerade beim Debuggen. Dabei ist mir aufgefallen, dass in sd_raw_config.h noch folgende Einträge stehen, die nicht durch ein 'if defined' dem entsprechenden Controllertyp angepasst werden: #define select_card() PORTB &= ~(1 << PB2) #define unselect_card() PORTB |= (1 << PB2) Beim mega8 ist PB2=SS, beim mega32 ist SS an PB4. Eine entprechende Änderung bringt aber auch nichts. Mache ich einen Denkfehler? Die folgende Schleife in sd_raw_init bringt nur 0x00 Werte: /* card needs 74 cycles minimum to start up */ for(uint8_t i = 0; i < 10; ++i) { /* wait 8 clock cycles */ uart_putc(sd_raw_rec_byte()); //sd_raw_rec_byte(); } Die darauffolgende Schleife mit 'response' bringt 512 mal 0x00. Kann man daraus schon schliessen, dass etwas grundsätzliches nicht stimmt? Gruß Malte
Ich habe gerade festgestellt, dass am CLK Eingang meiner Karte konstant 2.65V anliegen. Hinter dem 1.8k Widerstand, also am SCK Ausgang des Controllers, kann ich dagegen während der Initialisierungsphase ein Taktsignal messen. Die 2.65V sind offenbar so niederohmig, dass das Taktsignal nicht "durchkommt". Wie kann das sein? Gruß Malte
Hallo Malte, Bzgl. der beiden #defines hast Du Recht, die müssen jeweils an die MCU angepasst werden. Eine aktualisierte Version habe ich angehängt. Mit Deiner Schleifenausgabe stimmt tatsächlich etwas nicht. Könntest Du mal ein Foto oder besser noch einen Schaltplan von Deinem Aufbau posten? Sonst ist das alles ziemlich viel Raterei. Wenn Du die Spannungsteiler so aufgebaut hast, wie in meiner Schaltung bzw. der von Ulrich Radig, sollte das schon stimmen. Auf der CLK-Leitung liegt nur ein Takt, während Daten in die ein oder andere Richtung übertragen werden. Danach wird er wieder abgestellt. Der Takt ist zudem so schnell, dass Du ihn nur mit einem Oszilloskop und nicht mit einem gewöhnlichen Voltmeter messen kannst. Die Initialisierung läuft für gewöhnlich innerhalb von wenigen Millisekunden ab, auf einem Voltmeter erkennst Du da überhaupt nichts. Roland
Hallo Roland, ich hatte vergessen zu schreiben, dass ich das Taktsignal mit dem Oszi gemessen habe. Und, wie gesagt, am SCK-Pin des Controllers liegt es eine knappe Sekunde lang an (viel länger als wenige Millisekunden???). Direkt an der Karte, hinter dem Widerstand ist aber nix mehr vom Takt da, nur Gleichspannung. Die Spannungsteiler habe ich genau so wie du und Ulrich aufgebaut, die Pegel stimmen sonst auch (etwas unter VCC). Ich mache mal einen Schaltplan... Danke und Gruß Malte
...hier der Schaltplan. Ich habe keien Eagle-Vorlage für den Mega32, der Mega163 hat aber die gleiche Belegung.
Asche auf mein Haupt! Ich habe die Pin-Nummern der Karte verwechselt, da der 9te Pin ausser der Reihe liegt. Ich Trott.. Immerhin musste ich mich so etwas intensiver mit der Materie beschäftigen und habe dabei eine Menge gelernt. Danke noch mal für die Hilfe
Hallo Malte! Heißt das, dass es jetzt bei Dir funktioniert? Herzlichen Glückwunsch! Naja, immerhin hast Du jetzt schon mal einen Schaltplan, auf dem Du aufbauen kannst... ;-) Roland
Hallo Roland, ja, es funktionierte auf Anhieb. Klasse Software, dein reader! Habe noch folgendes festgestellt: Wenn man eine Datei erstellt, etwas reinschreibt, die Datei dann löscht und danach eine neue Datei mit gleichem Namen erstellt, steht wieder der alte Inhalt drin. Richtig? Gruß Malte
kommt von dos. wenn du eine datei löscht, wird nur der name herausgenommen, wenn dir der gleiche name wieder einfällt und dort reinschreibst, kannste die datei wieder herstellen.
Hallo Malte, Der alte Inhalt steht aber nur drin, wenn die Größe der neuen Datei > 0 ist, oder? Sonst würde da was nicht stimmen. Das Verhalten ist ok. Hat aber nicht unbedingt etwas mit DOS zu tun, sondern eher mit Geschwindigkeit. Da ich die FAT sequentiell nach freien Clustern durchsuche, allokiere ich für die Datei denselben Cluster, den die zuvor gelöschte Datei benutzt hat. Um die Geschwindigkeit hoch zu halten, verzichte ich darauf, beim Vergrößern der Datei die Cluster zu nullen. Bei Bedarf musst Du das Datenleck selbst schließen und den Cluster leeren. In der Doku der Funktion fat16_resize_file() steht das auch nochmal. Gruß, Roland
Hallo Roland, Ich habe gerade den Samstag damit verbracht ein kleines Board für den Kartenslot und eine Testplatine für meinen Mega16 zu Bauen. Ich kann jetzt von einer 128 MB SD-Karte eine Datei, dumpen. Vielen Dank. Großartige Arbeit. Auf meinen Mega88 hat das Image mit dem Test-Main leider nicht raufgepasst, auch nicht nachdem ich ein bisschen was wegdefiniert habe. Und ich denke der Mega 16 hat jetzt auch nicht mehr viel Luft für neuen Code, ich werde einen Mega32 Ordern. Ich habe noch eine 16MB-Karte, die kann das Programm aber nicht lesen. Ich würde mir noch ein paar Zeilen Doku zu dem Main wünschen, und ein paar grundsätzliche Hinweise zur Inbetriebsetzung. Aber nochmal danke und klasse Arbeit. servus Dirk
Hallo Roland, noch ein paar Fragen zu deinen Schreibfunktionen: Ich will den SD-Karte in einem Datenlogger einsetzen, der ständig läuft. Es kommen pro Sekunde etwa 600 byte, die in Blöcken zu 512 byte (oder mehr??) auf die Karte geschrieben werden sollen. Einmal pro Tag wird eine neue Datei angelegt ("Tagesfiles"). Das ergibt eine Größe von gut 5MB/Tag. Muss man beim Schreiben ans Ende einer bestehenden Datei die Datei vorher vergrößern (resize), oder wird die Größe beim Schreiben automatisch angepasst? Muss man für jeden Block die Datei öffnen und wieder schliessen, oder kann man sie offen lassen? Oder nach jedem Schreiben ein sd_raw_sync machen? Was bewirkt eigentlich das Schliessen der Datei, wird da noch was in die FAT geschrieben? Im Falle eines Stromausfalls wäre es nicht gut, wenn der gesamte bisherige Tagesfile verloren wäre. Wie muss ich vorgehen, um dem vorzubeugen / den Datenverlust zu minimieren? Bei DOS entsteht in solchen Fällen bei noch geöffneten Dateien ja ziemlicher Murks. Wie kann ich eine Funktion zur Ermittlung des verbleibenden Speicherplatzes auf der Karte realisieren? Beste Grüße Malte
@Dirk Schön, dass es funktioniert. So wie das Programm im Download konfiguriert ist passt es tatsächlich nicht mehr in 8kByte. Wenn man jedoch nur den Lese-Support benötigt und sd_raw_config.h bzw. fat16.h dementsprechend abändert, sollte ein einfaches Programm auf einem mega8 durchaus zu realisieren sein. Und was den mega16 angeht solltest Du mal den recht umfangreichen Code in main.c durch Deinen eigenen ersetzen. Ich weiß zwar nicht, was Du mit der SD-Unterstützung vorhast, aber solang Du nicht wesentlich mehr RAM brauchst, ist die Benutzung eines mega16 schon möglich. Benutzt Du die Optimierungsmöglichkeiten Deines Compilers? Könntest Du mir erläutern, welche zusätzliche Doku Du benötigst? Ich dachte eigentlich, die main()-Funktion wäre als Beispielanwendung weitgehend selbsterklärend. Die Dokumentation für die SD-, Partitions- und FAT16-Funktionen hast Du ja sicherlich schon im doc-Verzeichnis oder auf meiner Homepage entdeckt.
@Malte Die von Dir zu schreibenden Daten werden vom sd_raw-Code automatisch in 512-Byte-Blöcke aufgeteilt. Du kannst Daten beliebiger Größe schreiben oder lesen. Wenn Du allerdings jede Sekunde schreiben willst, kann dies zu Lasten der Lebensdauer der Karte führen. Du solltest daher eventuell in einem eigenen Puffer die Log-Daten sammeln und in einem Rutsch auf die Karte schreiben. Die von mir implementierte Schreibpufferung lindert dieses Problem zwar, jedoch ist ein schreibender Dateizugriff relativ komplex, sodass diese Pufferung nicht optimal greift. Du kannst die Dateien zu beliebigem Zeitpunkt öffnen oder schließen. Es sind beliebig viele Zugriffe in beliebig großen Zeitabständen möglich, ohne die Datei zwischendurch zu schließen. Wenn Du über das Ende einer Datei hinausschreibst, wird diese automatisch vergrößert. Du kannst auch fat16_seek_file() benutzen, um die Schreibposition innerhalb der Datei festzulegen. Auch diese Funktion ruft bei Bedarf fat16_resize_file() implizit auf. Wenn die Schreibpufferung aktiviert ist, sollte nach umfangreichen Zugriffen oder auch in einem regelmäßigen Zeitintervall sd_raw_sync() aufgerufen werden, um die Konsistenz der Daten und des Dateisystems zu gewährleisten. Danach kann die Karte abgezogen werden. sd_raw_sync() aufzurufen, während Dateien geöffnet sind, ist problemlos möglich. Die Ermittlung des freien Speicherplatzes ist relativ einfach möglich, man durchläuft dazu die FAT und zählt die freien Cluster. Bei Gelegenheit werde ich das mal implementieren. Gruß, Roland
@Roland: Doku benötigen wäre sicher etwas zuviel gesagt. Du hättest vieleicht noch erläutern können, welche PortPins du mit welchen an der Karte verbunden hast, insbesondere SS. Ebenso könnte man beschreiben, welche Einstellungen für das Terminalprogramm zu wählen sind, für das Testmain. Aber das bleibt dir überlassen und steht schliesslich alles im Code drin. Ein Chef von mir hat mal früher gesagt: Der Code ist die Doku. Ebenso ein Vorschlag, was man mit der Shell anstellen kann, wie oben in einem Beitrag: touch 1.txt write 1.txt 1000 ls ... Vieleicht noch ein Warnhinweis, daß Backspace im Terminalprogramm evtl. nicht so gut Tippfehler korrigiert, wie man denkt. Space am Ende von Dateinamen,... Aber das ist vieleicht mein persönliches Trauma. Konzentrier' dich, auf was dir wichtig erscheint und lass dich nicht von mir davon abhalten tolle Sachen zu machen. Wirst du den Code noch weiterpflegen? Gibt es noch known-bugs, mit Dateien, die sich überschreiben, oder Karten, die Probleme bereiten? Gruß Dirk
@Roland: Ich schliesse aus deinen Aussagen, dass ich noch einen externen RAM verwenden sollte. Ich werde mir mal das Projekt von Benedikt http://www.mikrocontroller.net/forum/read-4-121457.html ansehen. Du schreibst, dass man sd_raw_sync() auch bei geöffneter Datei aufrufen kann. Bedeutet dies, dass bei einem Stromausfall alles, was vor dem letzten Aufruf von sd_raw_sync() geschrieben wurde, erhalten bleibt, auch wenn die Datei beim Ausfall geöffnet war? Das wäre natürlich sehr komfortabel. @Dirk: auf Rolands Homepage ist ein Eagle Schaltplan. Als Terminal Programm (für Windows) habe ich sehr gute Erfahrungen mit Hterm (http://www.der-hammer.info/terminal/) gemacht. Gruß Malte
@Dirk Ich werde mal schauen, was von den genannten Sachen noch sinnvoll wäre aufzunehmen. Danke für die Anregungen. @Malte Viel RAM ist immer gut, zumindest bei Sachen wie Dateisystemen. Da könnte man dann die ganze FAT, das Root Directory und vieles mehr zwischenpuffern. Leider ist es aber mit den von Benedikt verwendeten Speichergrößen nicht mehr möglich, den Speicher direkt zu adressieren. Das verkompliziert den Speicherzugriff schon erheblich. Bzgl. sd_raw_sync() hast Du Recht. Schließlich heißt die Funktion ja auch so... Als Terminalprogramm verwende ich unter Debian cu. Das hat wenigstens kein nerviges Fenster sondern läuft in der Konsole :-) Grüße, Roland
Hallo Roland, sorry wegen der vielen Fragen. Ich bin, was Controller angeht, noch ein totaler Anfänger. Die Kiste läuft aber trotzdem schon ziemlich gut, dank der vielen Hilfen aus diesem Forum. Ich wollte mit dem RAM gar nicht in die Dateiverwaltung eingreifen, sondern einfach nur zB. 64kB Meßdaten zwischenspeichern und dann in einem Rutsch auf die Karte schreiben. Wäre das bei Verwendung deiner jetzigen Software sinnvoll? Falls ja, wie kann man das möglichst einfach realisieren? Kennst du vielleicht eine gute Quelle für so ein (wahrscheinlich relativ alltägliches) Projekt mit dem Mega32? Ich recherchiere schon seit einiger Zeit, habe aber noch nichts passendes gefunden. Gruß Malte
Hi Malte, Die 64k würden schon helfen, aber die Lösung ist das wohl nicht. Bei 600B/s wäre das ein Schreibzugriff alle 100s. Nach 12 Tagen sind 10000 Schreibzyklen in dem Verzeichniseintrag erreicht. Und das dürfte auch das Ende der SD-Karte sein. Hmm, Moment mal. Wenn Du zu Beginn die Datei direkt auf 5MB aufbläst (mit fat16_resize_file()) und dann vom Dateianfang aus reinschreibst, muss der Verzeichniseintrag nicht aktualisiert werden. Dann sollte die Karte wesentlich länger durchhalten. Bis demnächst, Roland
Was ihr nur alle mit den 10000 Schreibzyklen habt. Ein SD-Card hat einen internen Controller wenn ein Sektor nicht mehr schreibbar ist wird der durch einen Ersatzsektor ausgetauscht. Wieviele solche Ersatzsektoren vorhanden sind ist Sache der Hersteller und dürfte sich auch im Preis wiederspiegeln. Eine SD-Karte hat auch einen internen RAM ,das merkt man am schnelleren Multisektorzugriff. Da werden die Daten auch erstmal gesammelt. Ausserdem schafft flash heutzutage 100.000 Zyklen. Wenn es unbedingt Langzeit sein muss, nimmt man ein FRAM sammelt da die Daten und schreibt am Block.
Eben, die Anzahl der Ersatzsektoren kommt auf den Hersteller an. Bei den zur Zeit zu Schleuderpreisen angebotenen Karten handelt es sich ja oft um Billig-Hersteller. Die werden an der Stelle bestimmt sparen. Ich habe mir bereits eine Karte abgeschossen, sie ist jetzt dauerhaft schreibgeschützt. Ob das durch zu viele Schreibvorgänge während der Entwicklung des sd-reader geschehen ist kann ich nicht mit Bestimmtheit sagen, aber es ist meine Vermutung. Ich denke, wenn es mit wenig Aufwand möglich ist, den Flash zu schonen, dann sollte man das auch tun. Zumindest beim in meinem Code verwendeten Einzel-Blockzugriff wird durch die Karte nicht gepuffert, da man dann ja Datenverlust beim Abziehen der Karte zu befürchten hätte. Roland
>Die werden an der Stelle bestimmt sparen. Ich >habe mir bereits eine Karte abgeschossen, sie ist jetzt dauerhaft >schreibgeschützt. Ob das durch zu viele Schreibvorgänge während der >Entwicklung des sd-reader geschehen ist kann ich nicht mit Bestimmtheit >sagen, aber es ist meine Vermutung. Ich denke eher du hast irgendein (nicht dokumentiertes?) Register beschrieben beim testen... Ich hab beim testen mit meinem Code extreMEmory Karten zerschossen (reproduzierbar :-X). Hab jetzt zwei Karten wo nur noch der SPI Modus geht, MMC modus = keine Reaktion. Und das obwohl die Karten im Betrieb deselektiert waren (CS=high und dann ein §65 Display per SPI mit vielen Daten beschrieben -> mmc karte tot). Da vermute ich irgendeinen Bug in der mc karten statemachine oder so... Kanns mir sonst nicht erklären. Bei uralten 32MB mmc karten passiert das nicht :) Bye, Simon
@Roland: wie ich sehe versuchst du das FAT16 Dateisystem an dem String FAT16 zu erkennen, dies ist ein Eintrag in dem auch andere Sachen stehen können und er ist nicht obligatorisch für FAT16. Das Windowsformatprogramm macht dies allerdings seit Jahren so.
Ja stimmt, das ist nicht eindeutig. Aber es ist üblich, und eine einfach zu implementierende Möglichkeit. Es gibt mehrere Stellen, an denen diese Zeichenkette stehen kann. Ich will damit eigentlich nur Datenverlust vermeiden in dem Fall, dass ein Sektor fälschlicherweise als FAT16-Header interpretiert wird. Den Zweck erfüllt es auch, denke ich. Hast Du eine bessere Idee? Roland
55AA prüfen, auf Partitionssektor prüfen Bootsektor 55AA prüfen Clusteranzahl<4085 ||Clusteranzahl>65524 Sektoren pro Cluster 2^n ? evt. anfang der FAT cluster 0 und 1 Wert prüfen Bevor Microsoft die FAT Patente bekam gab es von Microsoft ein Dokument das aus der Einführungszeit von VFAT stammt und unterschiedliche FATimplementierungen und testroutinen (unter anderen Norton und PCTools) und mögliche Auswirkungen durch VFAT diskutiert. Ausserdem wird auf typische Fehler in anderen FATimplementierungen eingegegangen. Hier wird nochmals ausdrücklich darauf hingewiesen das FAT12 und FAT16 nicht durch den String sondern allein durch die Clusteranzahl auseinandergehalten werden. Auch PCIntern hat in der Bootsektorbeschreibung diesen String nicht als obligatorisch. Bezüglich Datenverlust: schon beim Einlesen des Hauptverzeichnisses dürfte es probleme geben.
Danke für die Anregungen. Ich halte Deine Ansätze aber für ziemlich Code-intensiv. Denn es sind mehrere Lesevorgänge erforderlich, Zusammenbauen der 16-Bit-Werte, das Prüfen der Werte, usw. Trotzdem, hast Du vielleicht noch einen Link zu dem von Dir erwähnten Microsoft-Dokument? Ich habe das mit kurzer Suche nicht mehr finden können. Danke und Gruß, Roland
>Denn es sind mehrere Lesevorgänge erforderlich, 1. Sektor laden Partitionssektor? wenn ja Bootsektor laden >Zusammenbauen der 16-Bit-Werte, das Prüfen der Werte, usw. Diese Werte musst du sowieso auslesen und das Zusammenbauen aus 8Bitwerten solltest du sowieso machen wenn du Interesse daran hast das dein FAT-Code auch auf anderen CPU läuft. >Trotzdem, hast Du vielleicht noch einen Link... Dieses Dokument ist aus dem Netz verschwunden als Microsoft die FAT-Patente erhalten hat. Ich muss mal suchen ob ich es noch finde. BTW: Hast du dir schonmal Gedanken gemacht was passiert wenn Microsoft an dich herantritt wegen der FAT-Implementierung?
Im Prinzip hast Du Recht. Wenn Du Dir meinen Code anschaust, siehst Du, dass ich nur die Bereiche aus dem MBR bzw. dem Partitionssektor lade, die mich für die Anwendung wirklich interessieren. Auf meinem mega168 ist RAM sehr knapp (und Flash bald auch), ich hatte mehrfach Probleme mit Kollisionen zwischen Heap und Stack. Deswegen will ich mir keine teuren Überprüfungen leisten. Ganz zu schweigen von Sektoren, die ich der Einfachkeit wegen komplett auf einen Schlag ins RAM lade. Im Übrigen sollte der Code schon plattformunabhängig sein. Alle multi-Byte-Werte baue ich einzeln zusammen, um nicht auf die Endianess der MCU angewiesen zu sein. Was Microsoft angeht denke ich, dass ich nicht der erste bin, der FAT16-Unterstützung implementiert. Mein Code basiert komplett auf Informationen, die bereits öffentlich im Internet zu finden sind. Wenn mich Microsoft ansprechen sollte, werde ich das Projekt natürlich sofort von meiner Seite entfernen.
@alle Ich habe nochmal eine neue Version auf http://www.roland-riegel.de/sd-reader/ bereitgestellt und auch an dieses Posting gehängt. Die Neuerungen sind: - Zwei Funktionen, die die Größe des gesamten und des freien Speicherplatzes berechnen. - Unterstützung der Backspace-Taste in der Mini-Shell. - Aktivierung des Idle-Mode beim Warten auf die UART. - Kurz-Dokumentation der Mini-Shell-Befehle. - (un)select_card() ist nun auch MCU-abhängig. Gruß, Roland
>Im Übrigen sollte der Code schon plattformunabhängig sein.
Ein Tip aus eigener Erfahrung, du hast sowieso nur eine Lese-/Schreib
und Kartendetektionsroutine. Der Rest sollte Hardwareunabhängig sein.
Wenn du die genannten Routine auf dem PC in eine Datei/Device umlenkst
kannst du alles auf dem PC entwickeln, aufgrund der erweiterten
Debuggingmöglichkeiten auf dem PC geht es wesentlich schneller voran
und es wird mit Sicherheit plattformunabhängig. Ganz nebenbei schont es
den flash der MCU und der SDCard.
Hallo Wolfram, Ganz zu Beginn hatte ich das schon so gemacht. Ich habe mir ein Image der SD-Karte gezogen und auf dem PC mittels fopen(), fread() und fclose() darauf zugegriffen. Als ich dann Dateien in dem Image auflisten und dumpen konnte sowie in Verzeichnisse wechseln konnte, bin ich dann auf den AVR umgestiegen. Gruß, Roland
@Roland: Ich habe zumindest das eine Dokument im Netz gefunden: http://www.walrus.com/~raphael/pdf/FatFormat.pdf#search=%22FAT%20General%20Overview%20Microsoft%20white%20paper%20pdf%22
Hallo Wolfram, Danke, dass Du das noch auftreiben konntest. Werde ich mir bei Gelegenheit mal durchschauen. Vielleicht findet sich ja noch das ein oder andere Detail, dass ich anders oder sogar falsch implementiert habe. Gruß, Roland
Hi! Wenn ich mich nicht verlesen habe so erkennt man die Fatart auch an den 1ten Byte jeder Fat. $0FF8 = Fat 12 $FFF8 = Fat 16 $0FFFFFF8 = Fat 32 oder Offset Description Size 000h Executable Code (Boots Computer) 446 Bytes 1BEh 1st Partition Entry (See Next Table) 16 Bytes 1CEh 2nd Partition Entry 16 Bytes 1DEh 3rd Partition Entry 16 Bytes 1EEh 4th Partition Entry 16 Bytes 1FEh Boot Record Signature (55h AAh) 2 Bytes Partition Entry (Part of MBR) Offset Description Size 00h Current State of Partition (00h=Inactive, 80h=Active) 1 Byte 01h Beginning of Partition - Head 1 Byte 02h Beginning of Partition - Cylinder/Sector (See Below) 1 Word 04h Type of Partition (See List Below) 1 Byte 05h End of Partition - Head 1 Byte 06h End of Partition - Cylinder/Sector 1 Word 08h Number of Sectors Between the MBR and the First Sector in the Partition 1 Double Word 0Ch Number of Sectors in the Partition 1 Double Word Partition Type Listing There are more than just these shown, but I've only included that ones relevant to MS Operating Systems. Value Description 00h Unknown or Nothing 01h 12-bit FAT 04h 16-bit FAT (Partition Smaller than 32MB) 05h Extended MS-DOS Partition 06h 16-bit FAT (Partition Larger than 32MB) 0Bh 32-bit FAT (Partition Up to 2048GB) 0Ch Same as 0BH, but uses LBA1 13h Extensions 0Eh Same as 06H, but uses LBA1 13h Extensions 0Fh Same as 05H, but uses LBA1 13h Extensions Die Fatstartadresse musst du sowiso berechnen, da kannste den Sector auch mal lesen. Wenn du es dann noch mit dem MBReintrag vergleichst sollte das eigentlich recht sicher sein. Du kannst natürlich auch die VBReinträge prüfen und aus der Clusterzahl auf die Fat schließen. schönen Tag noch, Uwe
@Uwe Die ersten zwei Bytes der FAT auszuwerten ist aber wohl auch bloß ein Notbehelf. Zudem wäre es noch durchaus denkbar, dass diese zwei Bytes zufällig korrekte Werte haben, der Datenträger in Wirklichkeit aber ganz was anderes als FAT16 enthält. Inzwischen habe ich die von Wolfram vorgeschlagene Unterscheidung anhand der Datenclusteranzahl implementiert. Dies wird im Microsoft-Paper sogar als einzig richtige Methode dargestellt. @alle Dadurch konnte ich auch gleich noch einen anderen Fehler beheben. Mein Code ging bisher davon aus, dass die FAT immer an einer Sektorgrenze endet. Daher konnte es vorkommen, dass bei nahezu voller Karte Cluster über die Kartengröße hinaus allokiert wurden. Den korrigierten Code werde ich wohl noch diese Woche hier posten. Gruß, Roland
Moin, erstmal Glückwunsch zu dem Projekt und Danke an alle, die sich hier richtig reinhängen, speziell an Roland. Ich möchte habe auch ein Projekt, bei dem ich eine SD/MMC als Datenlogger mit einbinden möchte und erstelle gerade das Konzept. Nun meine Frage: Um die Anzahl der Bauelemente und den benötigten Platz klein zu halten, wollte ich den Atmega168 direkt bei 3,3V betreiben und mir den Levelshifter sparen. Bis 10Mhz ist das ja laut Datenblatt zulässig oder habe ich noch was übersehen? Noch eine Bitte: Könnte jemand den Schaltplan mal als PDF, JPG oder PNG posten? Ich habe hier auf Arbeit kein Eagle .... Danke! abo
@Wirus Laut Datenplatt sind 10MHz@3,3V möglich. Achte aber darauf, dass je nach Art auch Dein Programmieradapter die Spannung unterstützen muss. Schaltplan ist angehängt. Gruß, Roland
So, wie angekündigt gibts nochmal eine neue Version auf http://www.roland-riegel.de/sd-reader/ Die wichtigsten Veränderungen sind: - Verbessertes Handling des Idle-Mode. - Anzeige weiterer Kartenparameter beim "disk"-Befehl. Dieser wird jetzt auch beim Start automatisch ausgeführt. - Identifikation von FAT16 anhand der Clusteranzahl. - Mögliche Clusterreservierung über die Kartenkapazität hinaus korrigiert. Quelltext wie immer auch angehängt. Roland
Hallo Roland, schönen Dank für die neue Version. Mir ist aufgefallen, dass die disk-funktion nur etwa die Hälfte des tatsächlich freien Speicherplatzes anzeigt. Eine leere 256MB disk zeigt folgendes: manuf: 0x02 oem: TM prod: SD256 rev: 68 serial: 0x707dd547 date: 03/05 size: 255066112 copy: 0 wr.pr.: 0/0 format: 0 free: 120578048/254795776 Unter Windows wird die korrekte Größe angezeigt. Filesystem anlegen und formatieren habe ich unter Linux gemacht (eine unter Windows formatierte disk wurde nicht erkannt). Habe ich beim formatieren vielleicht etwas falsch gemacht? Jedenfalls habe ich bei mkdosfs ausser dem device nichts weiter angegeben. fdisk zeigt 8 Köpfe, 61 Sektoren, 1020 Zylinder. Gruß Malte
Hallo Malte! Zwei mögliche Ursachen fallen mir ein: - Vor dem Auswerfen der Karte wurde nicht richtig synchronisiert. (unter Linux mit "eject /dev/sda", i.d.R. aber nicht nötig) - Windows verwendet die zweite FAT. Vernünftig erklären kann ich es mir aber eigentlich nicht. Könntest Du bitte mal unter Linux die ersten 256kByte auf der Karte dumpen und mir per Mail (feedback@roland-riegel.de) schicken? Zur Info (Annahme: SD-Karte als /dev/sda): dd if=/dev/sda1 of=fat.dump bs=16k count=16 Die Datei fat.dump schickst Du mir dann bitte zu. Mal sehen ob uns das irgendwie weiterhilft. Gruß, Roland
die kann man auch hier hochladen. Ihr immer mit eurem Email hinundhergeschickte. Dafür ist das Forum nicht da!
@Simon Die anderen Teilnehmer wird die FAT von Malte aber kaum interessieren, für sie ist es ja bloß Datenmüll. Oder was sollen Sie Deiner Meinung nach damit anfangen? Roland
Ok. Noch eine Sache: die neue Version (0824) funktioniert bei mir leider nicht wenn externe Interrupts benutzt werden. Bei den älteren Versionen hatte ich gar keine Probleme damit. Daher muss ich jetzt erstmal mit der älteren Version (0816) weiterarbeiten. Any idea? Vielleicht irgendwas mit dem Idle-Mode? Gruß Malte
Malte Ersetze einfach in "uart.c" #define USE_SLEEP 1 durch #define USE_SLEEP 0 und kommmentiere in der main() in "main.c" folgende Zeile set_sleep_mode(SLEEP_MODE_IDLE); einfach aus. Dann sollte es eigentlich sein wie vorher. Roland hat den Idle-Mode auf meinen ausdrücklichen Wunsch hin eingebaut. Wir haben lange diskutiert wie es am sinnvollsten ist. Nur an einen ext.Interrupt hat keiner gedacht.. Torsten
Wie Torsten schon sagt, ist das mit dem Sleep-Mode etwas heikel, und sollte eventuell für ein richtiges Projekt deaktiviert werden. Die ISR für den externen Interrupt sollte zwar schon ausgeführt werden. Wenn Du dort jedoch wie üblich nur ein Flag setzt, welches dann in der main() abgefragt und abgearbeitet wird, gibt es Probleme. Denn uart_getc() wartet immer noch auf das nächste Uart-Zeichen, und die main() kommt nicht zum Zug. Für das Beispiel-Programm auf meiner Homepage mag das Verhalten sinnvoll sein. Für ein anderes Projekt jedoch, wo die MCU noch andere Dinge zu erledigen hat, wohl eher nicht.
Welche aufgabe übernimmt eigentlich am avr immer der port ss an der mmc-karte? kann man auch eien anderen port nehmen?
Du kannst jeden beliebigen Port-Pin wählen. Die SS- oder auch CS-Leitung dient zur Adressierung der Karte. Wenn diese Leitung auf Low liegt, fühlt sich die Karte angesprochen und reagiert auf Befehle. Ist sie jedoch High, verhält sie sich passiv. Wenn Du am SPI-Bus nur die Karte angeschlossen hast, kannst Du Dir den Pin sparen und den entsprechenden Anschluss der Karte auf Masse legen. Ansonsten jedoch ist er nötig, um die Karte nicht durch den SPI-Verkehr mit anderen Geräten zu stören, und umgekehrt. Einige Chips, wie der ENC28J60, benötigen diese Leitung auch als eine Art "Befehlstakt". In diesem Fall kann die Leitung natürlich nicht eingespart werden.
da ich nur die mmc-karte anschliessen möchte und diese auch nur betreibe, kann man den anschluss an der mmc cs sofort auf masse legen?
hier sind ja alles fachleute. ich möchte die mmc-karte nicht mit dos und fat betreiben. ich möchte einfach die mmc-karte initialieren und dann 512bytes lesen, schreiben und evtl löschen. wo kann man in winavr-c einfache beispiele ausmachen. ich steige durch den oberen code für die mmc-karte nicht durch, finde als laie keine timing, adressen usw. danke. mfg
@Peter Lese Dir doch bitte meine obige Antwort sorgfältig durch, dass sollte Deine erneute Frage bzgl. des CS-Anschlusses der Karte beantworten. Was willst Du denn für die MMC-Ansteuerung über den SPI-Bus für Timing-Parameter oder Adressen wissen? Alles was Du für Deinen Anwendungszweck von meinem Code brauchst, steht in den Dateien sd_raw.c, sd_raw.h und sd_raw_config.h. Den Rest kannst Du weglassen. Mach Dir bitte die Mühe und beschäftige Dich mit dem Code. Im Unterverzeichnis doc/html findest Du nicht umsonst eine ausführliche Dokumentation der erforderlichen Funktionen. Roland
Hallo Roland, bin gerade dabei, einen Datenlogger mit einem Mega32 zu bauen und nutze dabei Deinen Code zur Ansteuerung der SD-Karte. Hatte auch null Probleme, den Code einzubinden, ist schön übersichtlich und gut kommentiert (besser als meiner...;-)), echt klasse. Leider sind die Ausführungszeiten mancher Funktionen zu lang, so dass mir (bei höheren Sampleraten) jedes Mal wenn ein Block auf die Karte geschrieben wird, samples verloren gehen, weil zum Beispiel die CPU auf die SD-Karte wartet: while(sd_raw_rec_byte() != 0xff); Ich suche nun nach einer Möglichkeit, diese busy waits zu vermeiden und statt dessen ins Hauptprogramm zurückzukehren, um bei Bedarf weitere Messungen anzutriggern (gemessen wird mit einem MAX127 über TWI). Ich habe schon versucht, statt der Warteschleife ein flag zu setzen und die Warteschleife dann am Anfang des nächsten Aufrufs auszuführen, hat aber nicht geklappt, wahrscheinlich weil nach dem write ein read durchgeführt werden muss, um den nächsten Block zu finden, und die Karte dann noch nicht bereit ist. Ich fürchte, der Code müsste massiv umgeschrieben werden, um hier deutliche Verbesserungen zu erzielen. Wahrscheinlich müsste dann auch Deine elegante Aufteilung in Schichten dran glauben... Irgendwelche Ideen, in welcher Richtung ich hier am schlauesten vorgehen könnte? Gruss, Thorsten
Hallo Thorsten! Wie groß ist denn Deine zu erwartende Datenrate, mit der Du auf die Karte schreiben willst? Nutzt Du den FAT16-Treiber, oder schreibst Du im Raw-Format? Die FAT-Schicht verursacht natürlich einiges an Overhead, um das Dateisystem zu verwalten. Bevor Du versuchst, den Code im großen Stil umzuschreiben, solltest Du die maximal mögliche Schreibdatenrate ermitteln. Wenn die Datenrate unter Deinen Anforderungen liegt, müsste man etwas Grundlegendes ändern (MCU-/SPI-Takt, Multi-Block-Writes, usw.). Ich habe noch nicht genau getestet, ob die SD-Karten während des eigentlichen Schreibvorgangs einen SPI-Takt benötigen. Wenn ja, muss die MCU ständig die Karte pollen, um überhaupt den Schreibvorgang abzuschließen. D.h. dann wäre das Busy-Waiting notwendig. Ansonsten könntest Du das Problem mit irgendwelchen Callback-Techniken (wie z.B. in sd_raw_read_interval()), Interrupts oder einem einfachen Multi-Threading lösen. Als alles erschlagende Methode könntest Du auch einen zweiten Controller einsetzen, der ausschließlich die Karte ansteuert und vom ersten Controller die Daten bekommt. Falls Du die Daten bisher in einem menschenlesbaren Format abspeicherst, bringt ein binäres Format auch wesentliche Einsparungen. Je nach Schreibgeschwindigkeit wirst Du wohl auch um eine großzügige Pufferung der Daten nicht herumkommen. Gruß, Roland
Hallo Roland und Thorsten, ich habe gerade exakt das gleiche Problem wie Thorsten und probiere schon eine ganze Weile rum. Bei meinem Datenlogger wird alle 2.5ms ein Interrupt ausgelöst, 2 bytes eingelesen und in ein Array geschrieben. Das macht 800B/s. Wenn das Array mit 512 bytes voll ist, wird es auf die Karte geschrieben (mit FAT16). Dieser Schreibvorgang dauert unterschiedlich lang. Normalerweise irgendwas zwischen 2.5 und 5ms, bei jedem achten Schreibvorgang (4096 bytes) aber etwa 5 mal so lange. Bei Puffergröße=256 bytes kommt der langsame Schreibvorgang auch immer nach 4096 bytes (16 x schreiben). Während des Schreibens gehen unregelmäßsig manchmal ein paar Samples verloren. Ich habe auch versucht, immer die jeweils 2 gelesenen bytes direkt innerhalb der ISR auf die Karte zu schreiben. Ohne Erfolg, auch beim schreiben von nur 2 bytes dauert irgendwas zu lange. Übrigens habe ich die zu schreibende Datei vorher mit resize genügend groß gemacht, daher sollte auf der FAT nicht mehr "rumgeorgelt" (O-Ton letzte c't) werden müssen. Dies war ja auch ein Tip von Roland, weiter oben in diesem Thread. Wenn ich den Buffer per UART rausschicke, gibt es keine Probleme. Der Sendevorgang wird zwar dauernd durch Interrupts unterbrochen, es kommt aber alles heil und vollständig an. Einzige Lösung die mir (wie Roland) eingefallen ist, wäre ein zweiter Controller. Bin natürlich interessiert daran, ob ihr den Code noch irgenwie schneller oder anders getimed hinkriegt. Gruß Malte
Hallo Roland, Meine erwartete Datenrate sind ca. 20kbytes/s, zur Verwendung kommt ein Binärformat. In reinen Schreibtests mit Deinem FAT16-Treiber konnte ich ca. 60kbytes/s erzielen, sollte also drin sein (mein Mega32 läuft mit 14.7456MHz). Mein Problem ist nicht der absolute Overhead, sondern die Latenzzeit, die auftritt wenn ein Block geschrieben wird. Ich verwende den FAT16-Treiber, da die Karte nach der Messung im PC ausgelesen werden soll. Da meine Datenrate recht konstant ist, bringt ein grösserer Datenpuffer nicht so richtig was (wenn die Daten schneller kommen, als der Prozessor sie wegschreiben kann, geht es halt einfach nicht, egal wie gross der Puffer ist). Ausserdem löst eine geringere Datenrate das Problem nicht, denn die Latenzzeit tritt zwar weniger oft auf, ist jedoch immer noch genauso gross (grösser als mein Samplingintervall). Eventuell könnte ein Puffer für die FAT16-Metadaten etwas bringen? Interrupts könnten auch was bringen, allerdings müssten dann auch FAT16-Routinen in die ISR gepackt werden, sonst wartet dann halt die FAT-Routine statt der sd_raw routine, und ich bin immernoch da, wo ich am Anfang war (daher meine Befürchtung, dass grössere Änderungen am Code nötig werden). Bei f/2 SPI Takt habe ich pro zu übertragendem Byte 16 Prozessortakte zur Verfügung, von den allerdings die meisten als Overhead (springen in und aus der ISR) wieder wegfallen. Das mit dem zweiten Controller habe ich mir auch schon überlegt, versuche ich aber zu vermeiden, weil dadurch eine Menge zusätzlicher Komplexität entsteht. Werde mir mal sd_raw_read_interval() genauer angucken. Danke für die Tips, Thorsten
Thorsten Die FAT-Routinen kannst Du nicht in eine ISR packen weil die einfach zu lange dauern. Nach kurzer Zeit gibt es einen Stack-Überlauf. Die einzige Lösung wäre ein FIFO-Buffer der in einer ISR gefüllt wird. Wenn Du Daten vom TWI lesen willst dann hast Du doch schon fast gewonnen. Dafür gibt es doch extra eine ISR. In der Hauptschleife schaust Du einfach ständig nach ob der FIFO neue Daten enthält und schreibst diese weg. Torsten
Hallo, > Da meine Datenrate recht konstant ist, bringt ein > grösserer Datenpuffer nicht so richtig was (wenn die > Daten schneller kommen, als der Prozessor sie > wegschreiben kann, geht es halt einfach nicht, > egal wie gross der Puffer ist). Wenn die Daten mit 20kB/s kommen und Du 60kB/s wegschreiben kannst, kommen sie doch nicht schneller als Du sie schreiben kannst?!? Ich denke, hier kommt es entscheidend darauf an, wie die Sample-Daten zustande kommen. Vorausgesetzt, die Daten werden durch den AVR parallel ohne Zutun des Programms erzeugt (z.B. im ADC), kann man es wie folgt machen: 1. Man reserviert sich zwei oder mehr Puffer, von denen immer genau einer "aktiv" ist. 2. Sind neue Daten verfügbar, wird ein Interrupt ausgelöst, in dessen ISR man die neuen Daten in den aktiven Puffer schreibt. Ist der Puffer voll, wird der andere Puffer aktiv und empfängt ab dann neue Daten. 3. In der Hauptschleife wird überprüft, ob es einen vollen Puffer gibt. Wenn ja wird dieser auf die Karte geschrieben und anschließend geleert. Statt mehreren Puffern könnte man je nach Datenrate auch einen einzelnen als Ringpuffer nehmen und zu schreiben beginnen, wenn mehr als 512 Byte enthalten sind. Natürlich muss das Timing bzw. die Datenrate so passen, dass in der Zeit, in der der eine Puffer auf die Karte geschrieben wird, der andere nicht überläuft. Werden die Daten nicht über einen Interrupt angekündigt, kann man die Warteschleife in sd_raw_write() vielleicht dennoch durch einen Timer-Interrupt unterbrechen, der dann weitere Datensamples zwischenspeichert. Gruß, Roland
Torsten, Ich dachte auch nicht dran, die FAT16-Routinen samt ihren Wartezyklen in eine ISR zu packen, sondern die SPI-ISR so zu schreiben, dass sie bei jedem Aufruf entscheidet, was als nächstes zu tun ist (Byte zum SPI schreiben/lesen, wenn der Block fertig ist, gucken was als nächstes zu tun ist, z.B. die FAT Einträge draus lesen usw.). Würde aber trotzdem eine ziemlich grosse ISR geben, auch wenn bei jedem Aufruf nur ein kleiner Teil davon zur Ausführung kommt. Das ist auch meiner Meinung nach nicht der richtige Weg. Das Problem ist halt, dass viele der Routinen einfach "busy" warten, bis der Kartenzugriff abgeschlossen ist. Dadurch kriege ich immer die Latenzen, egal ob die Karte nun durch eine ISR oder eine andere Routine angesteuert wird. Was möglicherweise ginge, wäre aber der umgekehrte Weg: - Es gibt nicht einen Buffer, sondern zwei. Ein Pointer zeigt auf den gerade aktiven. - Wenn ein Block geschrieben werden soll, wird der SPI Interrupt aktiviert und das erste Byte geschrieben. Die ISR sorgt dann dafür, dass der Buffer asynchron auf die Karte geschrieben wird (Globale Statusvariablen kontrollieren das, wenn der Block fertig ist, deaktiviert die ISR den SPI Interrupt). Gleichzeitig wird der Zeiger auf den zweiten Buffer gestellt, so dass weitere Schreibzugriffe abgebuffert werden können. Die sd_raw Funktion kann sofort zurückkehren. Wo ich noch nicht so genau weiss, wie man es lösen könnte, ist das lesen: Was mache ich mit einer Routine, die einen Block einliest und dann bearbeitet? Die blockiert dann wieder... (zwangsläufig). Was hältst Du davon? Die andere Möglichkeit wäre, wie Du schreibst, die TWI ISR zu benutzen. Ich muss mir nur noch überlegen, wie ich die Messung antriggere, könnte ich in meiner RTC ISR am INT0 machen (die kriegt 32768Hz von der RTC). Ansonsten wäre das evtl. noch besser, denn die TWI ISR feuert nicht so schnell wie die SPI ISR (400kHz vs. 7MHz), d.h. mir bleiben mehr Takte dazwischen übrig. Mal gucken... Gruss, Thorsten
Roland, > Wenn die Daten mit 20kB/s kommen und Du 60kB/s wegschreiben kannst, > kommen sie doch nicht schneller als Du sie schreiben kannst?!? Sorry, hab mich wohl undeutlich ausgedrückt. Der grössere Datenpuffer bringt mir natürlich deswegen nix, weil ich Samples verpasse, während ich auf die Karte warte (und daher nicht messen & in den Buffer schreiben kann). Er würde aber auch bei zu hohen Datenraten nix bringen (das wollte ich eigentlich zum Ausdruck bringen). Mein Problem sind aber, wie gesagt, die Latenzen. Ich glaube inzwischen, dass es am besten ist, zu versuchen die Messungen komplett über die ISRs zu steuern, während der Kartenzugriff dann vom Hauptprogramm aus kontrolliert wird. Werde mal Versuche machen und berichten, wie es so klappt. Gruss, Thorsten
> - Es gibt nicht einen Buffer, sondern zwei. Ein Pointer zeigt auf den > gerade aktiven. Double Buffering eben, geht auch. Nur, der Mega hat wenig Speicher und der Verwaltungsaufwand dafür ist auch erheblich. Viel einfacher wäre es mit einem Ring-Buffer und Zeigern auf Head und Tail zu arbeiten. > - Wenn ein Block geschrieben werden soll, wird der SPI Interrupt > aktiviert und das erste Byte geschrieben. Die ISR sorgt dann dafür, > dass der Buffer asynchron auf die Karte geschrieben wird (Globale > Statusvariablen kontrollieren das, wenn der Block fertig ist, > deaktiviert die ISR den SPI Interrupt). Gleichzeitig wird der Zeiger > auf den zweiten Buffer gestellt, so dass weitere Schreibzugriffe > abgebuffert werden können. Die sd_raw Funktion kann sofort > zurückkehren. Das mit dem SPI Interrupt hört sich gut an. Leider muß dann Roland's Code komplett neu geschrieben werden. ;) > Wo ich noch nicht so genau weiss, wie man es lösen könnte, ist das > lesen: Was mache ich mit einer Routine, die einen Block einliest und > dann bearbeitet? Die blockiert dann wieder... (zwangsläufig). Ja, das stimmt. Schwierig zu lösen. > Was hältst Du davon? Google mal nach einem Projekt nennt sich "AVRCam". Das Konzept gefällt mir sehr gut. Dort wird mit einer globalen Event-FIFO gearbeitet. Fast alle Events sind nicht-blockierend. TWI wurde auch komplett in die ISR ausgelagert. Schau Dir die Quelltexte unbedingt einmal an. Wenn dieses Projekt CPU- und Speicher-intensives Video verarbeiten kann muß es doch möglich sein einen Datenlogger mit SD-Karte auf die Beine zu stellen. ;-) BTW: Die disk-info von Malte nach einer Neuformatierung >free: 120578048/254795776 gefällt mir überhaupt nicht. So ähnlich verhielt sich auch meine Karte auch kurz bevor sie ganz und gar den Geist aufgegeben hat. :-( Torsten
Hallo Thorsten, Ich glaube, wir missverstehen uns hier ein bisschen. Wovon ich oben ausgegangen bin ist, dass die Messung als solche durch eine Hardware-Einheit des AVR stattfindet, somit keine MCU-Zeit braucht und zum Schluss jeder Messung ein Interrupt (wie der des ADC) auslöst. Die Interrupts sind doch in der Hinsicht praktisch, dass sie eben das busy waiting unterbrechen. D.h. zeitkritische Sachen (wie das Erfassen eines Messwertes) erledigt man in einer ISR, und Arbeiten mit "hohen Latenzen" (wie das Schreiben auf die Karte) erledigt man im Hauptprogramm. Dem SPI bzw. der Karte ist es ja wurscht, ob durch die ISR-Abarbeitung eine kleine Verzögerung im Datenverkehr auftritt. Der Interrupt, der am Ende der Messung ausgelöst wird, garantiert, dass man den Messwert nicht verpasst. Das alles funktioniert natürlich nicht, wenn das Messen als solches keine Interrupts auslöst und noch dazu MCU-Zeit verbraucht. Was misst Du denn, und wie genau? Irgendetwas mit dem SPI-Interrupt anzufangen halte ich für sinnlos. Der feuert mit knapp 1MHz zu häufig. Natürlich muss das mit den Puffergrößen und den Verzögerungszeiten durch das Wegschreiben zusammenpassen. Aber irgendwann sind halt die Grenzen der MCU erreicht, was Verarbeitungsgeschwindigkeit und Speichergrößen betrifft. Eventuell sollte man dann einen anderen Controller wählen. Roland
Vielleicht mit weniger Worten in Pseudocode: ISR(ADC_complete) { puffer.add(ADC_value); } int main() { while(true) { if(puffer.size >= 512) { sd_raw_write(puffer.data(0 to 511)); puffer.remove(0 to 511); } } } Natürlich wäre der Zugriff auf den Puffer noch mit einer Semaphore oder ähnlichem zu synchronisieren. Roland
> Ich glaube, wir missverstehen uns hier ein bisschen. Wovon ich oben > ausgegangen bin ist, dass die Messung als solche durch eine > Hardware-Einheit des AVR stattfindet, somit keine MCU-Zeit braucht und > zum Schluss jeder Messung ein Interrupt (wie der des ADC) auslöst. Ich bin zwar der Torsten ohne 'h' im Namen aber der andere wollte Daten vom TWI mit Hilfe eines MAX127 (kenne ich nicht...) lesen. Also mit HW Unterstützung, und hoffentlich ohne busy-wait beim Register lesen. > Irgendetwas mit dem SPI-Interrupt anzufangen halte ich für sinnlos. Der > feuert mit knapp 1MHz zu häufig. Ops, Du hast recht. Eine ISR macht bei der Geschwindigkeit keinen Sinn mehr. Torsten
Oops, habe gerade gemerkt dass da während dem Schreiben meiner beiden Beiträge noch was dazugekommen ist. Ich meinte also nicht Dich, Thorsten S., sondern den anderen Thorsten :) Roland
@Torsten (ohne h) Das mit dem TWI hatte ich überlesen. D.h. für das Messen ist auch ein Polling nötig. Dann wird das natürlich schwieriger, ohje...
Hallo Roland, Also ich mach das genau so wie deinem Pseudocode. Das Problem ist nur, dass während sd_raw_write schon wieder neue Interrupts kommen. Wozu eigentlich das busy waiting? Wenn man den SPI nicht anderweitg verwendet, kann man die Karte doch in Ruhe schreiben lassen, während man den nächsten Puffer mit Daten vollmacht, ohne erst zu warten bis die Karte fertig ist. Das ist auf jeden Fall genug Zeit. Man kann dann ja VOR dem nächsten Schreiben das busy flag prüfen. Siehe http://elm-chan.org/docs/mmc/mmc_e.html Die von mir berichteten langen Schreibzeiten nach 4096 byte kommen übrigens von einem karteninternen Puffer (dito). Malte
Roland, ich sehe, worauf Du hinauswillst. Gemessen wird mit zwei MAX127 am I2C Bus. Für jeden Messwert müssen 5 bytes über den Bus (Adresse + Kontrollbyte zum starten der Messung, dann Adresse plus zwei Nullbytes zum lesen des Messwertes). Pro Messung (16 Kanäle) sind das also 80 bytes, bei 400kHz also ca. 1.6ms. Bei einem Sample-Interval von 4ms hätte ich dann noch ca. 2.4ms pro Takt zum speichern, was zwar im Mittel ausreicht, wegen dem angesprochenen Latenzproblem jedoch nicht funktioniert. Zur ISR: Bisher verwende ich eine modifizierte Variante des Atmel-Beispielcodes. Die ISR kümmert sich hier ums versenden/empfangen der Messages, initialisieren und verarbeiten tu ich die Ergebnisse in einer normalen Unterfunktion, die im Augenblick noch vom Hauptprogramm aus aufgerufen wird. Das Timing geschieht in einer ISR, die von einem 32kHz Takt angesteuert wird (der Takt kommt von der RTC). Wenn ich nun aus dieser ISR jeweils alle 4ms die erste TWI Message anschubse, und in der TWI-ISR dann (gesteuert über ein globales Flag) automatisch die nächste TWI Message vorbereite, wenn die letzte vollständig ist (usw.), könnte es gehen. Die Messwerte in der TWI-ISR in einen Buffer schreiben, der einfach im Hauptprogramm dann gemütlich auf die SD-Karte wandert. So könnte es gehen. Und das beste: Ich muss den SD-Code nicht verändern. Dafür halt die TWI-ISR. Werde das mal so probieren. @Torsten: Werde mir auch mal AVRCam angucken, danke für den Tip. Das Forum hier ist spitze :-) Gruss, Thorsten
Hallo Malte, Es macht doch nichts, wenn die Karte noch schreibt und schon der nächste Interrupt kommt? Wenn Du, wie oben beschrieben, zwei Puffer abwechselnd füllst und gleichzeitig den jeweils anderen schreibst, kommt Dir das ja nicht in die Quere. In meinem Pseudocode oben war puffer als Ringpuffer gedacht. D.h. Du kannst vorne Daten entnehmen, um sie auf die Karte zu schreiben, und gleichzeitig hinten neue Daten im Interrupt anhängen. Das dürfte bei Dir auch besser funktionieren als bei Thorsten, der eine wesentlich höhere Datenrate zu bewältigen hat. Das Verschieben der Wartezyklen an den Anfang könnte funktionieren. Müsste man mal ausprobieren. Wie schon erwähnt weiß ich nicht, ob die Karte den SPI-Takt, der durch das Polling erzeugt wird, zum Schreiben benötigt. Wenn das der Fall ist bringt das verschieben des Wartezyklus nichts. Roland
> Das mit dem TWI hatte ich überlesen. D.h. für das Messen ist auch ein > Polling nötig. Dann wird das natürlich schwieriger, ohje... Nein, ganz im Gegenteil, das macht es ja einfacher weil es eine extra ISR für TWI gibt. Mein Problem ist dass ich den MAX127 noch nicht kenne und die Art und Weise wie man an dessen Daten kommt. Ich google gleich mal. Langsam wird der Thread unübersichtlich, mehr als 100 Beiträge. Scrollen macht jetzt keinen Spaß mehr. Was haltet Ihr von einer Fortsetzung in einem neuen Thread? Torsten
Ich meinte, schwieriger im Sinne meines Lösungsvorschlages. Der funktioniert dann nicht mehr. Einen neuen Thread fände ich gut, aber nur für die Probleme von Malte und Thorsten. Antworten auf Fragen direkt zu meinem Code oder neue Versionen von diesem werde ich wohl weiterhin hierher posten. Roland
Hallo Roland, >Es macht doch nichts, wenn die Karte noch schreibt und schon der >nächste Interrupt kommt? Irgendwie scheinbar doch. Es fehlen immer Samples. Allerdings arbeite ich mit nur einem Puffer, weil ich denke, dass das Wegschreiben über SPI wesentlich schneller geht als das Einlaufen neuer Samples. Das muss ich mir aber noch mal genau überlegen, vielleicht liegt da der Hase im Pfeffer. >Wie schon erwähnt weiß ich nicht, ob die >Karte den SPI-Takt, der durch das Polling erzeugt wird, zum Schreiben >benötigt. In dem von mir oben zitierten Link steht folgendes: "In principle of the SPI mode, the CS signal must be asserted during a transaction, however there is an exception to this rule. When the card is busy, the host controller can deassert CS to release SPI bus for any other SPI devices. The card will drive DO signal low again when reselect it during internal process is in progress. Therefore a preceding busy check (wait ready immediataly before command and data packet) instead of post wait can eliminate waste wait time." Daraus schliesse ich, dass das Polling nicht benötigt wird. In dem Link steht übrigens noch so einiges über write-performance Verbesserung, zB. dass MMCs viel schneller sind als SDs... Gruß Malte
Hallo, das Verschieben der Wartezyklen an den Anfang hat bei mir nicht funktioniert. Nach ein paar Schreibzyklen stürzt irgendwas ab. Dafür funktioniert jetzt bei mir alles, nachdem ich einen zweiten Puffer eingeführt habe. Wenn der erste voll ist wird er per memcpy in den zweiten kopiert und der zweite dann weggeschrieben. Kann man sicher noch eleganter machen... Allerdings kann ich jetzt nur noch mit 256 bytes Puffergröße arbeiten. Bei 512 sagt der Compiler zwar, dass nur ~95% des Speichers voll sind, das Programm läuft aber trotzdem nicht mehr. Da muss ich nochmal ein bisschen im Forum suchen um zu verstehen, woran das liegt. Gruß Malte
Hallo Malte, Danke für das Zitat der Seite. Den Link kannte ich zwar schon, die Stelle über das Vorziehen der Wartezeit hatte ich jedoch auf die Schnelle nicht gefunden. Wenn ich dazu komme werde ich das mit dem Vorziehen selbst mal ausprobieren. Mal sehen ob ich da was zustande bringe. Das memcpy() kannst Du Dir sparen, wenn Du einen Pointer auf den Pufferanfang zum Schreiben verwendest. Dann brauchst Du dem Pointer für den Pufferwechsel nur die Adresse des anderen Puffers zuweisen und schon ist dieser aktiv. Wenn es mit 2x512 Bytes nicht mehr funktioniert, liegt das wahrscheinlich daran, dass der Stack in den Heap reinwächst. Kurz, Dir geht das RAM aus. Denn zu den 2x512Byte kommen ja noch die 512Byte Schreib-/Lesepuffer innerhalb von sd_raw hinzu. Dann bleiben von den 2kB des ATmega32 für alle Variablen, den Stack und den Heap nur noch 512Bytes übrig, und das wird wohl zu wenig sein. Gruß, Roland
Hi! >Wie schon erwähnt weiß ich nicht, ob die >Karte den SPI-Takt, der durch das Polling erzeugt wird, zum Schreiben >benötigt. Habe in der Hinsicht mal Messungen gemacht und festgestellt das langsame Karten dadurch viel schneller schreiben 1,8/1,1ms @250KHz/1MHz. Bei Karten, die von sich aus schon recht schnell sind, ist der Unterschied deutlich geringer 393us/372us. Wenn der Takt also ganz wegbleibt kann das schon zu Zeitverlust führen. Die gemessene Zeit ist übrigens der Abstand zwischen dem Ende des letzten zu schreibenden Byte und der Busy-reaktion, also reine Schreibzeit. Viel Erfolg, Uwe
Hallo, ich habe mal wieder ein Problem: das Vergrößern von files mit fat16_resize_file() funktioniert problemlos. Ein Verkleinern führt aber dazu, daß zumindest Windows das File zwar anzeigt aber nicht mehr öffnen kann. Eine Reparatur über "Fehlerüberprüfung" ist möglich, wenngleich etwas nervig. Ich benötige das Verkleinern aus folgendem Grund: Zunächst gehe ich davon aus, dass ich einen gesamten "Tagesfile" von etw 5MB aufnehmen werde und blähe die neue Datei auf diese Größe auf, um die vielen FAT Zugriffe zu vermeiden. Falls die Datenaufnahme aber vorher unterbrochen wird, möchte ich den File auf die entsprechende Länge zusammenstutzen. Der Ablauf ist etwa so: fat16_create_file(...) open_file_in_dir(...) fat16_resize_file (fd, 5000000) fat16_write_file(...) ... ... fat16_resize_file (fd, new_size) fat16_close_file (fd) sd_raw_sync() Wenn new_size >=5M ist geht es, sonst nicht. Woran könnte das liegen? Gruß Malte
Hallo Malte, Danke für den Hinweis. Aufgrund Deiner Probleme habe ich mir die Funktion fat16_resize_file() nochmal genauer angeschaut und festgestellt, dass diese seit der Version 20060808 zwei gravierende Fehler enthält. Durch diese wird u.a. beim Verkleinern einer Datei deren Clusterkette, die den Dateiinhalt enthält, in der FAT nicht richtig abgeschlossen. Das dürfte auch der Grund sein, warum Windows meckert. Ich werde hier morgen eine korrigierte Version posten. Gruß, Roland
@Malte In der angehängten fat16.c habe ich die folgenden Fehler korrigiert: - Verkleinerte Dateien werden korrekt abgeschlossen. - Abhängig von der Kartengröße wird der freie Speicherplatz nicht richtig berechnet, wenn die FAT-Größe UINT16_MAX übersteigt. Könntest Du die Datei bei Dir bitte kurz antesten? Nach Deinem Ok würde ich dann ein neues Quellcode-Paket erstellen. Danke und Gruß, Roland
Schande über mich! Die eben gepostete Version funktioniert nicht, wenn leere Dateien auf eine Größe kleiner der Clustergröße vergrößert werden. Die angehängte Version tut jetzt aber hoffentlich. Roland
Hallo Roland, vielen Dank für die überarbeitete Version. Das Verkleinern von Dateien klappt jetzt bei meinen Tests! Die Anzeige des freien Speicherplatzes stimmt nicht ganz: > ls 1455 MAIN.C 19200 malte.dat > disk manuf: 0x02 oem: TM prod: SD256 rev: 68 serial: 0x707dd547 date: 03/05 size: 255066112 copy: 0 wr.pr.: 0/0 format: 0 free: 240644096/254795776 H:\>dir Datenträger in Laufwerk H: hat keine Bezeichnung. Datenträgernummer: 44F0-263B Verzeichnis von H:\ 12.09.2005 23:54 1,455 main.c 01.01.1601 02:00 19,200 malte.dat 2 Datei(en) 20,655 Bytes 0 Verzeichnis(se), 254,275,584 Bytes frei Mir würde es aber auch reichen, wenn der return value von fat16_write_file() korrekt arbeitet, also '0 on disk full'. Ich habe das noch nicht ausprobiert. Es geht nur darum, während des Betriebes festzustellen, ob die Karte schon voll ist. Gruß Malte
Hallo Malte, Danke für den Test. Mit Deinem FAT-Image habe ich das Problem mit der nicht ganz korrekten Anzeige des freien Speicherplatzes auch, kann es mir aber nicht erklären. Vielleicht hilft es, wenn Du unter Linux nochmal mit mkdosfs -F 16 /dev/sda1 formatierst. Eventuell gibt es da durch vorangegangene Tests noch kleine Inkonsistenzen. Roland
Mal wieder gibts ein neues Source-Package auf meiner Homepage und auch angehängt an dieses Posting. Korrigiert habe ich wie schon diskutiert das Verkleinern von Dateien und die Berechnung des freien Speicherplatzes. @Malte Könntest Du bitte auf diese neue Version aktualisieren? Jetzt funktioniert die Speicherplatzberechnung auch mit Deiner FAT. Ich sollte Code-Änderungen einfach nicht voreilig posten... Gute Nacht, Roland
Hallo Roland, die Anzeige des freien Speichers nähert asymptotisch dem unter Windows angezeigten. Ich bekomme jetzt (nach einem mkdosfs -F 16 /dev/sda1): 254 369 799 (SD-reader) 254 435 328 (Win) Die Gesamtgröße der Karte wird bei beiden gleich angezeigt. Unter Windows wird allerdings, auch wenn sich nur ein 10 byte große Datei auf der Karte befindet, 4096 Byte belegter Speicher angezeigt. Ich weiss nicht, ob man der Sache auf den Grund gehen muß. Besten Dank und Gruß Malte
Hi Malte, Na das sieht ja schon ganz gut aus. Die 16 Cluster Unterschied machen nichts aus, das wird an einer im Detail unterschiedlichen Zählweise liegen. Ein bisschen wundern tut mich bloß, dass der AVR eine ungerade Bytezahl ausspuckt (oder ist das bloß ein Tippfehler?). Das mit der 10 Bytes großen Datei sollte mit meinem Code aber genauso sein, schließlich belegt sie einen ganzen Cluster von eben genau 4kB Größe. Und die Berechnung des freien Speicherplatzes beruht ja auf der Anzahl komplett freier Cluster. Viele Grüße, Roland
Hallo Roland, ich habe ein Problem mit der Funktion fat16_write_file, /* calculate new file position */ buffer += write_length; buffer_left -= write_length; fd->pos += write_length; Ist die zu schreibende Datei, die einzige Datei auf dem Datenträger wird der Dir. Eintrag nicht neu geschrieben. In der Abfrage sind dann beide Werte gleich: /* update directory entry */ if(fd->pos > fd->dir_entry.file_size) Wenn eine 2. Datei da ist, wird ein Update des Dir. aufgerufen. Funktioniert jedoch nicht. Im Dir. Eintrag steht dann noch die Alte Größe wobei die Dateidaten abgelegt wurden. Viele Grüße Reinhold
Hallo Reinhold, Könntest Du das bitte ein bisschen näher erläutern, was genau Dein Problem ist? Was hat insbesondere das von Dir zitierte if-Statement mit der Anzahl der gespeicherten Dateien zu tun? Kannst Du einen Korrekturvorschlag machen? Vielleicht verstehe ich dann besser, was Du meinst. Roland
Hallo Roland, das Problem hat sich geklärt. Die Funktion "sd_raw_sync()" wurde nicht angesprochen deshalb wurde das Dir. nicht upgedatet. Ein Speicherproblem war die 2. Ursache. Wenn ich allerdings an eine Datei ohne Offset Daten anhängen will hab ich ein Problem. Die Ursache liegt in der Funktion: fat16_seek_file(fd,&offset , FAT16_SEEK_END); Ist offset = 0 wird diese mit return 0 abgebrochen. Mein Vorschlag: uint8_t fat16_seek_file(struct fat16_file_struct* fd, int32_t* offset, uint8_t whence) { if(!fd ) // || !offset return 0; uint32_t new_pos = fd->pos; switch(whence) { case FAT16_SEEK_SET: new_pos = *offset; break; case FAT16_SEEK_CUR: new_pos += *offset; break; case FAT16_SEEK_END: new_pos = fd->dir_entry.file_size + *offset; break; default: return 0; } if(new_pos > fd->dir_entry.file_size && !fat16_resize_file(fd, new_pos)) return 0; fd->pos = new_pos; fd->pos_cluster = 0; *offset = new_pos; return 1; } Oder mache ich was falsch? Gruß und Danke schon mal für den Code Reinhold
Hallo Reinhold! Ja, Du machst etwas falsch, aber Du bist nicht der erste... Bitte schau Dir die Funktionsdeklaration nochmal genau an. Der Parameter offset ist vom Typ uint32_t* (man achte auf den Stern). Übergebe einen Zeiger auf eine Variable mit dem Inhalt 0, dann funktioniert es. Die Funktion nutzt den Zeiger, um Dir den absoluten Offset zurückzugeben, falls Du ihr nur einen relativen Offset gegeben hast. Gruß, Roland
Hallo Roland, jau ich hab meinen Fehler erkannt.... Gibt es einen Beispielcode zum anlegen eines Unterverzeichnisses? Ich muss mit Unterverzeichnissen arbeiten da im Rootverzeichnis die Anzahl der Einträge ja begrenzt ist. Bei einer neu formatierten SD soll dann das Verzeichnis automatisch angelegt werden. Gruß Reinhold
Hallo Reinhold, Am einfachsten wäre es natürlich, den Ordner am PC zu erstellen. Ansonsten könntest Du fat16_create_file() leicht abwandeln. Ein Ordner ist bei FAT16 nichts anderes als eine Datei mit den Verzeichniseinträgen als Inhalt. Wenn Du der Datei das Flag FAT16_ATTRIB_DIR gibst, wird sie zum Ordner. Eine Funktion gibt es dafür im Moment nicht. Roland
Hat das jemand schon einmal mit einer Kingston 256MB SD-Karte hinbekommen ? Ich bekomme bei dem Card init in Zeile 212 von sd_raw.c einen "test2" Fehler: /* reset card */ uint8_t response; for(uint16_t i = 0; ; ++i) { response = sd_raw_send_command_r1(CMD_GO_IDLE_STATE, 0); if(response == (1 << R1_IDLE_STATE)) { printf("test1\n"); break; } if(i == 0x1ff) { unselect_card(); printf("test2\n"); return 0; } } Die Karte läuft mit 3,3V über Spannungsregler. Teiler sind 3K3 und 2K Ohm, d.h. bei der Karte kommen die Pegel mit etwas über 3 Volt an. Nun ja, nicht so optimal. Würde auch 10k mit 6,6k gehen ? Dann liegt allerdings I=0,5mA recht niedrig ...
So, geht jetzt, war ein elektrischer Fehler. Vielen Dank für die Library !!! Jetzt steht dem Abenteuer SD-Card nichts mehr im Wege :-) Für den 644er musste ich Änderungen am Interface machen. Die Register enden dort alle mit "0" (z.B. SPCR0). Pete
Hallo Pete, Mit meiner Vermutung, dass etwas mit der SPI-Beschaltung nicht stimmt, bin ich jetzt wohl zu spät dran... Aber gut, dass es jetzt läuft. An was lag es denn genau? Das mit den unterschiedlichen Registernamen ist ein leidiges Thema. Es wird sich nicht vermeiden lassen, hier und dort einige Anpassungen an die jeweils verwendete MCU vorzunehmen. Einige gängige mega-Typen unterstütze ich ja von Haus aus, für exotischere AVRs will ich den Code aber nicht unnötig "ver-#ifdef-en", das wird dadurch eh alles schon nicht schöner. Gruß, Roland
Hallo Roland, die Funktion fat16_create_file() habe ich modifiziert zu fat16_create_dir(). Das Ende der Funktion habe ich dazu modifiziert. Der Dir-Eintrag im Root-Dir wird auch geschrieben aber das Unterverzeichnis funktioniert nicht richtig. ....... cluster_num= fat16_append_clusters(fs, 0, 1); /* write directory entry to disk */ dir_entry->cluster=cluster_num; dir_entry->attributes=FAT16_ATTRIB_DIR; dir_entry->date_create=format_time(); dir_entry->date_change=format_time(); dir_entry->entry_offset = dir_entry_offset; if(!fat16_write_dir_entry(fs, dir_entry)) return 0; return 1; #else return 0; #endif } Ach ja das dir_entry habe ich um die Zeit/Datumsinformation erweitert. Was muß ich machen damit das Unterverzeichnis richtig arbeitet? Gruß Reinhold
Hallo Roland, ich hab herausgefunden das die SUBs nur dann nicht funktionieren wenn in dem entsprechenden Cluster noch Datenmüll von gelöschten Dateien steht. Bei einer gereinigten frisch formatierten SD geht alles! Also muss ich jetzt nur noch den entsprechenden Cluster in dem das Verzeichnis sein soll vorher löschen oder? Gruß Reinhold
Hallo Reinhold, Ja, dass Nullen der neu allokierten Verzeichniscluster ist ok. Es reicht aus, wenn Du jedes 32. Byte (bei Offset 0 gestartet) auf 0 oder FAT16_DIRENTRY_DELETED setzt. Ich habe mich bisher darum gedrückt, da es ja doch einiges an Overhead darstellt. Siehe dazu den TODO-Kommentar in fat16_create_file(). Effizienter und ohne z.B. 128 einzelne Schreibzugriffe (bei Clustern von 4kB), die die Lebensdauer der Karte beinträchtigen, lässt sich das mit einer Callback-Technik a la sd_raw_read_interval() machen. Du solltest auch in jedem Unterverzeichnis zwei weitere Unterverzeichnisse mit den Namen "." und ".." anlegen, die auf das aktuelle Verzeichnis bzw. das Verzeichnis darüber verweisen. Erst dann ist eine korrekte Ordnernavigation gewährleistet. Roland
Mist, ich habe möglicherweise schon wieder eine Karte geschrottet. :( Folgendes ist mir passiert: Ich nutze Roland's Code um in einem Datenlogger alle 15min Temperaturwerte von DS18S20 Sensoren aufzuzeichnen. Nur ein paar chars, immer in die gleiche Datei. Nichts zeitkritisches, funktioniert super. Den 'sync' Befehl nach dem schreiben hab ich auch nicht vergessen. Nun dachte ich mir, es ist mal Zeit für einen Langzeit-Test und habe meiner Schaltung die auf einem Steckbrett aufgebaut ist einen Akku spendiert und in die Ecke gestellt. Und natürlich vergessen. Irgendwann habe ich mich wieder erinnert und nachgesehen. Klar war der Akku leer. Ok, Karte entfernt und in den Cardreader am PC gesteckt. Nun der große Schreck - XP meint: Der Datenträger ist nicht formatiert. Weder unter Linux noch unter XP mit dem SD-Formatter von Panasonic konnte ich die Karte wieder zum Leben erwecken. Fehlermeldung: write-protected. Gut, eine neue kostet kein Vermögen. Aber die alte Karte kann sich doch nicht einfach in Sonder-Müll verwandeln wenn die Stromversorgung mal einknickt. Hat jemand zufälligerweise ähnliche Probleme gehabt oder eine Idee ob man evtl. mit einem Raw-Write an bestimmter Stelle die Karte(n) retten könnte? Torsten
Ergänzung: Unter Ubuntu (Linux) sehe ich die Karte und kann auch Files lesen. Formatieren geht auch nicht. Weder mit mkdosfs noch mit cat /dev/zero </dev/speicherkarte>. Haben wir uns ausgesperrt ?? P.S.: T.S.: Könntest Du Deinen Code vielleicht hier posten ?
> Ich habe die gleichen Probleme :-( Wie ist es bei Dir passiert? > Haben wir uns ausgesperrt ?? Ich glaube schon. Auch weil es nicht meine erste Karte ist. Irgend etwas ist oberfaul. Mag sein wegen der sehr lückenhaften Doku. > Könntest Du Deinen Code vielleicht hier posten ? Hhmm. Das macht atm nicht wirklich Sinn weil ich bestimmt andere HW verwende und gerade erst angefangen habe. Mitten drin beim experimentieren so zu sagen - und noch lange nicht fertig. Wenn Du aber unbedingt darauf bestehst bekommst Du selbstverständlich den Quelltext. Ich brauche dann aber Deine Mail Adresse und Dein Einverständnis das Roland ein Cc bekommt. Torsten
Hallo T.S., meine email Adresse ist p e t e r - k r u s e (a t) g m x. n e t. Gerne mit Kopie an Roland. Ich vermute, dass beim Schreiben etwas daneben gegangen ist.(?) Gruss, Pete
@Pete Done. @AVR Nix Zwar kenne ich den thread schon, weil aber auch dieser so ellenlang ist, ist mir genau das entgangen. Danke für den Link. :-) Torsten
Habe aber auch in dem Thread keine Lösung für die "Read-only"-Karten gefunden...
Hallo, habt ihr eigentlich schon mal überlegt, wie man solch eine Fehlfunktion durch Stromausfall verhindern könnte? Würde mich sehr interessieren, denn sowas kann ja bei jedem autark arbeitenden Datenlogger passieren. Bei meinem System habe ich mich noch nicht getraut, den Stecker während des Schreibens zu ziehen. Kann man da vielleicht mit dem Brown Out Detector was machen? (Ich habe mich noch nicht damit beschäftigt) Gruß Malte
Nun ja, irgendwann möchte ich die Daten auch auf meinen PC übertragen. Ich werde vor dem Schreibvorgang eine LED anmachen und nach dem Schreiben wieder aus. Dann kann ich sehen, wann ich ziehen darf.
Hallo Ihr Kartentöter, Mal ne blöde Frage, Wie sieht denn euer CSD-Reg. an Bitpos.13-15 und 84-95 aus? Schreibschutz? Card Command Classes? Ich würde da jetzt mal anfangen zu suchen. MFG Uwe
@Malte Nachgedacht schon, aber noch keine Lösung gefunden. Der Brown Out Detector nützt in meinem Fall gar nichts, weil auch bei fast entladenem Akku die Zellspannung locker ausreicht. Nur bei hoher Belastung während eines Schreibvorganges auf die Karte knickt die Spannung ein. Dann, wenn es zu spät ist. @Pete Ich habe die Empfehlung in dem o.g. Thread mal angepasst & ausprobiert: if(sd_raw_send_command_r1(CMD_CLR_WRITE_PROT, 0)) { unselect_card(); return 0; } Geht nicht wie zu erwarten war. Vielleicht muß die '0' durch etwas anderes ersetzt werden. Bin ratlos. Torsten
@Torsten: Hast Du Deine Karte wieder reaktieren können ? Beantwortet ist auch noch nicht Deine Frage nach der Reaktivierung der Karte ...
Hallo hatte leider noch keine Zeit mich in den Source einzuarbeiten. Arbeite aber mit einer selbstgeschriebenen Software Aber einige Fragen bzw. Anmerkungen hätte ich schon. Wie stellt das Programm die relative Adresse der FAT Sektoren fest? Meiner Erfahrung nach liegt nähmlich der erste Sektor des Dateisystemes je nach Kartengrösse nicht ab der Adresse 0x00000000 sondern etwa bei einer 32 MB Karte erst bei 0x00006600. Obwohl er etwa in WINHEX bei 0x00000000 auftaucht.(Im Moment gehe ich nicht von einem Fehler meiner Software aus) Jetzt meine Frage könnte das mit dem Writeprotect-Managment der SD Karte zusammenhängen. Und was passiert wenn man in diese Sektoren Daten reinschreibt. Eine weitere Frage wäre weiss irgentjemand hier wo ich eine genaue Dokumentation über das WriteProtectmanagment herbekomme. In den Üblichen Datenblättern etwa von Sandisk steht dazu nämlich leider nicht viel drin.
Hallo, lässt sich der Code dahingehend einstellen, dass ich mittels ATmega8 schreibend auf das FAT16 - Dateiformat zugreifen kann? Welche Einstellungen müssen hierfür getätigt werden? Unter BASCOM mittels DOS-AVR bekommt man das nicht realisiert, weil hierfür einfach nicht genügend RAM zur Verfügung steht. Wieviel RAM benötigt diese Variante minimal für ein FAT-Dateisystem? Wie sieht es mit einem Low-Level_Zugriff aus? DANKE schon einmal.
Hi Sven, ich denke das sollte so auf dem Atmega8 funktionieren, falls ich die Tabelle von Rolands Homage richtig interpretiert habe Ich spreche von der ersten Tabelle auf der folgenden Seite: http://www.roland-riegel.de/sd-reader/ Grüsse Nik
Hi, hat jemand schon Verzeichnisse angelegt? Ich kriege es nicht richtig hin und habe immer alten Datenmüll in den Verzeichnissen. Danke!!! Gruß Holger
Hallo zusammen, Tut mir Leid, dass ich mich in letzter Zeit nicht beteiligt habe. Mit Semesteranfang und Umzug hatte ich einiges zu tun. Vielleicht kann ich nächste Woche wieder was am SD-Reader machen. Dann könnte ich auch mal eine Funktion zum Anlegen von Verzeichnissen implementieren. Hier plagen sich ja anscheinend einige Leute damit rum... ;-) Viele Grüße, Roland
So, wie versprochen zur Abwechslung mal wieder eine neue Version des sd-reader, zu finden im Anhang oder auf meiner Homepage http://www.roland-riegel.de/sd-reader/. Änderungen sind insbesondere: - Anlegen/Löschen von Verzeichnissen. - Löschen neu allokierter Cluster für Verzeichniseinträge. - Keine Verwendung von printf(). - malloc()/free() sind jetzt optional. Für Handles kann nun auch statischer Speicher verwendet werden. Bei Problemen bitte hier melden. Viel Spaß damit... Gruß, Roland
Seit etwa 2 Monaten verwende ich die ältere Version von 20060319 in einem eigenen Projekt. Alles läuft sehr stabil und zuverläßig. Danke an Roland noch einmal dafür! Nun ist in meinem Projekt etwas aufgetreten womit ich zu Anfang überhaupt nicht gerechnet habe: die Datenflut ist einfach zu groß. Alles in einer einzigen Datei im root zu speichern war zwar einfach umzusetzen aber nicht besonders klug. Aufgrund der Größe macht ein Download auch bei 115k nun keinen Spaß mehr. Kein Problem dachte ich, die neue Version von 20061101 nehmen und alles in einzelne Verzeichnisse packen. Doch damit habe ich einige Probleme: Wenn ich z.B. ein neues Verzeichnis anlege mit "mkdir Test", zeigt ein "ls" noch korrekt an das es wirklich angelegt wurde. Aber beim hineingehen mit "cd Test" und "ls" bleibt alles in einer Endlosschleife hängen. Der erste Eintrag wird ständig wiederholt. Nur ein Reset erlöst den uC. Was mich stutzig werden läßt: Nehme ich die Karte und erzeuge im Kartenleser unter XP ein gleich lautendes Verzeichnis, dann zurück zum uC, zeigt "ls" es korrekt an. Ein "cd Test" und "ls" zeigt erwartungsgemäß die Einträge "." und ".." an. Dann zurück mit "cd.." und "rm Test" um das Verzeichnis zu löschen. Aber jetzt kommt der Hammer: "mkdir Test" und "ls" funktioniert jetzt... Die Karte habe ich mehrmals mit dem SDFormatter V1.2 formatiert. Einen "sync" nach jeder Eingabe habe ich auch nicht vergessen. Der uC und die Karte haben genug 'Saft'. ;) Für jemanden der mit Dateisystemen nicht per 'Du' ist so wie ich ist der Quelltext auch wenn er so gut strukturiert ist wie im SD-Reader nicht so einfach zu durchschauen. Deswegen meine Frage an Roland: Kannst Du mir bitte helfen dem Problem auf den Grund zu gehen? Möglicherweise könnten einige printf's an der richtigen Stelle helfen. Torsten
Hallo André Kronfeldt, kiffen kann auch gesundheitsförderlich sein. In den Niederlanden werden bei bestimmtne Krankheiten z.B. MS nicht Interferoon und Opium sondern das Kiffen als Medikament empfohlen. Spezielle Züchtungen mit gesundheitsförderlichen Tendenzen statt Chemiekeule und Abhängigkeit. Spritzen kann man nur 2 Jahre. Rauchen kann man das ganze Leben lang. Roland weiter so. Denn in Zukunft gibt es CF Karten mit 8GB,16GB,32GB was ist mit denen.
Hallo Torsten, Bezüglich Deines Problems habe ich nicht auf Anhieb eine Idee. Allerdings kenne ich im aktuellsten sd-reader einen Bug, der Probleme mit neuen Verzeichnissen im Zusammenspiel mit Windows XP bereitet. Vielleicht hilft das Beheben dieses Fehlers bei Deinen Problemen weiter. Ich werde Dir heute oder morgen näheres per Mail zuschicken. Irgendwie werden wir das schon hinbekommen... Gruß, Roland
@ T.S.: Hast Du die defekte Karte wieder reaktivieren können oder eine Lösung für das Ziehen der Karte beim Schreiben gefunden ?
Hallo zusammen Ist es möglich die SD-Karte an einem anderen Port und somit nicht mit dem Hardware SPI des AVR zu betreiben mit dem Code von Roland Riegel? In sd_raw.c wird ja das SPCR Register entsprechend konfiguriert...
@ Pete 1) Nein. 2) Nein. Das habe ich auch noch nie gewagt. Mein Problem war ein anderes - nicht ausreichende Spannungsversorgung, siehe weiter oben. Die Lösung: aufpassen/vermeiden. ;) PS: Meine Mail blieb von Dir bis heute unbeantwortet. Torsten
@Adrian, Klar geht das. Du müsstest jedoch Software-SPI verwenden. Nach kurzer Lektüre des Interface-Timings sollte das aber kein Problem sein. Gruß, Roland
@Torsten: Sorry, eine PM ist an Dich unterwegs. Für das Ziehen der Karte oder Ausschalten habe ich eine kleine LED installiert, die mir anzeigt, wenn gerade nicht geschrieben wird. So richtig Austesten konnte ich das aber noch nicht. Defekte Karte ist an Kingston unterwegs :) Viele Grüße, Pete
Wow ! Kingston hat eine neue Karte geschickt ! DAS nenne ich Service und Schnelligkeit !!! (freu) Montag abgeschickt und Donnerstag die neue Karte in der Hand. Besser geht´s nicht :-)
Hallo, entschuldigt bitte die naive Frage aber leider fehlt mir der Durchblick: kann ich jetzt in Windows XP ein Paar Bitmap-Dateien erstellen, auf einer SD-Card speichern (mit einem ganz normalen USB-Kartenleser für den PC), um danach irgendeine beliebige dieser .bmp-Dateien mithilfe des Programms von Roland mit einem Mega8 auslesen? Anders gesagt, ich will einfach Paar .bmp Dateien bequem am PC erzeugen, die dann mit einem Mega auf einer LED-Matrix angezeigt werden werden, als Speichermedium eben die SD-Card. Es ist nur Lesen seitens des Mega nötig, er wird nicht auf die Karte schreiben. Bedeutet die FAT16 Unterstützung dieses Programms, dass ich eben so vorgehen kann und die .bmp-Dateien direkt vom PC auf der Karte kopieren kann, ohne sie irgendwie konviertieren zu müssen?
Du musst sie nur im µC wieder zu raw dekodieren, was aber nicht äußerst schwer ist.
@emil, Wenn der Mikrocontroller weiß, wie er das bmp-Format dekodieren/anzeigen kann, dann lautet die Antwort "Ja". Mein FAT16-Code liest exakt die selben Daten von der Karte, die das Grafikprogramm auf dem PC in die Datei auf der SD-Karte geschrieben hat. Gruß, Roland
@Roland, Hauke: danke, genau das brauche ist; mein µC weiss schon, wie er mit .bmp die LED Matrix steuert, nur schreibe ich sie z.Z. im Flash vom µC. Ich mach'mich jetzt an die Arbeit mit der SD-Karte.
Hallo zusammen, Es gibt wieder eine neue Version im Anhang und wie gehabt unter http://www.roland-riegel.de/sd-reader/ Vorsicht: Die vorgenommenen Änderungen korrigieren Fehler im FAT16-Code bei der Erstellung von neuen Verzeichnissen. Wenn Ihr eine alte Version nutzt und die Funktion fat16_create_dir() verwendet, solltet Ihr auf diese Version aktualisieren, sonst droht Datenverlust! Viele Grüße, Roland
Hallo, verwende den fat16 Zugriff von Roland Riegel. Im Prinzip funktioniert alles prima, allerdings habe ich Probleme wenn ich der Übersicht halber die verschiedenen Funktionsaufrufe zum erzeugen eines files in eine eigene Funktion packe. Der entstprechende Funktionsprototyp sieht folgendermassen aus: uint8_t create_log_file(struct fat16_fs_struct* fs, struct fat16_file_struct* fd1) In der Funktion erzeuge ich meinen gewünschten Dateinamen und mit der Funktion fat16_create_file die gewünschte Datei. Mit fd1 = fat16_open_file(fs, &file_entry) öffne ich die Datei und weise den als Paramter übergebenen Pointer struct fat16_file_struct* fd1 zu. Wenn ich nun in main nach Aufruf von create_log_file folgende Zeilen ausführe wird nichts in die Datei geschrieben. struct fat16_file_struct* fd1; create_log_file(fs, fd1); char buffer[]="test"; int8_t data_len = sizeof(buffer); /* write text to file */ fat16_write_file(fd1, (uint8_t*) buffer, data_len); sd_raw_sync(); Wenn ich allerdings die selben Zeilen unmittelbar vor der return Anweisung in create_log_file einfüge wird der String "test" wie gewünscht in die Datei geschrieben. Wird der Zeiger fd1 nach Beendigung von create_log_file ungültig (bzw. der Bereich wo der Zeiger hinverweist)? Oder wo könnte das Problem liegen? Vielen Dank für eure Tips M.
Hallo M., Das Problem liegt in Deinem falschen Verständnis der Argumentenübergabe in C. Du änderst fd1 nur innerhalb der Funktion. Wird diese verlassen, behält sie ihren alten Wert. Zwei Möglichkeiten: 1. Die elegantere struct fat16_file_struct* create_log_file(struct fat16_fs_struct* fs) { /* ... */ struct fat16_file_struct* fd = fat16_open_file(...); /* ... */ return fd; } 2. Die Abänderung Deiner Funktion uint8_t create_log_file(struct fat16_fs_struct* fs, struct fat16_file_struct** fd) { /* ... */ *fd = fat16_open_file(...); /* ... */ return 1; } Bei weiteren Fragen suche nach einem beliebigen C-Tutorial im Web. Gruß, Roland
Hallo, um meine Daten die ich auf die SD Karte schreibe zu komprimieren, lege ich die einzelnen Variablen die ich verwende nicht direkt auf der Karte ab sondern beschränke die Anzahl der Bits auf den tatsächlichen maximalen Wertebereich. Die Bits der verschiedenen Variablen verknüpfe ich zu einem Stream. Über einen Descriptor der jedem neuen Datensatz vorausgeht ist die Länge der einzelnen Datenfelder definiert. Im Prinzip scheint auch alles zu funktionieren, allerdings habe ich hin und wieder beim dekodieren nach einer wechselnden Anzahl von Datensätzen keine vernünftigen Daten mehr. Ich vermute die Ursache darin, dass beim speichern auf die Karte dann an dieser Stelle Daten verloren gegangen sind. Der Decodier Algorithmus kommt dann durcheinander weil er durch das fehlende Byte um 8 Bit verschobene und somit falsche Bits für den Descriptor einliest. In meiner Funktion zum abspeichern überprüfe ich zwar ob das zu speichernde Byte auch geschrieben wird, aber bekomme dort keine Fehlermeldung. Habe sowohl mit als auch ohne SD_RAW_WRITE_BUFFERING das gleiche Problem. Meine Funktion zum speichern und einen exemplarischen Aufbau des Aufrufs findet ihr im Anhang. Tips? Oder suche ich den Fehler an der falschen Stelle? Sind die Daten sicher auf der Karte gespeichert wenn fat16_write_file() die richtige Anzahl von geschrieben Daten als Rückgabewert liefert? Auf der anderen Seite bin ich mir relativ sicher, dass die Kompression und Dekompression richtig funktionieren. Schwierig weil der Fehler mehr oder weniger zufällig ist. Danke M.
Hallo M., Was mir an Deinem Code auffällt ist, dass data_block nie initialisiert wird. Je nachdem, was da nach dem Reset oder durch den vorhergehenden Datenstrom noch drinsteht, gibt es nur noch Datenmüll. Gruß, Roland
Hallo Roland, ich habe eine kleinen Service-Request für Deine Bibliothek. Ich arbeite öfter mal abwechselnd mit dem ATMega16 und ATMega644 und da haben leider die SPI-Register unterschiedliche Namen. Könntest Du es vielleicht etwas generischer in der Bibliothek bauen ? Das wäre prima :-) Gruss, Pete
@Pete, Die unterschiedlichen Namen sind mir bisher nicht untergekommen. Du kannst das in der Zwischenzeit aber auch selber machen. In sd_raw_config.h definierst Du Dir z.B. #define SPI_SPCR SPCR #define SPI_SPSR SPSR für den ATmega16, und entsprechendes für den ATmega644. In sd_raw.c ersetzt Du dann einfach alle Vorkommen von SPCR durch SPI_SPCR und SPSR durch SPI_SPSR. Entsprechendes für die einzelnen Bits, falls nötig. Gruß, Roland
Ja stimmt sollte besser static uint8_t data_block = 0x00; in der Deklaration heißen. Allerdings würde ein abweichender Startwert ja nur den ersten gespeicherten Wert beeinflussen. Das von mir geschilderte Problem tritt aber erst irgendwann später auf. M.
Hmm, mir ist grad eingefallen, dass afaik statische Variablen ja doch einen Standard-Initialisierungswert bekommen. Was genau Dein Problem sein könnte, weiß ich sonst aber auch nicht. Da mein FAT16-Code mittlerweile doch recht gut getestet sein sollte, glaube ich aber trotzdem, dass das Problem eher in Deinem Code liegt. Gruß, Roland
Kann gut sein, werde mich wohl auch damit beschäftigen bis ich die Ursache gefunden habe ;-) Kann es zu Problemen mit der SD Card Kommunikation kommen wenn während eines Schreibzykluses ein Interrupt ausgelöst wird? Ich empfange einen kontinuierlichen Datenstream über UART. Im Unterschied zu deiner Beispielkonsolenanwendung treten also immer Daten an der seriellen Schnittstelle auf und es kann dementsprechend auch während eines Schreibzykluses ein Interrupt ausgelöst werden. Gruss M.
Hmm, kann ich nicht mit Bestimmtheit ausschließen, aber ich denke nicht, dass Interrupts während eines SPI-Transfers Probleme machen. Der SPI-Bus ist unkritisch bzgl. des Timings und die Ansteuerung der Karte eigentlich auch. Hast Du die Bitstream-Algorithmen denn schonmal ohne die UART ausprobiert? Also zum Beispiel an Hand von festen oder aufgezeichneten Daten? Gruß, Roland
Hallo Roland, ich habe eine Frage zu dem Offset Parameter bei fat16_seek_file. Wenn ich ein append machen will, geht das dann so? : int32_t offset = 0; // zero bytes from the end of the file ==> append if(!fat16_seek_file(fd, &offset, FAT16_SEEK_END) { ... } Das klappt auch, wenn das File leer ist ? Beste Grüße, Pete
Pete wrote: > Wenn ich ein append machen will, geht das dann so? : Ja, genau so funktioniert es. > Das klappt auch, wenn das File leer ist ? Äh... Sollte es. Gibts Probleme? Gruß, Roland
Hi Roland, erst mal.....super Code den Du da hast!!! Ich benutze Deinen neuesten Code vom 20.01.07 auf einem Arm7. Funktioniert alles prima. Das einzige Problem das ich habe ist, wenn ich Verzeichnisse erstelle kann ich diese nicht unter Windows XP löschen da Windows mir immer sagt, das das Verzeichnis nicht leer ist. Selbst erstellte Dateien zu löschen funktioniert prima auch in ertellten Verzeichnissen. Hast Du eine Idee? Danke im voraus!!! Gruß Mille
Könntest Du mir bitte mal schildern, was Du genau gemacht hast, um den Fehler zu bekommen? Eine Schritt-für-Schritt-Anleitung wäre am besten, damit ich den Fehler reproduzieren und nachvollziehen kann. Als Randnotiz: Auf einem Arm hast Du vermutlich mehr RAM verfügbar als ich auf einem mega168. Deshalb wäre vielleicht die Verwendung eines anderen FAT16-Treibers performanter, da ich bei mir nur wenige Strukturen des Dateisystems zwischenpuffern kann. Grüße, Roland
Hi Roland, Dein Code funktioniert soweit prima außer dem einem Problem und wenn was funktioniert sollte man nicht umsteigen! ;-))) Wenn ich einfach ein Verzeichnis mit dem Namen "test" so anlege habe ich das Problem: struct fat16_dir_entry_struct dir_entry; fat16_create_dir(dd, "test", &dir_entry); Egal ob ich einfach nur ein Verzeichnis alleine anlege oder eine Datei hineinschreibe. Die Datei kann ich dann unter Windows XP löschen....das Verzeichnis allerdings nicht. Auch wenn ich nur ein Verzeichnis anlege ohne eine Datei dort rein zu schreiben! Windows meckert dann immer!!! Siehe das JPEG im Anhang. Auch wenn ich das Häkchen unter Schreibgeschützt weg mache kann ich das Verzeichnis nicht löschen. Vielleicht hast Du ja noch eine Idee? Gruß Mille
Ok, danke. Ich werde mir das mal anschauen, kann Dir aber noch nicht sagen, wann ich Zeit dafür finde.
Hallo, ich habe Probleme beim initialieren meiner Sd Karten. Wenn ich die Karten unter Windows formatiere kommt es relativ oft vor, dass die Karten nicht richtig funktionieren. Error beim Filessystem öffnen oder mein ganzes Program bleibt schon bei der Initialisierung hängen. Formatiere ich unter unix gehen die Karten dann vorerst wieder. Woran liegt das? Was verwendet ihr unter Windows zum formatieren eurer Karten? Bei einer meiner Karten habe ich zudem das Problem, dass ich hin und wieder nach Verwendung mit dem Mikrocontroller die Karte nicht mehr formatieren und die Dateien löschen kann - Fehlermeldung die Karte ist schreibgeschützt. Danke Jeremy
@Mille, Gestern habe ich Dein Problem nachvollziehen können. Nach einigem Gefummel mit dem Diskeditor habe ich auch den Grund gefunden. Windows mag die LFN-Verzeichniseinträge nicht, die meine Software für die "."- und ".."-Links erzeugt. Ich werde mal schauen, wie ich das möglichst einfach beheben kann, oder ob ich nicht mehr für alle Dateien einen LFN-Eintrag anlege. In den nächsten Tagen wirds dann wohl eine neue Software-Version für den sd-reader geben. Gruß, Roland
Hallo Roland, da ich demnächst einen offline Temperaturlogger bauen wollte, würde ich gerne die Messwerte auf SD-Card speichern. Was ich auf deiner Homepage so sehe, sieht sehr gut aus. Respekt. Zu den Eagle Files hätte ich zwei Fragen: verwendest du von Reichelt den "Connector 21"? Wie auch immer, wozu der Anschluss von "Pin" 10, 11, 12? Die sind doch für den Schreibschutz der Karte, oder? Wird der ausgewertet, bzw. benötigt man denn überhaupt? Wenn ich die Dokus zur SD-Karte richtig gelesen habe, dann obliegt die Auswertung des Schreibschutzes nur der Software, es wird also nichts hardwaremäßig an der Karte ausgeschaltet. Insofern: da ich eh auf die Karte schreiben möchte, kann ich mir die 2 Pins (+ 1 GND) auch sparen. Gruß, Dirk
@Dirk > Was ich auf deiner Homepage > so sehe, sieht sehr gut aus. Respekt. Danke. > Zu den Eagle Files hätte ich zwei Fragen: verwendest du von Reichelt > den "Connector 21"? Ja, richtig. > Wie auch immer, wozu der Anschluss von "Pin" 10, 11, 12? > Die sind doch für den Schreibschutz der Karte, oder? Wird der > ausgewertet, bzw. benötigt man denn überhaupt? Genau, das sind Schließer zum Erkennen ob eine Karte gesteckt ist und in welcher Position sich der Schreibschutzschalter befindet. Ausgewertet wird er von meiner Software schon (zumindest rudimentär), wobei das ganze optional ist. Du kannst die entsprechenden #defines in sd_raw_config.h auch zu 0 bzw. leer definieren. Dann ist quasi immer eine nicht schreibgeschützte Karte gesteckt. > Wenn ich die Dokus zur > SD-Karte richtig gelesen habe, dann obliegt die Auswertung des > Schreibschutzes nur der Software, es wird also nichts hardwaremäßig an > der Karte ausgeschaltet. Insofern: da ich eh auf die Karte schreiben > möchte, kann ich mir die 2 Pins (+ 1 GND) auch sparen. > Richtig und wieder richtig :) Viele Grüße, Roland
Hallo Roland, danke für die Antwort. Habe eben bei Reichelt die passenden Bauteile (mit kleinen Änderungen) bestellt. Gönne mir den Luxus der Pegelanpassung mittels Transistoren und extra Spannungsregler. Das bischen neu layouten war auch kein Thema. Was mir dabei aufgefallen ist: die Library in deiner Zip Datei der ersten Version enthält den Connector, allerdings scheinen die "Platzierungen/Pins" des Connectors nicht zu stimmen (sind um 0,05inch verrückt), so dass er im schematic nicht angeschlossen werden kann. War allerdings kein Problem: einfach das device mit verschobenen Pins neu erstellen, Rest passt wohl hoffentlich (jedenfalls nach chinesischem Datenblatt von Reichelt). Insofern bleibt unterm Strich nur zu sagen: Respekt vor deiner Leistung die Software dazu zu entwicklen und dich so in die Materie einzuarbeiten! Gruß, Dirk
Ja, bzgl. der Library hast Du recht. Habe die nicht selbsterstellt und hatte damals auch noch keine Erfahrung mit der Bearbeitung. Ich habe aber rausgefunden, dass Eagle eine korrekte Verbindung erstellt, wenn man das Netz am verschobenen Pin anfängt zu zeichnen. Nur wenn man das Netz an dem Pin abschließen will, funktioniert das nicht richtig. Gut, dass Du ordentliche Pegelwandler und nen eigenen Spannungswandler vorsiehst. Ich hatte mit dem Provisorium von Ulrich Radig zwar kaum Probleme, das kann aber auch an den von mir verwendeten Karten liegen. Wenn die Karte während dem Schreibvorgang Stromspitzen von 100 oder 200 mA braucht, ist das mit den Dioden wirklich nicht mehr optimal. Gruß, Roland
@Mille und alle anderen, Es gibt eine neue Version der sd-reader-Software auf meiner Homepage http://www.roland-riegel.de/sd-reader/ oder im Anhang. Ich empfehle eine Aktualisierung, da ich einige nicht ganz unwichtige Fehler gefunden und behoben habe: * Windows kann jetzt die vom sd-reader angelegten Ordner löschen. * Falls der Dateiname mit dem Byte 0xe5 beginnt, wird dies nun korrekt gehandhabt. (Dieses Byte dient normalerweise dazu, die Datei als gelöscht zu markieren.) * fat16_clear_cluster() löscht nun korrekt. Zuvor wurden 16 von 32 Bytes nicht richtig auf 0 gesetzt. * fat16_delete_file() gibt nun auch bei leeren Dateien den richtigen Wert zurück. Viele Grüße, Roland
@Roland, kurzes feedback. Funktioniert alles prima!!! Super Arbeit!!! ->froi<- Gruß Mille
@Gast, Ja, mein vServer ist down. Der Host hatte nach Auskunft des Providers einen Kernel-Panic, jetzt muss das Datei-System überprüft werden. @Mille, Danke! Gruß, Roland
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.