Forum: Mikrocontroller und Digitale Elektronik SD-Card (FAT32), Datei auslesen an bestimmter Stelle?


von Chris (Gast)


Lesenswert?

Hallo Leute,

habe den Code aus diesem Thread: 
Beitrag "MMC SD library FAT16 FAT32 read write"
in verwendung um eine SD-Card auszulesen.
Klappt auch fast perfekt. Vielen dank für die Tolle arbeit schon mal.

Ich habe jetzt hier eine relativ große Textdatei mit 220 Zeilen und bis 
zu 50 Zeichen. Die ganze Datei hat laut Windoof 11KB.
Wenn ich jetzt versuche die Komplette Datei einzulesen, bekomme ich über 
die serielle Schnittstelle aber nur die letzten 12 Zeilen angezeigt.
Vergrößere ich jetzt das Array zum zwischenspeichern des Monsterstrins 
habe ich im Programm scheinbar einen Speicherüberlauf und nichts geht 
mehr.

Zudem lese ich den String nicht nur aus sondern zerlege ihn auch noch in 
einzel strings, so das ich quasi alles doppelt habe.

Hier mal der entsprechende Ausschnitt aus dem Quellcode:
1
  //Hilfsvariablen festlegen
2
  char *sd_card[240], sd_card_string[1],adapter[1],get_c;
3
  unsigned char i=0;
4
5
    // Setzen einer Variable und dann runterzählen geht
6
    // am schnellsten
7
    unsigned long int seek = file.length;
8
    char *tmp;
9
    
10
    // Lesen eines chars und Ausgabe des chars.
11
    // Solange bis komplette Datei gelesen wurde.
12
    do 
13
    {
14
      get_c = ffread();
15
      sd_card_string[i] = get_c;
16
      i++;
17
    } 
18
    while (--seek);
19
20
    sd_card_string[i+1] = '\0';
21
    rs232_text(sd_card_string);                  //bis hier waren es  4h 50min Arbeit
22
    rs232_text("\n");
23
    rs232_text("\nEOF\n");
24
    ffclose();
25
    
26
    //Nun wird der Komplette String anhand von ; in Einzelne zerlegt
27
    sd_card[0] =  strtok_r(sd_card_string,";",&tmp);      //und hier stecken 5h 50min Arbeit drin
28
    for(i=1;i<=240;i++)
29
    {
30
      sd_card[i] =  strtok_r(NULL,";",&tmp);        //bis hier...also 10h 40min
31
    }

ändere ich jetzt sd_card_string[1] auf sd_card_string[10], macht mein 
Programm schon Probleme.

Als Controller verwende ich einen ATMega1284.
Dessen Speicher belegung sieht laut AtmelStudio derzeit wie volg aus:
Program Memory Usage            :     35992 bytes   27,5 % Full
Data Memory Usage     :  10553 bytes   64,4 % Full

nun war meine Idee die Datei nur an der für mich Interessanten stelle 
auszulesen. Aber wie mache ich das ? und geht das überhaupt?

LG Chris

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Besorg Dir ein C-Buch, und sieh Dir an, was dadrin zum Thema Arrays 
steht.
1
  //Hilfsvariablen festlegen
2
  char *sd_card[240], sd_card_string[1],adapter[1],get_c;
3
  unsigned char i=0;
4
5
    // Setzen einer Variable und dann runterzählen geht
6
    // am schnellsten
7
    unsigned long int seek = file.length;
8
    char *tmp;
9
    
10
    // Lesen eines chars und Ausgabe des chars.
11
    // Solange bis komplette Datei gelesen wurde.
12
    do 
13
    {
14
      get_c = ffread();
15
      sd_card_string[i] = get_c;
16
      i++;
17
    } 
18
    while (--seek);

Wieviele Zeichen passen in "sd_card_string"? Und wieviele versuchst Du 
dort hineinzustopfen?

von Chris (Gast)


Lesenswert?

