Forum: Mikrocontroller und Digitale Elektronik EEPROM Schreibfrequenz auf eine Adresse


von Micha (Gast)


Lesenswert?

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.

von Peter Z. (hangloose)


Lesenswert?

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
{

von Micha (Gast)


Lesenswert?

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.

von Guest (Gast)


Lesenswert?

Ein EEPROM ist nicht dazu da im ms Takt darauf zu schreiben, die Anzahl 
der Schreibzyklen ist begrenzt, so bekommst du das bald kaputt.

von Peter Z. (hangloose)


Lesenswert?

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 :-/

von Peter Z. (hangloose)


Lesenswert?

...aber vielleicht solltest ja du mal genau hinschauen ;-)

von Micha (Gast)


Lesenswert?

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.

von Εrnst B. (ernst)


Lesenswert?

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?

von Peter Z. (hangloose)


Lesenswert?

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.

von Micha (Gast)


Lesenswert?

@ 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.

von Guest (Gast)


Lesenswert?

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.

von Εrnst B. (ernst)


Lesenswert?

@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.

von Micha (Gast)


Lesenswert?

@ 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.

von Gustl (Gast)


Lesenswert?

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

von Micha (Gast)


Lesenswert?

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...

von Jan S. (jan_s)


Lesenswert?

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?

von Micha (Gast)


Angehängte Dateien:

Lesenswert?

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!

von Rainer (Gast)


Lesenswert?


von Peter Z. (hangloose)


Angehängte Dateien:

Lesenswert?

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
}

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

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?

von Peter Z. (hangloose)


Lesenswert?

Nö, das Bit heißt beim Tiny26 nur anders als beim Tiny13...

von Micha (Gast)


Lesenswert?

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...

von Micha (Gast)


Lesenswert?

Peter, kannst du mir zusätzlich einfach mal DEIN Hexfile schicken? (die 
ultimaiv krasse Methode...). Danke!

von Peter Z. (hangloose)


Angehängte Dateien:

Lesenswert?

dann teste mal...

von Micha (Gast)


Lesenswert?

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)?

von nachdenklicher (Gast)


Lesenswert?

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

von Alexander V. (avogra)


Lesenswert?

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

von Peter Z. (hangloose)


Lesenswert?

Kann ich dir leider erst am Montag schicken wenn ich wieder am PC in der 
Arbeit bin...

von Micha (Gast)


Lesenswert?

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.

von Peter Z. (hangloose)


Lesenswert?

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

von Alexander V. (avogra)


Lesenswert?

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

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Micha schrieb:
> Dein Hex funktioniert.

Unterschied könnte noch die Optimierung sein.

Was hast Du? -O, -Os, -O2?
Welchen Optimierungsgrad hat Peter benutzt?

von Rainer (Gast)


Lesenswert?

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"

von Micha (Gast)


Lesenswert?

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!

von Peter D. (peda)


Angehängte Dateien:

Lesenswert?

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

von Micha (Gast)


Lesenswert?

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?

von Thomas (kosmos)


Lesenswert?

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.

von Thomas (kosmos)


Lesenswert?

habe gerade gesehen das man 4 Takte Zeit hat, deswegen dürfen keine 
Interrupts dazwischen funken.

von Micha (Gast)


Angehängte Dateien:

Lesenswert?

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...

von Micha (Gast)


Angehängte Dateien:

Lesenswert?

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.

von Thomas (kosmos)


Lesenswert?

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.

von nachdenklicher (Gast)


Lesenswert?

> 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

von Rainer (Gast)


Lesenswert?

Ich würde jetzt mal ein Byte ins EEprom schreiben und dann schauen, was 
ausgegeben wird.
Wie funktioniert das mit dem memory dump?

von Vuvuzelatus (Gast)


Angehängte Dateien:

Lesenswert?

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.

von Micha (Gast)


Lesenswert?

@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.

von Sascha W. (sascha-w)


Lesenswert?

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

von Thomas (kosmos)


Lesenswert?

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.

von Micha (Gast)


Lesenswert?

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.

von Rainer (Gast)


Lesenswert?

...wie in einem anderen Beitrag erwähnt kann aber auch der Compiler 
ein Problem sein.

von Thomas (kosmos)


Lesenswert?

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?

von Micha (Gast)


Lesenswert?

@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!

von Peter Z. (Gast)


Lesenswert?

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...

von Micha (Gast)


Lesenswert?

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?

von Peter Z. (Gast)


Lesenswert?

Ich verwende den AVRISP mkII mit AVR Studio
Der macht auch 2 mal Reset...

von Thomas (kosmos)


Lesenswert?

auch 100mSek Pause dazwischen?

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.