Forum: Mikrocontroller und Digitale Elektronik Arduino & EEprom


von Toxic (Gast)


Lesenswert?

Eines vorweg: mein Programm funktioniert wie es soll aber es gibt da 
eine
Sache die mir nicht gefaellt,weil ich keine Antwort darauf habe.
Zur Sache:
Mein Programm speichert bestimmte Usereinstellungen im EEprom ab.Das ist 
an sich nix besonderes=>
==========================
Mit "EEPROM.put(0,205);  // brightness"
===========================
wird also an der Adresse 0 der Wert 205 abgelegt.(Ich verwende die 
Arduino-GUI).
Mein Problem ist nun,dass bei einem Usergewollten "Factory"-Reset 
mehrere Daten im EEprom in einem Rutsch abgelegt werden.
Das funktioniert auch, aber nur wenn die Adressen in aufsteigender(!) 
Reihenfolge durchlaufen werden.
Also in etwa so:
EEPROM.put(0,205);
EEPROM.put(4,12);
EEPROM.put(10,124);

Wenn ich aber die Reihenfolge aendere:
EEPROM.put(10,124);
EEPROM.put(4,12);
EEPROM.put(0,205);

werden teilweise falsche Daten abgelegt.

Dieses Problem habe ich nur wenn die EEpromdaten innerhalb einer Routine 
und die Adressen in beliebiger Reihenfolge abgelegt werden.
Hat dazu jemand eine Erklaerung?

Hinweis:
Als Gast kann ich erst wieder in einer Stunde antworten,sollte hier eine 
Antwort eintrudeln....

von Veit D. (devil-elec)


Lesenswert?

Hallo,

nimm einmal statt put das write. Bleibt der Effekt erhalten?

: Bearbeitet durch User
von Einer K. (Gast)


Lesenswert?

Put ist für komplexere Datentypen gedacht.

> EEPROM.put(10,124);
> EEPROM.put(4,12);
> EEPROM.put(0,205);

Wenn du bytes ablegen willst:
EEPROM[10] = 124;
EEPROM[4]  = 12;
EEPROM[0]  = 205;


Davon mal abgesehen:
Ich halte es für fast schon verwerflich die Adressen händisch zu 
berechnen.
Überlasse das besser dem Kompiler.
Der kann das.

von Wolfgang (Gast)


Lesenswert?

Veit D. schrieb:
> nimm einmal statt put das write. Bleibt der Effekt erhalten?

EEPROM.write() schreibt nur Einzelbytes, aber keine Objekte.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

beim TO Vorhaben sehe ich nur Bytes.

von Einer K. (Gast)


Lesenswert?

Veit D. schrieb:
> sehe ich nur Bytes
Ich sehe da int!
Darum überschreibt jedes seiner Put auch 2 Zellen.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

sagen wir mal so. Die Adresse ist ein int und der Wert ein Byte.  :-)
Kommt praktisch gesehen aufs Gleiche raus - Datensalat. Ja da lauert 
eine kleine Falle die ich auch nicht auf dem Schirm hatte.
https://www.arduino.cc/en/Reference/EEPROMWrite

von Toxic (Gast)


Lesenswert?

Veit D. schrieb:
> nimm einmal statt put das write. Bleibt der Effekt erhalten?

So wie ich Zeit habe teste ich es mal mit nur int/long/ etc- Daten

Arduino Fanboy D. schrieb:
> Put ist für komplexere Datentypen gedacht.

Ich will fuer meine EEpromdaten spaeter eine "structure" anlegen.

Arduino Fanboy D. schrieb:
> Wenn du bytes ablegen willst

Ich verwende byte/long/float etc....
Die Testzeilen sind nur als Beispiel angegeben.

Arduino Fanboy D. schrieb:
> EEPROM[10] = 124

Diese Zeile ist mir im Bezug auf die Arduino-Referenz neu.Wenn ich hin 
und wieder mal auf die Schnelle was mit einem Arduino mache (meist 
verwende ich Pics...)schaue ich kurz in der Referenz nach wie was 
geschrieben werden muss und gehe dann auch davon aus ,dass es 
funktioniert.

Fuer mich stellt sich immer noch die Frage ,warum ich mit der 
EEPROM.put-Anweisung solche Probleme bekomme.
Auch wenn sie fuer komplexere Datentypen geschrieben und damit 
eigentlich universeller ausgelegt wurde,sollte sie doch in der Lage sein 
ein paar EEPROM-Zeilen in beliebiger Reihenfolge abzufertigen.

====================

Arduino Fanboy D. schrieb:
> Veit D. schrieb:
>> sehe ich nur Bytes
> Ich sehe da int!
> Darum überschreibt jedes seiner Put auch 2 Zellen.

Ich sehen da eigentlich auch nur "bytes" aber ist es moeglich,dass der 
Compiler einen Wert von z.B. "205" implizit als Integer erkennt?
Dann duerfte mein Programm eigentlich ueberhaupt nicht laufen.

von Einer K. (Gast)


Lesenswert?

Toxic schrieb:
> aber ist es moeglich,dass der
> Compiler einen Wert von z.B. "205" implizit als Integer erkennt?

AVR?
Natürlich sind Literale wie 205 int!
und 40000 wird zu einem long

Scheinbar schreibst du:
 EEPROM.put(0,205);// schreibt int
Meinst aber
 EEPROM.put(0,byte(205)); // schreibt byte



Toxic schrieb:
> Ich will fuer meine EEpromdaten spaeter eine "structure" anlegen.
Das ist der richtige Ansatz.
Damit gehst du u.A, auch diesen Problemen aus dem Weg.
Denn dann stimmen deine Adressen und Datenblocklängen

