Struct mit Schleife durch zählen

OP #1476518
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
Gast #1476543
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.
Gast #1476566
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);
Gast #1476586
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.
Gast #1476840
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
#1476846
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.
OP #1476861
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
Gast #1476907
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
#1476915
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.
OP #1476923
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
#1476953
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
#1476972
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.
#1477005
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.
#1477054
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.
OP #1536161
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
#1536320
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?

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren