Forum: Mikrocontroller und Digitale Elektronik Struct mit Schleife durch zählen


von Denny S. (nightstorm99)


Lesenswert?

Hallo!

Ich stehe hier gerade vor einem kleinen Problem und weiß nicht so recht 
weiter und im Inet kann ich auch nicht so richtig was finden!

Ich habe eine Struktur:
1
struct heizung
2
{
3
  union 
4
  {
5
    double wert;
6
    uint8_t byte[4];
7
  }  Tag_temp;
8
  
9
  union 
10
  {
11
    double wert;
12
    uint8_t byte[4];
13
  }  Nacht_temp;
14
  
15
  union 
16
  {
17
    double wert;
18
    uint8_t byte[4];
19
  }  Manuell_temp;
20
  
21
  unsigned char aktiv;        
22
  unsigned char Zeit;              
23
  unsigned char Zeit_Tag[MAX_TIMERS];    
24
  unsigned char Zeit_Stunde[MAX_TIMERS];    
25
  unsigned char Zeit_Minute[MAX_TIMERS];    
26
  unsigned char Zeit_Wert[MAX_TIMERS];    
27
  unsigned char anheizen;          
28
} ;
diese möchte ich jetzt mit einer Schleife durch zählen und jedes Byte in 
ein FRAM (ähnlich Eeprom) schreiben. Und das ganze muss man dann auch 
wieder laden können und zurück in Struktur geben.

So wie stelle ich sowas am besten an und stimmt die Reihenfolge der 
Bytes nach dem einlesen dann noch?
Ich hoffe mir kann einer helfen!

Danke
Gruß Denny

von holger (Gast)


Lesenswert?

>So wie stelle ich sowas am besten an

Mit einem Pointer auf deine Struktur.

> und stimmt die Reihenfolge der
>Bytes nach dem einlesen dann noch?

Wenn du es richtig machst stimmt es dann auch wieder.

von Di P. (drpepper) Benutzerseite


Lesenswert?

Schau dir zum Thema "Welches element einer struct bekommt welchen Teil 
des Speichers" nochmal ein paar Tuts an.
Ansonsten kann ich mich meinen Vorredner nur anschließen.

von Wolfgang Mües (Gast)


Lesenswert?

Hallo Denny,

ich würde AUF JEDEN FALL jedes Element einzeln lesen und schreiben (also 
nicht einen byte * auf den Anfang setzen und bis sizeof (struct heizung) 
kopieren.

Weil:
- ein Compiler kann überall Lücken einfüllen.
- ein Compiler kann alle Elemente des structs in beliebiger Reihenfolge 
anordnen!

und am schlimmsten:
- die nächste Version den Compilers kann es komplett anders machen!

"Hilfe, ich habe mein Programm erneuert, und jetzt sind meine 
gespeicherten Messwerte Müll!".

Am einfachsten kleine Lese- und Schreibroutinen für die elementaren 
Datentypen schreiben und damit dann die einzelnen Elemente 
transferieren.

Also z.B.

void WriteInt( unsigned eeprom_adresse, int wert);

von holger (Gast)


Lesenswert?

>Weil:
>- ein Compiler kann überall Lücken einfüllen.

attribute packed ;)

von Denny S. (nightstorm99)


Lesenswert?

Hallo!

@Wolfgang

Vielen Dank für die Hinweise. Dann werde ich es so lassen wie ich es 
jetzt habe, da schreibe ich jede Variable einzeln. Ich dachte man könnte 
es so ein wenig vereinfachen und schneller machen. Gerade weil in der 
Struktur noch ein paar Arrays sind.

Danke

Denny

von Naja (Gast)


Lesenswert?

Ich finde die Antwort von Wolfgang ein wenig problematisch, wenn sie 
auch sicherlich gut gemeint ist.

Das Problem ist, das ein Anfänger, weder das Ursprungsproblem wirklich 
beurteilen kann, noch die Probleme die Wolfgang aufwirft beurteilen 
kann.

Die einfachste Variante ist dann aber die mit dem gecasteten Zeiger, mit 
dem der Inhalt unter Zuhilfename von sizeof kopiert oder weggeschrieben 
wird.