Toxic schrieb:
> Arduino Fanboy D. schrieb:
>> EEPROM[10] = 124
>
> Diese Zeile ist mir im Bezug auf die Arduino-Referenz neu.Wenn ich hin
> und wieder mal auf die Schnelle was mit einem Arduino mache (meist
> verwende ich Pics...)schaue ich kurz in der Referenz nach wie was
> geschrieben werden muss und gehe dann auch davon aus ,dass es
> funktioniert.
Funktioniert auch!
Musst dich nur an die Regeln halten, welcher dir der Kompiler vorgibt!
Lesestoff: https://www.arduino.cc/en/Reference/EEPROMObject

Tipp:
Auch EEPROM[10]++ usw. funktioniert damit.

von Toxic (Gast)


Lesenswert?

Arduino Fanboy D. schrieb:
> Scheinbar schreibst du:
>  EEPROM.put(0,205);
> Meinst aber
>  EEPROM.put(0,byte(205));

So isses und so funktioniert es auch bei mir(zufaelligerweise?).
Wenn der Compiler meine banale "205" aber als "int" ansieht und dann die 
EEprom-Anweisung dies auch als 2-byte Wert abspeichert dann waere  mein 
Programm tot,da er damit die naechsten EEpromzellen ueberschreibt.
Wenn der Complier implizit denken darf ,will ich das aber auch und "205" 
ist fuer mich ein Byte ;-) kleiner Scherz...


Und nochmal: Ich kann einzelne Zellen manuell im Programm nach belieben 
abspeichern und das funktioniert ohne Datenverlust.Die Katastrophe(naja 
etwas uebertrieben...) entsteht erst bei der sequentiellen Abarbeitung 
der EEpromanweisungen.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

nach nochmaligen nachdenken hatte ich das mit dem int für Daten auch 
begriffen, so wie das Fanboy meinte.  :-)

Einen bunt gemischten struct Datensatz habe ich soeben ausprobiert. 
Funktioniert einwandfrei. Wenn du dir noch für jedes struct die Länge 
ermitteln lässt, kannste beim Eeprom schreiben/lesen locker flockig an 
die richtige Startadresse hüpfen.

von Einer K. (Gast)


Lesenswert?

Toxic schrieb:
> Und nochmal: Ich kann einzelne Zellen manuell im Programm nach belieben
> abspeichern und das funktioniert ohne Datenverlust.Die Katastrophe(naja
> etwas uebertrieben...) entsteht erst bei der sequentiellen Abarbeitung
> der EEpromanweisungen.

Du kannst beschweren wie du willst.
205 ist ein int Literal!
So steht es in der C++ Referenz.

z.B. auch hier:
https://en.cppreference.com/w/cpp/language/integer_literal

Der Kompiler befolgt die Regeln.
Ihm ist es völlig egal, ob du sie kennst, oder sie gut findest.
Mit deinen Fantasien kann er nichts anfangen, auch weil er nicht in 
deinen Kopf schauen kann.
Du kannst dir tolle Sachen ausdenken, aber er gehorcht nur dem, was du 
schreibst.

von Hummel (Gast)


Lesenswert?

Toxic schrieb:
> So isses und so funktioniert es auch bei mir(zufaelligerweise?).
> Wenn der Compiler meine banale "205" aber als "int" ansieht und dann die
> EEprom-Anweisung dies auch als 2-byte Wert abspeichert dann waere  mein
> Programm tot,da er damit die naechsten EEpromzellen ueberschreibt.
> Wenn der Complier implizit denken darf ,will ich das aber auch und "205"
> ist fuer mich ein Byte ;-) kleiner Scherz...
>
>
> Und nochmal: Ich kann einzelne Zellen manuell im Programm nach belieben
> abspeichern und das funktioniert ohne Datenverlust.Die Katastrophe(naja
> etwas uebertrieben...) entsteht erst bei der sequentiellen Abarbeitung
> der EEpromanweisungen.

Jetzt mal Butter bei die Fische, zeige deinen echten Code und nicht so 
ein konstruiertes Beispiel, welches das von dir beschriebene Problem 
nicht hat.
Natürlich betrachtet der Compiler einen normalen Zahlenwert als Integer 
wenn du ihm nichts anderes sagst, oder wenn nicht eine implizite 
Typkonversion erfolgt. Und das ist bei der 'put' Funktion nicht der 
Fall. In deinem Beispiel werden immer 2 Bytes geschrieben.

Ich bin überzeugt, in deinem funktionierenden Code werden Variable 
übergeben, die natürlich einen bestimmten Typ haben.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

ich glaube ich weiß was der TO meint bzw. fragen möchte.
Auch wenn die Werte als int interpretiert und geschrieben werden, so ist 
zwischen seinen Adressen 0, 4 und 10 genügend Platz für die jeweils 2 
Byte großen int Werte. Und dennoch kommt Müll bei raus. Dafür habe ich 
auch keine Erklärung.

: Bearbeitet durch User
von Einer K. (Gast)


Lesenswert?

Veit D. schrieb:
> Wenn du dir noch für jedes struct die Länge
> ermitteln lässt, kannste beim Eeprom schreiben/lesen locker flockig an
> die richtige Startadresse hüpfen.

Das kann alles der Kompiler!
Dazu muss man nur die Struktur in EEPROM anlegen
1
struct Test
2
{
3
  char a,b,c,d;
4
  // hier weitere Daten
5
};
6
Test test EEMEM;
7
8
// später dann
9
char a;
10
EEPROM.get(int(&test.a),a); // nur ein char aus der Struktur importieren

von Wech (Gast)


Lesenswert?

so is es richtiig 1
macht ihmm minnuspunkte dran bis er verschwiendet
der hat ja keine anung von nix

von Einer K. (Gast)


