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.
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.
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.
@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
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
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
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!
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.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.