Forum: Mikrocontroller und Digitale Elektronik Array aus SD Karte laden


von Georg (Gast)


Lesenswert?

Hallo,

folgendes Problem:

Ich würde gerne ein Kennfeld aus einer Text-Datei in ein Array laden.

Ich verwende einen ATmega32 und programmiere mit AVR Studio/GGC

Das Kennfeld ist in der Textdatei besteht aus einer variablen Anzahl aus 
Stützstellen, z.B.:

(0;300) (500;200) (1000;300) ...

Das erste Element ist einen Drehzahl, das Zweite das dazugehörige 
Moment.

Das einlesen der Werte ist soweit kein Problem. Ich weiß nur nicht wie 
ich sie in ein Array ablegen kann, da die Anzahl der Wertepaar variabel 
ist.

Vielleicht kann mir da jemand weiterhelfen.


Gruß
Georg

von Karl H. (kbuchegg)


Lesenswert?

Georg schrieb:

> Das einlesen der Werte ist soweit kein Problem. Ich weiß nur nicht wie
> ich sie in ein Array ablegen kann, da die Anzahl der Wertepaar variabel
> ist.

Daran solltest du arbeiten:
Die darf zwar im File variabel sein, im Mega32 willst du aber ein fix 
dimensioniertes Feld haben. Du musst daher das Feld mit einer Größe 
anlegen, so dass du auf jeden Fall durchkommen wirst.

Natürlich gibt es auch Möglichkeiten, wie man sich Arrays dynamisch 
wachsen lassen kann. Auf einem Mega32 ist das allerdings nicht sinnvoll. 
Die Verwaltung des ganzen kostet dir mehr, als wie wenn du einfach den 
freien Speicher komplett in das Array steckst. Und wenn der freie 
Speicher aus ist, dann ist er aus. Da geht dann nichts mehr. Dann schon 
lieber soviel wie möglich in das Array investieren, als 10% vom speicher 
für die Verwaltung von dynamisch allokiertem Speicher zu reservieren.

von Robert L. (lrlr)


Lesenswert?

generell:

kommt auf die Auswertung an, wenn du mehr werte einlesen musst, als du 
speicher hast, z.b. den Mittelwert über 50 werte (vor und nach dem 
aktuell) rechnen willst, mach zwei buffer mit je 100 werten..


wenns nur um ein paar wenige werte geht, den maximalwert reservieren 
(wie schon vorgeschlagen)

oder in deinem fall z.b. Fix 101 werte.. (0%-100%) und jeden der 100ert 
werte durch z.b interpolieren ermitteln..) dann wäre nachherige zugriffe 
einfacher (einheitlicher)

von Georg (Gast)


Lesenswert?

Alles klar, benutze jetzt eine feste Anzahl an Werten.

Hab gerade noch ein anderes Problem und zwar werden Zeiche wie '(' oder 
'[' nicht erkannt.

