Forum: Mikrocontroller und Digitale Elektronik sizeof() funktioniert nicht bei Array im EEPROM


von dasrotemopped (Gast)


Lesenswert?

Hallo zusammen,

ich versuche gerade, diverse char Arrays mit verschiedener Länge vom 
Flash ins EEprom zu verlagern um Flash Speicher zu sparen ( Atmega48 ). 
Die char Arrays werden im Programm nur gelesen. Die char Arrays gebe ich 
dann auf die UART Schnittstelle aus. Bei der Ausgabe muss ich allerdings 
wissen, wie lang die einzelnen Arrays sind. Leider funktioniert sizeof() 
nicht, das gibt immer 2 als Rückgabewert für die im EEprom gespeicherten 
Arrays aus. Kann ich die Länge der Arrays irgendwie per Software 
feststellen ?

Gruß,

dasrotemopped

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

sizeof funktioniert korrekt; Du übergibst einen Pointer, und sizeof 
gibt Dir dessen Größe zurück. Und die ist auf einem AVR nunmal 2.

Um die Größe eines Arrays zu bestimmen, sind andere Mechanismen 
anzuwenden, und das ist unabhängig davon, ob die Arrays nun im EEPROM 
oder im RAM stehen.

Es ist anzunehmen, daß Deine "char Arrays" Strings sind - die übliche 
Funktion zur Stringlängenbestimmung ist strlen, wobei Du aufgrund der 
vergurkten Architektur des AVR wohl auf strlen_P zurückgreifen musst.

von Karl H. (kbuchegg)


Lesenswert?

Rufus Τ. Firefly schrieb:

> Es ist anzunehmen, daß Deine "char Arrays" Strings sind - die übliche
> Funktion zur Stringlängenbestimmung ist strlen, wobei Du aufgrund der
> vergurkten Architektur des AVR wohl auf strlen_P zurückgreifen musst.


Ich geh auch mal davon aus, dass wir hier in Wirklichkeit über Strings 
reden.

> Die char Arrays gebe ich dann auf die UART Schnittstelle aus.
> Bei der Ausgabe muss ich allerdings wissen, wie lang die einzelnen
> Arrays sind.

Nö, muss man ncht.
Die Länge von Strings ergibt sich automatisch durch das abschliessende 
'\0' Zeichen. Bei der Behandlung von Strings, insbesonderer bei der 
Ausgabe muss man ganz selten die Größe vorab wissen. Im Regelfall gibt 
man einfach aus, bis man auf das \0 Zeichen stößt und das wars. Kein 
Mensch muss vorher wissen wie lang der String dabei ist.

von Μαtthias W. (matthias) Benutzerseite


Lesenswert?

Rufus Τ. Firefly schrieb:
> Es ist anzunehmen, daß Deine "char Arrays" Strings sind - die übliche
> Funktion zur Stringlängenbestimmung ist strlen, wobei Du aufgrund der
> vergurkten Architektur des AVR wohl auf strlen_P zurückgreifen musst.

Das wird bei einem String im EEPROM aber auch nicht funktionieren. Das 
EEPROM liegt ja nochmal in einem anderen "Adressbereich".

Ich sehe auch keinen Grund warum sizeof() nicht funktionieren sollte. 
Wahrscheinlich wird der Operator eben, wie schon vermutet, auf die 
falsche Variable angewendet nämlich auf einen Pointer und nicht auf das 
Array selbst.

Matthias

von Floh (Gast)


Lesenswert?

dasrotemopped schrieb:
> Die char Arrays gebe ich
> dann auf die UART Schnittstelle aus. Bei der Ausgabe muss ich allerdings
> wissen, wie lang die einzelnen Arrays sind.

Ausgabe bis Zeichen gleich '\0' NUL-Byte ist, dann stop.
Spart ne Menge Probleme. :-)

von Μαtthias W. (matthias) Benutzerseite


Lesenswert?

Floh schrieb:
> dasrotemopped schrieb:
>> Die char Arrays gebe ich
>> dann auf die UART Schnittstelle aus. Bei der Ausgabe muss ich allerdings
>> wissen, wie lang die einzelnen Arrays sind.
>
> Ausgabe bis Zeichen gleich '\0' NUL-Byte ist, dann stop.
> Spart ne Menge Probleme. :-)