Lesenswert?

Veit D. schrieb:
> Und dennoch kommt Müll bei raus. Dafür habe ich
> auch keine Erklärung.
Nöö..
Das ist nur, weil er Fantasiecode zeigt.

Man kann Arduino Libs hassen, oder lieben....
Aber die EEPROM Lib ist eine der besseren und funktioniert perfekt!

von Veit D. (devil-elec)


Lesenswert?

Hallo,

das das alles der Kompiler automatisch kann war mir so nicht bewusst. 
Ist gut zu wissen. Danke.

Ich dachte bei meiner Ausführung vorhin daran das man vielleicht mehrere 
structs haben könnte und dann beim schreiben oder lesen nur das struct 
an die Eeprom Funktion im Gesamten übergibt und später auch im Gesamten 
wieder ausliest. Dabei mit der ermittelten Länge von bequem von struct 
Anfang zum nächsten struct Anfang springt. Falls das jetzt irgendwie 
verständlich rüberkommt.

Okay, bei genauer Betrachtung doppelt gemoppelt. Man hat eben wie immer 
verschiedene Möglichkeiten für den Zugriff auf seine Variablen.  :-)
1
#include <EEPROM.h>
2
3
// definieren des Datensatzes
4
struct Daten
5
{
6
  byte d1;
7
  float d2;
8
  long d3;
9
  const char text[20] {"Testausgabe"};
10
} daten, daten2; 
11
12
const byte lengthDaten = sizeof(Daten);
13
const unsigned int adresse {100};
14
15
void setup() {
16
17
  Serial.begin(250000);
18
  Serial.println(F("\nSTART #### ####"));
19
  
20
  daten.d1 = 123;
21
  daten.d2 = 1.23;
22
  daten.d3 = 987654;
23
  
24
  Serial.println(lengthDaten);
25
26
  Serial.println();
27
  Serial.println(daten.d1);
28
  Serial.println(daten.d2);
29
  Serial.println(daten.d3);
30
  Serial.println(daten.text);
31
32
  EEPROM.put(adresse, daten);
33
  EEPROM.get(adresse, daten);
34
35
  Serial.println();
36
  Serial.println(daten.d1);
37
  Serial.println(daten.d2);
38
  Serial.println(daten.d3);
39
  Serial.println(daten.text);
40
41
  Serial.println();
42
  EEPROM.get(int(&daten.d1), daten.d1);
43
  EEPROM.get(int(&daten.d2), daten.d2);
44
  EEPROM.get(int(&daten.d3), daten.d3);
45
  EEPROM.get(int(&daten.text), daten.text);
46
47
  Serial.println(F("daten2 ..."));
48
  daten2.d1 = 246;
49
  daten2.d2 = 99.78;
50
  daten2.d3 = 345678;
51
  EEPROM.put(adresse+lengthDaten, daten2);
52
  EEPROM.get(adresse+lengthDaten, daten2);
53
54
  Serial.println(daten2.d1);
55
  Serial.println(daten2.d2);
56
  Serial.println(daten2.d3);
57
  Serial.println(daten2.text);
58
59
  Serial.println();
60
  EEPROM.get(int(&daten2.d1), daten2.d1);
61
  EEPROM.get(int(&daten2.d2), daten2.d2);
62
  EEPROM.get(int(&daten2.d3), daten2.d3);
63
  EEPROM.get(int(&daten2.text), daten2.text);
64
}
65
66
void loop()
67
{
68
}

von Einer K. (Gast)


Lesenswert?

Veit D. schrieb:
> // definieren des Datensatzes
> struct Daten
> {
>   byte d1;
>   float d2;
>   long d3;
>   const char text[20] {"Testausgabe"};
> } daten, daten2;

Veit D. schrieb:
> Serial.println();
>   EEPROM.get(int(&daten2.d1), daten2.d1);
>   EEPROM.get(int(&daten2.d2), daten2.d2);
>   EEPROM.get(int(&daten2.d3), daten2.d3);
>   EEPROM.get(int(&daten2.text), daten2.text);
Das geht so nicht!
Denn daten und daten2 liegen im SRAM.
Deine Adressen sind damit falsch.
1
struct Daten
2
{
3
  byte d1;
4
  float d2;
5
  long d3;
6
  const char text[20] {"Testausgabe"};
7
} daten EEMEM, daten2;
So liegt daten im EEPROM und daten2 im SRAM

Veit D. schrieb:
> Dabei mit der ermittelten Länge von bequem von struct
> Anfang zum nächsten struct Anfang springt. Falls das jetzt irgendwie
> verständlich rüberkommt.

Du kannst viele Variablen ins EEPROM legen.
Der Kompiler berechnet die Adresse immer richtig(wenn du selber keinen 
Bock schießt)

z.B auch
float a EEMEM = 3.2; // Arduino erzeugt *.eep Datei
neben deiner Struktur.

von Karl M. (Gast)


Lesenswert?

Hallo,

und sich auch die richtigen Fuse Bits eingestellt.
Siehe die AVR application notes..

von Veit D. (devil-elec)


Lesenswert?

Hallo,