z.B.
1
if(strcmp(buffer,'['){
2
 ...
3
}

Hier wird nicht in die if-Struktur gesprungen, auch wenn in der 
Textdatei das entsprechende Zeichen vorhanden ist. In buffer steht 
jeweils ein eingelesenes Zeichen aus der Text-Datei.

Ander Zeichen werden gefunden

z.B.

if(strcmp(buffer,'a'){
...
}

... funktioniert.


Jemand eine Idee woran das liegen könnte?

von Karl H. (kbuchegg)


Lesenswert?

Georg schrieb:
> Alles klar, benutze jetzt eine feste Anzahl an Werten.
>
> Hab gerade noch ein anderes Problem und zwar werden Zeiche wie '(' oder
> '[' nicht erkannt.
>
> z.B.
>
1
> 
2
> if(strcmp(buffer,'['){
3
>  ...
4
> }
5
> 
6
>


Schmeiss dein C-Buch weg, und kauf dir ein neues. Deines taugt nichts, 
wenn es dir den Unterschied zwischen '[' und "[" und damit den 
Unterschied zwischen einem einzelnem Zeichen und einem String nicht klar 
macht.

> Jemand eine Idee woran das liegen könnte?

Könnte es sein, dass du gar kein C Buch hast?
Betsell dir am besten heute noch eines bei Amazon (oder einem sonstigen 
Buchhandel deiner Wahl). In der Zwischenzeit kannst du mit der 
Minimal-Kurzfassung aus der FAQ zum Thema "Strings" vorlieb nehmen.
http://www.mikrocontroller.net/articles/FAQ#Wie_funktioniert_String-Verarbeitung_in_C.3F
(Da gibt es einen Absatz der rot hervorgehoben ist. Wie gemünzt für 
deine Situation)

Und setz den Warning-Level deines Compilers wieder auf ein vernünftiges 
Mass hoch. Deine Version hätte nie ungestraft durch den Compiler gehen 
dürfen.

> if(strcmp(buffer,'a'){
> ...
> }
>
> ... funktioniert.

Zitat: Das glaub ich nicht, Tim.

von Georg (Gast)


Lesenswert?

Jaahh ... ich habe natürlich "[" geschrieben ... sorry, vertippt.

Das Problem bleibt aber das gleich. Sonst noch jemand einen Tipp.


Gruß

von Karl H. (kbuchegg)


Lesenswert?

Georg schrieb:
> Sonst noch jemand einen Tipp.

Aber natürlich:
Poste kompletten Code. Auf keinen Fall aber Codeschnipsel die du 
abtippst.

Dann kann man sich auch auf die Suche nach den Fehlern machen, die du
a) wirklich begangen hast
b) selber nicht siehst

Aber ich tippe mal ins Blaue:
Deine Stringterminiierung stimmt nicht.

von Georg (Gast)


Lesenswert?

Ja, ich denke die Stringterminierung war's.

Ich habe den buffer so deklariert
1
buffer[1]

da ich ja immer ein Zeichen einlese.

Da ich ja nun die FAQs gelesen habe,
;-)

 weiß ich, dass ein String ja immer auch aus einer 'Endmarkierung' 
besteht.

Nun habe ich den buffer so deklariert:
1
buffer[2]

Nun funktioniert es. Danke für den Hinweis!

Gruß
Georg

PS: Ich hoff ich hab das auch richtig erkannt und keinen totalen Mist 
erzählt. ;-)

von Karl H. (kbuchegg)


Lesenswert?

Georg schrieb:
> Ja, ich denke die Stringterminierung war's.
>
> Ich habe den buffer so deklariert
>
>
1
buffer[1]
>
> da ich ja immer ein Zeichen einlese.

... stellt sich die Frage, warum du dann überhaupt auf Strings setzt?
Einzelne Character zu bearbeiten geht einfacher und schneller

Aber ich seh schon: Du willst deinen Code nicht herzeigen, auf das man 
dir ein paar Techniken und Verfahren zeigen könnte, die dir das Leben 
einfacher machen könnten.

von Georg (Gast)


Lesenswert?

Hier der Code:
1
#define TORQUE 0
2
#define REV 1
3
4
char ReadBuffer[2];
5
char mapBuffer[6];
6
uint16_t map[50][2];
7
uint8_t map_index;
8
uint8_t byte_count=0;
9
10
11
12
void get_map(void){
13
  do{
14
    byte_count=Fread(ReadBuffer,1);
15
      if(byte_count!=1)break;
16
                        if(strcmp(ReadBuffer,"[")==0){
17
        mapBuffer[0] = '\0';
18
        do{
19
          byte_count=Fread(ReadBuffer,1);
20
          if(byte_count!=1)break;
21
          if(strcmp(ReadBuffer,";")==0)break;
22
          strcat(mapBuffer,ReadBuffer);
23
        }while(1);
24
        map[map_index][TORQUE]=atoi(mapBuffer);
25
        mapBuffer[0] = '\0';
26
        do{
27
          byte_count=Fread(ReadBuffer,1);
28
          if(byte_count!=1)break;
29
          if(strcmp(ReadBuffer,"]")==0)break;
30
          strcat(mapBuffer,ReadBuffer);
31
        }while(1);
32
        map[map_index][REV]=atoi(mapBuffer);
33
        }
34
        map_index++;
35
  }while(1);
36
37
}

von Georg (Gast)


Lesenswert?

Die Struktur der Daten in der Textdatei:

[0;300][500;2300][1000;4300][1500;2300] ...

von Georg (Gast)


Lesenswert?

Würde natürlich sehr gerne lernen, wie man sowas besser macht.