1. Selbst wenn die struct nicht gepackt ist, werden allenfalls die 
Lücken mitkopiert, aber sie schaden nicht. (pragma pack ist trotzdem 
noch ein guter Tip).
2. Es ist zwar richtig, das ein Compiler die Strukturmitglieder beliebig 
anordnen kann, aber er macht es bei ein und der selben Struktur immer 
gleich. (Eigentlich habe ich bis jetzt nur Compiler gesehen, welche die 
Elemente entsprechend der Reihenfolge in der Deklaration anordnen. 
Selbst Optimierungen die sich aus einer anderen Reihenfolge ergeben 
würden sah ich bis jetzt nicht). Wenn also die Reihenfolge bei dem 
selben Compiler immer gleich ist, gibt es mit der Zeigermethode keine 
Probleme.

Nichts desto trotz sind Wolfgangs Tips unter folgenden Bedingungen 
relevant. Es wird ein anderer Prozessor mit anderer Wortbreite verwendet 
oder der Compiler ist ein völlig anderer. Trifft eines von beidem zu 
sollte man mal in das Map-File gucken. Dennoch wird mit grosser 
Wahrscheinlichkeits nichts passieren.

Und die Methode von Wolfgang, die Elemente zu Fuss zu schreiben, ist 
eigentlich die naheliegendste und sicherste. Der TO könnte das 
vermutlich wissen, fragt aber dennoch. Man sollte mal fragen wieso 
eigentlich.

An den TO der Tip: Schau Dir mal im Map-File an, wie die Struktur im 
Speicher aussieht. Dann kannst Du beides beurteilen und weisst auch 
warum der "Trick" (es ist eigentlich keiner) mit dem Zeiger 
funktioniert.
Da Du schon mit unions arbeitest, sollte Dir die Idee mit einem 
sozusagen überdeckendem anderen Datentyp bekannt sein.

von Karl H. (kbuchegg)


Lesenswert?

Und wenn deine Struktur wirklich so aussieht und da nicht auch noch was 
dazukommt, solltest du die Idee mit den union noch einmal überdenken. 
Diese 3 union bringen nichts ausser Konfusion.

von Denny S. (nightstorm99)


Lesenswert?

Hallo!

@ Karl heinz Buchegger

Wie kann man dann soetwas lösen mit den Unions?


Gruß Denny

von Denny S. (nightstorm99)


Lesenswert?

Also ich weise dem Double in der Union meine gemessene Temperatur zu und 
dann schreibe ich aus dem Array die einzelnen Bytes in ein I2C Epprom.

Kann man das auch anders lösen?


Gruß Denny

von Nick M. (Gast)


Lesenswert?

> Wie kann man dann soetwas lösen mit den Unions?

Du nimmst den pointer auf dein struct, castest den auf byte* und 
schreibst sizeof(heizung) Bytes ins EEPROM.

Solange du den compiler nicht wechselst, ist das kein Problem. Du darfst 
aber optionen wie pack oder alignment nicht ändern.
Wenn du es "richtig" machen willst und alle Teile der struct einzeln 
wegschreiben willst, wirds aufwendiger. Dann kannst du gleich noch ein 
Versions-tag vornedran setzen falls später mal Erweiterungen zur struct 
kommen.

Die unions hast du etwas misbraucht, die verwendet man zusammen mit z.B. 
einem enum oder typ-Identifizierung um verschiedene Inhalte übereinander 
zu legen und so Speicherplatz einzusparen. Für deinen Falls tut es ein 
casting auf byte.


Gruß,
Nick

von Karl H. (kbuchegg)


Lesenswert?

Über den double solltest du aich noch einmal nachdenken.

Ist es wirklich notwenidig, dir Temperatur auf 5 Nachkommastellen zu 
halten. Ist es wirklich notwendig Temperaturen in der Größenordnung von 
ein paar Millionen halten zu können?

Oder ist es nicht eher so, dass 1 Nachkommastelle mehr als ausreichend 
ist? Kannst du überhaupt so genau messen?

Du könntest zb mit dir vereinbaren, dass dein Pgm die Temperatur in 
Zehntelgrad speichert. Ein Wert von 231 wären dann 23.1 Grad. Dann ist 
ein int völlig ausreichend.

von Denny S. (nightstorm99)


Lesenswert?

> Oder ist es nicht eher so, dass 1 Nachkommastelle mehr als ausreichend
> ist? Kannst du überhaupt so genau messen?
>
> Du könntest zb mit dir vereinbaren, dass dein Pgm die Temperatur in
> Zehntelgrad speichert. Ein Wert von 231 wären dann 23.1 Grad. Dann ist
> ein int völlig ausreichend.

Ja eine stelle nach dem Komma ist völlig ausreichend.
1
unsigned int temp1;
2
3
temp1 = (unsigned int)(temperatur * 10);

Wäre das so richtig?