Geht aber schon bei rechtsbündiger Ausgabe nicht mehr so einfach.

Matthias

von dasrotemopped (Gast)


Lesenswert?

Hier habe ich mal den C-Code zur Ansicht :

  char r_on[] ="ON";
  char r_off[] ="OFF";
  char relais[] ="Relais already ";
  char def[] ="no op";
  char help_0[] EEMEM ="0 - Relais ausschalten";
  char help_1[] EEMEM ="1 - Relais einschalten";
  char help_2[] EEMEM ="l - Loop starten";
  char help_3[] EEMEM ="s - shot (nur mit 230V)";
  char help_4[] EEMEM ="b - break (nur mit 230V & Relais ein)";
  char help_5[] EEMEM ="x - break (nur mit 230V & Relais ein) after 2 
sec off";
  char help_6[] EEMEM ="h - Hilfe";

void send_eemem_string(char a[])
  {
  uint8_t cnt=0;
  /*
  while(a[cnt]!=(char)0){
    uart_send_byte(eeprom_read_byte(&a[cnt]));
    cnt++;
    }
  */

  for(uint8_t x=0;x<22;x++)
    {
    uart_send_byte(eeprom_read_byte(&a[x]));
    }

  }

void relais_on()
  {
    if((PORTC &~ 0b00010000 )){
    PORTC = 0b00010000;
    } else{
    send_eemem_string(help_1);
    uart_send_lf();
    uart_send_string(relais);
    }
    uart_send_string(r_on);
    uart_send_lf();
  }

Probleme macht halt die send_eemem_string() Routine, die 
uart_send_string() Routine ist nur eine mehrfach aufgerufene 
uart_send_byte() Routine.
Auf das NUL Byte prüfen hat schon mal nicht funktioniert,
mit strlen und strlen_P auch nicht.
Vielleicht mache ich mir ein Hilfsarray mit den Stringlängen, das sollte 
bei statischen Strings noch praktikabel sein. Oder hat noch jemand eine 
Idee ?

Gruß,

dasrotemopped

von Sven (Gast)


Lesenswert?

Wieso holst Du dir das EEPROM-Array nicht als Block wie im AVR-GCC 
Tutorial beschrieben ?
1
eeprom_read_block(myByteBuffer,eeFooByteArray1,sizeof(myByteBuffer));

von Floh (Gast)


Lesenswert?

dasrotemopped schrieb:
> Auf das NUL Byte prüfen hat schon mal nicht funktioniert,
> mit strlen und strlen_P auch nicht.

deutet darauf hin dass kein Nul-Byte hinterlegt wird.

Beende deine Texte mal mit \0, also z.b.

dasrotemopped schrieb:
> char help_0[] EEMEM ="0 - Relais ausschalten\0";
und tests nochmal aus.
:-)

von Messfehler (Gast)


Lesenswert?

1
size_t strlen_E(EEMEM char * a) __attribute__((__pure__));
2
size_t strlen_E(EEMEM char * a) {
3
  size_t l=0;
4
  while (eeprom_read_byte(a++)) ++l;
5
  return l;
6
}
oder so...

von Karl H. (kbuchegg)


Lesenswert?

dasrotemopped schrieb:

> Probleme macht halt die send_eemem_string() Routine, die
> uart_send_string() Routine ist nur eine mehrfach aufgerufene
> uart_send_byte() Routine.
> Auf das NUL Byte prüfen hat schon mal nicht funktioniert,
> mit strlen und strlen_P auch nicht.

Dann hast du irgendwo einen Fehler gemacht.

Es gibt keinen Grund, warum
1
void send_eemem_string( const char* eString )
2
{
3
  char c = eeprom_read_byte( eString++ );
4
5
  while( c ) {
6
    uart_send_byte( c );
7
    c = eeprom_read_byte( eString++ );
8
  }
9
}

nicht funktionieren sollte.