Gruß
Georg

von Karl H. (kbuchegg)


Lesenswert?

Du arbeitest gefährlich! Die strcat an mapBuffer sind alle nicht 
abgesichert. Mit einer Falscheingabe kannst du dein Programm ganz leicht 
-> BUMM!

Definiere dir char Arrays mit denen du auf Strings gehst nicht so auf 
knirsch! Lass dir ein wenig Platz um zumindest kleinere Fehler auffangen 
zu können.

Arbeite mit sizeof um Arraygrenzen zu prüfen, das mindeste sind aber ein 
paar #define um Arraygrößen festzulegen und dann mit diesen #define die 
Bereichsgrenzen im Programm zu prüfen.

Fread erwartet von dir lediglich die Adresse eines Speichers, an dem es 
die von dir vorgegebene Anzahl an Zeichen ablegen kann. Da du sowieso 
nur 1 Zeichen haben willst, kannst du ihm auch die Adresse eines char 
übergeben, in welches Fread das Zeichen reingibt. Dann brauchst du nicht 
die teuren Funktionen strcat, strcmp zur Weiterverarbeitung benutzen.

Wenns leicht geht, rechne auch immer damit, dass die Eingaben Fehler 
enthalten. Ein Leerzeichen da ein Tabulator dort. Schreib den Code nach 
Möglichkeit so, dass dir die nicht in die Quere kommen.
1
#define TORQUE 0
2
#define REV 1
3
4
#define MAP_SIZE 50
5
6
char ReadBuffer
7
char mapBuffer[6];
8
uint16_t map[MAP_SIZE][2];
9
uint8_t map_index;
10
uint8_t byte_count = 0;
11
uint8_t nextMap = 0;
12
13
void get_map(void)
14
{
15
  while( Fread( &ReadBuffer, 1 ) != -1 ) {
16
17
    if( ReadBuffer == '[' ) {
18
19
      nextMap = 0;
20
      while( Fread( &ReadBuffer, 1 ) != -1 ) {
21
22
        if( ReadBuffer == ';' )
23
          break;
24
25
        if( nextMap < sizeof( mapBuffer ) - 1 )
26
          mapBuffer[ nextMap++ ] = ReadBuffer;
27
      }
28
      mapBuffer[ nextMap ] = '\0';
29
      if( map_index < MAP_SIZE )
30
        map[map_index][TORQUE] = atoi( mapBuffer );
31
32
      nextMap = 0;
33
      while( Fread( &ReadBuffer, 1 ) != -1 ) {
34
35
        if( ReadBuffer == ']' )
36
          break;
37
38
        if( nextMap < sizeof( mapBuffer ) - 1 )
39
          mapBuffer[ nextMap++ ] = ReadBuffer;
40
      }
41
      mapBuffer[ nextMap ] = '\0';
42
      if( map_index < MAP_SIZE )
43
        map[map_index][REV] = atoi( mapBuffer );
44
    }
45
    map_index++;
46
  }
47
}

In dieser Funktion kommt der Code um eine Zahl einzulesen 2 mal vor. 
Wäre ein Kandidat um in eine Funktion ausgelagert zu werden. Der 
Codegröße und der Übersicht von get_map würde das zu Gute kommen. Oder 
hast du bemerkt, dass auch jedes Leerezeichen zwischen den Klammern 
ebenfalls den map_index um 1 erhöht. Ich habs auch nicht gesehen, ehe 
ich refakturiert habe
1
uint16_t get_number( char Terminator )
2
{
3
  uint8_t i = 0;
4
5
  while( Fread( &ReadBuffer, 1 ) != -1 ) {
6
7
    if( ReadBuffer == Terminator )
8
       break;
9
10
    if( i < sizeof( mapBuffer ) - 1 )
11
      mapBuffer[ i++ ] = ReadBuffer;
12
  }
13
  mapBuffer[ i ] = '\0';
14
15
  return atoi( mapBuffer );
16
}
17
18
void get_map(void)
19
{
20
  uint16_t torque;
21
  uint16_t rev;
22
23
  while( Fread( &ReadBuffer, 1 ) != -1 ) {
24
25
    if( ReadBuffer == '[' ) {
26
     
27
      torque = get_number( ';' );
28
      rev    = get_number( ']' );
29
30
      if( map_index < MAP_SIZE ) {
31
        map[map_index][TORQUE] = torque;
32
        map[map_index][REV]    = rev;
33
34
        map_index++;
35
      }
36
    }
37
  }
38
}

