Hi zusammen! Ich hab kürzlich eine interessante Entdeckung gemacht, vielleicht hat das ja einer von euch auch schon mal bemerkt: Beim ATTiny13 habe ich in kurzem Zeitabstand Werte auf ein und dieselbe Adresse geschrieben und erwartet, daß das zuletzt geschriebene Datum am Ende in der Adresse zu finden ist. Dem war nicht so. Und das hängt anscheinend noch nicht mal davon ab, wieviel Zeit man zwischen 2 Schreibzugriffen auf dieselbe Adresse läßt (ganz abgesehen von der Zeit, bis das EEPE-Bit wieder auf 0 geht). Schreibt man in der gleichen Geschwindigkeit auf verschiedene Adressen, funktioniert das wie erwartet. Wenn ich alle 10-500ms (oben offen?) auf die gleiche Adresse schreibe und auch das EEPE-Bit beachte, dann landet nur der erste Zugriff tatsächlich in der Speicherstelle, danach nichts mehr. Das Datenblatt gibt diesbezüglich nichts her (bis auf den Hinweis bzgl. EEPE-Bit) und im Forum hab ich auch noch nichts drüber gefunden. Hat das schon mal einer von euch entdeckt? Oder was mache ich falsch? Anbei ein Code-Beispiel. In der Adresse 0x08 habe ich am Ende 0x00 anstelle 0x13. // Test.c, 28.02.11, V1 // für ATTiny13 // CKSEL[1-0]-Fuses auf [01] setzen (0=prog, 1=unprog), damit Sys-Clk = int. RC-Osz. 9,6MHz // SUT[1-0] auf [01] für langsamsten Startup // F_CPU in Makefile auf 9.600.000 definiert void Write_EE(unsigned char adr, unsigned char data) { while (EECR & 0x02); // warten bis Zugriff möglich EEDR = data; EEARL=adr; cli(); EECR=0x04; // Write vorbereiten EECR=0x06; // Write auslösen sei(); } int main(void) { CLKPR = 0x80; CLKPR = 0x00; // Prescaler auf 1: 9,6MHz / 1 = 9,6MHz PORTB = 0; DDRB |= OUTBIT; unsigned char a; for (a=0;a<20;a++) { Write_EE(0x08,a); _delay_ms(10); _delay_ms(10); _delay_ms(10); PORTB ^= OUTBIT; } while(1); } Erwartungsgemäß funktioniert es, wenn man Write_EE(0x08,a); durch Write_EE(a,a); ersetzt, was natürlich nicht mehr das gleiche ist. Gruß, Micha.
folgendes steht im Datenblatt: The EEMPE bit must be written to one before a logical one is written to EEPE, otherwise no EEPROM write takes place Warum nimmst du nicht den Code vom Datasheet dann wirds auch funktionieren...
1 | void EEPROM_write(unsigned char ucAddress, unsigned char ucData) |
2 | }
|
3 | /* Wait for completion of previous write */
|
4 | while(EECR & (1<<EEPE)) |
5 | ;
|
6 | /* Set Programming mode */
|
7 | EECR = (0<<EEPM1)|(0>>EEPM0) |
8 | /* Set up address and data registers */
|
9 | EEARL = ucAddress; |
10 | EEDR = ucData; |
11 | /* Write logical one to EEMPE */
|
12 | EECR |= (1<<EEMPE); |
13 | /* Start eeprom write by setting EEPE */
|
14 | EECR |= (1<<EEPE); |
15 | {
|
Hallo Peter, deine Antwort zielt eigentlich nicht auf mein Problem. Abgesehen davon: wenn du genau hinschaust, dann tut meine Funktion Write_EE nichts anderes, als was im Datenblatt vorgegeben ist. Hast du die Datenblattvorgabe mal im Unterschied zu meiner Funktion ausprobiert wenn du sagst "dann wirds auch funktionieren"? Ich werde das heute abend mal probieren und es würde mich wundern, wenn das einen Unterschied macht. Aber man weiß ja nie... Bericht folgt... Micha.
Ein EEPROM ist nicht dazu da im ms Takt darauf zu schreiben, die Anzahl der Schreibzyklen ist begrenzt, so bekommst du das bald kaputt.
Stimmt ich hab es nicht ausprobiert. Naja ok wenn... EECR=0x04; // Write vorbereiten EECR=0x06; // Write auslösen das gleiche ist wie... /* Write logical one to EEMPE */ EECR |= (1<<EEMPE); /* Start eeprom write by setting EEPE */ EECR |= (1<<EEPE); Entschuldigung das ich nicht "genau" hingeschaut habe :-/
...aber vielleicht solltest ja du mal genau hinschauen ;-)
Hallo Peter, danke für nochmal hinschauen ;-) Es ist ja nicht so, daß ich mit dieser Funktion nicht auf's EEPROM schreiben könnte, das geht ja! Mir ist nur aufgefallen, daß das nicht "beliebig" schnell gemacht werden kann (ohne dabei die Mindest-Schreibzeit, bis EEPE wieder auf 0 geht, zu unterschreiten; das ist völlig klar und steht auch im Datenblatt, daß das nicht geht), und daß es davon abhängt, ob ich die gleiche oder eine andere Adresse wähle. Ich habe gestern abend sogar gesehen, daß das nicht geht, wenn ich im Abstand von 500ms auf die gleiche Adresse schreibe. Das ganze hab ich auch schon mal mit einem anderen Tiny13 probiert (sozusagen fabrikneu): gleiches Bild!?!? Werde auf jeden Fall heute Abend mal die Datenblatt-Variante ausprobieren. @Gast: Es gilt, was das Datenblatt hergibt: nach t_write (Seite 16: 3,4ms) kann der nächste Schreibzyklus gestartet werden; und Atmel garantiert eine Mindestanzahl von 100.000 Schreibzyklen. Da steht nirgendwo, daß das "nicht dafür gedacht ist" (warum nicht?), und solange ich nicht die 100k überschreite (fabrikneues Teil) muß ich mich auch noch nicht anfangen, zu wundern, wenn es nicht geht! Also ich denke, entweder hakt das an einer ganz bestimmten Stelle im Chip oder ich mache mit dem Programm unbewußt was Ungeschicktes.
Micha schrieb: > oder ich mache mit dem Programm unbewußt was Ungeschicktes. Oder in der Hardware... Wie schaut die Spannungsversorgung aus, hat der Tiny seinen Abblock-Kerko? Brown-Out aktiviert?
Ich könnte mir halt vorstellen dass es daran liegt das du das Bit EEMPE (0x04) in EECR schreibst und danach nochmal EEMPE und dazu EEPE (0x06) in EECR schreibst. Kann natürlich auch sein das ich mich täusche... Weil ich habs ja nicht ausprobiert ;-) Hatte aber niemals Probleme mit den Routinen vom Datasheet.
@ Ernst: der Abblock-C ist so kurz angebunden wie es kürzer nicht geht, die 5V(!) kommen vom Regler, 5mm weiter weg, und der ist geprüft stabil (Ripple < 10mV @10MHz). (endlich mal einer, der mitdenkt ;-)) Brown Out hab ich nicht aktiviert, aber der schützt ja nicht in der Hinsicht, als das eigentlich noch schnell geschrieben wird, sondern das eben kein Blödsinn geschrieben wird (rechtzeitiger Abbruch). Nachdem aber die Spannung stabil und bei 5V liegt... meiner Meinung nach weit weg von kurzzeitiger Versorgungslücke; und warum funktionierts dann mit unterschiedlichen Adressen...??? @Peter Das gleiche passiert doch mit EECR |= (1<<EEMPE); EECR |= (1<<EEPE); ( --> |= <-- !!) und ist lt. Datenblatt nötig! Ich hatte auch nie Probleme mit "meiner" Funktion. Bis "gestern". Und dieser besonderen Situation. Frag mich nicht, wie lange ich gedoktort habe, um das "Phänomen" soweit einzukreisen. Ich denke, ich frag mal Atmel wenn ich heut abend nichts weiter rauskriege.
Dann rechne mal nach du Spezialist, wenn du Zyklisch alle 3,4ms ins EEPROM schreibst sind die 100k Schreibzugriffe von deinem tollen fabrikneuen Teil in weniger als 6 Minuten erfolgt und das wars dann.
@Micha: Dann wirds wohl nicht an der Spannung liegen. Micha schrieb: > Das gleiche passiert doch mit > > EECR |= (1<<EEMPE); > EECR |= (1<<EEPE); ( --> |= <-- !!) Nicht unbedingt.... Ein IO-Register ist zwar für den Compiler eine normale Speicherzelle, aber nicht "in Hardware". Die "|=" - Zugriffe werden in je einen einzelnen "SBI"-Opcode übersetzt, bei der Ausführung werden die anderen Bits nicht angetastet. Beim Zuweisen werden beide Bits "neu beschrieben". Das kann einen Unterschied machen (sh. Interrupt-Flag-Register), muss aber nicht.
@ Gast: Paß mal auf, mein Lieber: zum ersten: auf deine kindischen Forums-Spitzen kann ich dankend verzichten. zum zweiten: niemand behauptet, minutenlang auf eine EEPROM-Zelle schreiben zu wollen, und wenn's so wär, dann muß derjenige wissen was er tut, wenn's länger als 6 Minuten dauert. zum dritten: 100k ist das Minimum, nirgendwo steht, wieviele Zyklen wirklich fehlerfrei laufen. Abgesehen davon: schon mal selbst probiert wieviel geht? in Abhängigkeit von der Temperatur, Versorgungsspannung, etc.? Die Welt ist da etwas größer.... Aber hast brav gerechnet. Nur während der 1. Minute hat es auch noch nicht funktioniert. Hätte mich mehr gefreut, wenn du einen konstruktiven Beitrag wie Ernst geliefert hättest. @ Ernst: Stimmt. Hängt zum einen vom Compiler ab, zum anderen von der Realisierung des Opcodes im Chip. Da kenne ich mich nicht aus, bin nämlich nicht der Chipdesigner ;-). Werde mal die Assembler-Codes vergleichen und wie schon gesagt, den "Datenblatt-Code" probieren.
Micha schrieb: > Hallo Peter, > > deine Antwort zielt eigentlich nicht auf mein Problem. > Abgesehen davon: wenn du genau hinschaust, dann tut meine Funktion > Write_EE nichts anderes, als was im Datenblatt vorgegeben ist. Und wenn Du mal genau hinschaust, macht deine Funktion eben nicht das Gleiche wie im Datenblatt vorgegeben. Dort steht nix davon, dass man beim Setzen von EEPE EEMPE zurücksetzt. Und genau das hat Peter Z. nun schon mehrfach versucht Dir zu erläutern. Aber wenn Du meinst.... Bei Tausenden von Anwendern funktioniert das Beschreiben des EEPROMs problemlos, nur bei Dir nicht. Und dann liegt's ganz bestimmt nicht an deinem Programm? Thomas
Hallo Gustl, mit EECR=0x06; setze ich EEMPE nicht zurück (0x06=0b00000110). Die Tausende von Anwendern schreiben nicht unbedingt mit der Geschwindigkeit... Und wie ist zu erklären, daß es problemlos funktioniert, wenn ich auf unterschiedliche Adressen schreibe? Hatte gestern abend noch keine Zeit für den Datenblatt-Code, nächster Versuch heute abend...
Micha schrieb: > mit EECR=0x06; setze ich EEMPE nicht zurück (0x06=0b00000110). Du schreibst damit einen 8Bit Wert, was bei EECR durchaus einen Unterschied macht im Gegensatz zum Setzen einzelner Bitz mit SBI/CBI, wie auch bei vielen anderen "Registern" eines AVRs. Ich denke auch genau da liegt der Hund begraben, weil sich hier eine Timing Logik hinter der Sequenz wie einzelne Bits gesetzt werden müssen verbirgt. Ich tippe daher das evtl. nicht der erste Schreibzugriff erfolgreich ist, sondern evtl. der zweite?
Also hier für alle (auch für mich) zur Überraschung: dieses Programm // Test.c, 28.02.11, V1 // für ATTiny13 // CKSEL[1-0]-Fuses auf [01] setzen (0=prog, 1=unprog), damit Sys-Clk = int. RC-Osz. 9,6MHz // SUT[1-0] auf [01] für langsamsten Startup // F_CPU in Makefile auf 9.600.000 definiert #include<avr/io.h> #include<util/delay.h> #include<avr/interrupt.h> #define OUTBIT 0x02 // Treiber an PB1 void Write_EE(unsigned char adr, unsigned char data) { while (EECR & (1<<EEPE)); // warten bis Zugriff möglich EEARL = adr; EEDR = data; // cli(); EECR |= (1<<EEMPE); // Write vorbereiten EECR |= (1<<EEPE); // Write auslösen // sei(); } int main(void) { CLKPR = 0x80; CLKPR = 0x05; // Prescaler auf 1: 9,6MHz / 1 = 9,6MHz PORTB = 0; DDRB |= OUTBIT; unsigned char a; for (a=0;a<20;a++) { Write_EE(9,a); _delay_ms(10); _delay_ms(10); _delay_ms(10); PORTB ^= OUTBIT; } while(1); } produziert folgendes Bild im EEPROM: -> Dump1.gif und eigentlich sollte ja an Adresse 9 am Ende eine 0x13 stehen. Ändert man die Zeile Write_EE(9,a); ab in Write_EE(a,a); so erhält man -> Dump2.gif was genau erwartungsgemäß ist. So. Und das erklär mir mal einer. Wenn ich mit der Version "Write_EE(9,a);" herumprobiere finde ich folgende Zusammenhänge: - Ändern der Controllertaktfrequenz ändert nichts. - Ändern der Schreibreihenfolge auf Adress- und Datenregister ändert (logischerweise) nichts - Auskommentieren der _delay_ms() Funktionen bringt soviel, daß bei Adresse 9 mal eine 0x07 oder 0x06 steht statt einer 0x00 wie im Dump1. Ich bin auf neue Vorschläge gespannt!
Jetzt hab ich es kurz mal mit einem Tiny26 ausprobiert. Wie zu erwarten war gibt es bei mir keine Probleme mit folgendem Code...
1 | void EEPROM_write(unsigned int uiAddress, unsigned char ucData) |
2 | {
|
3 | /* Wait for completion of previous write */
|
4 | while(EECR & (1<<EEWE)) |
5 | ;
|
6 | /* Set up address and data registers */
|
7 | EEAR = uiAddress; |
8 | EEDR = ucData; |
9 | /* Write logical one to EEMWE */
|
10 | EECR |= (1<<EEMWE); |
11 | /* Start eeprom write by setting EEPE */
|
12 | EECR |= (1<<EEWE); |
13 | }
|
14 | |
15 | void main(void) |
16 | {
|
17 | unsigned char x; |
18 | |
19 | while (1) |
20 | {
|
21 | for (x=0;x<20;x++){ |
22 | EEPROM_write(9, x); |
23 | }
|
24 | while(1); |
25 | }
|
26 | }
|
Da sehe ich einen Unterschied:
1 | void Write_EE(unsigned char adr, unsigned char data) |
2 | {
|
3 | while (EECR & (1<<EEPE)); // warten bis Zugriff möglich |
und
1 | void EEPROM_write(unsigned int uiAddress, unsigned char ucData) |
2 | {
|
3 | /* Wait for completion of previous write */
|
4 | while(EECR & (1<<EEWE)); |
Könnte damit das Thema erledigt sein?
Nö, das Bit heißt beim Tiny26 nur anders als beim Tiny13...
Rufus, ich weiß nicht genau welchen Unterschied du meinst, aber EEWE heißt das Bit lt. Datenblatt beim Tiny26, EEPE beim Tiny13, ist aber das gleiche. Gleiches gilt für EEMPE/EEMWE. Ich habe auch mein Programm komplett zusammengestrichen zu unsigned char a; void Write_EE(unsigned int adr, unsigned char data) { while (EECR & (1<<EEWE)); // warten bis Zugriff möglich EEARL = adr; EEDR = data; EECR |= (1<<EEMWE); // Write vorbereiten EECR |= (1<<EEWE); // Write auslösen } int main(void) { for (a=0;a<20;a++) { Write_EE(9,a); } while(1); } sogar die Adresse als unsigned int laufen lassen, was bei 6 bzw. 7 bit nicht wirklich was bringt und (jetzt kommt's!) das ganze auf dem Tiny13 und ebenfalls auf einem Tiny26 laufen lassen: wieder das gleiche Bild!!!! Ich finde im EEPROM meistens 0x05, manchmal 0x04, beim Tiny13 ähnlich wie beim Tiny26. Das Programm ist identisch zu dem von Peter Z.; die Hardware ist sauber aufgebaut (s.o.). Und schreiben auf eine variable Adresse (Write_EE(a,a);) funktioniert problemlos! Ich weiß nicht, ob euch noch was einfällt. Peter, vielleicht kannst du mir nochmal genau deine Fuse-Settings geben. Ansonsten muß ich mal Atmel fragen, ob die einen Grund ahnen...
Peter, kannst du mir zusätzlich einfach mal DEIN Hexfile schicken? (die ultimaiv krasse Methode...). Danke!
Dein Hex funktioniert. Da landet wie gewünscht eine 0x13 da wo sie hin soll. Und das funktioniert auch mit den Fuse-Settings, die ich vorher hatte (ohne BOD, andere Frequenz, Startup Time etc.) Jetzt kommen wir der Sache näher. An der Stelle schon mal vielen Dank für deine Hilfe. Da ich im PonyProg schon gesehen hab, daß dein Hex-File ca. doppelt so lange ist als meins, trotz praktisch gleichem Source-File, kannst du mir evtl. das Assembler-Listing schicken? Oder das gezippte Projekt (AVR-Studio, nehme ich an)?
Hier ist ein GETESTETER code, den ich aus einem eigenen Programm für den Tiny-13 entnommen habe: data=irgendein register ;======================================= ; HOLE BYTE AUS EEPROM ; zl=EEadr data=output ;======================================= EE_GET: sbic EECR,EEPE rjmp ee_get out EEAR,zl sbi EECR,EERE ;read-strobe nop in data,EEDR ret ;======================================= ; SCHREIBE BYTE AUS EEPROM ; zl=EEadr data=input ;======================================= EE_SET: sbic EECR,EEPE ;wait until ready rjmp ee_set push data ldi data,(0<<EEPM1)|(0<<EEPM0) out EECR,data ;set programming mode pop data out EEAR,zl out EEDR,data sbi EECR,EEMPE ;master write-enable sbi EECR,EEPE ;write-strobe ret Joe
Micha schrieb: > void Write_EE(unsigned int adr, unsigned char data) > { > while (EECR & (1<<EEWE)); // warten bis Zugriff möglich > EEARL = adr; ein evtl. kleiner aber feiner Unterschied eurer beiden versionen: Micha, du weist einen int-Wert dem 8-bit Register EEARL zu. eigentlich sollte sich da doch der compiler beschweren. Rainer hat EEAR (ohne L) benutzt, was ja letztendlich auf die 2 Register EEARL und EEARH zugreift. Ich kann mich noch dunkel erinnern, dass ich bei nem mega mit 256byte EE2 sehr willkürliche Ergebnisse hatte, wenn ich das high-byte (EEARH) nicht explizit auf 0 setze. Ich weiß nicht, wie die tinys das handhaben, aber da wär auf jeden fall mal n unterschied im Programm. Grüße, Alex
Kann ich dir leider erst am Montag schicken wenn ich wieder am PC in der Arbeit bin...
Sehr gutes Auge, Alex, ich seh den Wald vor lauter Bäumen nicht mehr... Aber: beim Tiny13 heißt das Register lt. Datenblatt offiziell so (EEARL), da gibt's auch nur das eine, während beim Tiny26 heißt es EEAR. Das ist aber vom Compiler beim compilieren von EEARL mit dem Tiny26 trotzdem gefressen worden und vollkommen richtig übersetzt, hab's im Assembler nachgeprüft. Aber selbst diese vermeintlichen Fehler und Unterschiede erklären nicht schlüssig und umfassend das Verhalten, das ich einige Postings weiter oben beschrieben hab. Peter, in deinem Hex ist einiges mehr an Code, als das was du gepostet hast, ich hab mal das Hex disassembliert. Da sind einige Schleifen und sonstige RCALLs drin, die im C-Code nicht auftauchen. Nicht, daß wir da Äpfel mit Birnen vergleichen... Sonst kriege ich/wir nicht raus, woran es wirklich liegt.
Da sind eigentlich nur Initialisierungen drin. Das sollte aber keine Rolle spielen. Ich schick dir am Montag das asm File dann wirst es ja sehen. Übrigens verwende ich den Codevision Compiler und nicht den GCC
Noch ein kleiner Brocken, der aber auch nur bedingt zur Erklärung geeignet ist: Der Tiny13 hat ja die tollen neuen Programming Mode Bits, die du nicht anrührst. Wenn die, warum auch immer, auf write only, also ohne erase stehen, könnte es sein, dass der EE2 die Schreibzugriffe je nach Speicherstrategie irgendwie zusammenmischt. Beim Programmieren wird ja standardmäßig auch der komplette EE2 gelöscht. wenn das programm dann läuft ist er jungfräulich und bei einem zugriff auf jede speicherzelle auch tatsächlich kein erase notwendig. wenn du aber wiederholt auf dieselbe schreibst, gibts ärger. Warum dein Programm aber dann auf nem tiny26 fehler verursacht macht den gedanken dann wieder zu nichte :/ edit: Peter Z. schrieb: > Da sind eigentlich nur Initialisierungen drin. genau das könnte theoretisch den unterschied machen
Micha schrieb: > Dein Hex funktioniert. Unterschied könnte noch die Optimierung sein. Was hast Du? -O, -Os, -O2? Welchen Optimierungsgrad hat Peter benutzt?
Hast Du den Beitrag gelesen? Beitrag "Re: EEPROM Schreibfrequenz auf eine Adresse" Auszug aus dem Datenblatt von Atmel: The EEMWE bit determines whether setting EEWE to one causes the EEPROM to be written. When EEMWE is set, setting EEWE within four clock cycles will write data to the EEPROM at the selected adress if EEMWE is zero. ... When EEMWE has been written to one by software, hardware clears the bit to zero after four clock cycles. Kann das damit zusammenhängen? Siehe auch Optimierungsgrad des Compilers. Im Verlauf dieses Beitrags hat der Optimierungsgrad auch das gewünschte Ergebnis gebracht. Beitrag "Internes EEPROM beschreiben bei Atmega 644" Dieser Assemblercode funktioniert wohl: Beitrag "Re: EEPROM Schreibfrequenz auf eine Adresse"
Hi Rainer, den Beitrag vom Atmega644 hatte ich nicht ganz gelesen. Aber jetzt. Ob Schorsch nun glücklich wurde oder nicht, erfährt ja der geneigte Leser nicht mehr in dieser Geschichte. Aber Spaß beiseite: Nachdem Peters Programm einfach von dem verschieden ist, das bei mir läuft, wird und muß hier der Hase im Pfeffer liegen (es geht auf Ostern zu), egal ob das jetzt ab Compiler selbst oder an dessen Optimierungseinstellungen liegt. Unterm Strich zählt der Assembler-Code und genau danach geht der Controller und nach nichts anderem. Also gilt es, den Assembler-Code auseinander zu pflücken und genau zu schauen, wo die Stelle ist, die für den Unterschied zwischen "sicher schreiben" und "vielleicht mal schreiben" sorgt. Egal welcher Grund da wie tief auch immer dahintersteckt. Und wenn die Stelle gefunden ist und der physikalisch/syntaktische Hintergrund (wie hochtrabend zu so später Stunde) nicht klar ist, dann muß Atmel ran. Schauen wir mal. Ich kann nicht versprechen, daß ich den Code schon im Lauf der kommenden Woche zerlegt habe. Grüße an alle Mitstreiter!
Micha schrieb: > Dein Hex funktioniert. Na dann schau dochmal in Dein List-File (*.lss). Ich vermute auch, Du hast ne falsche Optimierung genommen. Anbei mal mein Listing und Hex (nicht getestet). Mit der Option: -fno-inline-small-functions kann man das Listing besser lesen (kein unerwünschtes Inlining). Peter
Die Option geb ich dann beim Compiler an? Wo da??? :-/ Wenn man sich das Listfile anschaut, sieht man ja deutlich, daß der im Datenblatt vorgeschlagene Code rauskommt für die Schreib-Funktion (Write_EE). Im Datenblatt steht aber nichts davon, daß noch irgendwelche anderen Register in bestimmter Art und Weise gesetzt sein müssen, damit der Schreibvorgang IMMER klappt. Ich kann mir nur vorstellen, daß durch die andere Optimierungseinstellung und bestimmte Initialisierungen des Compilers einfach bestimmte Register (und damit Chipfunktionen) so gesetzt sind/werden, daß der Schreibvorgang dann eben IMMER korrekt abläuft, fehlen die Inits oder Optis, dann klappt's eben nicht immer, was aber nicht im Sinne des Erfinders (Atmel) sein kann, solange die Schreibprozedur in beiden Fällen identisch und korrekt ist (was hier ja der Fall ist!). Ginge man streng nach Datenblatt vor, müßte ein (wie ganz oben beschriebenes) mehrfaches EEPROM-Schreiben direkt nach Reset (Initial Values!) ohne irgendwelche Umwege (Compiler-Initialisierungen) korrekt funktionieren. Demnach wäre es mal einen Versuch wert, wenn ich genau dieses in Assembler programmiere und ausprobiere. Bericht folgt. Fallen euch da Pferdefüße dazu ein?
http://www.atmel.com/dyn/resources/prod_documents/doc0932.pdf http://www.atmel.com/dyn/resources/prod_documents/avr100.zip Note: If the code initiates a write to EEPROM shortly after Reset, keep in mind the following: If EEPROM contents are programmed during the manufacturing process, the MCU might change the code shortly after programming. When the programmer then verifies the EEPROM contents, this might fail because the EEPROM contents have already been modified by the MCU. Also notice that some In-System Programmers will allow the MCU to execute a short time between each step in the programming and verification process Im Beispielcode von Atmel werden die Global Interrupts nach der Adressübergabe deaktiviert danach in 2 getrennten Schritten Master Write und EEPROM Write nacheinander gesetzt, anscheinend darf hier nichts zwischenfunken evtl. darf sogar nur eine bestimmte Zeit dazwischen vergehen. Vielleicht muss auch jedesmal die Adresse obwohl Sie sich nicht ändert mit übertragen werden. Ich habe bisher keine Probleme damit gehabt, allerdings habe ich mich auch an die Aplication Note gehalten.
habe gerade gesehen das man 4 Takte Zeit hat, deswegen dürfen keine Interrupts dazwischen funken.
Hier kommt das Ergebnis des "Rudimentär-Tests". Ich hab ein absolut abgespecktes Assembler Programm geschrieben: .include "tn26def.inc" rjmp RESET ;Reset Handle reti; rjmp INT0 ;Handle reti; rjmp IO_PINS ;Handle reti; rjmp TIMER1_CMPA ;Handle reti; rjmp TIMER1_CMPB ;Handle reti; rjmp TIMER1_OVF1 ;Handle reti; rjmp TIMER0_OVF0 ;Handle reti; rjmp USI_STRT ;Handle reti; rjmp USI_OVF ;Handle reti; rjmp EE_RDY ;Handle reti; rjmp ANA_COMP ;Handle reti; rjmp ADC ;Handle .def AP =R16 .def ZAEHLER =R17 .org 12 ;--------------------------------------------------------------- ; Subroutine EEPROM_Write ; schreibt den Wert in AP in die EEPROM-Adresse in YL ;--------------------------------------------------------------- EEPROM_Write: sbic EECR, EEWE; EEWE-bit pollen, bis Schreibzugriff fertig rjmp EEPROM_Write out EEDR,AP out EEAR,YL sbi EECR, EEMWE sbi EECR, EEWE ret ;*********************************************************************** ***** ;* ;* Program EEPROM_test.ASM ;* ;*********************************************************************** ***** Reset: cli; Interrupt aus ldi AP,low(RAMEND) out SP,AP; Stapelzeiger initialisieren clr ZAEHLER write_loop: mov AP, ZAEHLER ldi YL, 0x09 rcall EEPROM_write inc ZAEHLER cpi ZAEHLER, 0x14 brne write_loop ever: rjmp ever Und das produziert wieder den "falschen" Output (siehe Bild). Statt der 0x05 sollte eine 0x13 stehen. Nachdem das notwendigste initialisiert wird, und alles andere (z.B. Interrupts) komplett außen vor bleibt, müßte nach Datenblatt alles ganz normal ablaufen. Aber irgendwo ist da ein Haken, und ich schätze, der liegt in einer Asynchronität oder einer Zeitverletzung, die nicht programmverschuldet ist. Oder womöglich geht der Schreiblogik die Puste in Form einer intern gepufferten Löschspannung aus. Reine Vermutungen. Mal Atmel fragen...
Anbei mal noch das zugehörige .hex-File. Vielleicht möchte ja einer mal das Hexenwerk reproduzieren. Wäre interessant, ob das einer 1:1 nachvollziehen kann.
simulieren klappt, wie ließt du das EEPROM aus? Erweitere mal dein Programm um eine Routine die nach dem Schreiben die Daten aus der gleichen Adresse ausließt und es über eine LED-Ausgabe auf den 2ten Port wiedergibt 5 LEDs sollten ja reichen für deine x13 oder b10011. Hast du dir mal die Atmel Notiz durchgelesen die ich zitiert habe? Vielleicht ist das genau dieses Problem.
> Mal Atmel fragen...
Ist nicht nötig.
Absolut nicht nötig!
Es REICHT VÖLLIG AUS, das Datenblatt, Timingdiagramme zu lesen.
Und zwar nicht im Daumenkino, mal so rasch nebenbei.
Sondern einfach mal konkrete und in Ruhe.
Dann klappt das.
Ganz besonders mit dem ASM-Code, den ich vor Tagen schon in diesem
Beitrag gepostet hatte. Der ist (tausenfach!) getestst, verifiziert, den
Atmel-Daten konform, und kein Gegenstand irgendwelcher Debatten!
Läuft in ca. 2000 Anlagen störungsfrei, jeden Tag der Woche.
Lesen hilft!
Raten hilft nicht!
Glaskugeln helfen nicht!
Vermutungen von Tante Else um die Ecke helfen nicht!
Selten dümmliche Debatten helfen nicht!
Es hilft am Ende NUR lesen!
Dann wird auch klar, dass im EE-Schreibzugriff Interupts STÖREND SIND!
Was auch im Datenblatt nachzulesen wäre...
Joe
Ich würde jetzt mal ein Byte ins EEprom schreiben und dann schauen, was ausgegeben wird. Wie funktioniert das mit dem memory dump?
Gewisse Probleme mit dem EEPROM sind bekannt: Beitrag "EEprom Schreib / Lese Problem ?" Zitat aus http://www.rn-wissen.de/index.php/Avr-gcc/Interna: "Manche AVRs haben einen Silicon-Bug, der bei Verwendung der EEPROM-Adresse 0 zu Fehlern führt." Zitat aus http://www.rn-wissen.de/index.php/AVR-Errata: "So wie alles und jedes auf der Welt sind auch AVR-Mikrocontroller nicht frei von Fehlern: sie haben Silicon Bugs bzw. Hard-Bugs, also Fehler, die etwa durch den Herstellungsprozess oder mangelhaftes Chip-Design verursacht werden." Ich habe ein eigenes Testprogramm in Assembler geschrieben und damit die von Dir beschriebene Störung reproduzieren können. Die Kandidaten waren vier ATTiny13V (mit identisch aussehender Aufschrift "0707/ATTINY13V/10PU") und ein Attiny25V ("0630/ATTINY25V/10PU"). Bei zwei der vier ATtiny13 funktioniert die EEPROM-Adresse 0 nicht korrekt. Bei den übrigen Controllern und allen anderen Adressen konnte ich dagegen keine Auffälligkeiten feststellen. Die fehlerhaften Adresse-0-EEPROM-Zellen "vergessen" anscheinend manchmal den Wert, mit dem sie beschrieben wurden. Gelesen wird dann ein anderer Wert. Mehrfaches Lesen ändert den Wert nicht, aber das erneute Beschreiben der Zelle mit dem ehemaligen Wert zeitigt meistens einen anderen falschen Lesewert. Auf der Errata-Seite wird als Ursache des Bugs das Zusammenbrechen der Spannung vermutet. Dies widerlegen die Tests mit meinem Programm jedoch. Allerdings scheint der "Gedächtnisverlust" einer fehlerhaften Adresse-0-EEPROM-Zelle mit sinkender Betriebsspannung häufiger und schwerer zu werden. Eine interessante Frage wäre, ob das Problem beim ATtiny13A, der laut ATMEL "optimized version" des ATtiny13 [1], auch auftritt. Wäre wünschenswert, wenn der in der Appnote erwähnte neue Herstellungsprozess es gleich mitgelöst hätte. ----------------------- [1] Zitat aus der Appnote "AVR520: Migrating from ATtiny13 to ATtiny13A", erster Satz unter Introduction: "In order to optimize the manufacturing process and to further reduce current consumption, an optimized version of ATtiny13 has been introduced. [...] the manufacturing process is not the same [...]" Wer das Programm laufen lassen will, sollte die Hinweise im Kasten ganz oben beachten. Das Prog beschreibt die EEPROM-Zellen an den Adressen 0 und 1 mit bestimmten Werten, liest danach die Zellen wieder aus und gibt die ausgelesenen Werte seriell im Format "8N1" mit 9600 Baud aus. Dadurch hat man Einblick in das Geschehen ohne den Controller resetten zu müssen. Als Terminalprogramm auf dem PC habe ich TeraTerm genommen, aber es geht auch mit Hyperterminal oder jedem anderen. Die EEPROM-Testwerte sind zunächst 00 FF 55 AA 0F F0 00 FF 00 FF 33 CC 0F F0 00 FF und danach alle von 00 bis FF. Die Datei ATtiny13_Buggy_EEPROM.txt enthält eine Ausgabe des Programms bei fehlerhaftem Controller.
@Thomas: Das EEPROM lese ich mit PonyProg aus, das sollte so korrekt sein. Die App-Note hab ich mir durchgelesen, hab aber nichts Neues entdeckt. Auf welches Problem sprichst du an? @Joe: Datenblatt und App-Note habe ich gelesen, Timing-Diagramme gibt's keine dazu. Gegenfrage: Hast du mal meinen Code gelesen? Was ist daran anders als an deinem? Bzw. was ist daran grundsätzlich falsch? Welche Zeile, welcher Befehl? Das wäre mal eine Antwort, die mir wirklich helfen würde, nicht das spitze Komödiengeschreibe außenrum was denn hilft und was nicht. Kannst du mir vielleicht eine Antwort darauf geben, ob es essentiell ist, zuerst das EEDR zu schreiben, dann das EEAR oder umgekehrt? Ich bin ja begeistert wenn du ganze 2000 Anlagen damit laufen hast. Gegenfrage: Schreiben die auch 20mal unmittelbar hintereinander auf die gleiche Adresse? Manchmal kommt's mir so vor, als wenn die Leute im Daumenkinoverfahren, so schnell mal nebenbei den Beitrag lesen, und dann nicht begreifen, um welches spezielle Problem es geht. Und nochmal für die, die es nicht schon x-mal weiter oben gelesen haben: Ich habe SONST auch kein Problem mit dem EEPROM! @Vuvuzelatus: Dein Beitrag trifft vielleicht nicht ganz des Pudels Kern, da ich ja nicht auf Adresse 0 schreibe (oh Gott, noch eine Mausefalle...). Aber gut zu wissen, daß offenbar Silicon-Bugs nicht ausgeschlossen sind.
Hallo, dein Problem wird sein das der Controller zum auslesen von Ponyprog nach dem 1. Reset noch mal losläuft und erst mit einem 2. oder ... Reset mit dem auslesen des EEProm begonnen wird. Dadurch beginnt die Schreibschleife wieder neu, und wird dann bei 5 unterbrochen! Mach vor begin der Schreibschleife eine Pause oder starte per Taster. Sascha
ich meine diese Notiz: Note: If the code initiates a write to EEPROM shortly after Reset, keep in mind the following: If EEPROM contents are programmed during the manufacturing process, the MCU might change the code shortly after programming. When the programmer then verifies the EEPROM contents, this might fail because the EEPROM contents have already been modified by the MCU. Also notice that some In-System Programmers will allow the MCU to execute a short time between each step in the programming and verification process Wobei ich es nicht ganz verstehe. Ein Problem scheint es zu sein wenn man gleich nach dem Reset loschreibt. Probiere mal ne längere Warteschleife. Der letzte Satz sagt das manche Programmer (evtl. dein Bastelladapter und Ponyprog Software) der MCU erlauben einzelne Befehle auszuführen und erst dann das EEPROM auslesen was zu Änderung der Daten führen kann. Wenn ich mal etwas mehr Zeit habe probier ichs mit einem ATTiny26 und einem ATM16 aus. Vielleicht hat dein AVR aber auch eine defekte Speicherstelle im EEPROM, probier doch mal eine andere Adresse aus.
Hier kommt die Auflösung des Rätsels : The Oscar goes to Sascha Weber and Thomas O.!!!!! Ich habe zuerst "Schreiben nach Taste" ins Programm gebaut, nach Erfolg dann einfach eine ausreichend lange Warteschleife vor der Schreibschleife. Nachmessen mit dem Oszi hat dann auch klar gezeigt, daß PonyProg tatsächlich zweimal den Reset betätigt, wenn man einmal das EEPROM auslesen möchte. Dazwischen ist eine Lücke von 100ms, in der der Controller läuft. Und das reicht für x-mal Schreiben, je nach Start-up-Time, Code etc. Wenn ich die Warteschleife von 0 ab verlängert habe, hat sich auch gezeigt, daß der Wert in der Speicherstelle immer kleiner wurde, bis dann nach "0x00" die erwartete "0x13" stand. Logisch und schlüssig ist dann auch, daß ohne Warteschleife im Programm bei "Write_EE(a,a)" alle Speicherzellen von 0 bis 19 erwartungsgemäß beschrieben und ausgelesen werden, da diese ja jeweils nur mit dem selben Wert überschrieben werden, und wie weit der Controller damit in der 100ms-Lücke kommt, sieht man ja nicht. Das "Problem" liegt also nicht - an einem Code, der nicht 100%ig genau dem aus dem Datenblatt entspricht (es geht auch ein bißchen anders ;-)) - an falschen Compiler-Optimierungen - an Versorgungsspannungsproblemen, - ... etc. (s.o.) sondern in meinem Fall einzig und allein an dem Doppel-Reset des Programmers. Das Programm von Peter Z. läuft bei mir wahrscheinlich auch deshalb, weil da ja wie beschrieben andere/mehr Initialisierungen drin sind, die anscheinend die Zeitspanne zwischen den beiden Resets überbrückt. Ich hab den Assembler-Code nicht weiter analysiert. Damit findet der Beitrag sein entspanntes Ende in der Auflösung. Vielen Dank an alle, die unvoreingenommen und neutral mitgedacht und konstruktive Beiträge geliefert haben. Spezieller Dank nochmal und Gratulation an Sascha und Thomas.
...wie in einem anderen Beitrag erwähnt kann aber auch der Compiler ein Problem sein.
gut das sichs geklärt hat, schreib mal dem Programmierer von Ponyprog von dem Problem, vielleicht kann das mit dem doppelten Reset ja behoben werden. Kannst du genauere Infos zu den Resetimpulsen geben? Wie lange ist z.B. die Pause dazwischen, oder wann beginnt die Pause nach dem ersten Mal Reset?
@Rainer: Ja, durchaus. Deswegen hab ich extra "in meinem Fall" geschrieben. Möglich ist vieles und den Unterschied hat man ja bei Peter Z.'s Compilierung gesehen. @Thomas: Die Idee ist zwar nicht schlecht, ich denke aber, daß das mit den zwei Resets nicht ganz unbeabsichtigt ist, sondern eher daher rührt, daß beim ersten Zugriff die Identifizierung des Bausteins durchgeführt wird, dann nimmt "man" den Reset nochmal weg um beim zweiten Zugriff "vom Start weg" (Initialzustand) die Befehlssequenz für das Auslesen des EEPROMs zu schicken. Wie schon geschrieben, ist zwischen den beiden Zugriffen eine Pause von 100ms "Freilauf", die womöglich auch von den Einstellungen im .ini-File von PonyProg (SPIBusSpeed=[NORMAL|SLOW|FAST|...]) abhängt, das habe ich noch nicht überprüft. Ist auch nicht so wichtig für mich, da ich den kompletten Zusammenhang jetzt verstanden habe und "bei Bedarf" entsprechend darauf reagieren kann, bzw. das entsprechende Verhalten des Controllers berücksichtigen kann. Frohe Ostern!
Hallo Micha, ja stimmt hatte ein Delay vor der Main... Hatte den gleichen Fehler als ich alles andere rausgenommen hatte. Naja hat sich ja alles noch aufgeklärt ^^ Sorry für die verspätete Rückmeldung...
Hi Peter, find ich echt gut, daß du dich nochmal gerührt hast. So fügen sich die sämtlichen Teile zu einem passenden Ganzen. Cool! Demnach müßtest du aber auch einen Programmer haben, der 2 Zugriffe mit Resets macht und den Controller dazwischen eine Zeitspanne laufen läßt. Aber du verwendest nicht PonyProg, oder war's der Compiler der verschieden war?
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.