vestehe. Der benötigte Syntax wird komplizierter. Leider steht das mit 
dem EEMEM nicht in der Arduino Referenz. Kommt man da nicht besser man 
schreibt und liest das Gesamte struct und greift dann normal wie immer 
auf die Member zu? In daten2 stehen hinterher die Werte von daten.
1
#include <EEPROM.h>
2
3
// definieren des Datensatzes
4
struct Daten
5
{
6
  byte d1 {0};
7
  float d2 {0};
8
  long d3 {0};
9
  const char text[20] {"abcyxz"};
10
} daten, daten2; 
11
12
const byte lengthDaten = sizeof(Daten);
13
const unsigned int adresse {100};
14
15
void setup() {
16
17
  Serial.begin(250000);
18
  Serial.println(F("\nSTART #### ####"));
19
20
  daten.d1 = 123;
21
  daten.d2 = 1.23;
22
  daten.d3 = 987654;
23
  
24
  Serial.print(F("Länge vom struct: ")); Serial.println(lengthDaten);
25
  Serial.println(F("Ausgabe daten ..."));
26
  Serial.println(daten.d1);
27
  Serial.println(daten.d2);
28
  Serial.println(daten.d3);
29
  Serial.println(daten.text);
30
31
  // Datensatz umkopieren übers EEprom
32
  EEPROM.put(adresse, daten);
33
  EEPROM.get(adresse, daten2);
34
35
  Serial.println();
36
  Serial.println(daten2.d1);
37
  Serial.println(daten2.d2);
38
  Serial.println(daten2.d3);
39
  Serial.println(daten2.text);
40
}
41
42
void loop()
43
{
44
}

von Toxic (Gast)


Angehängte Dateien:

Lesenswert?

Arduino Fanboy D. schrieb:
> Mit deinen Fantasien kann er nichts anfangen

Sie das Ganze doch nicht so ernst....natuerlich ist mir klar ,das es 
Regeln gibt ansonsten waere ich ueberhaupt nicht in der Lage auch nur 
eine einzige Zeile Code zu schreiben.

Angehaengt der komplette Code fuer die EEprom-Routine,so wie er zu 100% 
funktioniert.
Bitte nun keine Diskussionen darueber,warum ich dies und das so 
umstaendlich mache.Ich schreib meine Programme frei herunter und raeume 
dann spaeter auf...Das Programm ist mittlerweile so komplex,dass ich um 
einen FlowChart nicht herumkomme wenn ich noch nach Jahren wissen 
moechte wie und wo ich was gemacht habe.

Hier 2 lausige Videos ,dass meinen Programmuell in der Praxis zeigt.War 
nur fuer meinen Bruder gedacht damit er weiss was er geschenkt 
bekommt....
Es handelt sich um den 20 Millionsten Spotwelder...die Welt kann nicht 
genug davon haben.

https://www.youtube.com/watch?v=ajXF-H-lbus&feature=youtu.be
https://www.youtube.com/watch?v=hlXl4XwpfZM&feature=youtu.be

von my2ct (Gast)


Lesenswert?

Toxic schrieb:
> Angehängte Dateien:
> EEprom_Reset.txt

Du darfst Dateien gerne mit der richtigen Extension hochladen.
Dann funktioniert beim Betrachter auch das Syntax Highlightning

von Einer K. (Gast)


Lesenswert?

Veit D. schrieb:
> Der benötigte Syntax wird komplizierter.

Eigentlich nicht!

Du kannst bei jeder Variablen angeben, in welcher Sektion sie landen 
soll.
Der Compiler erwartet per default alle Variablen in einer der SRAM 
Sections.
Die kann er ohne Klimmzüge lesen und auch schreiben
1
#define NOINIT __attribute__ ((section (".noinit")))
2
3
char r; // im normalen RAM 
4
unsigned resetcounter NOINIT; // diese liegt im RAM wird allerdings nicht automatisch initialisiert
5
// überlebt also auch einen Reset.
6
7
char s PROGMEM; // im Flash
8
char t EEMEM; // im EEPROM

Für PROGMEM und EEMEM Zugriffe musst du spezielle Zugriffsmethoden 
verwenden.
Also die dir schon bekannte Syntax.
z.B. EEPROM.get() oder pgm_read_byte()

C kennt zusätzlich
char u __flash;
C++ leider nicht.

------------

Veit D. schrieb:
> const byte lengthDaten = sizeof(Daten);
> const unsigned int adresse {100};

Natürlich kannst du auch die Adressen händisch bestimmen/festlegen. Mit 
dem Risiko auch mal einen schwer zu findenden Bock zu schießen.
Ins besondere wird das schwierig, wenn sich viele Adressberechnungen 
über viele Dateien verteilen.

Veit D. schrieb:
> Kommt man da nicht besser man
> schreibt und liest das Gesamte struct
Kann man natürlich tun.
... wenn man nicht mit RAM geizen muss....


PS:
Habe mir eine EEPROM Klassen Erweiterung gebaut, die das etwas 
Typesicherer macht, unterzubringen in EEPROM.h bei den schon vorhandenen 
get und put
1
    
2
    template< typename T > T &get( T &ram, T &t ){
3
        return get((int)ram,t);
4
    }  
5
      
6
    template< typename T > const T &put( T &ram, const T &t ){
7
        return put((int)ram,t);
8
    }


Nutzung:
1
float imEeprom a EEMEM;
2
float imRam b;
3
EEPROM.get(a,b);// aus dem EEPROM ins Ram
4
EEPROM.put(a,b);// aus dem Ram ins EEPROM schreiben

von Veit D. (devil-elec)


Lesenswert?

Hallo,

