mikrocontroller.net

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


Autor: Denny S. (nightstorm99)
Datum:

Bewertung
0 lesenswert
nicht 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:
struct heizung
{
  union 
  {
    double wert;
    uint8_t byte[4];
  }  Tag_temp;
  
  union 
  {
    double wert;
    uint8_t byte[4];
  }  Nacht_temp;
  
  union 
  {
    double wert;
    uint8_t byte[4];
  }  Manuell_temp;
  
  unsigned char aktiv;        
  unsigned char Zeit;              
  unsigned char Zeit_Tag[MAX_TIMERS];    
  unsigned char Zeit_Stunde[MAX_TIMERS];    
  unsigned char Zeit_Minute[MAX_TIMERS];    
  unsigned char Zeit_Wert[MAX_TIMERS];    
  unsigned char anheizen;          
} ;
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

Autor: holger (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Di Pi (drpepper) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Wolfgang Mües (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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);

Autor: holger (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Weil:
>- ein Compiler kann überall Lücken einfüllen.

attribute packed ;)

Autor: Denny S. (nightstorm99)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Naja (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Denny S. (nightstorm99)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo!

@ Karl heinz Buchegger

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


Gruß Denny

Autor: Denny S. (nightstorm99)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Nick Müller (muellernick)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Denny S. (nightstorm99)
Datum:

Bewertung
0 lesenswert
nicht 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.
unsigned int temp1;

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

Autor: Nick Müller (muellernick)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Hc Zimmerer (mizch)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Denny S. (nightstorm99)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Hc Zimmerer (mizch)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Kannst du mir das mit dem "packed" erklären und wo muss das hin?
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?
uint8_t *writepointer = (uint8_t *) &DeineStruct;
for (uint8_t i = 0; i < sizeof(DeineStruct); i++, writepointer++)
  eeprom_write_byte(writepointer, *writepointer);

um eine struct ins EEProm zu schreiben.

Edit: Fehler in der structure-Definition

Autor: Hc Zimmerer (mizch)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>
> uint8_t *writepointer = (uint8_t *) &DeineStruct;
> for (uint8_t i = 0; i < sizeof(DeineStruct); i++, writepointer++)
>   eeprom_write_byte(writepointer, *writepointer);
> 

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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Man kann natürlich auch ganz einfach die

eeprom_write_block und eeprom_read_block benutzen.

http://www.mikrocontroller.net/articles/AVR-GCC-Tu...

...
struct heizung MyHeizung;

...

  eeprom_read_block( &MyHeizung, eeMyHeizung, sizeof( MyHeizung ) );

...

  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.

Autor: Denny S. (nightstorm99)
Datum:

Bewertung
0 lesenswert
nicht 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.
i2c_write(byte1);
i2c_write(byte2);

Mal als Beispiel.

Gruß Denny

Autor: Hc Zimmerer (mizch)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Hc Zimmerer (mizch)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Oh, da war der Ersteller schneller.  Mift.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.
>
>
> i2c_write(byte1);
> i2c_write(byte2);
> 
>
> 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.

Autor: Denny S. (nightstorm99)
Datum:

Bewertung
0 lesenswert
nicht 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:
//#########################################################################################################
void lesen_heizung_2(unsigned char z)
{
  LED4_ON;
  
  uint8_t dummy;
  uint8_t *writepointer = (uint8_t *) &ZIMMER[z];
  
  i2c_start_wait(pgm_read_word(&chip_select[z])+I2C_WRITE);
  i2c_write(pgm_read_byte(&adresse[z]));
  i2c_rep_start(pgm_read_word(&chip_select[z])+I2C_READ);
  
  for (uint8_t i = 0; i < sizeof(ZIMMER[z]); i++, writepointer++)
  {
    *writepointer = i2c_readAck();
  }
  dummy = i2c_readNak();
  
  i2c_stop();
  LED4_OFF;
}
//#########################################################################################################
void schreiben_heizung_2(unsigned char z)
{
  LED4_ON;
  
  uint8_t *writepointer = (uint8_t *) &ZIMMER[z];
  
  i2c_start_wait(pgm_read_word(&chip_select[z])+I2C_WRITE);
  i2c_write(pgm_read_byte(&adresse[z]));
  
  for (uint8_t i = 0; i < sizeof(ZIMMER[z]); i++, writepointer++)
  {
    i2c_write(*writepointer);
  }
  
  i2c_stop();
  LED4_OFF;
}

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

Autor: Hc Zimmerer (mizch)
Datum:

Bewertung
0 lesenswert
nicht 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:
>   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

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.