Oh danke für die Hilfe....

aber hatte ich nicht erwähnt das ich auch schon sd_card_string 
vergrößert habe übrigens auch schon auf die 11000Byte was 11K Zeichen 
entsprechen müsste da ein char 8Bit/1Byte speichern kann und das aber 
mein controller schon bei sd_card_string[10] aufgibt weil scheinbar der 
Speicherüberläuft. und ich deshalb gerne die Datei nur an einer 
Bestimmten stelle auslesen möchte.

LG

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Und? Warum musst Du die Bytes, die Du aus der Datei gar nicht benötigst, 
alle im RAM aufheben?

Denk mal drüber nach.

von Mw E. (Firma: fritzler-avr.de) (fritzler)


Lesenswert?

geh mit seek zur zeile die du brauchst.

von Karl H. (kbuchegg)


Lesenswert?

So eine Textdatei bearbeitet man aber nicht, indem man den kompletten 
Text erst mal in den Speicher holt.

Wenn man den Speicher hat, dann holt man sich eine Zeile in den Speicher 
und holt sich dann aus der Zeile die Teile raus, die man haben will.
Danach kommt die nächste Zeile drann. etc. etc.

von Karl H. (kbuchegg)


Lesenswert?

> //Nun wird der Komplette String anhand von ; in Einzelne zerlegt

und wenn du überhaupt zeilenübergreifend anhand der ';' deinen Text 
zerlegst, dann musst du das Einlesen und das Zerlegen ineinander 
geschachtelt gleichzeitig machen.

Dann klappt das auch auf einem kleinen µC.

So lange ist das noch nicht her, dass übliche Computer keine 2GByte 
Hauptspeicher hatten, sondern nur lausige 64K. Und trotzdem konnte man 
damit schon umn einiges größere Texte als 64KByte bearbeiten (und in den 
64K musste das Programm auch noch mit drinnen sein).
Das ist nur heute auf PC so, dass man mit dem Hauptspeicher nicht mehr 
haushalten muss und den einfach mal zuballert. Hat man den Speicher 
nicht, dann muss dann da eben ein wenig intelligenter rangehen.

von Chris (Gast)


Lesenswert?

Martin Wende schrieb:
> geh mit seek zur zeile die du brauchst.

seek ist gut habe ffseek() in file.c gefunden allerdings habe ich jetzt 
noch nichts gefunden was mir den offset meiner gesuchten zeile liefert.

Ich müsste wenn ich Zeile 5 lesen will ja erst mal rausfinden ab welcher 
Stelle diese anfängt und wo sie endet. Für den Zeilenanfang müsste ich 
ja irgendwie das 4. 0x0D finden für </r> und dann noch einen sektor 
drauf rechnen, für das ende so lange lesen bis 0x0D kommt

also so in etwa:

suchen??

mit ffseek() den pointer auf die richtige Startadresse setzen? und
lesen:
1
do 
2
{
3
 get_c = ffread();
4
 sd_card_string[i] = get_c;
5
 i++;
6
} 
7
while (!0x0D);

LG Chris

von Karl H. (kbuchegg)


Lesenswert?

Chris schrieb:
> Martin Wende schrieb:
>> geh mit seek zur zeile die du brauchst.
>
> seek ist gut habe ffseek() in file.c gefunden allerdings habe ich jetzt
> noch nichts gefunden was mir den offset meiner gesuchten zeile liefert.

Gibts auch nicht.
Vergiss den Vorschlag.
Wenn deine Zeilen nicht gleich lang sind, bleibt dir nichts anderes 
übrig als das File von vorne bis hinten Zeile für Zeile (oder Zeichen 
für Zeichen) zu lesen.