nochmal zurück zum TO Eingangsproblem. Ich habe versucht das 
nachzustellen.
Egal ob ich mit read/write oder put/get arbeite, klappt immer. 
¯\_(ツ)_/¯
1
// https://www.mikrocontroller.net/topic/511826
2
3
#include <Streaming.h>
4
#include <EEPROM.h>
5
6
const byte anz {3};
7
byte daten1[anz] = {112, 134, 156};
8
byte daten2[anz];
9
10
const unsigned int startAdresse {80};
11
const byte abstand {4};
12
13
void setup()
14
{
15
  unsigned int addrBuffer {0};
16
  
17
  Serial.begin(250000);
18
  Serial.println(F("\nSTART #### ####"));
19
20
  Serial.println(F("Testausgabe ..."));
21
  for (auto &i : daten1)
22
  {
23
    Serial.println(i);
24
  }
25
26
  Serial.println(F("daten1 ins Eeprom schreiben ..."));
27
  addrBuffer = startAdresse;
28
  for (auto &i : daten1)
29
  {
30
    EEPROM.put(addrBuffer, i);
31
    Serial << "addr: " << addrBuffer << "  " << i << endl;
32
    addrBuffer += abstand;
33
  }
34
35
  Serial.println(F("Eeprom lesen und in daten2 schreiben ..."));
36
  addrBuffer = startAdresse;
37
  for (auto &i : daten2)
38
  {
39
    EEPROM.get(addrBuffer, i);
40
    Serial << "addr: " << addrBuffer << "  " << i << endl;
41
    addrBuffer += abstand;
42
  }
43
44
  Serial.println(F("daten1 ins Eeprom rückwärts schreiben ..."));
45
  addrBuffer = startAdresse + abstand + abstand;
46
  for (auto &i : daten1)
47
  {
48
    EEPROM.put(addrBuffer, i);
49
    Serial << "addr: " << addrBuffer << "  " << i << endl;
50
    addrBuffer -= abstand;
51
  }
52
53
  Serial.println(F("Eeprom vorwärts lesen und in daten2 schreiben ..."));
54
  addrBuffer = startAdresse;
55
  for (auto &i : daten2)
56
  {
57
    EEPROM.get(addrBuffer, i);
58
    Serial << "addr: " << addrBuffer << "  " << i << endl;
59
    addrBuffer += abstand;
60
  }
61
}
62
63
void loop()
64
{
65
}

von Einer K. (Gast)


Lesenswert?

Veit D. schrieb:
> addrBuffer
Genau das Gehampel meine ich, darauf sollte man verzichten!

Wenn du genau weißt, dass du klüger bist als der Compiler und nie Fehler 
machst, dann ok...
Erst wenn du mir das garantieren kannst, empfehle ich es dir.
(aber keinem anderen)

von Veit D. (devil-elec)


Lesenswert?

Arduino Fanboy D. schrieb:
> Veit D. schrieb:
>> Kommt man da nicht besser man
>> schreibt und liest das Gesamte struct
> Kann man natürlich tun.
> ... wenn man nicht mit RAM geizen muss....

Man muss immer eine Hand breit RAM unterm Hintern haben.  :-)
Ich muss das mit dem EEMEM nochmal in Ruhe geistig durchdringen.
Danke für die neuen Hinweise und Erkenntnisse.

von Hummel (Gast)


Lesenswert?

Toxic schrieb:
> Angehaengt der komplette Code fuer die EEprom-Routine,so wie er zu 100%
> funktioniert.

Unter komplett verstehe ich auch das Lesen des EEPROM. Aber dein Problem 
lässt sich auch mit den vorhandenen Codefragmenten erklären, die in 
einem wesentlichen Punkt von deinem konstruierten Beispiel abweichen. Da 
hatten die Adressen ausreichend Abstand voneinander.

Beim aktuellen Code stehen nach der Schreibsequenz
      EEPROM.put(27, 1);              // preheat on/off => 0 = off
      EEPROM.put(28, 12);             // preheat_time in %
      EEPROM.put(29, 12);             // pulseduration

folgende Daten in den EEPROM Zellen:

  27: 0x01    LSB put(27,1)
  28: 0x0C    LSB put(28,12) hat MSB put(27,1) überschrieben
  29: 0x0C    LSB put(29,12) hat MSB put(28,12) überschrieben
  30: 0x00    MSB put(29,12) wird dann auch überschrieben

Das sieht für dich funktionierend aus, da du angenommen hast, daß nur 1 
Byte geschrieben wird, und du deshalb von diesen Adressen jeweils nur 1 
Byte lesen wirst (diesen Code hast du leider nicht geliefert).

Wenn nun die Schreibreihenfolge geändert wird
      EEPROM.put(29, 12);             // pulseduration
      EEPROM.put(28, 12);             // preheat_time in %
      EEPROM.put(27, 1);              // preheat on/off => 0 = off

stehen folgende Daten im EEPROM:

  27: 0x01   LSB put(27,1)
  28: 0x00   MSB put(27,1) hat LSB put(28,12) überschrieben
  29: 0x00   MSB put(28,12) hat LSB put(29,12) überschrieben
  30: 0x00   MSB put(29,12)

Wenn du jetzt die EEPROM Adressen 27..29 byteweise ausliest, stimmt der 
Wert von Adresse 27 und Adresse 28 und 29 sind falsch.

> Es handelt sich um den 20 Millionsten Spotwelder...die Welt kann nicht
> genug davon haben.

Dann hast du unter diesen Voraussetzungen Glück gehabt, daß deine 
Hardware nicht in Rauch aufgegangen ist.

von Einer K. (Gast)


Lesenswert?

Arduino Fanboy D. schrieb:
> PS:
> Habe mir eine EEPROM Klassen Erweiterung gebaut, die das etwas
> Typesicherer macht, unterzubringen in EEPROM.h bei den schon vorhandenen

Sorry, da habe ich einen alten experimentellen Stand erwischt..

Dieses ist der Aktuelle:
1
    template< typename T > T &get( T &e, T &r ){
2
        return get((int)&e,r);
3
    }  
4
      
5
    template< typename T > const T &put( T &e, const T &r ){
6
        return put((int)&e,r);
7
    }

von Veit D. (devil-elec)


Lesenswert?

Hallo,

