Forum: Compiler & IDEs Verwirrung EEPROM ATmega168


von Frank H. (avrnooby)


Lesenswert?

Hallo,

das ist jetzt sicher wieder ne ganz dumme Frage, aber ich komm allein
nicht weiter:

Ich will das EEPROM des ATmega168 byteweise schreiben und lesen. Das
geht mit den Befehlen von <avr/eeprom.h> auch soweit ganz gut.

Das Programm hat aber noch andere Interruptroutinen laufen (Timer 0,1,2
; USART und I2C). Das geht auch schon alles zu meiner vollen
Zufriedenheit. Jetzt soll das EEPROM als nichtflüchtiger
Betriebsstundenzähler genutzt werden und über einen Ringpuffer (damit
ich nicht immer dieselbe EEPROM-Stelle beschreibe) alle 10 Minuten mit
einem neuen Wert geschrieben werden. Soweit so gut, geht alles.

Jetzt habe ich im Userguide des ATmega48/88/168 auf Seite 23-25 (Kapitel
7.6.3) mit Erschrecken was von 1,8ms bis zu 3,3ms Schreib-, bzw.
Lesezeit für das EEPROM gelesen. Ich sehe da keinen Hinweis das damit
ein ganzer Block gemeint ist, vermute also ein Byte braucht so lange zum
Schreiben und Lesen. Weil aber während des EEPROM-Schreibens laut
Userguide S. 24 unten die globalen Interrupts disabled werden sollen,
würde mir das doch meine ganzen anderen Interrupts versauen, vor allem
die UART-Kommunikation ?!? Was passiert z.B. wenn ich das EEPROM
schreibe, also mit cli() die interrupts disabled sind und genau in der
Zeit kommt mit 9600Baud ein Zeichen auf dem UART an ? Merkt sich der
Prozessor das und springt den UART_RX -interrupt an, nachdem ich mit
sei() wieder freigegeben habe ?

Noch eine Frage: Weil mir die Zeit die im Userguide steht mit mindestens
1,8ms zum schreiben und lesen auf das EEPROM unwahrscheinlich lang
vorkam, habe ich folgendes gemacht: Ich setze ein Pin auf HIGH, schreibe
dann mein Byte, setze den Pin dann wieder auf LOW. Das ganze hab ich mit
dem Oszi gemessen und komme auf ca. 12,4 µs !! Das Byte wurde korrekt
geschrieben. Daraus folgt wohl, das ich wohl irgendwas im Datenblatt
übersehen habe oder ich einfach zu dämlich bin, vermutlich eine Mischung
aus beidem.

Bin für jeden Tip dankbar, vielleicht gibt es ja Application Notes die
das Interrupt und Timingverhalten des ATmega168 näher beleuchten ?

MfG, avrnooby.

von Johannes M. (johnny-m)


Lesenswert?

Die Interrupts müssen nicht während des gesamten Schreibzyklus 
gesperrt sein. Es muss nur gewährleistet sein, dass zwischen dem 
Schreiben des EEMPE-Bits und des EEPE-Bits kein Interrupt auftritt. Die 
beiden Schreibzugriffe müssen innerhalb von 4 Taktzyklen durchgeführt 
werden. Ich weiß jetzt allerdings nicht, wie das in der eeprom.h 
implementiert ist.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Falls Dir die Werte im Datenblatt seltsam vorkommen oder das Datenblatt 
unpräzise ist und Du definitiv irgendwelche Margen einhalten musst, 
ist's mit einer Messung ja nicht wiederlegt, daß es auch länger dauern 
kann...

Vielleicht ist Nachfragen beim Hersteller ne Option? Mit den AVR 
Support-Tickets von Atmel hab ich sehr gute Erfahrungen gemacht, wenn 
ein Datenblatt unvollständig ist: kompetent und flott.

von Johannes M. (johnny-m)


Lesenswert?

@Johann:
Ich finde das Datenblatt (auch an der betreffenden Stelle) sehr 
eindeutig. Ich vermute mal, dass der OP mit "User Guide" das Datenblatt 
meint, zumal auch die Seitenzahlen übereinstimmen.