von sebastians (Gast)


Lesenswert?

Du könntest auch einen ganz anderen Ansatz nehmen: Gib dem 
Microcontroller eine Binärdatei. Mach die Umwandlung von Text- in 
Binärdatei offline. Dann kannst du eine Skriptsprache deiner Wahl 
nehmen, mit der sich einfach Texte parsen lassen. Syntaxfehler erkennst 
du schon beim konvertieren, brauchst dich im Microcontroller nicht mehr 
drum kümmern. Und der Code im Microcontroller wird kleiner+schneller.

von Georg (Gast)


Lesenswert?

@Karl heinz Buchegger
Danke für die vielen Tipps, hab es so umgesetzt. Werde noch versuchen 
möglichst viele Fehleingaben abzufangen.

@sebastians
Danke für den Vorschlag. Finde es aber recht gut, einfach in eine 
Textdatei schreiben zu können.


Gruß
Georg

PS. Wie kommt es eigentlich zu der Kompiler-Warnung:
warning: pointer targets in passing argument 1 of 'Fread' differ in 
signedness

von Karl H. (kbuchegg)


Lesenswert?

Georg schrieb:
> PS. Wie kommt es eigentlich zu der Kompiler-Warnung:
> warning: pointer targets in passing argument 1 of 'Fread' differ in
> signedness

Schau dir den Protoypen von Fread an.
Höchst wahrscheinlich will Fread einen Pointer auf einen unsigned char 
habe, du übergibst ihm aber die Adresse eines char.

von Georg (Gast)


Lesenswert?

Ja, natürlich, so ist es.

Warum eigentlich
1
  while( Fread( &ReadBuffer, 1 ) != -1 ) {

sollte es nicht
1
  while( Fread( &ReadBuffer, 1 ) != 0 ) {


heißen?

Zumindest bleibt das Prog. in der Funktion get_map hängen.

von Karl H. (kbuchegg)


Lesenswert?

Mein Fehler
Durch deine dichte Schreibweise
1
    byte_count=Fread(ReadBuffer,1);
2
      if(byte_count!=1)break;
hab ich ein Minus gesehen, wo keines ist

Bau dir doch Leerzeichen ein. Das kostet dir nichts! Seit du 6 Jahre alt 
bist, hast du dein Gehirm darauf trainiert, dass beim Lesen dort wo ein 
Leerraum ist, ein neues Wort anfängt. Benutze dieses Training! Dann kann 
man auch Programmtext 'im ganzen Satz' lesen ohne erst mit den Augen vor 
und rück durch den Buchstabensalat zu müssen um rauszufinden wo etwas 
anfängt und wo es wieder aufhört.
1
    byte_count = Fread( ReadBuffer, 1 );
2
      if( byte_count != 1 )
3
        break;

Die Absicht ist daher:
Aus der Schleife raus, wenn Fread nicht 1 Zeichen lesen konnte. Die 
Schleife soll also laufen, solange Fread 1 Zeichen lesen kann
1
    while( Fread( &ReadBuffer, 1 ) == 1 ) {

(kommt an anderen Stellen auch noch vor)

von Georg (Gast)


Lesenswert?

Alles klar. Werde in Zukunft versuchen, übersichtlicher zu 
programmieren.

Noch mal zu der signed-warnung.

Ich benutze die Funktionen atoi() und Fread() ja jeweils mit der 
Variablen ReadBuffer. Fread() möchte einen unsigned char-Pointer und 
atoi einen char-Pointer.

Ich bekommen also bei einem der Funktionsaufrufe immer die 
signed-Warning.

Wie bekomme ich die nun weg?

von Karl H. (kbuchegg)


Lesenswert?

Georg schrieb:

> Wie bekomme ich die nun weg?

Die kannst du an dieser Stelle getrost wegcasten. Nach dem Motto: Wir 
wissen das das passt, also geben wir der Funktion was sie haben will.
1
 while( Fread( (unsigned char*)&ReadBuffer, 1 ) == 1 ) {
2
   ...

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.