die Erklärung stimmt leider noch nicht. Geschrieben und gelesen wird ja 
wirklich nur ein Byte. Nimm mal meinen Code davor und ändere den 
'abstand' von 4 auf 1. Funktioniert immer noch.
Man darf sich nicht durcheinanderbringen lassen das die Adresse ein int 
ist. Das spielt keine Rolle. An Adresse 200 passt ein Byte und an die 
Adresse 201 passt das nächte Byte. Was intern mit dem Adresszähler 
passiert sieht man nicht. Das ist wie bei Zeigern. Welche Adresse 
reserviert wird kann einem egal sein. Es wird nur ausreichend Speicher 
für den Datentyp reserviert auf den er zeigt.

Wenn der EEprom 1024 Bytes speichern kann. Dann sind 1024 'int' Adressen 
hinterlegt. An jeder Adresse hat ein Byte Platz.

Hoffentlich habe ich das richtig erklärt ...

Der TO müßte wirklich einmal nachvollziehbaren Testcode zeigen der sein 
Problem auch zeigt. Sonst wird das nichts.

Edit:
Arduino Fanboy: Danke fürs Update

: Bearbeitet durch User
von Einer K. (Gast)


Lesenswert?

Veit D. schrieb:
> Geschrieben und gelesen wird ja
> wirklich nur ein Byte.

 EEPROM.put(29, 12);
12 ist ein int
put schreibt aus dem Grund 2 byte
Belegt also Adresse 29 und 30
Was vorher auf 30 stand, ist unwiederbringlich verloren

Ein einfaches sizeof(12) beweist dir das.

von Toxic (Gast)


Lesenswert?

Hummel schrieb:
> Dann hast du unter diesen Voraussetzungen Glück gehabt, daß deine
> Hardware nicht in Rauch aufgegangen ist.

Bevor ich Software teure MosFets ansteuere teste ich per Oszi alle 
Signale.Ich habe viel Zeit investiert fuer die ganze Displayansteuerung 
und zig implementierten Optionen.
Ich habe zudem alle Parameter wie Impulslaenge etc waehrend in der 
Software/Hardwarephase staendig veraendert und gecheckt.Irgendwie 
scheine ich ein Hans im Glueck gewesen zu sein...

Wenn ich also bei Zahlenwerten bis 255 mit einem 2-Byte Ergebnis rechnen 
muss,ist ein Ueberschreiben nicht zu vermeiden,da ich die Adressen 
(nicht alle) direkt hintereinander ansiedelte.
Das von dir gezeigte Beispiel wuerde in meinem Falle beim Veraendern der 
"pulseduration" den "preheat_time"-Wert durch ueberschreiben dessen 
MSB's total veraendern und einen voellig absurden Wert live(!)auf dem 
Display abgeben.In einem der Videos sieht man aber,dass dies trotz allem 
nicht der Fall ist und auch die Osziwerte vollkommen mit den berechneten 
Werten uebereinstimmen. What the fuck...

Nun voellig egal:
Der Fehler (auch wenn alles funktioniert...) liegt ganz offensichtlich 
bei mir.Ein vermeintliches Byte ist eben kein Byte sondern ein 
2-Byte-Wert.
Ich werde morgen,die EEpromadressen entsprechend abaendern ,nochmal 
testen und mir vor allem klar machen warum ich soviel Glueck hatte,dass 
waehrend der Testphasen nicht einmal Mist aufgetaucht ist.

Ein Dank an alle die hier gepostet und ihre Zeit investiert haben.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

Ich glaube wir müssen alle einen Reset machen.
Das sizeof(12) zwei Byte ergibt bestreitet niemand.
Kann man jedoch nicht mit der Eeprom Lib blind vergleichen. In der 
Arduino Referenz wird immer von Byte geredet. Schauen wir einmal genauer 
nach in der Eeprom Header Datei.
https://github.com/arduino/ArduinoCore-avr/blob/master/libraries/EEPROM/src/EEPROM.h
Ganz unten in der Klasse steht überall uint8_t für den eigentlichen 
Wert.
Das erklärt zur Zeit für mich warum mein Testcode auch mit Adress 
abstand 1 noch funktioniert.

Wenn ich morgen versuche bewußt int Werte hintereinander zu schreiben, 
dann sollte wirklich Datenmüll rauskommen.

Für heute ist bei mir erstmal Schluss.

von Einer K. (Gast)


Lesenswert?

Toxic schrieb:
> Ich werde morgen,die EEpromadressen entsprechend abaendern

Und ich rate dir (nochmal), den Compiler die Adressen und 
Größenberechnungen machen zu lassen.

von Einer K. (Gast)


Lesenswert?

Veit D. schrieb:
> Arduino Referenz wird immer von Byte geredet.
Nöö...

https://www.arduino.cc/en/Reference/EEPROMPut
> data: the data to write, can be a primitive type
> (eg. float) or a custom struct

> Kann man jedoch nicht mit der Eeprom Lib blind vergleichen.
Doch!
Wenn man in die Lib schaut, sieht man, dass sizeof() verwendet wird.
1
    template< typename T > const T &put( int idx, const T &t ){
2
        EEPtr e = idx;
3
        const uint8_t *ptr = (const uint8_t*) &t;
4
        for( int count = sizeof(T) ; count ; --count, ++e )  (*e).update( *ptr++ );
5
        return t;
6
    }
Kann man also vergleichen ...

von Hummel (Gast)


Lesenswert?

Toxic schrieb:
> Wenn ich also bei Zahlenwerten bis 255 mit einem 2-Byte Ergebnis rechnen
> muss,ist ein Ueberschreiben nicht zu vermeiden,da ich die Adressen
> (nicht alle) direkt hintereinander ansiedelte.
> Das von dir gezeigte Beispiel wuerde in meinem Falle beim Veraendern der
> "pulseduration" den "preheat_time"-Wert durch ueberschreiben dessen
> MSB's total veraendern und einen voellig absurden Wert live(!)auf dem
> Display abgeben.In einem der Videos sieht man aber,dass dies trotz allem
> nicht der Fall ist und auch die Osziwerte vollkommen mit den berechneten
> Werten uebereinstimmen. What the fuck...