Das grundsätzliche Problem mit dem EEPROM ist, dass der eigentliche 
Schreibvorgang tatsächlich sehr lange dauert, während das Schreiben der 
Adresse und das Setzen der Write-Enable-Bits, wodurch der Schreibvorgang 
initialisiert wird, sehr schnell geht. Will man mehrere Bytes 
hintereinander schreiben, muss man mit der Busy-Wait-Funktion warten, 
bis der aktuelle Schreibvorgang abgeschlossen ist (bzw. bis das EEPE 
wieder Null wird). Die Sache mit den Interrupts steht eigentlich 
eindeutig unter der Erläuterung des Ablaufs des Schreibens ("Caution: An 
interrupt between step 5 and step 6 will make the write cycle fail, 
...").

Die beiden Dinge (warten, bis EEPROM bereit und kein Interrupt zwischen 
EEMPE- und EEPE-Write) sind eigentlich das einzige, was beachtet werden 
muss. Wenn man mit den Funktionen aus der eeprom.h mehrere Bytes 
schreiben will, dann werden die Busy-Waits zwischen den einzelnen Bytes 
selbstverständlich automatisch eingebaut, da die Funktionen jeweils vor 
dem Schreibvorgang warten, bis das EEPROM bereit ist (also das EEPE Null 
ist). Wenn Du mal zwei Bytes direkt hintereinander schreibst, wirst Du 
auch merken, dass das Schreiben des zweiten dann die im Datenblatt 
genannte Zeit in Anspruch nimmt.

Übrigens habe ich mal in die eeprom.h geschaut und dort steht bei der 
Definition von eeprom_write_byte u.a. zu lesen:
1
        "/* START EEPROM WRITE CRITICAL SECTION */\n\t"
2
        "in  r0, %[__sreg]    \n\t"
3
        "cli        \n\t"
4
        "sbi  %[__eecr], %[__eemwe]  \n\t"
5
        "sbi  %[__eecr], %[__eewe]  \n\t"
6
        "out  %[__sreg], r0    \n\t"
7
        "/* END EEPROM WRITE CRITICAL SECTION */"
Hier wird also eindeutig vor den kritischen Anweisungen ein cli 
ausgeführt und danach ein sei

von Lutz (Gast)


Lesenswert?

Krümelkackerklugscheißermodus an

Es wird nach dem kritischen Teil kein sei() eingefügt. Es wird lediglich 
das vor dem cli() gesicherte SREG zurückgeschrieben. Wär ja sonst auch 
ein dicker Hund.

Krümelkackerklugscheißermodus aus

von Frank H. (avrnooby)


Lesenswert?

Vielen Dank für die Antworten, das hat mir sehr geholfen.




Lutz wrote:

> Es wird nach dem kritischen Teil kein sei() eingefügt. Es wird lediglich
> das vor dem cli() gesicherte SREG zurückgeschrieben. Wär ja sonst auch
> ein dicker Hund.


Bedeuted dass, dass ich das sei() selber machen muss, nachdem
"eeprom_write_byte" beendet ist ? Im Moment schalte ich es nicht wieder 
an, die Interrupts scheinen aber zu laufen.

MfG

von Lutz (Gast)


Lesenswert?

Nein, brauchst Du nicht.
Der "Witz" liegt darin, dass der Zustand vor dem cli() wieder 
hergestellt wird. War sei() bzw. I in SREG zu diesem Zeitpunkt aktiviert 
(z.B. gleich am Programmanfang), wird es danach automatisch wieder 
gesetzt. Wenn es vorher nicht gesetzt war, dann wird es auch nachher 
nicht gesetzt sein. Wenn automatisch ein sei() implementiert wäre, 
würden ja sonst nach jedem Schreibzugriff auf EEPROM der globale 
Interrupt Enable gesetzt, auch wenn man das gar nicht wollte!

von Christian Erker (DL3CE) (Gast)


Lesenswert?

Nein, es wird lediglich der Status der Interrupts (an / aus), welcher 
vor der EEPROM Operation bestand, wiederhergestellt. Würde die Funktion 
einfach ein sei(); machen, währen die Interrupts nach der Funktion IMMER 
an, was nicht gewollt ist.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Johannes M. wrote:

> Das grundsätzliche Problem mit dem EEPROM ist, dass der eigentliche
> Schreibvorgang tatsächlich sehr lange dauert, während das Schreiben der
> Adresse und das Setzen der Write-Enable-Bits, wodurch der Schreibvorgang
> initialisiert wird, sehr schnell geht. Will man mehrere Bytes
> hintereinander schreiben, muss man mit der Busy-Wait-Funktion warten,
> bis der aktuelle Schreibvorgang abgeschlossen ist (bzw. bis das EEPE
> wieder Null wird).

Immerhin gibt's noch den EEREADY-IRQ, falls man nicht warten mag. 
Alternativ kann also eine IRQ-getriebene EEPROM-Routine implementiert 
werden.

> Übrigens habe ich mal in die eeprom.h geschaut und dort steht bei der
> Definition von eeprom_write_byte u.a. zu lesen:
>
1
>         "/* START EEPROM WRITE CRITICAL SECTION */\n\t"
2
>         "in  r0, %[__sreg]    \n\t"
3
>         "cli        \n\t"
4
>         "sbi  %[__eecr], %[__eemwe]  \n\t"
5
>         "sbi  %[__eecr], %[__eewe]  \n\t"
6
>         "out  %[__sreg], r0    \n\t"
7
>         "/* END EEPROM WRITE CRITICAL SECTION */"
8
> 
9
>

Hier ginge auch folgendes, was die IRQ-Latenz weniger belastet:
1
        "/* START EEPROM WRITE CRITICAL SECTION */\n\t"
2
        "in  r0, %[__sreg]    \n\t"
3
        "cli        \n\t"
4
        "sbi  %[__eecr], %[__eemwe]  \n\t"
5
        "out  %[__sreg], r0    \n\t"
6
        "sbi  %[__eecr], %[__eewe]  \n\t"
7
        "/* END EEPROM WRITE CRITICAL SECTION */"

Wenn ich mich recht entsinne, war das früher auch mal so implementiert.

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.