Edit:
Da haben wirs schon

  while(a[cnt]!=(char)0){

Du kannst hier natürlich nicht über a[cnt] auf den Character zugreifen. 
Die Daten liegen im EEPROM! Um an den Character zu kommen, musst du ihn 
auch mit der Lesefunktion von dort lesen ehe du ihn testen kannst. Genau 
so, wie du es bei

   uart_send_byte(eeprom_read_byte(&a[cnt]));

richtigerweise getan hast.

von Karl H. (kbuchegg)


Lesenswert?

Sven schrieb:
> Wieso holst Du dir das EEPROM-Array nicht als Block wie im AVR-GCC
> Tutorial beschrieben ?
>
>
1
eeprom_read_block(myByteBuffer,eeFooByteArray1,sizeof(myByteBuffer));

Schlechte Idee.
Dazu muss er erst recht wieder die Stringlänge kennen, die er nur erhält 
indem er den String zuerst einzelzeichenweise durchgeht.
Ausserdem braucht man dann einen entsprechend großes Array, in dem der 
Block zwischengespeichert werden kann.

von Karl H. (kbuchegg)


Lesenswert?

Floh schrieb:
> dasrotemopped schrieb:
>> Auf das NUL Byte prüfen hat schon mal nicht funktioniert,
>> mit strlen und strlen_P auch nicht.
>
> deutet darauf hin dass kein Nul-Byte hinterlegt wird.

Dann wäre sein Compiler schadhaft.

von dasrotemopped (Gast)


Lesenswert?

habe mal die Vorschläge durchprobiert :

  uint8_t help_L[] EEMEM ={22,22,16,23,37,53,9};
  char help_0[] EEMEM ="0 - Relais ausschalten\0";
usw...
  char help_6[] EEMEM ="h - Hilfe\0";

void send_eemem_string(char a[], uint8_t l)
  {
// funktioniert !
/*
  char c = eeprom_read_byte( a++ );

  while( c ) {
    uart_send_byte( c );
    c = eeprom_read_byte( a++ );
  }
*/

// mit \0 funktioniert die Ausgabe nicht, es fehlen Buchstaben in der 
Ausgabe

  uint8_t cnt=0;

  while( !(strcmp(eeprom_read_byte(&a[cnt]),'\0')) ){
    uart_send_byte(eeprom_read_byte(&a[cnt]));
    cnt++;
    }


// funktioniert ! mit Hilfsarray help_L
/*
  for(uint8_t x=0;x<l;x++)
    {
    uart_send_byte(eeprom_read_byte(&a[x]));
    }
*/
  }

Da ich aber jetzt 2 funktionierende Lösung habe würde ich sagen "Mission 
accomplished !"

Danke an alle,

dasrotemopped.

von Karl H. (kbuchegg)


Lesenswert?

dasrotemopped schrieb:

>   while( !(strcmp(eeprom_read_byte(&a[cnt]),'\0')) ){

Oh Mann.
Du kannst doch nicht ein 2 Zeichen mit strcmp vergleichen!
strcmp vergleicht Strings und keine Einzelzeichen.

Was ist falsch an

  while( eeprom_read_byte(&a[cnt]) != '\0' )

Zu einfach? Zu banal? Zu simpel?

(Jetzt mal von der Tatsache abgesehen, dass es sinnlos ist, zweimal 
aufwändig dasselbe Zeichen mittels eeprom_read_byte aus dem Eeprom zu 
lesen)

> Da ich aber jetzt 2 funktionierende Lösung habe würde ich sagen "Mission
> accomplished !"

Nun ja.
Deine fehlgeschlagenen Versuche zeigen eigentlich, dass du mit 
Stringverarbeitung noch sehr weit auf Kriegsfuss stehst.
Und deine '2-te funktionierende Lösung' - nope, das willst du nicht 
wirklich machen. Das ist ausser kompliziert und fehleranfällig nur noch 
kompliziert und fehleranfällig.


Lerne Stringverarbeitung und fang an dich von der Sichtweise zu lösen, 
dass man auf die Character eines Strings per Array-Zugriff rankommt. 
Manchmal ist das eine gute Sichtweise, aber meistens ist die Sichtweise 
besser und einfacher, nach der man einen Pointer auf den Anfang eines 
Strings hat und mit dem Pointer durch den String durchgeht um damit 
Zeichen für Zeichen zu holen.

   while( *string ) {

     Zeichen = *string;

     mach was mit Zeichen

     string++;
  }

In deinem Fall kannst du natürlich nicht die Pointer Dereferenzierung 
benutzen, also den *. Stattdessen wird die Zugriffsfunktion, in deinem 
Fall für EEPROM benutzt, die den Pointer 'dereferenziert' und das 
Zeichen holt.

von dasrotemopped (Gast)


Lesenswert?

mit
> while( eeprom_read_byte(&a[cnt]) != '\0' )

funktioniert jetzt auch die 3. Lösung

Wie bekomme ich jetzt noch die Compiler Warnung

../LineSyncer.c:43: warning: pointer targets in passing argument 1 of 
'__eerd_byte_m48' differ in signedness

weg ? Diese Warnung kommt bei allen Lösungen durch 
"eeprom_read_byte(&a[cnt]".

Gruß,

dasrotemopped.

@ Karl Heinz : wenn ich schon perfekt C könnte müsste ich keine Fragen 
stellen. Bin mit Sicherheit nur ambitionierter Anfänger mit dem AVR 
Studio und C auf Atmel.

von Karl H. (kbuchegg)


Lesenswert?

dasrotemopped schrieb:
> mit
>> while( eeprom_read_byte(&a[cnt]) != '\0' )
>
> funktioniert jetzt auch die 3. Lösung
>
> Wie bekomme ich jetzt noch die Compiler Warnung
>
> ../LineSyncer.c:43: warning: pointer targets in passing argument 1 of
> '__eerd_byte_m48' differ in signedness

Sieh beim Prototypen von eeprom_read_byte nach, was es erwartet:
einen Pointer auf signed char oder einen Pointer auf unsigned char.

An dieser Stelle wissen wir, dass es ok ist, wenn der Pointer auf das 
jeweils falsche zeigt. Du willst nur das Byte haben und es spielt keine 
Rolle, ob eeprom_read_byte den Pointer nun auf unsigned oder auf signed 
char haben will, weil es mit dem Byte an dieser Stelle ja sowieso nichts 
macht, ausser es zurückzugeben.

WEnn du weist, welchen Pointer eeprom_read_byte haben will, dann castest 
du dir das dann einfach zurecht.

> @ Karl Heinz : wenn ich schon perfekt C könnte müsste ich keine Fragen
> stellen.

Darum gehts nicht.
Fragen ist ok.
Nur einige Fehler sind so banal (wie zb nicht zwischen Einzelzeichen und 
Strings zu unterscheiden und str... zu verwenden, wenn es einfach nicht 
angebracht ist), dass sie schon weh tun.

> Bin mit Sicherheit nur ambitionierter Anfänger mit dem AVR
> Studio und C auf Atmel.

Genau deswegen, ist ein gern gegebener und eigentlich sehr guter Rat: 
Mach deine ersten Schritte mit C mit einem guten Buch und auf dem PC.
Arbeite das erste Drittel des Buches auf einem PC durch. Alles, bis dann 
im Buch File-Verarbeitung beginnt. Also insbesondere auch 
Stringverarbeitung und einfache Pointer-Dinge, die kommen normalerweise 
im ersten Drittel vor. Dann hast du ein gutes Rüstzeug um auf einen µC 
zu wechseln und dich dann mit den µC-spezifischen Dingen rumzuschlagen.
PC deswegen, weil:
* Alles so funktioniert wie es im Buch beschrieben ist.
* Du eine Konsolenausgabe hast, die von Anfang an funktioniert und einem
  das Leben für Hilfs und Zwischenausgaben zur Fehlersuche erleichtert
* Man einen richtigen Debugger zur Verfügung hat um dem Programm
  zuzusehen und auf die Finger zu sehen, während es läuft.

Der Simulator um AVR-Studio ist zwar nicht schlecht, aber im Vergleich 
zu den Möglichkeiten am PC, ist er nur ein müder Abklatsch.

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.