Da du ja in deinem gezeigten Code fortlaufend aufsteigende Adressen 
beschrieben hast, passiert trotz Überschreiben von EEPROM Zellen nichts 
wenn du jeweils nur (wie von mir angenommen) nur das LSB ausliest und 
anzeigst. Wenn du dann in deinem Code 1-Byte Variable benutzt um 
Einstellungen zu verändern, dann passiert auch kein weiteres 
überschreiben. Nur wenn du dem 'put' einen direkten Zahlenwert 
übergibst, wird implizit ein Integer angenommen.

Beispiel:

uint8_t preheadTime;
EEPROM.get(28,preheatTime);
preheatTime++;
EEPROM.put(28,preheatTime);   // hier wird nur 1 Byte geschrieben

EEPROM.put(28,155);           // hier werden 2 Bytes geschrieben

> Nun voellig egal:
> Der Fehler (auch wenn alles funktioniert...) liegt ganz offensichtlich
> bei mir.Ein vermeintliches Byte ist eben kein Byte sondern ein
> 2-Byte-Wert.
> Ich werde morgen,die EEpromadressen entsprechend abaendern ,nochmal
> testen und mir vor allem klar machen warum ich soviel Glueck hatte,dass
> waehrend der Testphasen nicht einmal Mist aufgetaucht ist.

Im Prinzip brauchst du die Adressen nicht ändern, du musst nur dem 
Compiler mitteilen, daß er ein uint8_t schreiben soll.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

wenn man den Thread so liest kann man den Eindruck gewinnen die EEprom 
Lib könnte nur mit int umgehen, was ja nicht der Fall ist. Deswegen 
zeigte ich auf die Klasse wo man sieht das read/write nur mit Bytes 
umgeht. Das put/get alles verdaut ist auch klar, wurde aber nicht gleich 
ersichtlich. Das mein Testcode nicht das zeigt was der TO zeigte ist mir 
nun auch klar, da ich explizit ein Byte Array verwendet habe und es 
deswegen keine Rolle spielte ob ich read/write odder put/get verwende. 
Der TO hat nur Literale eingesetzt die zu int formierten. Wenn das 
Antworten später noch nicht so ankam wie gedacht sollte man den 
geneigten Leser nochmal an der Stelle abholen wo er falsch abgebogen 
ist. Dann verkürzt sich auch die Threadlänge. Man sollte aufeinander 
zugehen, sonst redet man zu lange aneinander vorbei.

Aber das wurde ja nun alles zum Guten erklärt. Ende gut alles gut.  :-)

von Einer K. (Gast)


Lesenswert?

Veit D. schrieb:
> wenn man den Thread so liest kann man den Eindruck gewinnen die EEprom
> Lib könnte nur mit int umgehen, was ja nicht der Fall ist
Wenn man nur auf sein Herz hört und die geschriebenen Worte 
geflissentlich ignoriert, ja, dann könnte man das meinen.
Andere könnten auch meinen, dass die EEPROM Lib nur Byte verarbeiten 
kann.
Was genauso falsch ist.

Richtig ist: put schreibt das, was man ihm vorwirft!
Es liegt in der Verantwortung des Programmierers, das jeweilige 
Verhalten von put zu bestimmen.
Was dann dazu führt, dass man ihm auch byte vorwerfen muss, wenn man 
byte speichern will.

Veit D. schrieb:
> Das put/get alles verdaut ist auch klar, wurde aber nicht gleich
> ersichtlich.
Wenn man die Doku ignoriert, dann ist das nicht offensichtlich.

Veit D. schrieb:
> Das mein Testcode nicht das zeigt was der TO zeigte ist mir
> nun auch klar, da ich explizit ein Byte Array verwendet habe und es
> deswegen keine Rolle spielte ob ich read/write odder put/get verwende.
So ist es...
Und so, oder so ähnlich kommt es, wenn ein Fragesteller Fantasiecode 
zeigt, welcher NICHT das Problem überprüfbar abbildet.

Veit D. schrieb:
> Der TO hat nur Literale eingesetzt die zu int formierten. Wenn das
> Antworten später noch nicht so ankam wie gedacht sollte man den
> geneigten Leser nochmal an der Stelle abholen wo er falsch abgebogen
> ist.
Eigentlich eine gute Idee...
Aber wenn nach einem gefühlten Dutzend Erklärungen, von mehreren 
Personen, der Groschen nicht fallen will, die irrige Vorstellung nicht 
aus dem Kopf will, dann ist es halt schwierig.
Dabei sollte es in der Programmiererwelt schon bekannt sein, dass sich 
logische Fehler gerne bis zum Tag der Inbetriebnahme/Auslieferung 
verstecken.
Und dann mit roher Gewalt das ganze Projekt in die Tonne befördern.

Toxic schrieb:
> Ich will fuer meine EEpromdaten spaeter eine "structure" anlegen.
Hier ist der logische Fehler, zumindest einer davon.
Sowas tut man nicht "später"!
Sondern man legt eigentlich erst die Datenstrukturen an, oder fest, und 
baut dann den Code dazu, welcher auf den Strukturen reitet.


Veit D. schrieb:
> Dann verkürzt sich auch die Threadlänge.
Irrtum begehen
Irrtum erklären
Irrtum einsehen
Irrtum beseitigen
Das steckt sich ein wenig, ins besondere, wenn da noch ein fulminantes 
Beharrungsvermögen dazu kommt.

