Forum: Mikrocontroller und Digitale Elektronik Nach den Reset oder Stromausfall verliert EEPROM die Daten


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Sonne M. (dipoko)


Bewertung
0 lesenswert
nicht lesenswert
Hallo Zusammen!

Ich nutze den AT90CAN128 als MCU, der mit 16 MHz arbeitet, um Signale 
über ein CAN Interface PCAN-USB auf den CAN-Bus sowohl zu lesen als auch 
zu schreiben. Mit Hilfe einer Bedienugsoberfläche werden folgende IDs 
initialisiert:

 NodeID, cmdID und StatusID

Von der Simulation bis zum Betrieb läuft alles perfekt und einwandfrei. 
Die Daten können gesendet und empfangen werden, parallel können Weitere 
Controller ins Netzwerk eingefügt werden und auf Befehle gut reagieren.

Problem: bei jedem Stromausfall oder Reset verliert der EEPROM sein 
Name, da keine Steuerung nicht mehr möglich ist.
Das neue Initialisieren des IDs ist dann notwendig, um die Anlagen über 
den Controller zu steuern. Dieses Verhalten ist ähnlich für alle 
Controller.Das ist ziemlich antrengend jedes Mal die IDs für jeden 
Controller zu intialisieren.

Übrigens die MCU muss nicht neu programmiert werden.

Hardwaremäßig haben wir, unter AVR Studio, das BODLEVEL auf  4.0 V 
gesetzt, ursprünglich war es bei 2.5 V (empfohlen ist diesen Wert so 
hoch wie möglich zu stellen). Am Ausgang liegt ein Elko mit 100 uF, der 
für einen langsamen Abfall der Spannung sorgt. Die Spannungsversorgung 
ist stabil.
Weiter unter AVR Studio, das Aktivieren der Funktion „EESAVE“ hat keine 
Änderung gebracht.

Vielleicht hat jemand ein Idee oder Erfahrung mit solch einem Problem 
gemacht. Ich wäre für weitere Hilfe dankbar.


Grüß Sonne


PS: Für die Anregungen habe ich nur der Programmsteil eingefügt (siehe 
unten), der die EEProm-Routine betrifft.
1
// eeprom informations
2
#define DATA_VERSION            0x0001    // data version
3
#define EEPROM_DATA_HEADER_POS  0x0010    // start at address 0x10
4
5
// struct for eeprom data
6
typedef struct
7
{
8
UINT16    version;
9
BYTE      node_id;    
10
UINT16    cmd_id;  
11
UINT16    status_id; 
12
} EEPROM_DATA_HEADER;
13
14
// Callback function defines
15
typedef CHAR (CAN_CallBack)(UINT16 nID, BYTE btLength);
16
17
// Global Parameter
18
#ifdef     MAIN
19
#define   EXTERN
20
#else
21
#define   EXTERN   extern
22
#endif 
23
EXTERN   EEPROM_DATA_HEADER  m_DataHeader;
1
#include <inttypes.h>
2
#include <avr/io.h>
3
#include <avr/interrupt.h>
4
#include <avr/eeprom.h>
5
#include <string.h>
6
#include <stdio.h>
7
#include <util/delay.h>
8
9
// Hauptprogramm 
10
INT16 main(void)   
11
{
12
main_Initialize();
13
  while(1)
14
  {
15
  //--- do something
16
  //--- delay for 25 ms 
17
  delay_ms(1);
18
  }
19
//--- write the status to the master 
20
can_WriteStatus(m_DataHeader.node_id, m_uint16CurrentSpeed, status, m_uint16Debug);
21
22
  //--- the end of the main program 
23
  return 0;
24
}
25
26
INT16 main_Initialize(void)
27
{
28
asm("cli");                  //--- switch of the interrupts 
29
//--- reset all ports
30
//--- set the port direction 
31
main_LoadEEProm();           //--- load the eeprom data
32
main_SaveEEProm();
33
timer_Init();                //--- init the timer
34
Init_CAN(500000);            //--- init the can bus
35
can_InitProtocol (m_DataHeader.node_id);
36
interrupt_Init();            //--- init interrupt
37
asm("sei");                  //--- switch the interupts back on
38
return 0;
39
}
40
41
VOID main_LoadEEProm()
42
{
43
// read the eeprom data 
44
eeprom_read_block(&m_DataHeader, (void*)EEPROM_DATA_HEADER_POS, sizeof(EEPROM_DATA_HEADER));
45
46
//--- check if version is correct, if not write a default setting 
47
if ( m_DataHeader.version == DATA_VERSION )   
48
49
  {
50
  return;
51
  }
52
53
//--- version is not valid, build a default data header and parameterset
54
55
m_DataHeader.version    =  DATA_VERSION;  // set valid version
56
m_DataHeader.node_id    =  1;             // set node id 1
57
m_DataHeader.cmd_id     =  10;            // set cmd id
58
m_DataHeader.status_id  =  11;            // set status id
59
60
main_SaveEEProm();                        //--- save the eeprom data 
61
}
62
63
VOID main_SaveEEProm()
64
{
65
eeprom_busy_wait();
66
eeprom_write_block(&m_DataHeader, (void*)EEPROM_DATA_HEADER_POS, sizeof(EEPROM_DATA_HEADER));
67
}
68
69
VOID main_SaveMotorData(BYTE byteNodeID, UINT16 cmdID, UINT16 statusID)
70
{
71
72
//--- clear the old status register  
73
can_WriteStatus(m_DataHeader.node_id, 0, 0, 0);   
74
  
75
//--- set the new node id 
76
m_DataHeader.node_id   = byteNodeID;
77
m_DataHeader.cmd_id    = cmdID;
78
m_DataHeader.status_id = statusID;
79
80
main_SaveEEProm();
81
}

: Bearbeitet durch Moderator
von Frank K. (fchk)


Bewertung
0 lesenswert
nicht lesenswert
Sonne Madiba schrieb:

> Problem: bei jedem Stromausfall oder Reset verliert der EEPROM sein
> Name, da keine Steuerung nicht mehr möglich ist.

