Forum: Compiler & IDEs Speichern eines INT Wertes im EEPROM des ATTiny13


von Marc Steinbach (Gast)


Lesenswert?

Guten Tag,
ich habe eine Schaltung, auf der eine LED Leiste aufgelötet ist. Nun 
kann ich die Helligkeit über Taster steuern. Ich möchte gerne über einen 
dritten Taster die Helligkeit speichern (im EEPROM) und bei einem 
neustart wieder aufrufen.

Allerdings funktioniert das nicht. Von daher benötige ich entweder 
hinweise auf was ich achten muss, bzw. ein kleines Testprgramm, welches 
einfach nur etwas in den EEPROM speichert und wieder ausgibt.

Mit freundlichen Grüßen
Marc Steinbach

von Karl H. (kbuchegg)


Lesenswert?

Marc Steinbach schrieb:

> Allerdings funktioniert das nicht.

Schlechte Aussage.

Bessere Aussage:
Ich habe diesenb und jenen Code geschrieben, hier ist er.
Kann mir wer sagen, warum hier keine Werte ins EEPROM geschrieben bzw. 
gelesen werden?


Auf Deutsch: Zeig deinen Code!


> Von daher benötige ich entweder
> hinweise auf was ich achten muss,

Wie hast du es denn gemacht?
Du hast fix fertige EEPROM Funktionen zur Verfügung, die du nur benutzen 
musst.

von Karl H. (kbuchegg)


Lesenswert?

> Ich möchte gerne über einen dritten Taster die Helligkeit speichern
> (im EEPROM) und bei einem neustart wieder aufrufen.

Kann man machen.
Man kann aber auch einfach jedesmal, wenn der Benutzer die Helligkeit 
verändert gleich danach den Wert ins EEPROM schreiben. AUch wenn die 
Anzahl der Schreibvorgänge einer Grenze unterliegt, wirst du dich an 
diese Grenze bei normaler Benutzung nicht annähern. Da müsstest du schon 
2 Jahre lang rund um die Uhr, 7 Tage die Woche, alle 5 Sekunden die 
Helligkeit ändern. Und das tut normalerweise keiner.

von katastrophenheinz (Gast)


Lesenswert?

Hi,

kleines Testprogramm gibt's im ATtiny13-Manual auf Seite 18. Ist 
eigentlich ganz einfach. Zusätzlich kennt die avr-gcc-lib (falls du die 
nutzt) auch C-Routinen zum Schreiben/Lesen von Datentypen <> byte.

Es hat sich als voreilhaft erwiesen, bei Nutzung des EEPROMs in den 
Fuses die EESAVE-Fuse zu programmieren, sonst ist mit jedem 
Überschreiben des Flash auch der Inhalt des EEPROM gelöscht .

von katastrophenheinz (Gast)


Angehängte Dateien:

Lesenswert?

Hi,
falls du nicht weiterkommst, kannst du beigefügten Codeschnipsel als
Anhaltspunkt nehmen. ( mit avr-gcc und der entsprechenden lib übersetzt)