Veit D. schrieb:
> Man sollte aufeinander
> zugehen, sonst redet man zu lange aneinander vorbei.
Dazu gehören mindestens 2, auch gerne mehr.

Veit D. schrieb:
> Aber das wurde ja nun alles zum Guten erklärt.
Das warten wir mal ab, was die "Einsicht", so an Früchten trägt.


----
Irgendwie scheint es mir, du wolltest die Hilfe hier im Thread 
kritisieren...
Ok, kannst du machen...
Aus meiner Sicht ist dieses ein erstaunlich friedlicher Arduino Thread.
Keinerlei Bösartigkeiten.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

> Aus meiner Sicht ist dieses ein erstaunlich friedlicher Arduino Thread.
Das soll auch so bleiben.

Vielleicht habe ich auch zwischendurch zur eigenen Verwirrung 
beigetragen. Da will ich mich nicht ausnehmen, hinsichtlich des kleinen 
Kommunikations- bzw. Verständnisproblems. War nicht böse gemeint.

von Toxic (Gast)


Lesenswert?

Im kann im Moment noch kein Feedback(keine Zeit...) geben um euch den 
fatalen von mir selbst produzierten Bockmist nochmal zu bestaetigen.
Die irrige Annahme,dass Werte =< 255 nur ein Byte im EEprom in Anspruch 
nehmen hat mich in die Bredouille gebracht.So ein Mist aber auch.Ich war 
so felsenfest davon ueberzeugt,dass es mir nie in den Sinn kam,dass da 
ein Problem sein koennte.
Ich habe eure letzten Postings gelesen und werde mir den einen oder 
anderen Tip zu Gemuete fuehren.
Ich hatte eigentlich nicht vor im Forum nachzufragen,da 
(zufaelliegerweise)
mein Projekt funktionierte und ich es schon auf's Abstellgleis geschoben 
hatte.
Trotzdem hatte es mich gewurmt,dass ich dieses seltsame Verhalten nicht 
nachvollziehen konnte.
OK - ich steh jetzt natuerlich da wie eine Dumpfbacke und ich koennte 
mich auch selbst dafuer ohrfeigen.Aber immerhin hab ich dank eurer Hilfe 
einen bei mir im Gehirn eingebrannten Denkfehler eliminieren koennen und 
dafuer gibt es ein 👍

von Einer K. (Gast)


Lesenswert?

Toxic schrieb:
> OK - ich steh jetzt natuerlich da wie eine Dumpfbacke und ich koennte
> mich auch selbst dafuer ohrfeigen.Aber immerhin hab ich dank eurer Hilfe
> einen bei mir im Gehirn eingebrannten Denkfehler eliminieren koennen und
> dafuer gibt es ein 👍

Dazu kann ich nur eins sagen:
> Irren ist menschlich
> Im Irrtum verharren, ist Dummheit

Also, mir scheint, du bist auf dem richtigen Weg!

von Toxic (Gast)


Lesenswert?

Hier ne kurze Rueckmeldung:
Die EEprom-Probleme sind beseitigt - was auch zu erwarten war.
Ein Dank nochmal an alle,die hier gepostet haben !

von Veit D. (devil-elec)


Lesenswert?

Hallo,

ich hätte noch eine Zusatzfrage. Wenn ich mit write ein int schreibe, 
warum gibts dann beim kompilieren keine einzige Warnung? Es wird doch 
sonst auch immer alles angemeckert was nicht passt, overflow warning 
o.ä.. Das verstehe ich im Moment nicht.

von Einer K. (Gast)


Lesenswert?

EEPROM.write(10,205); wird nicht angemäckert, weil 205 durchaus in ein 
Byte passt.
Der implizite Cast geht darum ohne Murren durch.

Gegenbeispiel:
1
E:\Programme\arduino\portable\sketchbook\sketch_jan26a\sketch_jan26a.ino:12:19: warning: unsigned conversion from 'int' to 'uint8_t' {aka 'unsigned char'} changes value from '4711' to '103' [-Woverflow]
2
   12 |   EEPROM.write(10,4711);
3
      |                   ^~~~

Dieses geht aus C historischen Gründen auch in C++ durch:
1
  int tt = 205;
2
  EEPROM.write(10,tt); // erzwungener impliziter Cast
3
 //Das gleiche wie:
4
  EEPROM.write(10,(uint8_t)tt); // expliziter Cast
Kann man drüber jammern, oder auch nicht

Das Problem wurde erkannt und eine Abhilfe geschaffen.
Dieses ist eine explizite Übergabe (modernes C++):
1
  int tt = 205;
2
  EEPROM.write(10,{tt});
Wird angemäckert
1
E:\Programme\arduino\portable\sketchbook\sketch_jan26a\sketch_jan26a.ino:15:20: warning: narrowing conversion of 'tt' from 'int' to 'uint8_t' {aka 'unsigned char'} [-Wnarrowing]
2
   15 |   EEPROM.write(10,{tt});
3
      |                    ^~

von Veit D. (devil-elec)


Lesenswert?

Hallo,

aja okay, wegen dem impiliziten cast macht der das einfach und gut ist.
Das mit der {} Klammer ist ein guter Tipp. Mache ich bis jetzt nur beim 
initialisieren.
Dann kann man nicht nur bei einer Variablen Initialisierung den Datentyp 
prüfen lassen, sondern immer und überall auch bei deren Übergabe. Das 
ist ja cool. Danke.

von Einer K. (Gast)


Lesenswert?

Fein!
Schön, wenn ich dir einen Tipp geben konnte!
Ja, auf die Art kann man so manchen schwer zu findenden Fehler 
vermeiden.

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.