Wie habt Ihr das überprüft? Habt Ihr einen JTAG Debugger, mit dem Ihr 
den Systemstart im Einzelschritt durchsteppen könnt? Wenn nein, besorgt 
Euch einen!

fchk

von Sonne M. (dipoko)


Bewertung
0 lesenswert
nicht lesenswert
Hallo Frank. K
Danke für die schnelle antwort

>> Problem: bei jedem Stromausfall oder Reset verliert der EEPROM sein
>> Name, da keine Steuerung nicht mehr möglich ist.
>
> Wie habt Ihr das überprüft? Habt Ihr einen JTAG Debugger, mit dem Ihr
> den Systemstart im Einzelschritt durchsteppen könnt?
Ich habe leider keinen JTAG Debugger, wie schon erwähnt solange die 
Versorgung vorhanden ist, läuft die Anlage einwandfrei.

warum sollen bei jedem Reset oder Stromausfall die IDs neu 
initialisieren werden?

Deswegen bitte ich um Hilfe. Ich habe nur begrenzte Kenntnisse in 
Software Sachen.

Grüß Sonne

von Quack (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Sonne Madiba schrieb:
> PS: Für die Anregungen habe ich nur der Programmsteil eingefügt (siehe
> unten), der die EEProm-Routine betrifft.

Hast du auch mal nur mit diesem Teil das Problem reproduiert? Wie 
belegest du, dass a) die Daten mal im EEPROM waren und b) sie nicht mehr 
im EEPROM sind?

von Karl H. (kbuchegg) (Moderator)