von Jürgen (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Man kann aber auch einfach jedesmal, wenn der Benutzer die Helligkeit
> verändert gleich danach den Wert ins EEPROM schreiben. AUch wenn die
> Anzahl der Schreibvorgänge einer Grenze unterliegt, wirst du dich an
> diese Grenze bei normaler Benutzung nicht annähern. Da müsstest du schon
> 2 Jahre lang rund um die Uhr, 7 Tage die Woche, alle 5 Sekunden die
> Helligkeit ändern. Und das tut normalerweise keiner.


Wenn der Wert eine feste Adresse im EEPROM hat, dann stimmt die obige 
Rechnung nicht. Denn dann sind es nur knapp 6 Tage.

von Karl H. (kbuchegg)


Lesenswert?

Jürgen schrieb:
> Karl Heinz Buchegger schrieb:
>> Man kann aber auch einfach jedesmal, wenn der Benutzer die Helligkeit
>> verändert gleich danach den Wert ins EEPROM schreiben. AUch wenn die
>> Anzahl der Schreibvorgänge einer Grenze unterliegt, wirst du dich an
>> diese Grenze bei normaler Benutzung nicht annähern. Da müsstest du schon
>> 2 Jahre lang rund um die Uhr, 7 Tage die Woche, alle 5 Sekunden die
>> Helligkeit ändern. Und das tut normalerweise keiner.
>
>
> Wenn der Wert eine feste Adresse im EEPROM hat, dann stimmt die obige
> Rechnung nicht. Denn dann sind es nur knapp 6 Tage.

OK, ok.
Ich habs nicht nachgerechnet.
Können wir uns trotzdem darauf einigen, dass es für praktische Zwecke 
bei normaler Benutzung keine echte Beschränkung ist?

(Nicht das Marc jetzt meint, er produziert da jetzt einen EEPROM Killer)

von Jürgen (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Können wir uns trotzdem darauf einigen, dass es für praktische Zwecke
> bei normaler Benutzung keine echte Beschränkung ist?

OK. Wenn er 25 mal pro Tag den Wert ändert, dann dauert es knapp 11 
Jahre bis die garantierte Anzahl von Zyklen erreicht ist. Der Zeitraum 
kann verlängert werden, wenn nur geschrieben wird, wenn der Wert sich 
tatsächlich geändert hat. Also vorher prüfen was im Speicher steht.

von Marc Steinbach (Gast)


Lesenswert?

Vielen Dank erstmal für die schnellen Antworten.
Was ich vielleicht noch dazu schreiben muss ist, das ich bis jetzt nur 
in anderen Programmiersprachen tätig war und von daher nur wenig Ahnung 
in C++.


Ich hänge jetzt mal den Programmiercode an und hoffe das Ihr mir meinen 
Fehler aufzeigen könnt.
1
//---------------------------------------------------------------------------
2
// Initialisierungen
3
//---------------------------------------------------------------------------
4
void init()
5
{
6
  // Ports initialisieren
7
  sbi(DDRB,0);       // PORTB0 auf Ausgang (OC0A)
8
9
  cbi(DDRB,1);       // PORTB1 auf Eingang mit PullUp
10
  sbi(PORTB,1);      // für Store Taster
11
12
  cbi(DDRB,3);       // PORTB3 auf Eingang mit PullUp
13
  sbi(PORTB,3);      // für + Taster
14
15
  cbi(DDRB,4);       // PORTB4 auf Eingang mit PullUp
16
  sbi(PORTB,4);      // für - Taster
17
18
  // Timer 0 initialisieren
19
  TCCR0A=0b10000001;    // PWM Clear OC0A
20
  TCCR0B=0x01;       // Teiler 1/1
21
}
22
//---------------------------------------------------------------------------
23
24
//---------------------------------------------------------------------------
25
// Zeitschleife
26
//---------------------------------------------------------------------------
27
void time()
28
{
29
30
  for (int i = 0; i < 10000; i++);
31
32
}
33
//---------------------------------------------------------------------------
34
35
//---------------------------------------------------------------------------
36
// dutyUp  Wert erhöhen
37
//---------------------------------------------------------------------------
38
void dutyUp()
39
{
40
  duty = duty +5;      // "duty" um 1 im Wert erhöhen
41
  time();        // 15ms warten  
42
}
43
//---------------------------------------------------------------------------
44
45
//---------------------------------------------------------------------------
46
// dutyDown  Wert verringern
47
//---------------------------------------------------------------------------
48
void dutyDown()
49
{
50
  duty = duty - 5;    // "duty" um 1 verringern  
51
  time();        // 15ms warten    
52
}
53
//---------------------------------------------------------------------------
54
55
//---------------------------------------------------------------------------
56
// dutySet  Wert in EEProm schreiben
57
//---------------------------------------------------------------------------
58
void dutySet()
59
{
60
  duty = duty - 5;    // "duty" um 1 verringern  
61
  time();        // 15ms warten    
62
}
63
//---------------------------------------------------------------------------
64
65
//----------------------------------------------------------------------
66
// Daten aus EEPROM lesen,
67
// PE: adresse
68
// PA: byte
69
//----------------------------------------------------------------------
70
71
int eeRead(int adresse)
72
{
73
74
  int daten;
75
  while(EECR&0x02);  // warte bis letzte schreibaktion fertig
76
  EEARL=adresse;
77
  sbi  (EECR,0);  // Leseaufforderung
78
  while(EECR&0x01);  // warte auf Fertigmeldung
79
  daten=EEDR;    // Daten übernehmen
80
  return daten;
81
82
}
83
//----------------------------------------------------------------------
84
// Daten in den EEPROM schreiben,
85
// PE: adresse, byte
86
//----------------------------------------------------------------------
87
88
void eeWrite(int adresse, int daten)
89
{
90
91
  while(EECR&0x02);  // warte bis letzte schreibaktion fertig
92
  EEAR=adresse;
93
  EEDR=daten;
94
  EECR=0x04;    // set EEPROM Write Enabled
95
  EECR=0x06;    // set EEPROM Write
96
97
}
98
99
//---------------------------------------------------------------------------
100
// Main-Funktion
101
//---------------------------------------------------------------------------
102
main()
103
{
104
  init();                 // Initialisierungen
105
  duty = eeRead(0x02);    // Startwert an duty 
106
  sei();                  // Interupt's ein
107
  while (true)            // Mainloop-Begin
108
  {
109
    if (!(PINB & 2)) 
110
    {
111
      // Wenn Store gedrückt 
112
      eeWrite(0x02, duty);
113
    }
114
115
    if (!(PINB & 8))        // Wenn + gedrückt 
116
      dutyUp();             // "dutyUp()" aufrufen
117
118
    if (!(PINB & 16))       // Wenn - gedrückt  
119
      dutyDown();           // "dutyDown()" aufrufen
120
121
    if (!(PINB & 24))       // Wenn + und - gedrückt  
122
      duty = 255;           // auf 100% setzen       
123
124
    if (duty <= 0b0000101)  // wenn kleiner gleich 5
125
      duty = 5;             // immer 5
126
127
    if (duty > 0b11111111)  // wenn größer 255
128
      duty = 255;           // immer 255
129
130
    OCR0A = duty;           //PWM-Wert an Timer
131
132
  }
133
  // Mainloop-Ende
134
}

von Karl H. (kbuchegg)


Lesenswert?

Marc Steinbach schrieb:
> Vielen Dank erstmal für die schnellen Antworten.
> Was ich vielleicht noch dazu schreiben muss ist, das ich bis jetzt nur
> in anderen Programmiersprachen tätig war und von daher nur wenig Ahnung
> in C++.

Das hat damit nichts zu tun.
Der springende Punkt ist, dass es fix/fertige EEPROM Routinen gibt, die 
du nur benutzen brauchst, so wie du auch eine 'for-Schleife' einfach so 
benutzt ohne dich darum zu kümmern wie der Compiler das macht. Ein 
wichtiger Punkt in jeder höheren Programmiersprache ist das Wissen 
darüber: Welche Funktionen hab ich eigentlich schon fix&fertig zur 
Benutzung zur Verfügung.

AVR-GCC-Tutorial: EEPROM

von katastrophenheinz (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Der springende Punkt ist, dass es fix/fertige EEPROM Routinen gibt, die
>
> du nur benutzen brauchst, so wie du auch eine 'for-Schleife' einfach so
>
> benutzt ohne dich darum zu kümmern wie der Compiler das macht. Ein
>
> wichtiger Punkt in jeder höheren Programmiersprache ist das Wissen
>
> darüber: Welche Funktionen hab ich eigentlich schon fix&fertig zur
>
> Benutzung zur Verfügung.

Dem ist nichts hinzuzufügen und die Fehlersucherei wäre dir erspart 
geblieben.

Grundsätzlich: Wenn du die vordefinierten Konstanten verwenden würdest, 
dann wäre dein Code viel leichter lesbar und wartbar. Also
statt:
1
 EECR=0x04;    // set EEPROM Write Enabled
2
 EECR=0x06;    // set EEPROM Write
 besser
1
 EECR=(1<<EEMPE);              // set EEPROM Write Enabled
2
 EECR=(1<<EEMPE)|(1<<EEPE);    // set EEPROM Write

Hier liegt vermutlich auch der Fehler in deinem Code: Das EEPE-Bit darf 
nicht zusammen mit EEMPE gesetzt werden. Probier mal:
1
 EECR=(1<<EEMPE);              // set EEPROM Write Enabled
2
 EECR=(1<<EEPE);               // set EEPROM Write

Um das dann ganz richtig zu machen, müsste diese sequenz dann noch als 
atomic_block ausgeführt werden, damit kein Interrupt dir die beiden 
schreiboperationen auseinanderziehen kann. Wie gesagt: Bei Nutzung der 
Bibliotheksroutinen hätten die das alles für dich gleich richtig 
gemacht.

von Karl H. (kbuchegg)


Lesenswert?

Deine ganze Zeitsteuerung ist Murks.
Wenn dein Compiler gut drauf ist, bleibt von deiner Funktion time() 
nichts übrig.

Wenn du wirklich eine Verzögerungszeit brauchst, dann gibt es da die 
Funktion _delay_ms.  Und die verzögert dann auch richtig.

Deine Verwendung von Dezimalkonstanten ist so ziemlich die schlechteste 
Art und Weise, wie du an dieser Stelle bestimmte Dinge ausdrücken 
kannst. Dafür hast du an anderer Stelle Binärschreibweise benutzt, wo es 
erstens nicht notwendig und zweitens auch nicht sinnvoll ist

    if (duty <= 0b0000101)  // wenn kleiner gleich 5

?
Ein Kommentar wie dieser muss dir sagen, dass du irgendwas grundlegend 
falsch machst. So einen Kommentar willst du grundsätzlich nicht haben.
Wenn du ausdrücken willst, dass duty kleiner/gleich 5 sein soll, dann 
schreib das auch so - #aber im Code!#

    if (duty <= 5)

kein Mensch braucht jetzt mehr den Kommentar! Jetzt steht die 
Information von vorher aus dem Kommentar direkt im Source Code. Für 
jeden sichtbar und in 2 Zehntel Sekunden erfassbar. Und das beste am 
ganzen: der Kommentar kann nie falsch sein, weil es gar keinen Kommentar 
gibt (und das ist eine gute Sache: Wenn du die Wahl hast zwischen "ich 
kann etwas im Kommentar schreiben" und "ich kann es im Code direkt 
schreiben", dann bevorzuge immer den Code und lass diesen Kommentar weg. 
Grundregel: Im Code steht das WIE, im Kommentar steht das WARUM. Ein 
Kommentar, der mir nur genau dasselbe erzählt wie der Code, ist ein 
schlechter Kommentar. Hier zb
1
  init();                 // Initialisierungen
Was soll der Kommentar? Das hier die Funktion init aufgerufen wird, sehe 
ich auch so. Dass init wohl nicht die Selbstzerstörung einleiten wird, 
kann ich mit ein wenig Phantasie aus dem Funktionsnamen erschliessen. 
Was also erzählt mir der Kommentar neues? Richtig - nichts. Weg damit)

Wenn du in der Programmierung bleiben willst, dann solltest du auf 
vernünftige Schreibweise wechseln. Gute Programme schreiben hat auch 
damit zu tun, dass man ordentliche und die jeweils der Situation 
angemessene Schreibweisen wählt.

1
#define F_CPU 1000000
2
3
#include <avr/io.h>
4
#include <avr/eeprom.h>
5
#include <util/delay.h>
6
7
#define KEY_STORE    PB1
8
#define KEY_PLUS     PB3
9
#define KEY_MINUS    PB4
10
11
#define INCREMENT    5
12
#deinfe DUTY_DEFAULT 255
13
14
#define EEPROM_DUTY  ((uint8_t*)0x02)
15
16
17
uint8_t duty;
18
19
//---------------------------------------------------------------------------
20
// Initialisierungen
21
//---------------------------------------------------------------------------
22
void init()
23
{
24
  DDRB |= (1<<PB0);    // OC0A
25
26
  // Taster Pins auf Eingang und jeweiligen Pullup ein
27
  DDRB &= ~( (1<<KEY_STORE) | (1<<KEY_PLUS) | (1<<KEY_MINUS) );
28
  PORTB |= (1<<KEY_STORE) | (1<<KEY_PLUS) | (1<<KEY_MINUS);
29
30
  // Timer 0 initialisieren
31
  // Mode1: Phase Correct PWM, Top = 0xFF
32
  //        Clear on upcounting, set on downcouting
33
  // Prescaler: 1
34
  TCCR0A = (1<<COM0A1) | (1<<WGM00)
35
  TCCR0B = (1<<CS00);
36
}
37
//---------------------------------------------------------------------------
38
39
//---------------------------------------------------------------------------
40
// Main-Funktion
41
//---------------------------------------------------------------------------
42
int main()
43
{
44
  init();
45
46
  duty = eeprom_read_byte( EEPROM_DUTY );
47
48
  while (true)            // Mainloop-Begin
49
  {
50
    // Store?
51
    // -> EEPROM
52
    if( !( PINB & (1<<KEY_STORE) ) )
53
      eeprom_write_byte( EEPROM_DUTY, duty );
54
55
    // Beide Tasten?
56
    //   Default
57
    if( !(PINB & (1<<KEY_PLUS)) &&
58
        !(PINB & (1<<KEY_MINUS)) )
59
      duty = DUTY_DEFAULT;
60
61
    // +
62
    // Erhöhen
63
    else if( !(PINB & (1<<KEY_PLUS)) )
64
    {
65
      if( duty < 255-INCREMENT )
66
        duty += INCREMENT;
67
      else
68
        duty = 255;
69
    }
70
71
    // -
72
    // Erniedrigen
73
    else if( !(PINB & (1<<KEY_MINUS)) )
74
    {
75
      if( duty > 0+INCREMENT )
76
        duty -= INCREMENT;
77
      else
78
        duty = 0;
79
    }
80
81
    OCR0A = duty;
82
83
    _delay_ms( 25 );
84
  }
85
  // Mainloop-Ende
86
}

von katastrophenheinz (Gast)


Lesenswert?

... einen hab ich noch:

Verwende bei Funktionsdefinitionen immer die korrekten Datentypen oder 
zumindest explizite Typecasts.

Also statt
1
void eeWrite(int adresse, int daten)
besser
1
void eeWrite(uint16_t adresse, uint8_t daten)

Sonst kommt garantiert der Tag oder auch zwei, an dem du dir wegen 
irgendwelcher impliziter Typecasts den Wolf suchst. Außerdem wird der 
Stack nicht mit sinnlosen 0-Bytes zugemüllt.

von Marc Steinbach (Gast)


Lesenswert?

Es funktioniert. An dieser stelle vielen Dank für die Hilfe vor allem an 
katastrophenheinz mit dessen hilfreichen Kommentaren letzten Endes der 
Fehler gefunden wurde.

Allerdings möchte ich auch sagen, das mir die herablassende Art von Karl 
Heinz Buchegger missfällt.
Ich denke, wenn man keine produktive Hilfe geben möchte sollte man sich 
Einfach mal zurückhalten und den Menschen, die produktive Hilfe geben 
können zu Wort kommen lassen und sich selber ein wenig zurücknehmen.
Außerdem sollte auf eine höffliche Umgangssprache geachtet werden.

Trotzdem funktioniert es jetzt, ich bin zufrieden mit der Funktion und 
dankbar für die Hilfe.
In diesem Sinne noch einen angenehmen Tag.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Marc Steinbach schrieb:
> Es funktioniert. An dieser stelle vielen Dank für die Hilfe vor allem an
> katastrophenheinz mit dessen hilfreichen Kommentaren letzten Endes der
> Fehler gefunden wurde.

Das heisst, Du wolltest gar keinen INT-Wert (16 Bit), sondern lediglich 
ein BYTE (8 Bit) im EEPROM speichern? Dann ist Dein gewählter Titel 
ziemlich irreführend.

> Allerdings möchte ich auch sagen, das mir die herablassende Art von Karl
> Heinz Buchegger missfällt.

Du hast einen komplett falschen Eindruck gewonnen. Karl Heinz ist 
überhaupt nicht herablassend, aber offenbar rutschen solche Wörter wie 
"Murks" bei Dir in den falschen Hals. Ich kann Dich beruhigen: Wenn Karl 
Heinz "Deine ganze Zeitsteuerung ist Murks" schreibt, dann ist sie auch 
Murks. Was gefällt Dir nicht an der Wahrheit?

> Ich denke, wenn man keine produktive Hilfe geben möchte sollte man sich
> Einfach mal zurückhalten und den Menschen, die produktive Hilfe geben
> können zu Wort kommen lassen und sich selber ein wenig zurücknehmen.

Karl Heinz ist einer der produktivsten Helfer hier im Forum. Du solltest 
hier erstmal eine Weile mitlesen, bevor Du Dir solche unqualifizierten 
Urteile erlaubst. Lies Dir die (langen und ausführlichen) Ratschläge von 
Karl Heinz nochmal durch und nimm sie Dir zu Herzen. Dann schlägst Du 
vielleicht nicht morgen schon wegen dem nächsten Problem hier auf.

> Außerdem sollte auf eine höffliche Umgangssprache geachtet werden.

Du hättest vorher angeben sollen, dass Du zartbesaitet bist. Dann hätte 
ich Karl Heinz vorher ein paar Samthandschuhe geschenkt ;-)

Karl Heinz' Umgangssprache ist völlig normal. Wir sind hier auch nicht 
im Kindergarten, wo man erst ein paar lustige Smarties in die Luft 
werfen muss, bevor Tacheles geredet wird.

von Karl H. (kbuchegg)


Lesenswert?

Marc Steinbach schrieb:

> Ich denke, wenn man keine produktive Hilfe geben möchte

Ich wüsste nicht, was noch produktiver sein könnte, als dir anhand eines 
Beispiels zu zeigen, wie du deinen Code qualitativ mit einfachen Mitteln 
pushen kannst und dir auch noch (hoffentlich nachvollziehbar) zu 
erklären, warum das dann ein besseres Ergebnis bringt.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Karl Heinz Buchegger schrieb:
> Ich wüsste nicht, was noch produktiver sein könnte, [...]

Er wollte nur den Fisch, aber nicht wissen, wie man ihn angelt ;-)

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.