Hat einer mal ne Seite wo das mit dem Casten genau steht? Habe ich noch 
nicht ganz verstanden.

Danke
Gruß Denny

von Nick M. (Gast)


Lesenswert?

> Hat einer mal ne Seite wo das mit dem Casten genau steht?

Das hast du doch schon gemacht.
"temp1 = (unsigned int)(temperatur * 10);"
Das (unsigned int) ist eines.
Allerdings in dem Zusammenhand flasch. Schreib einfach
temp1 = temperatur * 10. Darfst aber auch runden.


Gruß,
Nick

von Hc Z. (mizch)


Lesenswert?

Dem Compiler ist nach C-Standard nicht erlaubt, struct-Mitglieder 
umzusortieren.  Sie müssen in der vorgegebenen Reihenfolge angeordnet 
sein.  Das Padding kann verhindert werden, indem das Attribut 'packed' 
mitgegeben wird (bei WinAVR sind structs bereits implizit gepackt, wer 
ganz sicher gehen will gegen zukünftige Änderungen, schreibt das 
Attribut noch dazu).  Somit ist es völlig OK, die struct byteweise 
einzulesen und rauszuschreiben, solange man innerhalb derselben 
Architektur bleibt.

Die Warnungen gegen ein Umordnen sind gut gemeint, aber nicht 
zutreffend.

von Denny S. (nightstorm99)


Lesenswert?

Hazeh Zimmerer schrieb:
> Dem Compiler ist nach C-Standard nicht erlaubt, struct-Mitglieder
> umzusortieren.  Sie müssen in der vorgegebenen Reihenfolge angeordnet
> sein.  Das Padding kann verhindert werden, indem das Attribut 'packed'

Kannst du mir das mit dem "packed" erklären und wo muss das hin?

> mitgegeben wird (bei WinAVR sind structs bereits implizit gepackt, wer
> ganz sicher gehen will gegen zukünftige Änderungen, schreibt das
> Attribut noch dazu).  Somit ist es völlig OK, die struct byteweise
> einzulesen und rauszuschreiben, solange man innerhalb derselben
> Architektur bleibt.

Hättest du da mal ein Beispiel wie sowas gemacht wird?

Gruß Denny

von Hc Z. (mizch)


Lesenswert?

> Kannst du mir das mit dem "packed" erklären und wo muss das hin?
1
typedef struct a {char x, ...} __attribute__ ((packed)) DingsBums;

In der Praxis kannst Du aber davon ausgehen, dass bei einer 
8-Bit-Architektur Packen keinen Sinn hat, weil nie ein Padding gemacht 
wird, und Dir dies sparen.

>>  Somit ist es völlig OK, die struct byteweise
>> einzulesen und rauszuschreiben, solange man innerhalb derselben
>> Architektur bleibt.
>
> Hättest du da mal ein Beispiel wie sowas gemacht wird?
1
uint8_t *writepointer = (uint8_t *) &DeineStruct;
2
for (uint8_t i = 0; i < sizeof(DeineStruct); i++, writepointer++)
3
  eeprom_write_byte(writepointer, *writepointer);

um eine struct ins EEProm zu schreiben.

Edit: Fehler in der structure-Definition

von Hc Z. (mizch)


Lesenswert?

>
1
> uint8_t *writepointer = (uint8_t *) &DeineStruct;
2
> for (uint8_t i = 0; i < sizeof(DeineStruct); i++, writepointer++)
3
>   eeprom_write_byte(writepointer, *writepointer);
4
>

Korrektur:  das schreibts an die Stelle ins EEProm, an der's auch im 
SRAM steht, was natürlich sinnlos ist.  Bei eeprom_write_byte gehört als 
erstes Argument die EEProm-Adresse mit Postinkrement hin.

von Karl H. (kbuchegg)


Lesenswert?

Man kann natürlich auch ganz einfach die

eeprom_write_block und eeprom_read_block benutzen.

http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Block_lesen.2Fschreiben
1
...
2
struct heizung MyHeizung;
3
4
...
5
6
  eeprom_read_block( &MyHeizung, eeMyHeizung, sizeof( MyHeizung ) );
7
8
...
9
10
  eeprom_write_block( &MyHeizung, eeMyHeizung, sizeof( MyHeizung ) );

Im gcc-Tutorial gibt es da einen schönen Abschnitt darüber.

Padding ist natürlich grundsätzlich ein Thema. Allerdings ist es auf 
einem AVR müssig darüber nachzudenken, weil es im gcc in diesem Fall 
keines gibt.