Bewertung
0 lesenswert
nicht lesenswert
Das Zurückschreiben hier
1
int main()
2
{
3
...
4
  main_LoadEEProm();           //--- load the eeprom data
5
  main_SaveEEProm();
6
...

ist keine gute Idee. Was soll das bringen? WEnn das EEPROM korrekt 
gelesen werden konnte, dann schreibst du maximal das zurück, was du 
sowieso schon gelesen hast. Besser wäre es, den entsprechenden 
Schreibaufruf in die Leseroutine zu integrieren, und dann aufzurufen, 
wenn die Leseroutine bemerkt, dass sie keine gültigen Daten lesen 
konnte.

Denn: Beim EEPROM hast du nur eine relativ kleine beschränkte Menge an 
Schreibvorgängen. D.h. du willst nur dann ins EEPROM schreiben, wenn es 
auch notwendig ist. Konnte alles einwandfrei vom EEPROM gelesen werden, 
dann ist es nicht notwendig zu schreiben.


Hast du denn irgendeine Form der Ausgabe zur Verfügung? LCD, Terminal, 
irgendwas? Damit man sich mal in Byteform ansehen kann, was denn vom 
EEPROM gelesen wurde. Ist es nur ein einzelnes Byte, in dem vielleicht 
ein einzelnes Bit gekippt wurde? Oder ist das ganze EEPROM in Unordnung?

von oszi40 (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Kaputte Befehle funktionieren nicht. Ob das Programm schon alles sauber 
geschrieben hat BEVOR der Strom weg ist? WO erkennt Deine Schaltung 
rechtzeitig, daß in einigen ms der Strom weg ist? Reicht der Sieb-Elko 
im Netzteil dafür?

von Oliver (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Sonne Madiba schrieb:
> Hardwaremäßig haben wir, unter AVR Studio, das BODLEVEL auf  4.0 V
> gesetzt,

Habt ihr auch den Brownout-Reset aktiviert?

Oliver

von (prx) A. K. (prx)


Bewertung
0 lesenswert
nicht lesenswert
Man kann das EEPROM m.W. auch per Programmer auslesen. Wär vielleicht 
mal sinnvoll, konkrete Unterschiede vorher/nacher festzustellen. Jeder 
Programmiervorgang per ISP enthält übrigens einen Reset.

: Bearbeitet durch User
von Εrnst B. (ernst)


Bewertung
0 lesenswert
nicht lesenswert
Du schreibst recht oft ins EEPROM. Zweimal beim Init, einmal bei jeder 
CAN-Message?
=> Ungünstig, das EEPROM verträgt nur eine begrenze Anzahl von 
Schreibzyklen.

Dann: Weil du so oft schreibst, und jedem Schreibvorgang ein Erase 
vorausgeht: Wenn der Strom/BOD genau dazwischen zuschlägt, ist der 
Datenblock (das gerade geschriebene Byte) futsch.

ggfs. mit zwei kopien deines Structs im EEPROM arbeiten, wenn eine 
kaputt ist, die andere Nehmen.

Und: Wenn schon so oft geschrieben wird, evtl eeprom_update_block statt 
write_block verwenden, um die Hardware zu schonen.


Wenn's das alles nicht ist:
Speicherverbrauch checken! Nicht das im nicht-gezeigten Codeteil der 
Stack überläuft, und dir deine Config-Daten überbügelt.

von (prx) A. K. (prx)


Bewertung
0 lesenswert
nicht lesenswert
Werte, die häufig geschrieben werden, sollte man besser als Kombination 
aus Seriennummer und Inhalt in einem Array als Rundumspeicher schreiben.

Beim Init sucht man darin die höchste S/N und inkrementiert diese für 
jeden neuen Eintrag, der im nächsten Array-Slot landet. Am Ende des 
Arrays wieder vorne anfangen.

Wenn man diesen Einträgen dann noch eine CRC8 hinzu fügt, dann kann man 
eindeutig den letzten korrekt gespeicherten Wert finden. Und die 
einzelnen EEPROM-Zellen werden viel weniger beansprucht.

: Bearbeitet durch User
von (prx) A. K. (prx)


Bewertung
0 lesenswert
nicht lesenswert
Εrnst B✶ schrieb:
> ggfs. mit zwei kopien deines Structs im EEPROM arbeiten, wenn eine
> kaputt ist, die andere Nehmen.

Wobei dann eine Prüfsumme/CRC als Teil des jeweiligen Inhalts sehr 
sinnvoll ist. Und zwar am Schluss der beiden Structs.

: Bearbeitet durch User
von Karl H. (kbuchegg) (Moderator)


Bewertung
0 lesenswert
nicht lesenswert
Auf jeden Fall wäre es jetzt erst mal wichtig, ein komplettes Programm 
zu haben, welches tatsächlich genau so auf dem Mega läuft und bei dem 
das beschriebene Verhalten auftritt.
Sonst ist das ein Stochern im Nebel, denn es könnte ja auch genausogut 
sein, dass du im nicht gezeigten Code dir selber besagte Struktur 
überschreibst.

von (prx) A. K. (prx)


Bewertung
0 lesenswert
nicht lesenswert
Siehe auch meine Frage nach dem konkreten Inhalt im Fehlerfall. Das wär 
schon ein recht deutliches Indiz.

: Bearbeitet durch User
von Peter D. (peda)


Bewertung
0 lesenswert
nicht lesenswert
Du müßtest ne Warnung kriegen, daß can_WriteStatus() hinter der Mainloop 
toter Code ist.

von Frank K. (fchk)


Bewertung
0 lesenswert
nicht lesenswert
Sonne Madiba schrieb:

>> Wie habt Ihr das überprüft? Habt Ihr einen JTAG Debugger, mit dem Ihr
>> den Systemstart im Einzelschritt durchsteppen könnt?
> Ich habe leider keinen JTAG Debugger, wie schon erwähnt solange die
> Versorgung vorhanden ist, läuft die Anlage einwandfrei.

Klar, dann durchläuft das Programm nicht die Initialisierungsroutine.

> warum sollen bei jedem Reset oder Stromausfall die IDs neu
> initialisieren werden?

Das siehst Du mit einem passenden Debugger. Der spart Dir eine Menge 
Zeit. Ich hoffe, Dein Board hat einen Anschluss für JTAG. Ansonsten 
musst Du den dazufrickeln. Lies das Datenblatt des 90CAN128 und die 
Onlinehilfe in AVR Studio für den jeweiligen Debugger.

Alternativ: Hat das Board einen seriellen Port? Dann hänge einen PC mit 
Terminalprogramm (putty, zoc, zur Not Hyperterm) dran, wo Du 
Debugausgaben machst. Lasse Dir an wichtigen Stellen den EEPROM-Inhalt 
und/oder den Inhalt wichtiger Variablen in Textform auf der seriellen 
Schnittstelle ausgeben. So kannst Du herausfinden, ob Dein Programm 
bestimmte Teile ausführt und den Grund herausfinden. Mit einem 
JTAG-Debugger geht es einfacher, ohne dass Du noch Code schreiben musst, 
aber wenn Du keinen kaufen kannst, dann ist das eine Notlösung.

fchk

von Sonne M. (dipoko)


Bewertung
0 lesenswert
nicht lesenswert
Εrnst B✶ schrieb:
>Du schreibst recht oft ins EEPROM. Zweimal beim Init, einmal bei jeder
>CAN-Message?
richtig
ich kann deine Anregungen leicht nachvollziehen, ich überlege mir 
gerade, wie ich das Ganze anders gestalten kann.


Karl Heinz schrieb:
>Hast du denn irgendeine Form der Ausgabe zur Verfügung? LCD, Terminal,
>irgendwas? Damit man sich mal in Byteform ansehen kann, was denn vom
>EEPROM gelesen wurde. Ist es nur ein einzelnes Byte, in dem vielleicht
>ein einzelnes Bit gekippt wurde? Oder ist das ganze EEPROM in Unordnung?

mit dem controller steuere ich Motoren, die entsprechend auf alle 
Befehle gut und richtig reagieren.

von Sonne M. (dipoko)


Bewertung
0 lesenswert
nicht lesenswert
Peter Dannegger schrieb:
> Du müßtest ne Warnung kriegen, daß can_WriteStatus() hinter der Mainloop
> toter Code ist.

es gibt zum Glück keine Warnung, das Compilieren erfolgt fehlerfrei

von Wilhelm F. (Gast)


Bewertung
0 lesenswert
nicht lesenswert
oszi40 schrieb:

> WO erkennt Deine Schaltung
> rechtzeitig, daß in einigen ms der Strom weg ist?

Gern gemachte Fehler bei externem EEPROM und batteriegepuffertem SRAM, 
das Schaltungsdesign. Die Pins am EEPROM können mal zufällige Werte 
annehmen, was es als Befehl interpretiert, die es dann zufällig löschen.

In einem Bastelbuch von 1988 habe ich noch eine Uhrenschaltung mit 8051 
und RTC58321. Dort versah man den Enable des Uhrenbausteins wenigstens 
mit einem Handumschalter, um komische Dinge bei Hoch- und Runterfahren 
der Betriebsspannung zu vermeiden.

von Sonne M. (dipoko)


Bewertung
0 lesenswert
nicht lesenswert
Karl Heinz schrieb:
> Auf jeden Fall wäre es jetzt erst mal wichtig, ein komplettes Programm
> zu haben, welches tatsächlich genau so auf dem Mega läuft und bei dem
> das beschriebene Verhalten auftritt.
> Sonst ist das ein Stochern im Nebel, denn es könnte ja auch genausogut
> sein, dass du im nicht gezeigten Code dir selber besagte Struktur
> überschreibst.

Das komplettes Programm hat insgesamt 13 Dateien, für eine bessere 
Analyse kann ich dir das zuschicken.

von Reinhard Kern (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Hallo,

ich finde die ganze Diskussion dreht sich um ein falsches Problem. IDs 
sind doch im Prinzip für die Lebensdauer einer Steuerung einzugeben, 
d.h. sie ändern sich selten oder nie. Sowas speichert man im EEProm 
unmittelbar nach der Eingabe und rührt dann erst wieder dran, wenn sie 
geändert werden müssen, also so gut wie nie.

Solche Parameter erst wegzuschreiben wenn der Strom schon ausgefallen 
ist, ist eine absolute Schnapsidee und alle Probleme damit sind 
überflüssig.

Gruss Reinhard

von Peter D. (peda)


Bewertung
0 lesenswert
nicht lesenswert
Sonne Madiba schrieb:
> es gibt zum Glück keine Warnung, das Compilieren erfolgt fehlerfrei

Fehlerfrei Compilieren ist nur eine ganz klitze kleine Voraussetzung für 
fehlerfrei Funktionieren.

Entweder Du hast die Warnung überlesen oder den Warning-Level zu niedrig 
gesetzt oder irgendwas zufälliges gepostet, nur nicht den exakten Code.
Alles kein Grund, Glück zu vermuten.

Das einzige, was der Compiler ohne Warnung hinter der Mainloop 
wegoptimiert, ist das "return 0;".

von Sonne M. (dipoko)


Bewertung
0 lesenswert
nicht lesenswert
Reinhard Kern schrieb:
> Hallo,
>
> ich finde die ganze Diskussion dreht sich um ein falsches Problem. IDs
> sind doch im Prinzip für die Lebensdauer einer Steuerung einzugeben,
> d.h. sie ändern sich selten oder nie. Sowas speichert man im EEProm
> unmittelbar nach der Eingabe und rührt dann erst wieder dran, wenn sie
> geändert werden müssen, also so gut wie nie.
>
> Solche Parameter erst wegzuschreiben wenn der Strom schon ausgefallen
> ist, ist eine absolute Schnapsidee und alle Probleme damit sind
> überflüssig.
>
> Gruss Reinhard
Danke sehr für diesen Beitrag. Ich denke damit ist das Problematik für 
weitere Anregungen klar.
Es geht um Speicherung von IDs, die nach dem Reset wideraufrufbar sein 
sollen.

schöne Grüße Sonne

von Karl H. (kbuchegg) (Moderator)


Bewertung
0 lesenswert
nicht lesenswert
Sonne Madiba schrieb:
> Karl Heinz schrieb:
>> Auf jeden Fall wäre es jetzt erst mal wichtig, ein komplettes Programm
>> zu haben, welches tatsächlich genau so auf dem Mega läuft und bei dem
>> das beschriebene Verhalten auftritt.
>> Sonst ist das ein Stochern im Nebel, denn es könnte ja auch genausogut
>> sein, dass du im nicht gezeigten Code dir selber besagte Struktur
>> überschreibst.
>
> Das komplettes Programm hat insgesamt 13 Dateien, für eine bessere
> Analyse kann ich dir das zuschicken.

Brauchst du gar nicht.
Ihr habt Probleme. Also finde ich es auch für euch zumutbar, euer 
Programm so abzuspecken, dass es dieses Verhalten und sonst nichts 
anderes zeigt.

Du hast die Hardware vor dir. Schreib ein Programm welches den EEPROM 
ausliest und irgendwie signalsiert ob die WErte noch vorhanden sind oder 
nicht. ABgesehen davon gibt es in diesem Testprogramm SONST NICHTS!

Und dann machst du einen Reset.
Wenn dann die Werte immer noch vorhanden sind, dann wird das Problem 
wohl anderweitig zu suchen sein. Sind die Werte aber weg, dann hast du 
ein kleines Programm von vielleicht 50 oder 60 Zeilen, mit dem man mal 
untersuchen kann, was da vor sich geht - unabhängig von allem anderen im 
Original-Programm, welches für diese Analyse erstmal nur weitere 
(unbekannte) Fehlerquellen darstellt.

von Wilhelm F. (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Karl Heinz schrieb:

> Du hast die Hardware vor dir. Schreib ein Programm welches den EEPROM
> ausliest und irgendwie signalsiert ob die WErte noch vorhanden sind oder
> nicht. ABgesehen davon gibt es in diesem Testprogramm SONST NICHTS!

So wie es aus sieht, ist er aber der Servicetechniker, nicht der 
Programmierer.

Hier:

Sonne Madiba schrieb:

> Deswegen bitte ich um Hilfe. Ich habe nur begrenzte Kenntnisse in
> Software Sachen.

von Karl H. (kbuchegg) (Moderator)


Bewertung
0 lesenswert
nicht lesenswert
Wilhelm F. schrieb:
> Karl Heinz schrieb:
>
>> Du hast die Hardware vor dir. Schreib ein Programm welches den EEPROM
>> ausliest und irgendwie signalsiert ob die WErte noch vorhanden sind oder
>> nicht. ABgesehen davon gibt es in diesem Testprogramm SONST NICHTS!
>
> So wie es aus sieht, ist er aber der Servicetechniker, nicht der
> Programmierer.

Dann muss er sich eben mit seinem Programmierer kurzschliessen, dass sie 
gemeinsam auf Fehlersuche gehen.
Was sollen wir hier aus der Ferne diagnostizieren, wo in 1000 Zeilen 
Code, von denen wir 950 nicht sehen, ein Problem stecken könnte?
Wohlgemerkt 'könnte', nicht 'muss'.
Aber bei Fehlersuche erst mal irgendetwas grundsätzlich auszuschliessen, 
ist IMHO fahrlässig. Wenn das Programm zu umfangreich ist, um damit eine 
halbwegs verlässliche Hardware-Diagnose machen zu können, dann muss man 
eben abspecken. Das war vor 30 Jahren auch nicht anders als heute.

(das richtet sich mehr an Sonne, als an dich Wilhelm. Nicht falsch 
verstehen)

: Bearbeitet durch Moderator
von Oliver (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Auch wenn ich mich jetzt wiederhole, solange diese Frage nicht mit ja 
beantwortet wird, erübrigen sich alle weiteren Diskussionen:

Oliver schrieb:
> Habt ihr auch den Brownout-Reset aktiviert?

Oliver

von Sonne M. (dipoko)


Bewertung
0 lesenswert
nicht lesenswert
Oliver schrieb:
> Auch wenn ich mich jetzt wiederhole, solange diese Frage nicht mit ja
> beantwortet wird, erübrigen sich alle weiteren Diskussionen:
>
> Oliver schrieb:
>> Habt ihr auch den Brownout-Reset aktiviert?
>
> Oliver

der Browout-Reset ist aktiviert

>So wie es aus sieht, ist er aber der Servicetechniker, nicht der
>Programmierer.

Ich bin zwar ein Hardwareentwickler, zum Thema Programmierung hatte ich 
C-Grundlagen Kenntnisse. Inzwischen  habe ich viel über C für 
Mikrocontroller gelesen und verschiedenen Tutorium gemacht. Ich habe 
sogar das Programm erweitern können, ohne die Grundfunktion zu stören. 
Natürlich habe ich noch viel zu lernen. Probieren und Übungen machen den 
Meister.

Der Programmierer ist leider nicht mehr erreichbar, ich muss das  Ganze 
nachvollziehen und meistern, Das will ich auch

von Wilhelm F. (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Karl Heinz schrieb:

> Dann muss er sich eben mit seinem Programmierer kurzschliessen, dass sie
> gemeinsam auf Fehlersuche gehen.
> Was sollen wir hier aus der Ferne diagnostizieren, wo in 1000 Zeilen
> Code, von denen wir 950 nicht sehen, ein Problem stecken könnte?

Eben, genau das.

von Daniel (Gast)


Bewertung
0 lesenswert
nicht lesenswert
ganz wichtig ist das Datenblatt genau zu prüfen.

Oft passiert es trotz BOD dass ein Flash/EEPROM überschrieben / 
teilweise gelöscht wird und das unabhängig vom MCU. Und zwar genau dann 
wenn die MCU außerhalb der Spec betrieben wird. Ein plötzlicher 
Stromausfall ist eine sehr häufige Ursache welche eine MCU in 
nicht-spezifizierten Bereich legt.
Speziell im Falle wenn die Spannung nicht ganz unter einen bestimmten 
Wert fällt, beim AT90CAN128 muss z.b. der Wert Vpot falling 
unterschritten werden BEVOR die Spannung wieder über dem BOD level 
anliegt. Ist das nicht der Fall, entscheidet der Zufall was dann 
passiert.

The Power-on Reset will not work unless the supply voltage has been 
below VPOT (falling). Power on Reset und BOD sind immer sehr verzahnt.

Sehr typisch bei Solar betriebenen Anwendung, fällt die Spannung ab, 
greift ja zunächst der BOD und kein Code wird ausgeführt. Fällt die noch 
weiter runter, aber nie unter den Vpot fallling wert, sondern geht kurz 
vorher wieder hoch (da ja nun plötzlich die Sonne scheint), wurde somit 
die Spec gebrochen, da die Spannung ja nie unter VPot falling ging.

Ich würde in solchen Fällen also immer den Spannungsverlauf prüfen, bei 
manchen MCU ist der Vpot falling recht nah an 0, bei anderen ist dieser 
Wert großzügig. Je näher an 0, desto schwieriger einen vernünftigen 
Reset nach Spec durchzuführen. Je nachdem wie lange der Stromausfall 
dauerte, und wie die Zeit zum Teil überbrückt wurde, kann dies ein Grund 
für EEPROM/FLash corruption sein.

von Karl H. (kbuchegg) (Moderator)


Bewertung
2 lesenswert
nicht lesenswert
Sonne Madiba schrieb:

> Der Programmierer ist leider nicht mehr erreichbar, ich muss das  Ganze
> nachvollziehen und meistern, Das will ich auch


Gut.
Dann akzeptier bitte, dass man zur Fehlersuche auch schon mal ein 
eigenes Testprogramm schreibt, in dem es sich nur um einen bestimmten 
Problemkreis dreht.
Du hast das EEPROM in Verdacht. Wie immer gibt es 2 Möglichkeiten: 
Hardware oder Software.
Aus deiner Fehlerbeschreibung geht nicht hervor, dass man irgendeines 
der beiden ausschliessen könnte. Du weisst noch nicht einmal 100% 
sicher, dass sich der EEPROM Inhalt tatsächlich geändert hat. Du denkst 
nur, dass das so ist (und das mag durchaus auch begründet sein), weil 
die 'Steuerung dann nicht mehr reagiert'. OK, das mag so sein, dass das 
aufgrund verloren gegangener Id genau so ist. Aber 100% sicher weißt du 
es nicht. Ergo: Das muss man erst mal nachsehen, ob diese Annahme 
stimmt.

Grundsätzlich: Das wahrscheinlich wichtigste in der Fehlersuche ist es, 
einen Blick hinter die 'Kulissen' zu kriegen. Alles ist dazu tauglich, 
solange es dir das ermöglicht. Das fängt bei einfachen LED an, und geht 
über LCD bis hin zu UART. In deinem Fall kommt noch das Auslesen des 
EEPROMS mittels ISP dazu.
Egal wie, aber eine Ausgabemöglichkeit ist enorm wichtig um Fehler zu 
suchen. Ohne eine solche, wird alles 300 mal so schwer, weil man ständig 
um die Ecke denken muss und mit Hypothesen arbeiten muss.
Daher: Eine Ausgabemöglichkeit muss her!
Und wenn das bedeutet, dass du deinen Motor zwischendruch abklemmst, 
dann ist das so. Wenn das bedeutet, dass du dir mit einem Meter Draht 
eine 'Notverbindung' zu einem LCD an ein paar freien Pins zusammenlötest 
und an die Platine heftest, dann ist das so. Wenn es bedeutet, dass du 
denselben fliegenden Aufbau mit einem MAX232 an eine RS232 machst, dann 
ist das so. Zur Fehlersuche ist alles erlaubt, solange es dem Zweck 
dient und nach Beendgung der Fehlersuche wieder rückgängig gemacht 
werden kann.

Weiters.
Es ist meist recht sinnlos ein komplexes Programm als ganzes auf den 
Prüfstand zu schicken. Du hast den Verdacht, dass dein EEPROM Ärger 
macht. Gut. Dann mach dir ein Testprogramm mit dem du genau das (und nur 
das) testen kannst. Ob der PID Regler für den Motor korrekt arbeitet, ob 
das CAN Interface korrekt arbeitet, das alles ist völlig irrelevant, 
wenn die Fragestellung lautet: Ist mein EEPROM zuverlässig oder nicht? 
Daher: raus damit. In deinem Testcode willst du möglichst wenig 
zusätzliche Komponenten haben, die ihrerseits für Fehler verantwortlich 
sein können. Daher willst du die gleich erst gar nicht in deinem 
Testcode haben. Jaaaaa, das bedeutet, dass man Code schreibt, den man 
nach ein paar Tagen, wenn das Problem behoben ist, oder wenn man davon 
überzeugt ist an der falschen Stelle nach dem Problem zu suchen, 
schlicht und ergreifend wegschmeisst. Gewöhn dich daran. So ist das nun 
mal. Es kommt gar nicht so selten vor, dass man Code auch schon mal 
wegwirft. Nur Anfänger kleben an einmal geschriebenem Code, wie das Pech 
auf der Haut. Für den Profi ist Code nur ein Werkzeug und wenn es nicht 
mehr gebraucht wird, dann wird es eben wieder entfernt. Wenn man 
natürlich für 3 Zeilen Code 2 Stunden vor dem Editor sitzt, dann tut das 
weh - das versteh ich schon. Hilft aber nichts.


Sinn der ganzen Sache ist es, Licht ins Dunkel zu bringen. Jedes Mittel 
das dazu tauglich ist, ist ok. Es wird nichts von vorne herein 
ausgeschlossen. Jede Annahme bleibt solange im Status 'Annahme', bis man 
mit einem Test abgeklärt hat, ob der Fall ausgeschlossen werden kann 
oder nicht. Du hast das EEPROM in Verdacht? ok, dann prüfe es! Ein 
Verdacht oder eine Annahme reicht nicht. Wir wollen Gewissheit.

Und auch das gibt es: das einen Vorversuche, die zu nichts führen, dann 
doch auf eine Idee bringen und dann stellt sich genau diese Idee als 
heiße Spur heraus.

: Bearbeitet durch Moderator
von Sonne M. (dipoko)


Bewertung
0 lesenswert
nicht lesenswert
Hallo,

an alle vielen Dank für eure konstruktiven Beiträge, diesen haben meine 
Neugier geweckt weiter zu forschen, sowohl Hardware-, als auch 
softwaremäßig.

Karl Heinz schrieb:

>Zur Fehlersuche ist alles erlaubt, solange es dem Zweck
>dient und nach Beendigung der Fehlersuche wieder rückgängig gemacht
>werden kann.

Ich werde mir klein Testprogramm schreiben, um die Teilfunktionen zu 
prüfen. Natürlich werde ich auch einen JTAG Debugger einsetzen.

ich werde mich in einem späteren Zeitpunkt wieder melden, wenn ich so 
weit bin.

Bis dahin ist das Thema für mich noch offen.

Grüß Sonne

von Bernd B. (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Hallo Sonne Madiba,

entschuldige bitte, ich habe nur schnell gelesen: Wurde das EEPROM schon 
benannt?

Ich hatte einen ähnlichen Fall vor langer Zeit mit einem EEPROM 24xxx 
(bzw. kompatibel) mit 128 Byte, bzw, 256 Byte.

Immer wurde die Speicherzelle 0 (Null) korrumpiert, nach einem "Crash". 
Manchmal war auch noch die nächst Zelle betroffen.

Frage 1: Typ?
Frage 2: Welche Zelle?

Gruß

Bernd

von Tobias F. (coldtobi)


Bewertung
0 lesenswert
nicht lesenswert
Sonne Madiba schrieb:
> Peter Dannegger schrieb:
>> Du müßtest ne Warnung kriegen, daß can_WriteStatus() hinter der Mainloop
>> toter Code ist.
>
> es gibt zum Glück keine Warnung, das Compilieren erfolgt fehlerfrei

Sind denn die Warnings überhaupt eingeschaltet beim Compilieren (-Wall)?

von Oliver (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Daniel schrieb:
> Speziell im Falle wenn die Spannung nicht ganz unter einen bestimmten
> Wert fällt, beim AT90CAN128 muss z.b. der Wert Vpot falling
> unterschritten werden BEVOR die Spannung wieder über dem BOD level
> anliegt. Ist das nicht der Fall, entscheidet der Zufall was dann
> passiert.

Das wäre allerdings schlecht, und steht eindeutig im Gegensatz zu dem, 
was im Datenblatt steht.

Oliver

von Peter D. (peda)


Bewertung
0 lesenswert
nicht lesenswert
Reinhard Kern schrieb:
> ich finde die ganze Diskussion dreht sich um ein falsches Problem. IDs
> sind doch im Prinzip für die Lebensdauer einer Steuerung einzugeben,
> d.h. sie ändern sich selten oder nie. Sowas speichert man im EEProm
> unmittelbar nach der Eingabe und rührt dann erst wieder dran, wenn sie
> geändert werden müssen, also so gut wie nie.

Genau so machen wir es auch und es gab nie Probleme mit dem EEPROM.
Schreibzugriffe auf den EEPROM erfolgen nur durch Bedienereingabe.

Und falls dochmal die CRC nicht stimmen sollte, wird der EEPROM mit 
default Werten aus dem Flash geladen.

von Tobias F. (coldtobi)


Bewertung
0 lesenswert
nicht lesenswert
Bernd B. schrieb:
> Hallo Sonne Madiba,
>
> entschuldige bitte, ich habe nur schnell gelesen: Wurde das EEPROM schon
> benannt?

Laut Code ist es das interne des AT90CAN.

> Ich hatte einen ähnlichen Fall vor langer Zeit mit einem EEPROM 24xxx
> (bzw. kompatibel) mit 128 Byte, bzw, 256 Byte.
>
> Immer wurde die Speicherzelle 0 (Null) korrumpiert, nach einem "Crash".
> Manchmal war auch noch die nächst Zelle betroffen.

Ja, das Problem hatten die ganz alten AVRs, das Adr 0 geschrottet wird 
wenn der Reset ungünstig kommt.

(Schlimmer ist allerdings ein fehlender BOD -- Amoklaufender CPU-Core 
hat bei uns z.B auch schon Flash gelöscht)


> Frage 1: Typ?
> Frage 2: Welche Zelle?
>
> Gruß
>
> Bernd

von Tobias F. (coldtobi)


Bewertung
0 lesenswert
nicht lesenswert
Peter Dannegger schrieb:
> Reinhard Kern schrieb:
>> ich finde die ganze Diskussion dreht sich um ein falsches Problem. IDs
>> sind doch im Prinzip für die Lebensdauer einer Steuerung einzugeben,
>> d.h. sie ändern sich selten oder nie. Sowas speichert man im EEProm
>> unmittelbar nach der Eingabe und rührt dann erst wieder dran, wenn sie
>> geändert werden müssen, also so gut wie nie.
>
> Genau so machen wir es auch und es gab nie Probleme mit dem EEPROM.
> Schreibzugriffe auf den EEPROM erfolgen nur durch Bedienereingabe.
>
> Und falls dochmal die CRC nicht stimmen sollte, wird der EEPROM mit
> default Werten aus dem Flash geladen.

.. wenn man genügend EEP frei hat kann man auch einfach 2x speichern ... 
So fängt man ganz elegant interrupted writes ab und kommt wieder auf die 
Füße...
Default-Werte wären dann nochmal eine Fallback-Ebene

von Tobias F. (coldtobi)


Bewertung
0 lesenswert
nicht lesenswert
Daniel schrieb:
> The Power-on Reset will not work unless the supply voltage has been
> below VPOT (falling). Power on Reset und BOD sind immer sehr verzahnt.

Nein.
VPOT ist rrlevant bei aktivierten BOD, Ausserdem V_BOD > B_POT


Zum BOD ist das Datenblatt klar:
"When the BOD is enabled, and VCC decreases to a value below the trigger 
level (VBOT- in Figure 7-5), the Brown-out Reset is immediately 
activated."
(VBOT+ in Figure 7-5), the delay counter starts the MCU after the 
Time-out period tTOUT has expired. The BOD circuit will only detect a 
drop in VCC if the voltage stays below the trigger level for longer than 
tBOD given in Table 7-3.

Die Zeit t_bod ist 2µs -- Wenn man nat. schaft von BOD knapp über POT in 
wesentlich kleiner als 2µs zu kommen und wieder hoch über BOD kanns 
kritisch werden... Dass nennt man dann allerdings Designfehler.

von Joerg F. (felge1966)


Bewertung
0 lesenswert
nicht lesenswert
Wenn die IDs sich so häufig ändern, sollte der TO eventuell überlegen, 
ob der EEPROM überhaupt das richtige Speichermedium ist. Eventuell ist 
ja ein batteriegepufferter SRAM da das Mittel der Wahl.

von Sonne M. (dipoko)


Bewertung
0 lesenswert
nicht lesenswert
Joerg F. schrieb
> Wenn die IDs sich so häufig ändern, sollte der TO eventuell überlegen,
> ob der EEPROM überhaupt das richtige Speichermedium ist.
Die IDs ändern sich nicht häufig, sie müssen nur nach dem Start jedes 
Mal neu initialisiert werden, ziemlich anstrengend, wenn viele 
Controller angeschlossen sind.
solange der Strom fließt bleiben sie unverändert

von oszi40 (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Wahrscheinlich wäre ein kleiner Speichertest auch eine Idee, um erst mal 
festzustellen ab welcher Zelle das Übel bei Reset/Spannungsausfall 
auftritt. Schreib zum Test eine bekannte Zahlenfolge hinein und prüfe 
nach Reset ab wo diese immer falsch ist.

von Softwareverwickler (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Bei der funktion
1
VOID main_SaveMotorData(BYTE byteNodeID, UINT16 cmdID, UINT16 statusID)
2
{
3
4
//--- clear the old status register  
5
can_WriteStatus(m_DataHeader.node_id, 0, 0, 0);   
6
  
7
//--- set the new node id 
8
m_DataHeader.node_id   = byteNodeID;
9
m_DataHeader.cmd_id    = cmdID;
10
m_DataHeader.status_id = statusID;
11
12
main_SaveEEProm();
13
}

kann es sein, dass der Compiler das Schreiben auf die globale Variable 
etwas anders gestaltet, als man zunächst vermutet. Es kann sein, dass 
die Zuweisungen erst beim Verlassen des Blocks tatsächlich Auswirkung 
auf die globale Variable haben. Also vor der Aufruf von 
main_SaveEEProm().
Bei der Datenabhängigkeitsanalyse wird der Compiler feststellen, dass 
weder die Parameter noch m_DataHeader nach der Zuweisung explizit 
benutzt werden und fügt sie dann in optimierter Form vor dem return ein.

Versuch mal m_DataHeader mit volatile zu deklarieren. Das hat allerdings 
den Nnachteil, dass Funktionen, die oft darauf zugreifen, langsamer 
weden.
Alternative: änder die Funktion main_SaveEEProm() ab
1
VOID main_SaveEEProm(EEPROM_DATA_HEADER* pHead)
2
{
3
   eeprom_busy_wait();
4
   eeprom_write_block(pHead, (void*)EEPROM_DATA_HEADER_POS, sizeof(EEPROM_DATA_HEADER));
5
}
dann ist für den Compiler die Abhängigkeit direkt ersichltich:
1
VOID main_SaveMotorData(BYTE byteNodeID, UINT16 cmdID, UINT16 statusID)
2
{
3
4
//--- clear the old status register  
5
can_WriteStatus(m_DataHeader.node_id, 0, 0, 0);   
6
  
7
//--- set the new node id 
8
m_DataHeader.node_id   = byteNodeID;
9
m_DataHeader.cmd_id    = cmdID;
10
m_DataHeader.status_id = statusID;
11
12
main_SaveEEProm(&m_DataHeader);
13
}

von Softwareverwickler (Gast)


Bewertung
0 lesenswert
nicht lesenswert
replace vor with nach ;-)

von Oliver (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Oliver schrieb:.
> Das wäre allerdings schlecht, und steht eindeutig im Gegensatz zu dem,
> was im Datenblatt steht.

So auch hier. Setze Datenblatt = C-Standard.

Function calls sind Sequence Points.

Oliver

von Oliver (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Nachtrag: Und selbst wenn es keine Sequence Points wären,,dürfte der 
Compiletär höchstens zu dem Schluß kommen, daß er keine Ahnung hat, ob 
die Funktion irgend etwas mir den globalen Variablen macht, und das er 
büdie Zuweisung daher doch besser vorher macht.

Wäre das nicht so, würden an die 100% aller existierenden Programme 
nicht funktionieren.

Oliver

von Oliver (Gast)


Bewertung
0 lesenswert
nicht lesenswert
oszi40 schrieb:
> Wahrscheinlich wäre ein kleiner Speichertest auch eine Idee, um
> erst mal
> festzustellen ab welcher Zelle das Übel bei Reset/Spannungsausfall
> auftritt.

Ich wette jetzt einfach mal, daß es beim ausschalten Speicherstelle 0 
zersemmelt, dann nach dem Einschalten die dort stehende Versionsid nicht 
erkannt, und daher der default-Datensatz geschrieben wird.

Und das alles deutet nunmal auf fehlenden Browm-out hin.
Aber der ist ja angeblich eingeschaltet...

Oliver

von Softwareverwickler (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Solche optimierungen sind mir leider schon untergekommen.
Liegt wohl an punkt 5. Bei punkt 2 klingt das ja noch ganz gut:
 ISO/IEC 9899:1999

5.1.2.3 Program execution

2 Accessing a volatile object, modifying an object, modifying a file, or 
calling a function that does any of those operations are all side 
effects,11) which are changes in the state of the execution environment. 
Evaluation of an expression may produce side effects. At certain 
specified points in the execution sequence called sequence points, all 
side effects of previous evaluations shall be complete and no side 
effects of subsequent evaluations shall have taken place. (A summary of 
the sequence points is given in annex C.)


5 The least requirements on a conforming implementation are:

— At sequence points, volatile objects are stable in the sense that 
previous accesses are complete and subsequent accesses have not yet 
occurred.

[...]

von Oliver (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Du musst schon den ganzen Abschnitt lesen. Wenn main_SaveEEProm in der 
selben Compilationseinheit steht, muß der Compiler erkennen, daß da die 
aktuellen Werte der globalen Variablne gebraucht werden, und die 
Zuweisung vorher ausführe. Wenn main_SaveEEProm nicht in der selben 
Compilationseinheit steht, muß er es sowieso machen.

Egal, was du da für Effekte gehabt hast, wenn dein Compiler Code der 
Art:
1
GlobalVar = 3; // nicht volatile
2
machWasMitGlobalVar();

kaputtoptimiert, dann ist das entweder ein Compilerfehler, oder der 
Fehler steckt ganz woanders.

Oliver

von Sonne M. (dipoko)


Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
@ Softwareverwickler (Gast)
@ Oliver (Gast)

>Ich wette jetzt einfach mal, daß es beim ausschalten Speicherstelle 0
>zersemmelt, dann nach dem Einschalten die dort stehende Versionsid nicht
>erkannt, und daher der default-Datensatz geschrieben wird.

ich werde mal die Speicherstelle für die Version anstatt auf Null (0) 
woanders hinlegen.

Danke für den Vorschlag

von (prx) A. K. (prx)


Bewertung
0 lesenswert
nicht lesenswert
Sonne Madiba schrieb:
> ich werde mal die Speicherstelle für die Version anstatt auf Null (0)
> woanders hinlegen.

Ist doch schon der Fall:
  #define EEPROM_DATA_HEADER_POS  0x0010    // start at address 0x10

Jetzt schau doch endlich mal nach, welche Bytes im EEPROM hinterher 
falsch sind und welchen falschen Wert sie haben.

: Bearbeitet durch User
von Εrnst B. (ernst)


Bewertung
0 lesenswert
nicht lesenswert
Fast in den Bereich des Voodoo würde der Workaround fallen, nach jedem 
Eeprom-Zugriff noch einen weiteren EEprom-Read auf Addresse 0 zu machen.

Idee dahinter: Wenn es beim Brown-Out, Spikes auf Vcc oder sonstigen 
Fiesheiten das EEProm  zersemmelt, dann 
warscheinlich/hoffentlich/vielleicht die Stelle, auf die das 
EEprom-Address-Register gerade zeigt.

Aber: Dann kann man auch gleich Klanglack auf den µC pinseln ...

Und, um nochmal ganz an den Anfang zurückzukehren:

Den µC hast du mal getauscht, oder?

Nicht dass du hier die ganze Zeit mit dem Exemplar herumdoktorst, dass 
schon Jahre im Einsatz war und durch die ununterbrochenen EEProm-Writes 
endlich seine Zellen-Lebensdauer überschritten hat? Geplante 
Obsoleszenz?

von Sonne M. (dipoko)


Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Εrnst B✶ schrieb
> Idee dahinter: Wenn es beim Brown-Out, Spikes auf Vcc oder sonstigen
> Fiesheiten das EEProm  zersemmelt, dann
> warscheinlich/hoffentlich/vielleicht die Stelle, auf die das
> EEprom-Address-Register gerade zeigt.
> Den µC hast du mal getauscht, oder?

Ja, mehrmals sogar

> Nicht dass du hier die ganze Zeit mit dem Exemplar herumdoktorst, dass
> schon Jahre im Einsatz war und durch die ununterbrochenen EEProm-Writes
> endlich seine Zellen-Lebensdauer überschritten hat? Geplante
> Obsoleszenz?

Die Eintragungen im EEPROM sind doch nicht falsch oder?

von Εrnst B. (ernst)


Bewertung
0 lesenswert
nicht lesenswert
Sonne Madiba schrieb:
> Die Eintragungen im EEPROM sind doch nicht falsch oder?


typedef struct
{
UINT16    version;
BYTE      node_id;
UINT16    cmd_id;
UINT16    status_id;
} EEPROM_DATA_HEADER;


Vergleich selber...

version= 0xFF02
node_id=0x01 (padding byte?)
cmd_id=0x0A00
status_id=0x0B00

von (prx) A. K. (prx)


Bewertung
0 lesenswert
nicht lesenswert
version   @ offset 0x11/12 = 0x0002
node_id   @ offset 0x13    = 0x01
cmd_id    @ offset 0x14/15 = 0x000A = 10
status_id @ offset 0x16/17 = 0x000B = 11

Unter der Annahme, dass die Versionsnummer nun 2 ist und das bisher 
erste Byte wie angekündigt übersprungen wurde, passt es.

: Bearbeitet durch User

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.