Mit seeken kommst du nur weiter, wenn deine Datei eine Struktur aus 
gleich lange aufgebauten Records hat oder du vorher erst mal das File 
einmal durchliest um festzustellen, wo die einzelnen Zeilen anfangen.
Das erste hast du nicht
Das zweite willst du nicht, denn wozu soll es gut sein, das File erst 
mal durchzulesen nur um dann nachher erst recht wieder von vorne bis 
hinten Zeile für Zeile zu bearbeiten.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Karl Heinz Buchegger schrieb:
> Wenn deine Zeilen nicht gleich lang sind, bleibt dir nichts anderes
> übrig als das File von vorne bis hinten Zeile für Zeile (oder Zeichen
> für Zeichen) zu lesen.

"Lesen" heißt hier aber nicht "im Speicher aufheben"!

von Chris (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Wenn deine Zeilen nicht gleich lang sind, bleibt dir nichts anderes
>
> übrig als das File von vorne bis hinten Zeile für Zeile (oder Zeichen
>
> für Zeichen) zu lesen.

ok also mache ich das etwa so
1
for(int i=0;i<zeile;i++)
2
{
3
     unsigned int n=0;
4
     do
5
     {
6
         sd_card_string[n] = ffread();
7
         n++;
8
9
     }while(ffread() != 0x0D);
10
11
}

und der letzte sd_card_string der übrig bleibt, ist der gesuchte?

LG

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Nein.

Du liest solange, bis Du ein Zeilenendezeichen erkennst. Jedes gelesene 
Byte wirfst Du sofort weg.

Hast Du ein Zeilenendezeichen erkannt, weißt Du, das die folgenden Bytes 
bis zum nächsten Zeilenendezeichen zur zweiten Zeile gehören.

Da Du die fünfte Zeile lesen willst, wiederholst Du den Vorgang solange, 
bis Du das vierte Zeilenendezeichen gelesen hast.

Erst dann musst Du Dir die gelesenen Bytes aufheben -- der Puffer 
dafür muss natürlich ausreichend dimensioniert sein.

von Karl H. (kbuchegg)


Lesenswert?

Rufus Τ. Firefly schrieb:
> Nein.
>
> Du liest solange, bis Du ein Zeilenendezeichen erkennst. Jedes gelesene
> Byte wirfst Du sofort weg.

Jaein.

Das kommt jetzt drauf an, was eigentlich das Ziel des ganzen ist.

ALso:
Was ist eigentlich gefordert? Wo soll die Reise eigentlich hingehen?
Für mich sieht es so aus, als ob die Datei in 'Wörter' aufgeteilt werden 
soll, wobei zwischen den Wörtern ein ';' steht.
Die nächste Frage lautet jetzt: Was hat es mit diesen Wörtern auf sich, 
was muss damit passieren?
Man liest ja eine Datei nicht einfach nur so zum Spass, sondern mit dem 
Inhalt soll ja irgendwas gemacht werden.


(Ich denke nämlich, das das Zwischenziel die n-te Zeile zu bestimmen, 
eigentlich für die eiegentliche Aufgabenstellung vollkommen irrelevant 
ist. Da hat er sich einfach nur in etwas verrannt)

von Chris (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> ALso:
>
> Was ist eigentlich gefordert?

Die Datei beinhaltet mehrer Zeilen Text.
Ich brauche aber nur den Text aus einer bestimmten zeile.

Hatte die Semikolon nur eingefügt, damit ich diese Später besser 
zerlegen kann, wird ja aber für das jetzige Vorhaben nicht mehr benötigt 
da nach dem Zeilenendezeichen gesucht wird.

Was ist den an meiner for schleife so falsch ausser das sd_card_string 
nicht komplett überschrieben wird wenn die vorige zeile > der folgenden.

LG Chris

von Karl H. (kbuchegg)


Lesenswert?

Chris schrieb:
> Karl Heinz Buchegger schrieb:
>> ALso:
>>
>> Was ist eigentlich gefordert?
>
> Die Datei beinhaltet mehrer Zeilen Text.
> Ich brauche aber nur den Text aus einer bestimmten zeile.

ok.
Na dann.

2 phasiges Vorgehen.
  1. Phase
  ********
      Alle Zeilen vor der interessierenden überlesen

  2. Phase
  ********
      Die Zeichen dieser Zeile komplett einlesen

> Hatte die Semikolon nur eingefügt, damit ich diese Später besser
> zerlegen kann

Genau das hat mich nämlich verwirrt.
(Es gibt nämlich auch Fileformate, bei denen der ; das Trennzeichen 
zwischen 'Befehlen' ist. Zb HPGL)

> Was ist den an meiner for schleife so falsch ausser das sd_card_string
> nicht komplett überschrieben

wozu willst du denn überhaupt die Zeichen zwischenspeichern, wenn du sie 
eh nicht brauchst?
Das kostet nur Zeit und du läufst Gefahr, dass du mal auf eine Zeile 
stösst, die länger als der Buffer ist, den du für eine Zeile 
bereitstellen musst.
Also: Wenn du zur n-ten Zeile willst, dann einfach vom Beginn des Files 
so lange dahinlesen, bis n-1 mal ein \n aufgetaucht ist.


Und du darfst natürlich nicht 2 mal ffread aufrufen, so wie du das 
gemacht hast. Und ein wenig Fehlerbehandlung musst du auch betreiben. 
Wenn ich in einer Datei mit 20 Zeilen die 40-te lesen lasse, dann wird 
das bei dir nicht gut gehen.

IM Prinzip und ohne Fehlerbehandlung
1
  char sd_card_string[240];
2
3
   //
4
   // Phase 1
5
   for( i = 0; i < ZeilenNr - 1; i++ )
6
   {
7
     while( ffread() != '\n' )
8
       ;
9
   }
10
11
   // Phase 2
12
   n = 0;
13
   while( ( c = ffread() ) != '\n' )
14
     sd_card_string[n++] = c;
15
16
   sd_card_string[n] = '\0';

Jetzt würds mich aber doch interessieren, wozu man auf einem µC die n-te 
Zeile aus einem Textfile braucht.

von Chris (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Jetzt würds mich aber doch interessieren, wozu man auf einem µC die n-te
> Zeile aus einem Textfile braucht.

Ich baue gerade für unsere Abteilung einen etwas komfortableren 
Durchgangsprüfer für größere Kabelsätze. Dazu wird der Kabelsatzt an 
mein Gerät angeschlossen, wo in wirklichkeit ein Steuergerät sitzen 
würde.
Dann nimmt man eine Prüfspitze und geht zu dem Stecker und dem Pin von 
dem man wissen möchte was er ist. Das Gerät schiebt mittels 
Schieberegister eine 1 durch, in meinem Fall, 240 Kontakte und erhält 
das Gerät die 1 an einem I/O zurück spuckt es einem anhand der 
mitgezählten Schritte die passende Bezeichnung von Stecker,Pin und 
dessen Bedeutung aus.
Und vorrausgesetzt ich habe keinen Fehler in der Schaltung wird noch der 
übergangswiderstand der Leitung gemessen.

Habe solch ein gerät schon mal in klein gebaut mit Daten für nur einen 
Kabelsatztypen, das hat auch so weit ganz gut Funktioniert, nur ist man 
damit halt kein Stück flexiebel und eine Widerstandmessung war auch 
nicht drin.

Zur Zeit wird das für die meisten Kabelsätze noch von Hand gemacht,
sprich; Multimeter auf Durchgang, 1.Messleitung in den Stecker/Pin von 
dem man wissen will, wo er hin ghet und was er macht und mit der 
2.Messleitung alle 240 Kontakte der Gegenseite durchklingel, wenn es 
piepst in den Kabelsatzplan schauen was man da gefunden hat.

Darum jetzt mit SD-Card um mehr Infos speichern zu können.

Danke für die Hilfe und LG
Chris

von Chris (Gast)


Lesenswert?

Moin Moin,

habe jetzt Karl Heinz Beispielcode übernommern, habe jetzt nur das 
Problem das er die zu öffnende Datei nicht mehr findet... Wie kann das 
denn sein? Es wurde an der Art der Dateiöffnung nichts verändert nur das 
daraufvolgende auslesen wurde angepasst. Habe Testweise noch mal das 
alte .hex File von gestern geflasht und auch dort wird die Datei nicht 
geöffnet. Jetzt habe ich zu guter letzt die SD-Card noch mal formatiert 
aber auch das hat nichts gebracht.

LG

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Chris schrieb:
> habe jetzt Karl Heinz Beispielcode übernommern, habe jetzt nur das
> Problem das er die zu öffnende Datei nicht mehr findet...
Ja, dann wirst du wohl erst nochmal einen Schritt zurück machen 
müssen, und schauen, ob wenigstens der Code von gestern noch 
funktioniert.

Und dann mal (wenigstens) versuchen, das zu verstehen, was du da 
gerade tust...

von Chris (Gast)


Lesenswert?

Chris schrieb:
> Habe Testweise noch mal das
>
> alte .hex File von gestern geflasht und auch dort wird die Datei nicht
>
> geöffnet.

von Chris (Gast)


Lesenswert?

Erst lesen dann schreiben ;) Es ging nach der zweiten Zeile noch 
weiter...

von Karl H. (kbuchegg)


Lesenswert?

Chris schrieb:

> daraufvolgende auslesen wurde angepasst. Habe Testweise noch mal das
> alte .hex File von gestern geflasht und auch dort wird die Datei nicht
> geöffnet. Jetzt habe ich zu guter letzt die SD-Card noch mal formatiert
> aber auch das hat nichts gebracht.

Kabel gerissen, Kurschluss irgendwo, ....
gibt da ein paar Möglichkeiten.


Danke für die Erläuterung.
Ja. So macht das natürlich Sinn.

von Klaus (Gast)


Lesenswert?

Chris schrieb:
> Ich baue gerade für unsere Abteilung einen etwas komfortableren
> Durchgangsprüfer für größere Kabelsätze. Dazu wird der Kabelsatzt an
> mein Gerät angeschlossen, wo in wirklichkeit ein Steuergerät sitzen
> würde.
> Dann nimmt man eine Prüfspitze und geht zu dem Stecker und dem Pin von
> dem man wissen möchte was er ist. Das Gerät schiebt mittels
> Schieberegister eine 1 durch, in meinem Fall, 240 Kontakte und erhält
> das Gerät die 1 an einem I/O zurück spuckt es einem anhand der
> mitgezählten Schritte die passende Bezeichnung von Stecker,Pin und
> dessen Bedeutung aus.
> Und vorrausgesetzt ich habe keinen Fehler in der Schaltung wird noch der
> übergangswiderstand der Leitung gemessen.

Ich würde diese Aufgabe etwas anders lösen:

Deine Messschaltung wird über USB-seriell an einen Laptop angeschlossen. 
Wenn erforderlich, erledigen zwei Optokoppler in RX und TX die 
galvanische Trennung. Die Messschaltung weiß nichts über die Kabel, sie 
liefert nur die Nummer des verbundenen Anschluß und den 
Überganswiderstand zum Laptop. Dort kann man sich dann austoben, bunte 
Oberflächen malen, alle Kabel mit allen Varianten verwalten, Statistiken 
erstellen und zum Schluß alles in die Warenwirtschaft einpflegen.

MfG Klaus

von Chris (Gast)


Lesenswert?

Hallo Klaus, ja das mit den Daten an den Laptop hatten wir auch schon 
überlegt und das Gerät verfügt auch über einen USB <---> Seriell Wandler 
um Messprotokolle zu generieren. Aber wir wollten es auch Stand Alone 
machen damit man nicht immer gleich nen Laptop oder Desktoprechner 
braucht.

Karl Heinz Buchegger schrieb:
> Kabel gerissen, Kurschluss irgendwo, ....

Nee leider nicht der Fall.
1
  while(PIND &= (1<<PD7)) // fragt CDI ab, liegt an Masse, wenn eine Karte eingesteckt ist
2
  {
3
    rs232_text("\nkeine Karte eingesteckt");
4
    WDH3224_TEXT(180,8,"SD-Karte ?",2,0);
5
  }
6
  
7
  WDH3224_TEXT(180,8,"          ",2,0);
8
  rs232_text("\nBoot");
9
  WDH3224_TEXT(180,8,"Boot...",2,0);
10
  
11
  // Versuch Karte zu Initialisieren, bis es klappt.
12
  // Unbedingt so, weil die Initialiesierung nicht immer
13
  // auf Anhieb klappt.
14
  while (FALSE == mmc_init())
15
  {
16
    nop();
17
  }
18
  
19
  rs232_text("...");
20
  
21
  
22
  // Fat initialisieren. Nur wenn das klappt sind weitere
23
  // Aktionen sinnvoll, sonst endet das Programm.
24
  if (!fat_loadFatData())
25
  return -1;
26
  
27
  // Wenn auf dem terminal "Boot... OK" zu lesen ist, ist Init OK.
28
  // Jetzt kann man Schreiben/Anhängen/Lesen.
29
  rs232_text("Ok\n");
30
  WDH3224_TEXT(180,29,"OK",2,0);
31
if (MMC_FILE_EXISTS == ffopen("TEST0000TXT"))
32
  {
33
    WDH3224_CLEAR();
34
    WDH3224_TEXT(3,0,"Datei wird geoeffnet",2,0);
35
    
36
    rs232_text("\nDatei wird geoeffnet:\n");
37
38
       
39
    // Phase 1
40
    for( i = 0; i < ZeilenNr - 1; i++ )
41
    {
42
       while( ffread() != '\n' );
43
    }
44
45
    // Phase 2
46
    n = 0;
47
    while( ( get_c = ffread() ) != '\n' )
48
    sd_card_string[n++] = get_c;
49
    sd_card_string[n] = '\0';
50
    
51
    WDH3224_TEXT(20,0,sd_card_string,1,0);
52
    rs232_text(sd_card_string);                
53
    rs232_text("\n");
54
    rs232_text("\nEOF\n");
55
    ffclose();
56
  
57
58
  }
59
60
  else 
61
  { 
62
    rs232_text("\nDatei konnte nicht gefunden werden");
63
    WDH3224_TEXT(150,2,"Datei konnte nicht gefunden werden",1,1);
64
  }

Er gibt mir Datei konnte nicht gefunden werden raus, das Heisst eine 
Karte ist eingesteckt und konnte auch initialisiert werden.

LG

von Karl H. (kbuchegg)


Lesenswert?

>... ffopen("TEST0000TXT")

Sicher, dass die Datei  TEST0000TXT heisst und nicht TEST0000.TXT ?

von Chris (Gast)


Lesenswert?

Ähm ja, steht da so seit gestern ;) und hat in der zwischenzeit ja ganz 
gut funktioniert abgesehen von heut morgen bis jetzt....

LG

von Karl H. (kbuchegg)


Lesenswert?

Passt schon.
Hab mir die Lib angesehen. Die will ihre 8+3 Filenamen in diesem Format 
haben. Nichts desto trotz muss sie bei dir im Windows als "TEST000.TXT" 
aufscheinen.

von Chris (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> im Windows als "TEST000.TXT"
>
> aufscheinen.Beitrag melden Bearbeiten Löschen

Habe ich eben gerade noch mal ausprobiert, aber auch so klappt da leider 
nichts. Ich versteh das nicht wie kann das denn sein das er von einen 
auf den anderen tag die datei nicht mehr finden kann?
Ich stolper echt von einem Problem ins nächste... echt mühselig im 
Moment

Danke noch mal für die Hilfe

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
Noch kein Account? Hier anmelden.