von Denny S. (nightstorm99)


Lesenswert?

Hallo!

Ich will ja nicht ins interne Epprom schreiben sondern in ein I2C Eeprom 
und
da gibs leider solche Funktionen nicht!
Benutze dafür die Lib von Peter Fleury.
1
i2c_write(byte1);
2
i2c_write(byte2);

Mal als Beispiel.

Gruß Denny

von Hc Z. (mizch)


Lesenswert?

Karl heinz Buchegger schrieb:
> Man kann natürlich auch ganz einfach die
>
> eeprom_write_block und eeprom_read_block benutzen.

Klar. Allerdings wollte der Threadersteller ins FRAM schreiben, weshalb 
ich ein anderes Beispiel gewählt habe.

von Hc Z. (mizch)


Lesenswert?

Oh, da war der Ersteller schneller.  Mift.

von Karl H. (kbuchegg)


Lesenswert?

Denny S. schrieb:
> Hallo!
>
> Ich will ja nicht ins interne Epprom schreiben sondern in ein I2C Eeprom
> und
> da gibs leider solche Funktionen nicht!
> Benutze dafür die Lib von Peter Fleury.
>
>
1
> i2c_write(byte1);
2
> i2c_write(byte2);
3
>
>
> Mal als Beispiel.

Ah. Entschuldigung. Der Thread ist schon etwas zu lang.
Also doch: Eine Schleife drüber.

Aber verpack dir die Block-Schreiberei/Leserei in eine eigene Funktion. 
Kannst ja die eeprom_read_block als Vorbild dafür nehmen, wie die 
Funktionssignatur aussehen könnte.

von Denny S. (nightstorm99)


Lesenswert?

Hallo!

Ich habe das mit den Schleifen nun gemacht und geht auch FAST gut.
Ich benutze mehrere Structuren die ich nach ein ander einlese, aber bei 
2 funktioniert es nicht richtig. Obwohl immer wieder die gleiche 
Funktion aufgerufen wird.
Dort liest er falsche Werte ein!

Hier mal die Funktionen:
1
//#########################################################################################################
2
void lesen_heizung_2(unsigned char z)
3
{
4
  LED4_ON;
5
  
6
  uint8_t dummy;
7
  uint8_t *writepointer = (uint8_t *) &ZIMMER[z];
8
  
9
  i2c_start_wait(pgm_read_word(&chip_select[z])+I2C_WRITE);
10
  i2c_write(pgm_read_byte(&adresse[z]));
11
  i2c_rep_start(pgm_read_word(&chip_select[z])+I2C_READ);
12
  
13
  for (uint8_t i = 0; i < sizeof(ZIMMER[z]); i++, writepointer++)
14
  {
15
    *writepointer = i2c_readAck();
16
  }
17
  dummy = i2c_readNak();
18
  
19
  i2c_stop();
20
  LED4_OFF;
21
}
22
//#########################################################################################################
23
void schreiben_heizung_2(unsigned char z)
24
{
25
  LED4_ON;
26
  
27
  uint8_t *writepointer = (uint8_t *) &ZIMMER[z];
28
  
29
  i2c_start_wait(pgm_read_word(&chip_select[z])+I2C_WRITE);
30
  i2c_write(pgm_read_byte(&adresse[z]));
31
  
32
  for (uint8_t i = 0; i < sizeof(ZIMMER[z]); i++, writepointer++)
33
  {
34
    i2c_write(*writepointer);
35
  }
36
  
37
  i2c_stop();
38
  LED4_OFF;
39
}

Ich habe das ja nach dem Beispiel oben im Thread gemacht.

Könnte es sein das es an der falschen Größe der Variablen liegt?
Sind Adressen immer uint8_t oder sollten sie nicht uint16_t sein?
Benutze ja ein Atmega2561 und der sollte mehr als 255 Adressen haben!

Gruß Denny

von Hc Z. (mizch)


Lesenswert?

> Könnte es sein das es an der falschen Größe der Variablen liegt?
> Sind Adressen immer uint8_t oder sollten sie nicht uint16_t sein?

In Deinem geposteten Code sehe ich nur eine Variable, die eine Adresse 
beinhaltet:
1
>   uint8_t *writepointer = (uint8_t *) &ZIMMER[z];
Die ist 16 Bit.

Dass sizeof(ZIMMER[z]) nie größer als 256 werden kann, hast Du schon 
abgeklopft, nehme ich an.  Hast Du auch abgeklopft, ob die Gesamtlänge 
ins EEProm passt?

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.