Forum: Mikrocontroller und Digitale Elektronik Arduino: page-write in I2C eeprom verliert sporadisch ein Byte


von Kai (ksb)


Angehängte Dateien:

Lesenswert?

Hallo geehrte Experten,

ich suche jetzt schon einige Tage nach der Ursache für folgendes 
Problem:
wie im Betreff geschrieben, geht bei der Speicherung sporadisch ein Byte 
verloren. Die zu schreibenden Daten kommen zurzeit vom seriellen Monitor 
-  später werden es zwecks Datensicherung erstellte Dateien sein.

Nach erfolgreicher Überwindung des Problems, dass der serielle Puffer 
nur 64 Zeichen aufnehmen kann, die Dateigröße aber mit mehr als 1k weit 
oberhalb davon liegt, werden die Testdaten mittlerweile erfolgreich und 
ohne Fehler in das RAM geschrieben.

Bei der Ausgabe des EEPROM-Inhaltes fehlt aber sporadisch ein Byte in 
der aufsteigenden Reihenfolge (Zwecks Fehlereingrenzung wurde eine 
Testdatei mit aufsteigenden Werten von 00 - 9F erstellt, welche über die 
Eingabezeile des seriellen Monitors gesendet wird).

Konkret kommt es vor, dass 10 mal alle 160 Bytes korrekt eingelesen 
werden, danach aber ein Byte fehlt.

Es wird keine Bibliothek für das Schreiben und Lesen des eeprom 
verwendet.
Die zuässigen Werte für den Pufferspeicher und die Pagegrenzen werden 
"zu Fuß" berechnet. Das versuchsweise Schreiben grösserer Datenmengen 
mit beliebiger Startadresse wurde getestet und ist fehlerfrei.

hier ein Beispiel fehlerhafter Daten (es fehlt der Wert 65):
1
EEPROM Settings
2
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F  16
3
10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F  32
4
20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F  48
5
30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F  64
6
40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F  80
7
50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F  96
8
60 61 62 63 64 66 67 68 69 6A 6B 6C 6D 6E 6F 70  112
9
71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F 80  128
10
81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F 90  144
11
91 92 93 94 95 96 97 97 99 9A 9B 9C 9D 9E FF FF  160

Im Anhang ein Auszug des Programms mit hoffentlich allen nötigen 
Informationen.

Vielen Dank für jegliche Hilfe und viele Grüße
Kai

Da ich nur alle 30 Minuten antworten kann, reiche ich hier nach, dass 
die Verarbeitung der Daten schneller geht, als sie bei 9600 BAUD 
eintreffen (zum Debuggen hatte ich die empfangenen Daten auch ins RAM 
geschrieben - der Inhalt war immer richtig).

Es war bereits ein Software-Handshake implementiert, welches das Problem 
aber nicht beseitigte.

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

Längeren Quelltext als Anhang, siehe Netiquette.

von Wastl (hartundweichware)


Angehängte Dateien:

Lesenswert?

Kai schrieb:
> Vielen Dank für jegliche Hilfe

Vielen Dank dass du Hinweise zum Posten von Sourcecode ignorierst.

von Nemopuk (nemopuk)


Lesenswert?

Offenbar fehlt ein Handshake. Dein Sender sendet die Daten schneller an 
den Mikrocontroller, als dieser sie verarbeiten kann. Du brauchst eine 
bidirektionale Kommunikation, damit der Mikrocontroller dem Sender ein 
"mach mal pause" Signal senden kann.

Alternativ kannst du alle Dateien ins RAM empfangen, und erst danach ins 
EEPROM schreiben - falls du dazu genug RAM frei hast.

: Bearbeitet durch User
von Andras H. (andras_h)


Lesenswert?

Nemopuk schrieb:
> Offenbar fehlt ein Handshake. Dein Sender sendet die Daten schneller an
> den Mikrocontroller, als dieser sie verarbeiten kann. Du brauchst eine
> bidirektionale Kommunikation, damit der Mikrocontroller dem Sender ein
> "mach mal pause" Signal senden kann.

Oder genügend buffer um die Daten in RAM zwischenzuspeichern.

von Nemopuk (nemopuk)


Lesenswert?

Andras H. schrieb:
> Oder genügend buffer um die Daten in RAM zwischenzuspeichern.

Habe ich gerade nach gereicht :-)

von Kai (ksb)


Lesenswert?

Hallo Nemopuk, hallo Andras,

Entschuldigung für das Einfügen des Quelltextes!

da ich nur alle 30 Minuten antworten kann, habe ich oben ergänzt, das 
die Bearbeitung wesentlich schneller geht, als die Daten bei 9600 BAUD 
eintreffen.

Der Maximalwert des Puffers erreicht maximal 30% (Zum Debuggen hatte ich 
die Daten auch ins RAM geschrieben. Der Inhalt ist immer richtig).

Ich hatte vorher einen Software-Handshake eingebaut, welcher am Problem 
aber nichts geändert hat.

Viele Grüße

Kai

von Nemopuk (nemopuk)


Lesenswert?

Kai schrieb:
> Der Inhalt ist immer richtig

Offenbar nicht, sonst würden keine Bytes fehlen.

von Kai (ksb)


Lesenswert?

Dar Ram-Inhalt ist richtig aber die EEPROM-Schreibroutine verschluckt 
ein Byte.

von Nemopuk (nemopuk)


Lesenswert?

Kai schrieb:
> Dar Ram-Inhalt ist richtig aber die EEPROM-Schreibroutine verschluckt
> ein Byte.

Das kann nicht sein, weil cnt für jedes empfangene Byte um 1 erhöht 
wird. Wenn es am Schreibzugriff ins EEPROM läge, dann wäre an der 
Position ein einzelnes falsches Byte, und danach kämen richtige Bytes. 
Hier fehlt aber ein Byte und alle nachfolgenden sind danach an der 
falschen Position.

Bei dir hat cnt am Ende weniger Bytes gezählt, als erwartet. Also wurde 
ein Byte zu wenig empfangen (ist verloren gegangen). Füge eine Debug 
Ausgabe vom cnt Wert hinzu, dann siehst du es.

: Bearbeitet durch User
von Michiii 0. (michiii_0)


Lesenswert?

Funktioniert es stets 10 Mal und beim 11. Mal nicht?

Ist es immer die 0x65, die fehlt?

Was sind die Werte der Debug-Ausgaben, wenn es funktioniert, und wenn 
nicht?

von Nemopuk (nemopuk)


Lesenswert?

Ich sehe gerade: Ich habe nicht geschnallt, das die beiden Nibbles als 
einzelnes Byte zusammen gefasst werden. Mein Smartphone Display taugt 
nicht gut zum Lesen von Quelltexten. Jetzt auf dem PC ist es mir klarer.

128 Zeichen Seriell ergeben 64 Bytes im EEprom

Eine Gegenfrage, bevor wir (ich) uns hier total verzetteln:

> byte bytes; byte x;

Was genau ist der Datentyp "byte"? Kann es sein, dass das ein Signed 
Integer ist, der genau an dieser Stelle überläuft?

: Bearbeitet durch User
von Kai (ksb)


Lesenswert?

Hallo Nemopuk,

vielen Dank für diesen Hinweis - richtig - cnt zählt im Fehlerfall 
weniger Bytes. Das Einfügen einer Debug-Ausgabe ist nicht ganz einfach, 
da diese das Timing stark verändert. Versuche ich gleich auf jeden Fall 
mal.

Hallo Michiii,

nein - es wird unterschiedlich oft richtig empfangen, dann wieder 
unterschiedlich oft verkehrt und es ist nicht immer die 0x65.

Viele Grüße
Kai

Hier mal die 0x62:
1
EEPROM Settings
2
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F  16
3
10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F  32
4
20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F  48
5
30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F  64
6
40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F  80
7
50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F  96
8
60 61 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F 70  112
9
71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F 80  128
10
81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F 90  144

und dazu der RAM-Inhalt:
1
RAM rx_test    
2
RAM rx_test    
3
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F  16
4
10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F  32
5
20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F  48
6
30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F  64
7
40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F  80
8
50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F  96
9
60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F  112
10
70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F  128
11
80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F  144
12
90 91 92 93 94 95 96 97 97 99 9A 9B 9C 9D 9E 9F  160

von Nemopuk (nemopuk)


Lesenswert?

Zähle mal, wie oft page_write() aufgerufen wird und gebe die Zahl tzum 
Schluss aus. Wo ist die Dokumentation (oder besser noch der Quelltext 
von page_write()?

von Mario M. (thelonging)


Lesenswert?

Kai schrieb:
> Dar Ram-Inhalt ist richtig aber die EEPROM-Schreibroutine verschluckt
> ein Byte.

Dann solltest Du uns den dafür notwendigen Programmteil zeigen.

von Kai (ksb)


Lesenswert?

Hallo Nemopuk,

ja - meine Variablennamen sind an dieser Stelle nicht so toll:
Die Variable bytes enthält die errechneten für bytes_in_page(addr+cnt).

Die Variable x enthält die Anzahl der aktuell verfügbaren Zeichen für 
die Auswertung.

byte ist gemäß Arduino-Referenz ein unsigned byte
[[https://docs.arduino.cc/language-reference/de/variablen/data-types/byte/]]

Die Zeichen 00 - 9F ergeben für cnt fehlerfrei gezählt einen Wert von 
160.
und es werden 160 Bytes im eeprom geschrieben - im Fehlerfall werden 
weniger Bytes ins eeprom geschrieben.

Viele Grüße

Kai

von Nemopuk (nemopuk)


Lesenswert?

Mal neben bei bemerkt: Ich kriege einen Knoten im Kopf, wenn ich mir die 
mathematischen Teile in deinem Quelltext anschaue. Das ist alles so von 
hinten durch die Brust ins Auge, dann durch die Nase wieder rein und am 
Popo raus. Hast du dir das selbst ausgedacht, oder kopiert?

von Wastl (hartundweichware)


Lesenswert?

Kai schrieb:
> im Fehlerfall werden weniger Bytes ins eeprom geschrieben.

Du verwendest offensichtlich ein externes "grosses" EEPROM.
Dabei gibt es zwei wichtige mögliche Fehlerquellen:

1) deine Programmierung entspricht nicht (ganz) den
Anforderungen deines EEPROMs.

2) Dein Aufbau ist mangelhaft. Die Stromversorgung spielt
dabei eine grosse Rolle. Viele Leute vergessen ein
Abblock-C am EEPROM anzubringen oder halten es bewusst
für nicht erforderlich.

von Kai (ksb)


Lesenswert?

Hallo Mario,

vielen Dank für deinen Hinweis:
1
void page_write(unsigned int addr, unsigned int number){ 
2
  Wire.beginTransmission(EXT_EE_ADDR);          // Start transmission to I2C address
3
  Wire.write((byte)(addr>>8));                  // Address eeprom high
4
  Wire.write((byte)(addr&0xFF));                // Address eeprom low    
5
  for(unsigned int i=0; i<number; i++){         // Write all bytes allowed on this page
6
  Wire.write((byte)(Ser_Arr[i]));}              // Byte from array
7
  Wire.endTransmission();                       // End transmission
8
  delay(5);                                     // Important
9
}

Hallo Nemopuk,

selbst ausgedacht :(


Hallo Wastl,

ja ein externes EEPROM mit Abblock-C. Der Fehler tritt sowohl bei einem 
fliegenden Aufbau mit einem Arduino-Mega als auch auf einer fertigen 
Platine mit einem 328P auf.

Viele Grüße Kai

PS: muss leider für ca. 1h weg

von Nemopuk (nemopuk)


Lesenswert?

Da nach dem Fehler alle folgenden Bytes im EEprom um ein Byte verschoben 
sind, vermute ich einen Fehler in der Berechnung der Adressen 
(Positionen im EEprom).

Du könntest alle berechneten Positionen (mit denen du page_write() 
aufrufst) mal in ein Array schreiben und zum Schluss zu Debug Zwecken 
ausgeben. Dann sehen wir, wie oft page_write() aufgerufen wurde, und mit 
welchen berechneten Adressen.

: Bearbeitet durch User
von Wastl (hartundweichware)


Lesenswert?

Kai schrieb:
> void page_write(unsigned int addr, unsigned int number)

Das wird so nicht gehen, da die Wire-Klasse nicht weiss dass
sie auf die Programmierung des EEPROMs Rücksicht nehmen muss.

Nemopuk schrieb:
> vermute ich einen Fehler in der Berechnung der Adressen
> (Positionen im EEprom)

Nö. Einfach nicht lang genug gewartet sondern EEPROM "überfahren".

von Nemopuk (nemopuk)


Lesenswert?

Wastl schrieb:
> Einfach nicht lang genug gewartet sondern EEPROM "überfahren".

Ich habe gerade mal in ein zufällig gewähltes DB geschaut, da werden 5ms 
als maximale Zeit genannt. Also das sollte man mal mit dem RICHTIGEN 
Datenblatt abgleichen und sicherheitshalber ein bisschen länger warten.

von Wastl (hartundweichware)


Lesenswert?

Nemopuk schrieb:
> da werden 5ms als maximale Zeit genannt

Ist richtig und steht im Code auch so drin. Wenn man es
wirklich gründlich timen will pollt man am Ende einer Page
so lange bis das EEPROM sich wirklich zurückmeldet dass es
wieder bereit ist.

Trotzdem sind die genannten Symptome Anzeichen genug dass
das EEPROM überfahren wird. Gleiches gilt für nicht abge-
blockte Versorgungsspannung ....

von Wastl (hartundweichware)


Lesenswert?

Wir haben bis jetzt keinen Schaltplan und keinen Aufbau gesehen.
Haben wir schon über evtl. nicht vorhandene Pullups gesprochen?

von Veit D. (devil-elec)


Angehängte Dateien:

Lesenswert?

Hallo,

damit man es besser erkennt.

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Wastl schrieb:
> Ist richtig und steht im Code auch so drin. Wenn man es
> wirklich gründlich timen will pollt man am Ende einer Page
> so lange bis das EEPROM sich wirklich zurückmeldet dass es
> wieder bereit ist.

Nein!
Nicht am Ende einer Page, sondern VOR jedem Schreibzugriff.
Das delay(5) ist auf jeden Fall gründlich daneben.

Dazu:
 Kai, hast du bedacht dass Wire nur einen 32 Byte großen Buffer 
verwaltet?
Überfüllungen werden abgeschnitten.
Wire.write() meldet den Misserfolg, aber das wird hier gründlich 
ignoriert.

: Bearbeitet durch User
von Wastl (hartundweichware)


Lesenswert?

Arduino F. schrieb:
> Nein!
> Nicht am Ende einer Page, sondern VOR jedem Schreibzugriff.

Seltsam. In den Datenblättern steht davon nichts. Ich habe
schon sehr viele I2C EEPROMs mit meinen eigenen Page-Write
Routinen beschrieben und habe dabei nie vor dem Schreiben
gewartet, sondern immer auf das OK des EEPROMs.

von Wastl (hartundweichware)


Lesenswert?

.. (bitte löschen)

: Bearbeitet durch User
von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Wastl schrieb:
> Arduino F. schrieb:
>> Nein!
>> Nicht am Ende einer Page, sondern VOR jedem Schreibzugriff.
>
> Seltsam. In den Datenblättern steht davon nichts. Ich habe
> schon sehr viele I2C EEPROMs mit meinen eigenen Page-Write
> Routinen beschrieben und habe dabei nie vor dem Schreiben
> gewartet, sondern immer auf das OK des EEPROMs.

Die Logik dahinter:
Wenn ich schreiben möchte, dann muss/kann ich doch vorher prüfen ob das 
EEPROM bereit ist. Gegebenenfalls warten.

Wenn ich nach dem Schreiben prüfe/warte, ist das verschwendete Zeit.

OK, wenn viele Schreibzugriffe Zeitnah erfolgen, ist egal, wo gewartet 
wird, aber das ist längst nicht immer der Fall.

von Kai (ksb)


Lesenswert?

Hallo,

zunächst vielen herzlichen Dank für die vielen Antworten, welche ich 
alle morgen kontrollieren bzw. umsetzen werde.

1. Nemopuk
1
Da nach dem Fehler alle folgenden Bytes im EEprom um ein Byte verschoben 
2
sind, vermute ich einen Fehler in der Berechnung der Adressen 
3
(Positionen im EEprom).
4
5
Du könntest alle berechneten Positionen (mit denen du page_write() 
6
aufrufst) mal in ein Array schreiben und zum Schluss zu Debug Zwecken 
7
ausgeben. Dann sehen wir, wie oft page_write() aufgerufen wurde, und mit 
8
welchen berechneten Adressen.

Werde ich morgen umsetzen.

2. Wastl
1
Das wird so nicht gehen, da die Wire-Klasse nicht weiss dass
2
sie auf die Programmierung des EEPROMs Rücksicht nehmen muss.
3
4
Ist richtig und steht im Code auch so drin. Wenn man es
5
wirklich gründlich timen will pollt man am Ende einer Page
6
so lange bis das EEPROM sich wirklich zurückmeldet dass es
7
wieder bereit ist.
8
9
Trotzdem sind die genannten Symptome Anzeichen genug dass
10
das EEPROM überfahren wird. Gleiches gilt für nicht abge-
11
blockte Versorgungsspannung 
12
....
13
Wir haben bis jetzt keinen Schaltplan und keinen Aufbau gesehen.
14
Haben wir schon über evtl. nicht vorhandene Pullups gesprochen?

Vielen Dank für alle Hinweise - Versorgungsspannung ist abgeblockt.
Pullups sind vorhanden. Am I2C-Bus hängen eine RTC, ein LCD und das 
externe eeprom.
1
Nö. Einfach nicht lang genug gewartet sondern EEPROM "überfahren".
2
3
Ich habe gerade mal in ein zufällig gewähltes DB geschaut, da werden 5ms 
4
als maximale Zeit genannt. Also das sollte man mal mit dem RICHTIGEN 
5
Datenblatt abgleichen und sicherheitshalber ein bisschen länger warten.

Die Wire-Schreibroutine wird ja nur mit der erlaubten Anzahl von Daten 
aufgerufen. Die Zeit werde ich kontrollieren. Die verwendete Angabe habe 
ich aus einem Beispiel übernommen.

3. Arduino F:
1
Nein!
2
Nicht am Ende einer Page, sondern VOR jedem Schreibzugriff.
3
Das delay(5) ist auf jeden Fall gründlich daneben.
4
5
Dazu:
6
 Kai, hast du bedacht dass Wire nur einen 32 Byte großen Buffer 
7
verwaltet?
8
Überfüllungen werden abgeschnitten.
9
Wire.write() meldet den Misserfolg, aber das wird hier gründlich 
10
ignoriert.

Die Schreibroutine habe ich aus einem Beispiel übernommen, wo nach dem 
Schreiben gewartet wird. Die Puffergröße wird beachtet. In dem 
beschriebenen Beispiel ist eine Puffergröße von 30 angegeben.

Viele Grüße
Kai

von Rainer W. (rawi)


Lesenswert?

Kai schrieb:
> Das Einfügen einer Debug-Ausgabe ist nicht ganz einfach,
> da diese das Timing stark verändert.

Nicht, wenn du die Daten über SPI ausgibst und mit dem Logikanalyzer 
mitliest. Die SPI-Ausgange kannst du deutlich schneller als die 9600Bd 
laufen lassen.

von Nemopuk (nemopuk)


Lesenswert?

Ein Logic Analyzer wäre auch am I2C Bus hilfreich. Dafür genügen schon 
die 10€ Dinger aus China.

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

Kai schrieb:
> Das Einfügen einer Debug-Ausgabe ist nicht ganz einfach,
> da diese das Timing stark verändert. Versuche ich gleich auf jeden Fall
> mal.

Falsch. Man muss halt die Debugausgaben passend puffern und dann 
ausgeben, wenn genügend Zeit ist.

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Kai schrieb:
> Die Puffergröße wird beachtet. In dem
> beschriebenen Beispiel ist eine Puffergröße von 30 angegeben.

OK!

von Kai (ksb)


Angehängte Dateien:

Lesenswert?

Hallo Rainer, Nemopuk, Wastl, Arduino F und Falk,

zunächst habe ich den Tipp von Arduino F umgesetzt und am Ende der 
Page-Write-Routine auf das ACK gewartet (beim ersten Aufruf läuft ja 
noch kein Page-Write, daher am Ende):
1
void page_write(unsigned int addr, unsigned int number){ 
2
  Wire.beginTransmission(EXT_EE_ADDR);          // Start transmission to I2C address
3
  Wire.write((byte)(addr>>8));                  // Address eeprom high
4
  Wire.write((byte)(addr&0xFF));                // Address eeprom low    
5
  for(unsigned int i=0; i<number; i++){         // Write all bytes allowed on this page
6
  Wire.write((byte)(Ser_Arr[i]));}              // Byte from array
7
  Wire.endTransmission();                       // End transmission
8
  //delay(5);                                   // Important
9
  Wire.beginTransmission(EXT_EE_ADDR);          // Start command and device Address queued
10
  Wire.write(0x00);                             // Dummy data and direction mode (R-W/=0) bit queued
11
  while (Wire.endTransmission() != 0x00);       // Transmission success status code as per Wire Library
12
}

Das hat am Verhalten nichts geändert, gefällt mir aber auch besser, als 
das delay(5).

Danach habe ich den Vorschlag von Nemopuk integriert und nach jedem 
Aufruf von Page_Write die Variable cnt und cur_byte in ein Array 
geschrieben und am Ende der Übertragung ausgegeben (siehe Anhang).

Das Ergebnis hat mich sehr überrascht - ich hätte nicht erwartet, dass 
die Daten so schnell bearbeitet (in das eeprom geschrieben) werden, dass 
sich der Puffer nur vor dem Start der Routine auf 11 Zeichen anwächst.

Leider gibt es neben fehlerfreien immer noch fehlerhafte Übertragungen, 
was ja auch nicht anders zu erwarten war:
1
EEPROM Settings
2
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F  16
3
10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F  32
4
20 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F 30  48
5
31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F 40  64
6
41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F 50  80
7
51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F 60  96
8
61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F 70  112
9
71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F 80  128
10
81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F 90  144
11
91 92 93 94 95 96 97 97 99 9A 9B 9C 9D 9E 9F FF  160
12
13
RAM rx_test    
14
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F  16
15
10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F  32
16
20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F  48
17
30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F  64
18
40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F  80
19
50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F  96
20
60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F  112
21
70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F  128
22
80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F  144
23
90 91 92 93 94 95 96 97 97 99 9A 9B 9C 9D 9E 9F  160

Viele Grüße und eine gute Nacht
Kai

: Bearbeitet durch User
von Nemopuk (nemopuk)


Lesenswert?

Kai schrieb:
> Das hat am Verhalten nichts geändert
Sehe ich anders, jetzt ein neues Fehlerbild dazu gekommen:

Vorher:
> 60 61 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F 70  112
62 fehlt, alle folgenden Bytes sind an der falschen Adresse.

Nachher:
> 20 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F 30  48
Die 21 fehlt, alle folgenden Bytes sind an der falschen Adresse.

> 91 92 93 94 95 96 97 97 99 9A 9B 9C 9D 9E 9F FF  160
Eine der beiden 97 müsste den Wert 98 haben.

Ich würde immer noch versuchen, einen großzügigen Delay (mehr als im 
Datenblatt steht) einzubauen. Nur zur Probe, ob dort der Knackpunkt 
liegt. Du kannst zusätzlich die Baudrate herabsetzen, wenn nötig 
(Pufferüberlauf).

Du hast eine leere Textdatei gezippt. Bitte reiche die richtige Datei 
nach.

: Bearbeitet durch User
von Helmut H. (helmuth)


Lesenswert?

Wire.endTransmission() überträgt die Daten aus dem transmit buffer und 
meldet Misserfolge, das muss ausgewertet werden:
    0: success.
    1: data too long to fit in transmit buffer.
    2: received NACK on transmit of address.
    3: received NACK on transmit of data.
    4: other error.
    5: timeout


Kai schrieb:
>
>   while (Wire.endTransmission() != 0x00);       // Transmission success
>

So wird endTransmission() im Fehlerfall einfach nochmal aufgerufen, 
keine Ahnung was dann passiert - macht nix und gibt 0 zurück ??

Wire.write() schreibt die Daten nur in den transmit buffer (und gibt 
deshalb auch nur die Anzahl der geschriebenen Bytes zurück).

: Bearbeitet durch User
von Peter D. (peda)


Angehängte Dateien:

Lesenswert?

Wastl schrieb:
> Ich habe
> schon sehr viele I2C EEPROMs mit meinen eigenen Page-Write
> Routinen beschrieben und habe dabei nie vor dem Schreiben
> gewartet, sondern immer auf das OK des EEPROMs.

Nun, vor dem Schreiben oder Lesen mußt Du doch erstmal die Adresse 
senden. Damit ist automatisch gesichert, daß der EEPROM ein vorheriges 
Schreiben beendet hat.
Ein EEPROM der busy ist, stellt sich tot, d.h. antwortet mit NACK auf 
seine Adresse. Ich versuche es dann einfach in einer Schleife nochmal, 
die bei 400kHz I2C-Takt bis zu 10ms wartet.

Man muß natürlich vor jedem Byte abtesten, ob ein Pageüberlauf erfolgt 
und dann erstmal die aktuelle Page wegschreiben.

Anbei mal meine I2C-EEPROM Lib.
Sie benutzt Bit-Banging I2C, da zumindest bei den ATmega8 usw. der 
I2C-Controller recht störempfindlich ist. Sie ist dadurch auch sehr 
leicht auf jede andere µC-Familie zu portieren.
Da ja keine Interrupts benutzt werden, hätte der I2C-Controller keinen 
Laufzeitvorteil. Auch der Flashverbrauch ist dadurch minimal.

von Wastl (hartundweichware)


Lesenswert?

Peter D. schrieb:
> Nun, vor dem Schreiben oder Lesen mußt Du doch erstmal die Adresse
> senden.

Ich hatte nicht vor meine Routine (die einwandfrei funktioniert)
im Detail in einem Absatz zu erklären. Belehrungen zum
Algorithmus brauchst du mir nicht zu geben, immerhin beziehst
du deine Belehrung auf meine Aussage und schreibst "doch", als
ob bei mir ein grober Annahme-Fehler vorliegt.

von Peter D. (peda)


Lesenswert?

Kai schrieb:
> zunächst habe ich den Tipp von Arduino F umgesetzt und am Ende der
> Page-Write-Routine auf das ACK gewartet

Das ist natürlich viel zu optimistisch.
Jedes Byte Schreiben kann fehlschlagen, daher muß auch jedesmal auf ACK 
geprüft werden.
Auf ACK man auch nicht warten, sondern man kann es nur prüfen. Der Slave 
legt es im 9. Takt vom Master auf den Bus oder eben nicht.

Und beim Lesen muß der Master auf das letzte Byte mit NACK antworten!

von Nemopuk (nemopuk)


Lesenswert?

Ich habe das Gefühl, dass einige hier keine Ahnung haben, wie die I2C 
Implementierung von Arduino funktioniert.

von Peter D. (peda)


Lesenswert?

Wastl schrieb:
> du deine Belehrung auf meine Aussage und schreibst "doch", als
> ob bei mir ein grober Annahme-Fehler vorliegt.

Lies nochmal gründlicher, da erfolgt nirgends eine Belehrung, sondern 
eine Erklärung. Und die Erklärung ist natürlich nicht nur für Dich 
gedacht, sondern ganz allgemein.
Das ACK auf die I2C-Adresse impliziert den Abschluß aller vorherigen 
Aktionen. Du kannst von mir aus gerne noch ein Delay anfügen.

von Peter D. (peda)


Lesenswert?

Nemopuk schrieb:
> Ich habe das Gefühl, dass einige hier keine Ahnung haben, wie die I2C
> Implementierung von Arduino funktioniert.

Meine Erklärungen beziehen sich ausschließlich auf den I2C-Standard, wie 
er von Philips/NXP festgelegt wurde. Die konkrete Realisierung in einem 
µC war nie Punkt der Diskussion und somit erst recht nicht, wie es in 
der Arduino-Welt erfolgt.
Es gibt nie nur einen einzigen Weg, der Implementierung. Wichtig ist 
nur, daß der Philips/NXP I2C-Standard eingehalten wird.

von Peter D. (peda)


Lesenswert?

Helmut H. schrieb:
> So wird endTransmission() im Fehlerfall einfach nochmal aufgerufen,
> keine Ahnung was dann passiert - macht nix und gibt 0 zurück ??

Damit hast Du voll recht, das ist ein grober Fehler.
I2C kennt kein Retry, wie z.B. der CAN-Bus. Man muß daher die 
fehlerhafte Aktion komplett wiederholen.

von Rainer W. (rawi)


Lesenswert?

Nemopuk schrieb:
> Ein Logic Analyzer wäre auch am I2C Bus hilfreich.

I2C Kommunikation erfordert wegen ACK mehr Zusammenarbeit zwischen den 
beiden Kommunikationsteilnehmern. Macht der Arduino des TO den Handshake 
im Hintergrund per Hardware?

von Nemopuk (nemopuk)


Lesenswert?

Peter D. schrieb:
> Man muß daher die fehlerhafte Aktion komplett wiederholen.

Wen man endTransmission() erneut aufruft, passiert genau das.

Rainer W. schrieb:
> Macht der Arduino des TO den Handshake im Hintergrund per Hardware?

Ja

Helmut H. schrieb:
> Wire.write() schreibt die Daten nur in den transmit buffer

Korrekt!

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

Nemopuk schrieb:
> Wen man endTransmission() erneut aufruft, passiert genau das.

Es wird also z.B. bei "2: received NACK on transmit of address" alles 
wiederholt?
Wo ist das denn dokumentiert?

Es spricht prinzipiell ja nichts dagegen, wenn die Funktionen derart 
komplex sind und hoffentlich auch ausgiebig getestet sind.
Obwohl ich nicht so richtig daran glaube, das die EEPROM Statemaschine 
wirklich im Hintergrund laufen kann. Das mit dem Pageüberlauf ist ja 
schon ziemlich speziell.
Auch müßte diese Superfunktion die Adreßlänge wissen, d.h. ist der 
EEPROM <= 24C16 oder >=24C164.

: Bearbeitet durch User
von Nemopuk (nemopuk)


Lesenswert?

Peter D. schrieb:
> Es wird also z.B. bei "2: received NACK on transmit of address" alles
> wiederholt.

Nicht automatisch durch das Framework. Der Programmierer der Anwendung 
entscheidet, ob und wie er auf Fehler reagieren will. Helmut schlug vor

Helmut H. schrieb:
> So wird endTransmission() im Fehlerfall einfach nochmal aufgerufen

Das war ein Vorschlag, keine Feststellung.

> Wo ist das denn dokumentiert?

Auf arduino.cc. Für den Anfang:
https://docs.arduino.cc/learn/communication/wire/
https://docs.arduino.cc/language-reference/en/functions/communication/wire/
https://github.com/arduino/ArduinoCore-avr/tree/master/libraries/Wire/src

Aber du diskutierst offenbar lieber über (meist gegen) Arduino, anstatt 
dessen Doku zu lesen oder wenigstens mal einen Blick in dessen Quelltext 
zu werfen. Alle sollen von deinen Codes lernen, was OK ist, denn sie 
sind gut. Das ganze käme jedoch wesentlich überzeugender rüber, wenn du 
dich auch mal mehr mit dem Code von Arduino befassen würdest.

Mag sein, dass Arduino für "Dummköpfe" designt wurde, aber nicht von 
Dummköpfen. Die Macher von Arduino sind durchaus fachkundig.

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

Nemopuk schrieb:
> Aber du diskutierst offenbar lieber über (meist gegen) Arduino

Da bist Du aber sowas von auf dem Holzweg. Ich benutze es nicht, das ist 
aber auch schon alles. Kritisiert habe ich es bisher nirgends.

Ich kann allerdings nicht sämtliche Arduino Doku im Kopf haben. Daher 
mußt Du mir schon zugestehen, daß ich manchmal auch dazu frage. Fragen 
stellen darf man ja wohl noch und ist keine Kritik!

von Peter D. (peda)


Lesenswert?

Kai schrieb:
> byte Ser_Arr[31];

Sicher, daß eine Page 31 Byte groß ist?

Ich kenne nur EEPROMs mit 32 Bytes per Page.

von Kai (ksb)


Lesenswert?

Hallo nemopuk, Helmut, Wastl und peda,

vielen Dank für eure Antworten und das hohe Niveau der Antworten.

Nemopuk:
1
Nachher:
2
> 20 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F 30  48
3
Die 21 fehlt, alle folgenden Bytes sind an der falschen Adresse.
4
5
> 91 92 93 94 95 96 97 97 99 9A 9B 9C 9D 9E 9F FF  160
6
Eine der beiden 97 müsste den Wert 98 haben.
7
8
Ich würde immer noch versuchen, einen großzügigen Delay (mehr als im 
9
Datenblatt steht) einzubauen. Nur zur Probe, ob dort der Knackpunkt 
10
liegt. Du kannst zusätzlich die Baudrate herabsetzen, wenn nötig 
11
(Pufferüberlauf).
12
13
Du hast eine leere Textdatei gezippt. Bitte reiche die richtige Datei 
14
nach.

Die doppelt vorhandene 97 habe ich gestern Abend tatsächlich übersehen 
und leider offenbar auch das Ergebnis des Logging nicht in die geöffnete 
Datei kopiert. Die Daten sind leider weg - ich wiederhole es gleich.

Helmut:
1
Wire.endTransmission() überträgt die Daten aus dem transmit buffer und 
2
meldet Misserfolge, das muss ausgewertet werden:
3
    0: success.
4
    1: data too long to fit in transmit buffer.
5
    2: received NACK on transmit of address.
6
    3: received NACK on transmit of data.
7
    4: other error.
8
    5: timeout

Vielen Dank für diese Information, welche mir bisher fehlte.

Peda:
1
Nun, vor dem Schreiben oder Lesen mußt Du doch erstmal die Adresse 
2
senden. Damit ist automatisch gesichert, daß der EEPROM ein vorheriges 
3
Schreiben beendet hat.
4
Ein EEPROM der busy ist, stellt sich tot, d.h. antwortet mit NACK auf 
5
seine Adresse. Ich versuche es dann einfach in einer Schleife nochmal, 
6
die bei 400kHz I2C-Takt bis zu 10ms wartet.
7
8
Man muß natürlich vor jedem Byte abtesten, ob ein Pageüberlauf erfolgt 
9
und dann erstmal die aktuelle Page wegschreiben.

Deine I2C-Lib ist natürlich die Königsklasse - wahrscheinlich würde ich 
mir bei der Umsetzung für meine Applikation aber viele neue Probleme 
einhandeln (RTC, LCD)?
Bedeutet dies, dass das Warten auf ein ACK am Ende des Schreibvorganges 
nicht erforderlich ist?

Zunächst werde ich versuchen, ein ähnliches Ergebnis wie gestern zu 
loggen.

Eine Anmerkung noch:
a) zwecks möglichst schnellen Abholens der seriellen Daten und
b) damit mir auf dem I2C-Bus nichts dazwischenfunkt
wird während der gesamten Übertragung die while-Schleife nicht 
verlassen.

Viele Grüße und einen schönen Sonntag
Kai

: Bearbeitet durch User
von Kai (ksb)


Lesenswert?

Peter D. schrieb:
> Kai schrieb:
>> byte Ser_Arr[31];
>
> Sicher, daß eine Page 31 Byte groß ist?
>
> Ich kenne nur EEPROMs mit 32 Bytes per Page.

Hallo peda,

bei diesen 30 Elementen handelt es sich um die Größe des Puffers der 
Wire-Bibliothek. Diese Informatíon hatte ich von Wolles Elektronikkiste 
übernommen:
[[https://wolles-elektronikkiste.de/eeprom-teil-2-externe-i2c-eeproms]]


Viele Grüße
Kai

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Peter D. schrieb:
> Kai schrieb:
>> byte Ser_Arr[31];
>
> Sicher, daß eine Page 31 Byte groß ist?
>
> Ich kenne nur EEPROMs mit 32 Bytes per Page.

Das verwendete hat offensichtlich 64 Byte per Page.
Der Wire Buffer hat nur 32 Byte
Unser Klient möchte nur 30 pro Transfer übertragen.

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

Kai schrieb:
> Bedeutet dies, dass das Warten auf ein ACK am Ende des Schreibvorganges
> nicht erforderlich ist?

Nochmal, auf das ACK kann man nicht warten, der Master kann das ACK nur 
abfragen (pollen).
Ich würde eine eigene Funktion zum Setzen der I2C-Adresse empfehlen. 
Diese sendet in einer Schleife STA+Adresse+STO, bis entweder ein ACK 
kommt oder ~10ms rum sind. Bei ACK entfällt natürlich das nachfolgende 
STO.
Eine feste Wartezeit würde ich nicht empfehlen, da dann das Schreiben 
deutlich länger braucht.

von Peter D. (peda)


Lesenswert?

Arduino F. schrieb:
> Unser Klient möchte nur 30 pro Transfer übertragen.

Dann muß aber "page_write" einen Pageüberlauf mit behandeln.
Schade, das "page_write" nicht gezeigt wird, das wäre schon sehr 
wichtig.

von Kai (ksb)


Lesenswert?

Kai schrieb:
> void page_write(unsigned int addr, unsigned int number){
>   Wire.beginTransmission(EXT_EE_ADDR);          // Start transmission to
> I2C address
>   Wire.write((byte)(addr>>8));                  // Address eeprom high
>   Wire.write((byte)(addr&0xFF));                // Address eeprom low
>   for(unsigned int i=0; i<number; i++){         // Write all bytes
> allowed on this page
>   Wire.write((byte)(Ser_Arr[i]));}              // Byte from array
>   Wire.endTransmission();                       // End transmission
>   delay(5);                                     // Important
> }

Hallo peda,

das war die ursprüngliche Version von page-write (hatte ich weiter oben 
nachgereicht).

von Peter D. (peda)


Lesenswert?

Kai schrieb:
> das war die ursprüngliche Version von page-write

Wie ich befürchtet habe. Es wird kein einziges mal der Status überprüft.
Ein Pageüberlauf wird auch nicht behandelt. Bei einem Pageschreiben 
werden nur die unteren Adreßbits hochgezählt. Ein Datenbyte wird also 
wieder an die erste Adresse der Page geschrieben.

von Kai (ksb)



Lesenswert?

Hallo nemopuk,

anbei die Log-Datei einer erfolgreichen und einer fehlerhaften 
Übertragung - an letzter Position ist das Ergebnis von 
Wire.EndTransmission angehängt.

Mittlerweile bin ich aber ziemlich sicher, dass der Fehler - wie bereits 
anfangs von dir vermutet - in der Berechnung der Variable cnt liegen 
muss,
denn diese ist im Fehlerfall < 160. Diese Variable wird aber durch ein 
fehlerhaftes Schreiben des eeproms überhaupt nicht beeinflusst.

Viele Grüße
Kai

: Bearbeitet durch User
von Nemopuk (nemopuk)


Lesenswert?

Kai schrieb:
> Mittlerweile bin ich aber ziemlich sicher, dass der Fehler - wie bereits
> anfangs von dir vermutet - in der Berechnung der Variable cnt liegen
> muss,
> denn diese ist im Fehlerfall < 160. Diese Variable wird aber durch ein
> fehlerhaftes Schreiben des eeproms überhaupt nicht beeinflusst.

Aha, jetzt kommen wir der Sache näher. Mir fällt da auf, dass page_write 
viel zu oft aufgerufen wird. Du wolltest doch große Blöcke zum EEprom 
übertragen (30 Bytes, nicht 2-3 Bytes), oder nicht?

: Bearbeitet durch User
von Kai (ksb)


Lesenswert?

Hallo peda,
1
Wie ich befürchtet habe. Es wird kein einziges mal der Status überprüft.
2
Ein Pageüberlauf wird auch nicht behandelt. Bei einem Pageschreiben 
3
werden nur die unteren Adreßbits hochgezählt. Ein Datenbyte wird also 
4
wieder an die erste Adresse der Page geschrieben.

Richtig - der Status wurde zu diesem Zeitpunkt nicht abgefragt - habe 
ich ergänzt - page write kommt immer mit dem Status 0 zurück.
Ein Pageüberlauf wird dadurch verhindert, dass vor Aufruf des page-write 
die Anzahl der auf dieser Page noch verfügbaren Bytes berechnet und nur 
die zulässige Anzahl im Aufruf übergeben wird.

Hallo Nemopuk,

ja - das wollte ich - offenbar kommen aber die Daten von der seriellen 
Schnittstelle viel langsamer als erwartet.

Übrigens ist mir leider ein Fehler bei der Erstellung der Testdaten 
passiert: die doppelte 95 war auch bereits im ersten Post des 
RAM-Inhaltes vorhanden - ich bitte dafür vielmals um Entschuldigung!

Viele Grüße
Kai

: Bearbeitet durch User
von Nemopuk (nemopuk)


Lesenswert?

Der ganze Algorithmus ist mir zu komplex. Ich durchblicke ihn nicht, du 
(Kai) offenbar auch nicht, und auch sonst hat noch keiner den Fehler 
darin gefunden.

Der Haupt-Fehler ist also dein undurchsichtiger Algorithmus. Warum so 
kompliziert, wenn es auch einfach geht? Schmeiß den Algorithmus weg und 
fange nochmal von vorne an.

Es ist echt blöd, dass der I²C Puffer von Arduino kleiner als die page 
size ist. Das macht die Sache komplizierter, als sie sein könnte. 
Vielleicht magst du auf ein anderes EEprom wechseln, das maximal 32 
Bytes pro Page hat. Oder du benutzt nicht Wire, um größere Puffer 
benutzen zu können. Schau dir auch mal 
https://github.com/sparkfun/SparkFun_External_EEPROM_Arduino_Library an. 
Man muss nicht jedes Rad neu erfinden.

: Bearbeitet durch User
von Arduino F. (Firma: Gast) (arduinof)


Angehängte Dateien:

Lesenswert?

Nemopuk schrieb:
> Der ganze Algorithmus ist mir zu komplex. Ich durchblicke ihn
> nicht, du
> (Kai) offenbar auch nicht, und auch sonst hat noch keiner den Fehler
> darin gefunden.
Ich auch nicht!


Aber kann hier ja mal meine Arduino I2C EEPROM/FRAM Baustelle rein 
werfen.
Vielleicht kann man sich ja da was abschauen, oder gar nutzen wie sie 
ist, denn sie funktioniert.

von Kai (ksb)


Lesenswert?

Hallo nemopuk, hallo Arduino F,

vielen Dank für eure Antworten und das ihr euch so intensiv mit meinem 
Problem auseinandersetzt.

@nemopuk:

ich komme noch einmal darauf zurück, dass du in deinem letzten Post 
ursprünglich sinngemäß geschrieben hattest:
"weshalb speicherst du nicht einfach alle Daten für eine Page im RAM und 
schreibst sie anschließend in einem Rutsch ins EEPROM".

Das war auch mein Ansatz. Ich schildere diesen noch mal, in der 
Hoffnung, dass ich dann selbst auf meinen Fehler komme:

1. Die ersten Zeilen handeln den Empfang der seriellen Schnitstelle. Die 
Routine darf bis zum Ende der Übertragung nicht verlassen werden,
   da andernfalls während der Loop zu viele Daten im seriellen Puffer 
eintreffen (Dazu dient ein Timer).
   Es müssen mindestens 2 Zeichen (= 1Byte) eingetroffen sein.
1
  while ((Serial.available() >= 2) ||           // As long as data are available
2
          (receive_timer >0)) {                 // Or receive timeout is running
3
4
    if (!rx_run){receive_timer=399;rx_run=1;}   // Set receive timeout counter (only once) !
5
    if (receive_timer){receive_timer--;}        // Decrease receive timeout counter

2. Jeweils 2 Zeichen werden in eine Hexadezimalzahl gewandelt.
1
    for (int i=0; i < x/2; i++){                // Process total number of bytes (=2 nibbles) received 2 4 6 8
2
3
      byte y = Serial.available();              // Current buffer state = no of char!
4
      if (y > serbuf){serbuf=y;}                // Buffer max size
5
6
      nibble = text2hex(Serial.read());         // read and convert 1. char = high nibble
7
      hi = nibble;                              // High nibble  
8
9
      nibble = text2hex(Serial.read());         // read and convert 2. char = low nibble
10
      lo = nibble;                              // Low nibble 
11
12
      val = hi<<4 | lo;                         // Combine hi + low = value

3. Die maximal erlaubte Anzahl von Bytes (Puffer- oder Page-Grenze) wird 
berechnet:
1
      bytes=bytes_in_page(addr+cnt);            // Determinate max. number allowed in this page


4. Solange keine der Grenzen (Wire-Puffer, Page) erreicht ist, werden 
die Daten in ein Array geschrieben (x ist die Anzahl der verfügbaren 
Zeichen:
1
      bytes=bytes_in_page(addr+cnt);            // Anzahl der max. erlaubten Bytes
2
 
3
      if ((cur_byte < x/2) &&                   // Not all data processed             | -> collect data
4
          (cur_byte < bytes)){                  // Below Buffer or page-write border  |
5
        Ser_Arr[cur_byte]= val;                 // Add data to page-write buffer      |
6
        cur_byte++;                             // Next index                         |
7
        }


5. Sobald eine der Grenzen (Wire-Puffer, Page) erreicht ist, werden die 
Daten ins EEPROM geschrieben
1
      if ((cur_byte == x/2)  ||                 // current count = received bytes      | -> page write
2
          (cur_byte == bytes)){                 // Buffer or page-write border reached |
3
        page_write(addr+cnt,cur_byte);          // Perform page-write to ext ee        |
4
        //debug_page_wr(addr);                  // DEBUG! show page write status       | 
5
        cnt=cnt+cur_byte;                       // Increase address by bytes added     | -> prepare next packet
6
        cur_byte = 0;                           // No Bytes left                       |
7
        }
8
      }

Beim Schreiben fallen mir nun folgende mögliche Fehlerursachen ein:

a) da der Timeout aktuell nur am Anfang der Übertragung gesetzt wird, 
könnte dieser vor dem Ende der Übertragung ablaufen.

b) da die Abfrage 1 auf Werte >= 2 abfragt, könnte die Wandlung 2) 
fehlschlagen.

c) Es könnte irgendein Interrupt die korrekte Adressberechnung stören.

Zurzeit arbeite ich daran, ein Logging der zu schreibenden und der 
tatsächlich geschriebenen Daten zu implementieren.
Erstes Zwischenergebnis ist, dass das wahrscheinlich immer erste Byte 
eines neuen Datenpaketes falsch geschrieben wird.
Zur Verifikation möchte ich aber erst einmal eine größere Anzahl von 
Übertragungen analysieren.

Viele Grüße

Kai

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

Nemopuk schrieb:
> https://docs.arduino.cc/language-reference/en/functions/communication/wire/

So wie ich das sehe, ist endTransmission() das eigentliche Arbeitspferd, 
alles andere füllt nur einen SRAM-Puffer.
Allerdings verstehe ich die recht knappe Beschreibung nicht ganz. Was 
bedeutet "sends a restart message". Wird bei einem Fehler das ganze 
Paket nun nochmal gesendet oder was?

Die Lib verwendet das HW-I2C und Interrupts. Allerdings bleibt das 
endTransmission() in einer Schleife hängen, bis Stop gesendet wurde. Es 
ist also keinen Deut schneller, als meine Lib mit SW-I2C.
Ich hab nur mal grob unter die Oberfläche gestöbert. Es ist ja doch ein 
riesen Klotz an Code.

Wenn ich das I2C noch für andere Sachen brauche, gebe ich dem EEPROM 2 
separate Pins, d.h. er hat ein I2C nur für sich. EEPROM ist ja typisch 
nicht zeitkritisch, kann also durch alles andere unterbrochen werden. 
Auch spart es Speicher, weil keine Doppelpufferung benötigt wird.
Und wenn noch viele andere Leitungen mit am I2C hängen, kann der EEPROM 
leicht gestört werden und falsche Daten schreiben. I2C kennt ja keine 
CRC. Ich speichere daher über einzelne Datenblöcke auch immer eine CRC 
mit im EEPROM.

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Peter D. schrieb:
>  Was
> bedeutet "sends a restart message". Wird bei einem Fehler das ganze
> Paket nun nochmal gesendet oder was?
Nein, das Flag entscheidet ob Wire.endTransmission(flag) eine  Stopp 
oder Repeated-Start Condition auf dem Bus setzt.

: Bearbeitet durch User
von Loco M. (loco)


Lesenswert?

Kai schrieb:
> Zurzeit arbeite ich daran, ein Logging der zu schreibenden und der
> tatsächlich geschriebenen Daten zu implementieren.
> Erstes Zwischenergebnis ist, dass das wahrscheinlich immer erste Byte
> eines neuen Datenpaketes falsch geschrieben wird.
> Zur Verifikation möchte ich aber erst einmal eine größere Anzahl von
> Übertragungen analysieren.

Was ist für dich ein neues Datenpaket? Ein neues Datenfile (0..9F), oder 
wird dein vorbereitetes File vom PC in einzelnen Häppchen verschickt?

Wenn du wirklich Hilfe willst, dann erwarte ich, dass du ein 
vollständiges und kompilierbares Programm (ino) zeigst, welches das 
Problem aufweist.
Dein reduziertes "Test.ino" File ist nicht kompilierbar, und keinem hier 
ist bekannt welchen Einfluss die nicht gezeigten Softwareteile ausüben 
könnten. Was du bisher gezeigt hast sind immer nur einzelne Brocken und 
nicht das Gesamtbild.

von Nemopuk (nemopuk)


Lesenswert?

Peter D. schrieb:
> Es ist also keinen Deut schneller, als meine Lib mit SW-I2C.

Das ist wohl so. Allerdings haben die Pins (soweit ich weiß) 
Entstörfilter, von denen eine Software Implementierung mit Bitbanging 
nicht profitieren kann. Was je nach Anwendung völlig egal ist.

von Gerald B. (gerald_b)


Lesenswert?

Du schreibst was davon, das du I2C nicht nur fürs EEPROM, sondern auch 
für RTC und LCD nutzt. Ist das alles auf einem Board, oder "irgendwie" 
funktionierend an 2 Meter Kabel zum Display angebunden?
Man kann sich über Normen hinwegsetzen, oft funktioniert das auch. Wenn 
dann plötzlich die Komplexität ansteigt, gibt es unvermittelt Probleme, 
die "unerklärlich" sind.
Hast du schon mal mit den Pullups rumgespielt? (verkleinert)
Weil wenn du bsp. auf der Leitung Echos hast, kann es sein, das das 
Display damit klar kommt, dein EEPROM aber nicht. Also testweise mal 
entbehrliche Busteilmehmer abklemmen und im Code auskommentieren und 
gucken, ob der Schreibzugriff dann funktioniert.

von Nemopuk (nemopuk)


Lesenswert?

Seine zählvariablen haben schon falsche Werte. Das kann nicht an der I2C 
Kommunikation liegen.

von Kai (ksb)


Lesenswert?

Hallo Loco M, hallo Gerald B,

@Loco:

Für mich ist diesem Fall ein neues Datenpaket ein neues Aufrufen des 
Page-Write mit bis zu 30 Bytes.

Ich kann deine Erwartung vollkommen verstehen, jedoch ist das 
vollständige Programm mittlerweile 32 kB groß.
Die Steuerung der Funktionen erfolgt über eine Menüstruktur.

Das Problem tritt in dem Programmteil auf, in welcher die serielle 
Datenübertragung an das EEPROM erfolgt.
Um alle möglichen Nebeneffekte auszuschließen, wird diese Routine für 
die Dauer eines Timeout nicht verlassen.


@ Gerald B:
1
Du schreibst was davon, das du I2C nicht nur fürs EEPROM, sondern auch 
2
für RTC und LCD nutzt. Ist das alles auf einem Board, oder "irgendwie" 
3
funktionierend an 2 Meter Kabel zum Display angebunden?
4
Man kann sich über Normen hinwegsetzen, oft funktioniert das auch. Wenn 
5
dann plötzlich die Komplexität ansteigt, gibt es unvermittelt Probleme, 
6
die "unerklärlich" sind.
7
Hast du schon mal mit den Pullups rumgespielt? (verkleinert)
8
Weil wenn du bsp. auf der Leitung Echos hast, kann es sein, das das 
9
Display damit klar kommt, dein EEPROM aber nicht. Also testweise mal 
10
entbehrliche Busteilmehmer abklemmen und im Code auskommentieren und 
11
gucken, ob der Schreibzugriff dann funktioniert.

Es gibt 2 Varianten des Aufbaus:

a) eine fliegende mit Arduino Mega (Kabellänge ca. 25cm)
   Der Arduino Mega hat gemäß Dokumentation 10k-Pullups.

b) eine doppelseitige Platine mit 328P (Gesamtlänge des I2C ca. 10cm)
   Es befinden sich 10k-Pullup in der Mitte des Busses.

Der Fehler tritt identisch bei beiden Aufbauten auf.

Abklemmen des LCD ist nicht möglich, da das Gerät dann nicht mehr 
bedienbar ist.
Code auskommentieren ist aus dem selben Grund nur eingeschränkt möglich.

@nemopuk:

genau - deshalb kämpfe ich noch mit dem Logging…..

Sobald das Logging läuft, weis ich mehr und werde berichten.

Viele Grüße
Kai

von Christian M. (christian_m280)


Lesenswert?

Kai schrieb:
> Der Arduino Mega hat gemäß Dokumentation 10k-Pullups.

Kai schrieb:
> Es befinden sich 10k-Pullup in der Mitte des Busses.

Aha. Viel zu hoch! Und nimm Zwei!

Gruss Chregu

von Falk B. (falk)


Lesenswert?

Christian M. schrieb:
> Kai schrieb:
>> Es befinden sich 10k-Pullup in der Mitte des Busses.
>
> Aha. Viel zu hoch! Und nimm Zwei!

Jain. Die meisten Sachen laufen auch mit 10k Pull Ups und 100kHz. Bei 
allem was man hier so gelesen hat, liegt der Fehler zu 99% in der 
Software.

I2C ist mit max. 3mA bei LOW definiert, also min. 1,6k bei 5V. 2-4,7k 
sind OK.

von Kai (ksb)


Angehängte Dateien:

Lesenswert?

Hallo Christian M und Falk B,

auch vielen Dank für eure Antworten.
1
Jain. Die meisten Sachen laufen auch mit 10k Pull Ups und 100kHz. Bei 
2
allem was man hier so gelesen hat, liegt der Fehler zu 99% in der 
3
Software.

Definitiv - ich war sicher, dass der Fehler in meiner Software liegt 
daher auch meine Frage hier im Forum. Der Wert der Byte Zählvariable cnt 
hat im Fehlerfall einen zu geringen Wert (z. B. 158 anstelle 160).

Manchmal sind 10 Übertragungen hintereinander in Ordnung, mal nicht.

Ich vermute den Fehler beim Sammeln der Daten - hier wird offenbar 
manchmal die Variable cur_byte nicht erhöht:
1
      if ((cur_byte < x/2) &&    // Not all data processed  | collect data
2
          (cur_byte < bytes)){   // Below page-write border |
3
        Ser_Arr[cur_byte]= val;  // Add data to pagebuffer  |
4
        cur_byte++;              // Next index              |
5
        }

Mittlerweile funktioniert das Logging auch (siehe Anhang).

Das Ergebnis ist, dass die fehlenden Daten auch nicht geschrieben 
wurden.
Die Routine verschluckt manchmal ein von der Schnittstelle korrekt 
gelesenes Byte (siehe RAM-Inhalt). Nach der Ursache suche ich gerade.

Viele Grüße
Kai

von Veit D. (devil-elec)


Lesenswert?

Hallo,

reduziere den Code soweit runter, dass du nur noch auf das EEprom 
schreibst und liest. Erstelle eine Tabelle im RAM womit du auf das 
EEprom Testdaten schreibst die dann wieder gelesen werden. Wenn es dann 
noch den Fehler zeigt, zeige den testbaren Sketch und den 
Hardwareaufbau. Schaltplan, Kabellängen usw. Zur Zeit macht das keinen 
Spass hier durchblicken zu wollen.

von Nemopuk (nemopuk)


Lesenswert?

Nochmal: Es scheitert nicht an den EEprom Zugriffen, sondern an dem 
Algorithmus für die Zähler und Adressen. Der Fehler steckt in dem 
undurchsichtigen Algorithmus.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

letzter Schuss ins Blaue ohne Angabe zum EEprom, Sketch usw.

Beachtest du wirklich die Pagegrenzen korrekt?
Sieht so aus wenn du 30 Bytes schreibst aber mit fortlaufender Adresse.
Angenommen dein EEprom hat 64Byte Pagegröße.
Dann liegen die in den Bereichen
1
  0 …  63 - Page 0
2
 64 … 127 - Page 1
3
128 … 191 - Page 2
4
192 … 254 - Page 3
5
usw.
Wenn du das EEprom mit 0xFF füllst, dann müssten mit 30 Byte Daten pro 
Page folgende Adressen 0xFF zeigen.
1
 0 …  29 - deine Daten
2
30 …  63 - 0xFF
3
64 …  93 - deine Daten
4
94 … 127 - 0xFF
5
usw.
Ansonsten schreibst du über die Pagegrenzen hinweg, spätestens mit dem 
3. Satz mit 30 Bytes.

Schreib das einmal ohne Page Write, jedes Byte einzeln.

von Kai (ksb)


Angehängte Dateien:

Lesenswert?

Hallo Veit D,

die Anzahl der für die jeweilige Page erlaubten Bytes wird vor dem 
Aufruf von Page-Write berechnet. Dieser wurde unabhängig von der 
Datenübertragung mehrfach getestet und funktioniert.

Anbei das Ergebnis des page write:

Page write test 22000 181
EEPROM
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F  16
10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F  32
20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F  48
30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F  64
40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F  80
50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F  96
60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F  112
70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F  128
80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F  144
90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F  160
A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF  176
B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF  192
C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF  208
D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF  224
E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF  240
F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF  256
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F  272
10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F  288
20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F  304
30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F  320
40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F  336
50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F  352
60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F  368
70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F  384
80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F  400
90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F  416
A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF  432
B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF  448
C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF  464
D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF  480
E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF  496
F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF  512

Das im fliegenden Aufbau mit Arduino Mega verwendete EEPROM ist dieses:

[[https://www.reichelt.de/de/de/shop/produkt/entwicklerboards_-_32_kb_eeprom-modul_i2c-334921?PROVID=2788&gad_source=1&gad_campaignid=1622074784&gclid=EAIaIQobChMIgc7a5K39jQMVUoSDBx1fPjByEAQYAiABEgJzxPD_BwE]]

Das Problem tritt genauso im Aufbau mit ATMEGA328P auf.

Viele Grüße
Kai

: Bearbeitet durch User
von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Wenn ich das richtig sehe hat jedes der Module und der Mega 10k Pullup.
Das macht insgesamt 2k. Evtl. etwas wenig.

Ist auf dem RTC Board nicht noch ein EEPROM?
Im bitteren Fall gar mit der gleichen Adresse?

: Bearbeitet durch User
von Veit D. (devil-elec)


Lesenswert?

Hallo Kai,

greife den Gedanken von Arduino F. auf und baue zusätzlich alles 
unnötige ab. Sodass du nur den Kern des Problem testest. Rüste ab, teste 
und wenn immer noch nicht zeige den auf das Problem reduzierten Sketch. 
Dann kann das Forum bestimmt helfen.

von Peter D. (peda)


Lesenswert?

Arduino F. schrieb:
> oder Repeated-Start Condition auf dem Bus setzt.

Das ist ja blöd, was soll das bringen?
Man muß doch im Fehlerfall das ganze Paket nochmal senden.
Es müßte also eine Funktion geben, die die Daten im Puffer wieder als 
gültig markiert.
Ansonsten muß man ja von hinten durch die Brust ins Auge alles nochmal 
präparieren.

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Peter D. schrieb:
> Arduino F. schrieb:
>> oder Repeated-Start Condition auf dem Bus setzt.
>
> Das ist ja blöd, was soll das bringen?

Du hältst es also für blöd diese Bedingungen setzen zu können?
Da solltest du nochmal drüber nachdenken!


Bedenke:
Wire handelt den I2C Bus ab, es hat keine Ahnung von EEPROM oder 
sonstigen I2C Dingern und ihre Bedürfnisse.

Mein Rat:
1. Wenn dir Wire nicht gefällt, dann nutze es nicht.
2. Bei Verbesserungsvorschlägen wende dich an das Arduino Team. z.B. 
ändere Wire auf Github. Und Ja, dann hat jeder was davon.

von Peter D. (peda)


Lesenswert?

Arduino F. schrieb:
> Du hältst es also für blöd diese Bedingungen setzen zu können?

Ja.
Ich wüßte nicht, wie ein Repeated-Start bei einem Fehler weiter helfen 
soll.
Es setzt einfach nur den Bus auf blockiert.

Das Repeated-Start wird nur benötigt, um nach der EEPROM-Adresse auf 
Lesen umzuschalten, also wenn eben kein Fehler aufgetreten ist.

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Peter D. schrieb:
> Das Repeated-Start wird nur benötigt, um nach der EEPROM-Adresse auf
> Lesen umzuschalten, also wenn eben kein Fehler aufgetreten ist.

Damit widersprichst du dir selber!
1. Du findest es blöd
2. Du hältst es für notwendig


Die irrsinnige Fantasie, dass das repteated in der Doku irgendwas mit 
Fehlern zu tun hat, existiert nur in deinem Kopf.

: Bearbeitet durch User
von Kai (ksb)


Lesenswert?

Veit D. schrieb:
> Hallo Kai,
>
> greife den Gedanken von Arduino F. auf und baue zusätzlich alles
> unnötige ab. Sodass du nur den Kern des Problem testest. Rüste ab, teste
> und wenn immer noch nicht zeige den auf das Problem reduzierten Sketch.
> Dann kann das Forum bestimmt helfen.

Hallo Arduino F und Veit D.,

vielen Dank für eure Tipps.

Ich habe nun alle anderen Komponenten bis auf das I2C-LCD abgebaut und 
im Programm auskommentiert. Die Page-Write-Routine liefert ohne EEPROM 
erwartungsgemäß 2 (received NACK on transmit of address) zurück.

Der Fehler tritt immer noch auf: cnt zählt sporadisch zu wenig Bytes.

Ergebnisse I2C-Scan:
I2C-LCD    0x27
I2C-EEPROM 0x50 und 0x58
I2C-RTC    0x57 und 0x68

Ich werde gleich ein Minimalprogramm erstellen, welches nur noch die 
relevanten Programmteile enthält und sobald es läuft anhängen.

Viele Grüße
Kai

von Christian M. (christian_m280)


Lesenswert?

Kai schrieb:
> Ich werde gleich ein Minimalprogramm erstellen, welches nur noch die
> relevanten Programmteile enthält

Macht man das nicht als Allererstes? :-O

Gruss Chregu

von Peter D. (peda)


Lesenswert?

Kai schrieb:
>  while ((Serial.available() >= 2) ||
>          (receive_timer >0)) {

Ich hasse Protokolle mit Timeout. Das gibt eigentlich immer Ärger, wenn 
nicht gleich, dann später.
Für Textnachrichten bietet es sich an, das <CR> als Endekennzeichen zu 
benutzen. Man sammelt also einfach die Bytes und nach <CR> fängt man 
erst an, die Nachricht zu parsen.
Hier mal ein Beispiel:
1
void receive_command(void)                              // receive from UART
2
{
3
  static char cbuf[CMD_MAX_LEN];                        // command buffer
4
  static uint8_t idx = 0;
5
  uint8_t ch;
6
7
  if (ukbhit0() == 0)
8
    return;
9
  ch = ugetchar0();
10
  switch (ch)
11
  {
12
    default:
13
      cbuf[idx] = ch;
14
      if (idx < sizeof(cbuf) - 1)
15
        idx++;
16
      return;
17
    case '\b':
18
      if (idx)
19
        idx--;                          // remove last char (interactive mode)
20
      return;
21
    case '\n':
22
    case '\r':
23
      if (idx == 0)                     // do nothing on empty commands
24
        return;
25
      cbuf[idx] = 0;
26
      idx = 0;
27
      execute(cbuf);
28
      uputchar0('\n');
29
#ifdef APPEND_CR
30
      uputchar0('\r');
31
#endif
32
      uputs0(cbuf);                     // send answer
33
  }
34
}

von Klaus (feelfree)


Lesenswert?

>    x = Serial.available();                     // Store total number of char 
received (max. 64)
>    for (int i=0; i < x/2; i++){                // Process total number of bytes 
(=2 nibbles) received 2 4 6 8


Ohne jetzt genauer nachgedacht zu haben: Ist der Fall "ungerades x" 
sauber abgedeckt?

von Kai (ksb)


Angehängte Dateien:

Lesenswert?

Hallo Christian M., hallo Klaus,

Christian M. schrieb:
> Kai schrieb:
>> Ich werde gleich ein Minimalprogramm erstellen, welches nur noch die
>> relevanten Programmteile enthält
>
> Macht man das nicht als Allererstes? :-O
>
> Gruss Chregu

Das Programm ist immer weiter gewachsen und mehr Komponenten sind 
hinzugekommen. Bisher konnte ich alle Probleme innerhalb von einigen 
Stunden lösen. Nachdem die auf dem Chip gespeicherte Datenmenge immer 
grösser wurden, entstand dann zuletzt mein Wunsch, diese Daten "mal 
eben" auf dem PC zu sichern. Das Sichern klappt auch prima.

Beim Rücklesen in den Chip fing dann mein Problem an:
leider ist da das Byte-Write viel zu langsam und ich musste auf 
Page-Write umschwenken - und da hänge ich seit mittlerweile mehreren 
Tagen....

Klaus schrieb:
>>    x = Serial.available();                     // Store total
> number of char
> received (max. 64)
>>    for (int i=0; i < x/2; i++){                // Process total number of bytes
> (=2 nibbles) received 2 4 6 8
>
> Ohne jetzt genauer nachgedacht zu haben: Ist der Fall "ungerades x"
> sauber abgedeckt?

vielen Dank für diesen Hinweis - noch nicht, ist aber als nächster 
Schritt vorgesehen.

Mittlerweile ist das Minimalprogramm fertig (siehe Anhang). Es läuft auf 
einem Arduino Uno, sollte auch auf einem Mega laufen. Angeschlossen ist 
nur das I2C-EEPROM von Reichelt:

[[https://www.reichelt.de/de/de/shop/produkt/entwicklerboards_-_32_kb_eeprom-modul_i2c-334921?PROVID=2788&gad_source=1&gad_campaignid=1622074784&gclid=EAIaIQobChMIgc7a5K39jQMVUoSDBx1fPjByEAQYAiABEgJzxPD_BwE]]

Am Anfang wird ein I2C-Scan durchgeführt, welcher die Adressen 0x50 und 
0x58 findet.
Hier sind die Testdaten, welche in den seriellen Monitor kopiert werden 
müssen:
1
000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9F

Ergebnis: Der Fehler ist bisher nicht wieder aufgetreten - das Programm 
macht genau das, was es soll:
1
Setup fertig
2
3
I2C-Scanner: 
4
0x50 0x58 
5
2 Addresses found 
6
7
Setup Ende
8
9
Bitte Testdaten 00-9F senden
10
Bytes left 0 Max. Buffer 21 cnt 160 OK 
11
12
EEPROM Settings
13
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F  16
14
10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F  32
15
20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F  48
16
30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F  64
17
40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F  80
18
50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F  96
19
60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F  112
20
70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F  128
21
80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F  144
22
90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F  160
23
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF  176
24
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF  192
25
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF  208
26
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF  224
27
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF  240
28
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF  256
29
30
RAM rx_test
31
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F  16
32
10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F  32
33
20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F  48
34
30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F  64
35
40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F  80
36
50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F  96
37
60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F  112
38
70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F  128
39
80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F  144
40
90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F  160
41
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  176
42
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  192
43
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  208
44
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  224
45
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  240
46
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  256

Es bleibt nun die entscheidende Frage:
Was könnte den beschriebenen Fehler, dass eine Variable nicht hochzählt 
in einem Programm auslösen?

Viele Grüße
Kai

von Falk B. (falk)


Lesenswert?

Kai schrieb:
> Was könnte den beschriebenen Fehler, dass eine Variable nicht hochzählt
> in einem Programm auslösen?

DU!

von Kai (ksb)


Lesenswert?

Falk B. schrieb:
> Kai schrieb:
>> Was könnte den beschriebenen Fehler, dass eine Variable nicht hochzählt
>> in einem Programm auslösen?
>
> DU!

Hallo Falk,

Hm - ja - sicher - wer auch sonst - aber es läuft zu diesem Zeitpunkt 
nur der angehangene  Programmteil, welcher alleine das Verhalten nicht 
zeigt.

Meine Vermutung geht in Richtung irgendwelcher Interrupts, welche das 
letzte hochzählen von cur_bytes verhindert....

Viele Grüße

Kai

von Kai (ksb)


Lesenswert?

Hallo,

Nachtrag:

1. den seriellen Monitor auf 9600 BAUD + kein Zeilenende einstellen.
2. Für mehrere Versuche hintereinander am Ende einfügen:
1
    rx_run=0;                          // Clear rx run flag
2
    ram_idx = 0;                       // Reset receive ram index
3
    cnt = 0;                           // Clear counter for next try
4
    ext_ee_clear();                    // Erase EEPROM
5
    }
6
}
Viele Grüße
Kai

von Nemopuk (nemopuk)


Lesenswert?

Kai schrieb:
> Meine Vermutung geht in Richtung irgendwelcher Interrupts, welche das
> letzte hochzählen von cur_bytes verhindert....

Interrupts verhindern keine Schreibzugriffe auf das RAM.

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Kai schrieb:
> Am Anfang wird ein I2C-Scan durchgeführt,
> welcher die Adressen 0x50 und 0x58 findet.

Das EEPROM hat keine 2 Adressen.
Da ist doch was faul.

: Bearbeitet durch User
von Veit D. (devil-elec)


Lesenswert?

Arduino F. schrieb:
> Kai schrieb:
>> Am Anfang wird ein I2C-Scan durchgeführt,
>> welcher die Adressen 0x50 und 0x58 findet.
>
> Das EEPROM hat keine 2 Adressen.
> Da ist doch was faul.

Kann das nicht mit dem I2C High Speed Modus zusammenhängen? Mein FRAM 
reagiert beim Scan auch mit einer 2. Adresse als Antwort.

von Kai (ksb)


Lesenswert?

Hallo Arduino F,

Arduino F. schrieb:
> Kai schrieb:
>> Am Anfang wird ein I2C-Scan durchgeführt,
>> welcher die Adressen 0x50 und 0x58 findet.
>
> Das EEPROM hat keine 2 Adressen.
> Da ist doch was faul.

Google meint zu dem Thema:

Übersicht mit KI
Ein I2C EEPROM kann tatsächlich zwei Adressen anzeigen, wenn es sich um 
ein Gerät mit konfigurierbaren Adresspins handelt, die über Lötpads oder 
Jumper eingestellt werden können. Die eine Adresse ist die, die für die 
normale Kommunikation mit dem Arduino verwendet wird, und die andere ist 
eine alternative Adresse, die durch die Einstellung der Adresspins 
aktiviert wird.

Viele Grüße
Kai

von Falk B. (falk)


Lesenswert?

Kai schrieb:
> Übersicht mit KI
> Ein I2C EEPROM kann tatsächlich zwei Adressen anzeigen, wenn es sich um
> ein Gerät mit konfigurierbaren Adresspins handelt, die über Lötpads oder
> Jumper eingestellt werden können. Die eine Adresse ist die, die für die
> normale Kommunikation mit dem Arduino verwendet wird, und die andere ist
> eine alternative Adresse, die durch die Einstellung der Adresspins
> aktiviert wird.

Ist Käse. Aber wenn deine Adresspins am EEPROM nicht fest auf GND oder 
VCC liegen, wackelt die Adresse und dein EEPROM reagiert zufällig.

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Veit D. schrieb:
> Arduino F. schrieb:
>> Kai schrieb:
>>> Am Anfang wird ein I2C-Scan durchgeführt,
>>> welcher die Adressen 0x50 und 0x58 findet.
>>
>> Das EEPROM hat keine 2 Adressen.
>> Da ist doch was faul.
>
> Kann das nicht mit dem I2C High Speed Modus zusammenhängen? Mein FRAM
> reagiert beim Scan auch mit einer 2. Adresse als Antwort.

Beim entwickeln meiner I2C EEPROM Klasse habe ich gefühlte 20 
Datenblätter auswendig gelernt.
Auch das zu diesem konkreten EEPROM.
Da drin steht nichts von 2 Adressen.
Klar: Es gibt EEPROMs, welche mit mehreren Adressen erscheinen. Aber das 
ist dann auch wohl dokumentiert.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

schalte einmal in der Arduino IDE alle Warnungen zum kompilieren ein. 
Korrigiere alle Warnungen. Es gibt mehrere unbenutzte und nicht 
initialisierte Variablen.

Kannst du bei Funktionsaufrufen fehlerhafte Parameterübergaben 
ausschließen?
Worauf ich hinaus möchte, sollte eine minimale Prüfung eingebaut werden 
bevor Werte weiterverarbeitet werden? Könnte im dümmsten Fall zur 
falschen Anzeige führen.

Was schlimmer ist.
Du hast in der Einlesefunktion
3x Serial.available()
2x Serial.read()

Warum das denn?
Warum erste Bedingung >=2 ?

Ich glaube dir fehlt ein Übertragungsprotokoll.
Solange einlesen bis ein definiertes Ende erkannt wurde. Standardmäßig 
Linefeed.
Dann eingelesene Daten verarbeiten. Bis dahin in einen Buffer einlesen.

von Kai (ksb)


Angehängte Dateien:

Lesenswert?

Hallo Falk,

Falk B. schrieb:
> Kai schrieb:
>> Übersicht mit KI
>> Ein I2C EEPROM kann tatsächlich zwei Adressen anzeigen, wenn es sich um
>> ein Gerät mit konfigurierbaren Adresspins handelt, die über Lötpads oder
>> Jumper eingestellt werden können. Die eine Adresse ist die, die für die
>> normale Kommunikation mit dem Arduino verwendet wird, und die andere ist
>> eine alternative Adresse, die durch die Einstellung der Adresspins
>> aktiviert wird.
>
> Ist Käse. Aber wenn deine Adresspins am EEPROM nicht fest auf GND oder
> VCC liegen, wackelt die Adresse und dein EEPROM reagiert zufällig.

mit Jumpern sind WP, A0-A2 auf GND gelegt.

Viele Grüße

Kai

: Bearbeitet durch User
von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Veit D. schrieb:
> Kann das nicht mit dem I2C High Speed Modus zusammenhängen? Mein FRAM
> reagiert beim Scan auch mit einer 2. Adresse als Antwort.
Bei 400 kbit müssen die Pullup "stronger" sein, als bei 100 kbit

: Bearbeitet durch User
von Veit D. (devil-elec)


Lesenswert?

Arduino F. schrieb:
> Veit D. schrieb:
>> Arduino F. schrieb:
>>> Kai schrieb:
>>>> Am Anfang wird ein I2C-Scan durchgeführt,
>>>> welcher die Adressen 0x50 und 0x58 findet.
>>>
>>> Das EEPROM hat keine 2 Adressen.
>>> Da ist doch was faul.
>>
>> Kann das nicht mit dem I2C High Speed Modus zusammenhängen? Mein FRAM
>> reagiert beim Scan auch mit einer 2. Adresse als Antwort.
>
> Beim entwickeln meiner I2C EEPROM Klasse habe ich gefühlte 20
> Datenblätter auswendig gelernt.
> Auch das zu diesem konkreten EEPROM.
> Da drin steht nichts von 2 Adressen.
> Klar: Es gibt EEPROMs, welche mit mehreren Adressen erscheinen. Aber das
> ist dann auch wohl dokumentiert.

Hallo,

ich ging davon aus das man alle I2C Decives für den High Speed Modus 
erst gezielt ansprechen muss um diesen Modus zu aktivieren. Ist bei dem 
EEprom nicht der Fall zu sein. Okay. Dann wundert es tatsächlich wo die 
2. Adresse herkommt wenn nur der Uno mit dem EEprom verkabelt ist.

Kai:
Das Display ist nicht dran?
Nur das EEprom am Bus?
Was hast du für einen Uno?
Nicht das hier ein Sonderling verwendet wird.

von Nemopuk (nemopuk)


Lesenswert?

Veit D. schrieb:
> ich ging davon aus das man alle I2C Decives für den High Speed Modus
> erst gezielt ansprechen muss um diesen Modus zu aktivieren

Kann es sein, dass du das mit SD Karten durcheinander bringst?

von Veit D. (devil-elec)


Lesenswert?

Arduino F. schrieb:
> Veit D. schrieb:
>> Kann das nicht mit dem I2C High Speed Modus zusammenhängen? Mein FRAM
>> reagiert beim Scan auch mit einer 2. Adresse als Antwort.
> Bei 400 kbit müssen die Pullup "stronger" sein, als bei 100 kbit

Ja ist klar. Reagiert dennoch mit einer Antwort auf seine 2. Adresse, 
was auch normal ist - wenn es wie mein FRAM so angesprochen werden 
möchte. Ich möchte aber den Thread damit nicht weiter ablenken.  ;-)

: Bearbeitet durch User
von Kai (ksb)


Lesenswert?

Hallo Veit D,

Veit D. schrieb:
> Hallo,
>
> schalte einmal in der Arduino IDE alle Warnungen zum kompilieren ein.
> Korrigiere alle Warnungen. Es gibt mehrere unbenutzte und nicht
> initialisierte Variablen.
>
> Kannst du bei Funktionsaufrufen fehlerhafte Parameterübergaben
> ausschließen?
> Worauf ich hinaus möchte, sollte eine minimale Prüfung eingebaut werden
> bevor Werte weiterverarbeitet werden? Könnte im dümmsten Fall zur
> falschen Anzeige führen.
>
> Was schlimmer ist.
> Du hast in der Einlesefunktion
> 3x Serial.available()
> 2x Serial.read()
>
> Warum das denn?
> Warum erste Bedingung >=2 ?
>
> Ich glaube dir fehlt ein Übertragungsprotokoll.
> Solange einlesen bis ein definiertes Ende erkannt wurde. Standardmäßig
> Linefeed.
> Dann eingelesene Daten verarbeiten. Bis dahin in einen Buffer einlesen.

1. Alle Warnungen sind eingeschaltet.

2. Meine Überlegungen dazu waren folgende:
  a)  Der Wert von Serial.available kann sich während der Bearbeitung
      vergrössern.
  b)  Das 1. Serial.available wird beim Eintritt abgefragt (0 = Ende)
      Das 2. lässt die Bearbeitung nur zu, wenn mindestens 1 Byte.
      Das 3. dient nur zur Ermittlung des max. Bufferwertes.

3. Ich benötige 2 x Serial.read, da ein Byte aus 2 ASCII-Zeichen 
besteht.
4. Die Bedingung >=2 lässt die Bearbeitung nur zu, wenn mindestens 1
   ganzes Byte (=2 ASCII-Zeichen) vorhanden sind.

Die später einzulesenden Dateien sind so groß, dass sie nicht in das RAM 
des ATMEGA328P passen. Daher werden die Daten aus einem 30Byte-Puffer 
direkt ins EEPROM geschrieben.

Viele Grüße
Kai

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Veit D. schrieb:
> Reagiert dennoch mit einer Antwort auf seine 2. Adresse,
> was auch normal ist.

Normal ist das im Datenblatt beschriebene Verhalten.
Wenn das da drin beschrieben ist, ist alles ok.
Ist es nicht beschrieben, ist da ein Problem!
Wo siehst du den Fehler in dieser Logischen Schlussfolgerung?

: Bearbeitet durch User
von Kai (ksb)


Angehängte Dateien:

Lesenswert?

Hallo Veit D.,

Veit D. schrie
> Kai:
> Das Display ist nicht dran?
> Nur das EEprom am Bus?
> Was hast du für einen Uno?
> Nicht das hier ein Sonderling verwendet wird.

Kein Display - nur das EEPROM - einen Elegoo Uno R3

Viele Grüße
Kai

Nachtrag: I2C-Clock-Frequenz ist auf 100kHz eingestellt
          DIe unbenutzten Variablen stammen aus der Version mit log

: Bearbeitet durch User
von Veit D. (devil-elec)


Lesenswert?

Arduino F. schrieb:
> Veit D. schrieb:
>> Reagiert dennoch mit einer Antwort auf seine 2. Adresse,
>> was auch normal ist - wenn es wie mein FRAM so angesprochen werden
>> möchte.
>
> Normal ist das im Datenblatt beschriebene Verhalten.
> Wenn das da drin beschrieben ist, ist alles ok.
> Ist es nicht beschrieben, ist da ein Problem!
> Wo siehst du den Fehler in dieser Logischen Schlussfolgerung?

Damit ich nichts falsches aus der Erinnerung schreibe, hier mein Thread 
von damals dazu.
Beitrag "NXP PCA8574 hat Phänomen mit 2 Adressen"

von Veit D. (devil-elec)


Lesenswert?

Kai schrieb:

> Kein Display - nur das EEPROM - einen Elegoo Uno R3
>
> Viele Grüße
> Kai
>
> Nachtrag: I2C-Clock-Frequenz ist auf 100kHz eingestellt
>           DIe unbenutzten Variablen stammen aus der Version mit log

Okay, klemme das EEprom einmal komplett ab und lasse den I2C Adress Scan 
laufen. Wird etwas erkannt?

von Kai (ksb)


Lesenswert?

Hallo Veit D.,

Veit D. schrieb:
> Kai schrieb:
>
>> Kein Display - nur das EEPROM - einen Elegoo Uno R3
>>
>> Viele Grüße
>> Kai
>>
>> Nachtrag: I2C-Clock-Frequenz ist auf 100kHz eingestellt
>>           DIe unbenutzten Variablen stammen aus der Version mit log
>
> Okay, klemme das EEprom einmal komplett ab und lasse den I2C Adress Scan
> laufen. Wird etwas erkannt?

Der I2C-Scan hängt, es wird keine Adresse gefunden:
1
Setup fertig
2
3
I2C-Scanner:
Viele Grüße
Kai

von Veit D. (devil-elec)


Lesenswert?

Hallo,

dein Scan Code zeigt an meinem nackten Arduino Mega 2560 63 gefundene 
Adressen an. Da ist was faul. Mein Scan Code zeigt nichts an.
1
#include <Wire.h>
2
3
void setup()
4
{
5
  Wire.begin();
6
  Serial.begin(9600);
7
  Serial.println("\nI2C Scanner");
8
}
9
10
void loop()
11
{
12
  byte error {255};
13
  byte address {0};
14
  int nDevices {0};
15
16
  Serial.println("Scanning...");
17
18
  for(address = 1; address < 78; address++ ) 
19
  {
20
    // The i2c_scanner uses the return value of
21
    // the Write.endTransmisstion to see if
22
    // a device did acknowledge to the address.
23
    Wire.beginTransmission(address);
24
    error = Wire.endTransmission();
25
26
    if (error == 0)
27
    {
28
      Serial.print("I2C device found at address 0x");
29
      if (address<16) 
30
        Serial.print("0");
31
      Serial.print(address,HEX);
32
      Serial.println();
33
34
      nDevices++;
35
    }
36
    else if (error==4) 
37
    {
38
      Serial.print("Unknow error at address 0x");
39
      if (address<16) 
40
        Serial.print("0");
41
      Serial.println(address,HEX);
42
    }    
43
  }
44
  if (nDevices == 0)
45
    Serial.println("No I2C devices found\n");
46
  else
47
    Serial.println("done\n");
48
49
  delay(5000);           // wait 5 seconds for next scan
50
}

Was wird damit ohne und mit EEprom angezeigt?

: Bearbeitet durch User
von Kai (ksb)


Lesenswert?

Hallo Veit D,

bei dir fehlt Wire.SetClock(100000) -danach hatte ich heute Mittag auch 
1 h gesucht ;)
1
void setup() {                // put your setup code here, to run once:  
2
3
 Wire.setClock(100000);
4
 Serial.begin(9600);


Viele Grüße
Kai

von Nemopuk (nemopuk)


Lesenswert?

Veit D. schrieb:
> dein Scan Code zeigt an meinem nackten Arduino Mega 2560 63 gefundene
> Adressen an.

Lass mich raten: Weil du keine Pull-Up Widerstande an I2C Bus hast (?)

von Veit D. (devil-elec)


Lesenswert?

Kai schrieb:
> Hallo Veit D,
>
> bei dir fehlt Wire.SetClock(100000) -danach hatte ich heute Mittag auch
> 1 h gesucht ;)
>
>
1
> void setup() {                // put your setup code here, to run once:
2
> 
3
>  Wire.setClock(100000);
4
>  Serial.begin(9600);
5
>
>
>
> Viele Grüße
> Kai

Nein, dass ist Default.

von Veit D. (devil-elec)


Lesenswert?

Nemopuk schrieb:
> Veit D. schrieb:
>> dein Scan Code zeigt an meinem nackten Arduino Mega 2560 63 gefundene
>> Adressen an.
>
> Lass mich raten: Weil du keine Pull-Up Widerstande an I2C Bus hast (?)

Nein. Auf dem Board sind Arduinoische 10k. Dafür reicht das. Außerdem 
zeigt mein Scan Code korrekt an. Ich möchte wissen was er bei Kai 
ausgibt.

von Helmut -. (dc3yc)


Lesenswert?

Und du verwendest keine guten Kabel/Steckverbinder! Das sind doch 
wahrscheinlich alles ausgeleierte Eisendrähte. Geh da mal mit einem 
Magneten ran, dann weisst du, ob es Kupfer oder Eisen ist. Löte lieber 
anständige Kabel dran.

von Nemopuk (nemopuk)


Lesenswert?

Das blöde ist, dass schon wueder der Fokus auf die I2C Kommunikation 
gelenkt wird, obwohl dieser Teil nicht die Problemursacte sein kann.

Kai schrieb:
> Ergebnis: Der Fehler ist bisher nicht wieder aufgetreten - das (neue 
abgespeckte) Programm
> macht genau das, was es soll

von Kai (ksb)


Lesenswert?

Hallo Veit D.,

dein Scan-Code läuft bei mir nicht richtig - er zeigt (mit EEPROM):

Scanning...
No I2C devices found

Viele Grüße
Kai

Nachtrag: bei meinem fehlte Wire.begin - jetzt zeigt er

I2C-Scanner:

No I2C Address found

Setup Ende

Bitte Testdaten 00-9F senden

: Bearbeitet durch User
von Veit D. (devil-elec)


Lesenswert?

Hallo Kai,

können wir uns auf eine verständliche Kommunikation verständigen?
Deine Antwort verstehe ich nicht. Kann ich 100x lesen und immer wieder 
neu deuten.

Was wird angezeigt mit

deinem Scan Code:
ohne EEprom ...
mit EEprom ....

mit meinem Scan Code:
ohne EEprom ...
mit EEprom ....

von Veit D. (devil-elec)


Lesenswert?

Nemopuk schrieb:
> Das blöde ist, dass schon wueder der Fokus auf die I2C Kommunikation
> gelenkt wird, obwohl dieser Teil nicht die Problemursacte sein kann.
>
> Kai schrieb:
>> Ergebnis: Der Fehler ist bisher nicht wieder aufgetreten - das (neue
> abgespeckte) Programm
>> macht genau das, was es soll

Doch. Wir sind wieder bei der Kommunikation. Wie erklärst du dir 2 
gefundene I2C Adressen?

von Thomas Z. (usbman)


Lesenswert?

Aus der Dokumentation:

"The Wire library implementation uses a 32 byte buffer, therefore any 
communication should be within this limit. Exceeding bytes in a single 
transmission will just be dropped."

Das Eprom kann auch keine 2 Adressen haben zumal die 0x58 außerhalb des 
EEprom Adressbereichs liegt.

Woher du deinen 30 Byte buffer hast erklährt sich mir nicht
Ich hab dein page_write mal etwas umgebaut.
1
byte page_write(unsigned int addr, byte nBytes)
2
{ 
3
  //todo check params 
4
  Wire.beginTransmission(EXT_EE_ADDR);     // EEP address
5
  Wire.write((byte)(addr>>8));             // Addr High
6
  Wire.write((byte)(addr&0xFF));           // Addr low 
7
  if (Wire.endTransmission(false)==success)// send 3 bytes + repeated Start
8
  {   
9
     for(byte i=0; i<nBytes; i++) // Write all bytes 
10
     {
11
        Wire.write((byte)(Ser_Arr[i]));   // Byte from array
12
     }
13
     byte status = Wire.endTransmission(); // seend the buffer
14
     delay(5);                             // Important            
15
     return status;                        // Status of page-write 
16
  }
17
  return 0xff;           //something went wrong
18
}

: Bearbeitet durch User
von Kai (ksb)


Lesenswert?

Hallo Veit .,

Veit D. schrieb:
> Hallo Kai,
>
> können wir uns auf eine verständliche Kommunikation verständigen?
> Deine Antwort verstehe ich nicht. Kann ich 100x lesen und immer wieder
> neu deuten.
>
> Was wird angezeigt mit
>
> deinem Scan Code:
> ohne EEprom ...
> mit EEprom ....
>
> mit meinem Scan Code:
> ohne EEprom ...
> mit EEprom ....

sehr gerne - ich hatte es oben ergänzt:

dein Scan-Code läuft bei mir nicht richtig - er zeigt (mit EEPROM):

Scanning...
No I2C devices found

Aus meiner Sicht, fehlen ein paar Klammern



Bei meinem Scanner fehlte Wire.begin - deshalb hat er ohne EEPROM 
gehangen - jetzt zeigt er

I2C-Scanner:

No I2C Address found

Viele Grüße
Kai

von Kai (ksb)


Lesenswert?

Bei deinem Scanner wird bis address < 78 gescannt, bei meinem bis 128 
(ich habe ein Beispiel aus dem Netz verwendet, in welchem es genauso 
war).

: Bearbeitet durch User
von Kai (ksb)


Lesenswert?

Hallo Thomas Z,

Thomas Z. schrieb:
> Aus der Dokumentation:
>
> "The Wire library implementation uses a 32 byte buffer, therefore any
> communication should be within this limit. Exceeding bytes in a single
> transmission will just be dropped."
>
> Das Eprom kann auch keine 2 Adressen haben zumal die 0x58 außerhalb des
> EEprom Adressbereichs liegt.
>
> Woher du deinen 30 Byte buffer hast erklährt sich mir nicht
> Ich hab dein page_write mal etwas umgebaut.byte page_write(unsigned int
> addr, byte nBytes)
> {
>   //todo check params
>   Wire.beginTransmission(EXT_EE_ADDR);     // EEP address
>   Wire.write((byte)(addr>>8));             // Addr High
>   Wire.write((byte)(addr&0xFF));           // Addr low
>   if (Wire.endTransmission(false)==success)// send 3 bytes + repeated
> Start
>   {
>      for(byte i=0; i<nBytes; i++) // Write all bytes
>      {
>         Wire.write((byte)(Ser_Arr[i]));   // Byte from array
>      }
>      byte status = Wire.endTransmission(); // seend the buffer
>      delay(5);                             // Important
>      return status;                        // Status of page-write
>   }
>   return 0xff;           //something went wrong
> }

Vielen Dank für deine Version, welche ich später testen werde.
Die von mir verwendete Angabe eines 30 Byte-Puffers habe ich  von:
[[https://wolles-elektronikkiste.de/eeprom-teil-2-externe-i2c-eeproms]]

Viele Grüße
Kai

von Nemopuk (nemopuk)


Lesenswert?

2 Bytes für die Speicheradresse, gefolgt von 30 Daten-Bytes.

von Kai (ksb)


Lesenswert?

7-Bit-Adresse -> 7F = 128

von Veit D. (devil-elec)


Lesenswert?

Hallo,

wenn dein Scanner Adresse 0x50 und 0x58 findet und mein Code bis 0x77 
nichts, sollte man stutzig werden.
Ändere meinen Code auf < 128 ab. Was zeigt er an?
Ändere deinen Code auf < 78 ab. Was zeigt er an?

Laut Spezifikation ist im 7 Bit Bereich an Adresse 77 Ende.

: Bearbeitet durch User
von Kai (ksb)


Lesenswert?

Hallo Veit,

dein Scanner scannt folgende Adressen:
  for(address = 1; address < 78; address++ )
Das ist dezimal und nicht 0x78.
Leider läuft der Sketch bei mir nicht, weil einige Klammern fehlen.

Mein Scanner scannt folgende Adressen:
  for(int i2ad=1; i2ad<0x7F; i2ad++){           // I2C adress-range

Viele Grüße
Kai

von Veit D. (devil-elec)


Lesenswert?

Hallo,

das kann gar nicht sein. Mein Code kompiliert ohne jede Warnung.
Das Einzigste was ich vermehrt habe ist der Range.

Das muss so lauten, in Hex Schreibweise - weil 78 != 0x78.
Haste richtig erkannt.  ;-)
for (address = 0x08; address < 0x78; address++ )

Beide Scanner sollten das Gleiche anzeigen. Wir müssen uns auch nicht 
daran festbeißen. Nur bleibt dann leider unklar was mit dem Bus los ist. 
Wird ein Problem verschleppt oder nicht.

: Bearbeitet durch User
von S. L. (sldt)


Lesenswert?

> jedoch ist das vollständige Programm mittlerweile 32 kB groß

Verstehe ich das richtig: eine (kleine) Teilfunktion darin bringt 
sporadisch Fehler, seit 130 Beiträgen wird diese isoliert betrachtet, 
und wenn sie tatsächlich isoliert läuft (in einem Minimalprogramm), ist 
der Fehler verschwunden?

von Kai (ksb)


Lesenswert?

S. L. schrieb:
>> jedoch ist das vollständige Programm mittlerweile 32 kB groß
>
> Verstehe ich das richtig: eine (kleine) Teilfunktion darin bringt
> sporadisch Fehler, seit 130 Beiträgen wird diese isoliert betrachtet,
> und wenn sie tatsächlich isoliert läuft (in einem Minimalprogramm), ist
> der Fehler verschwunden?

Hallo S. l.,

richtig

Viele Grüße
Kai

von Veit D. (devil-elec)


Lesenswert?

Hallo,

das wird nicht seit 130 Beiträgen isoliert betrachtet. Das wird jetzt 
erst isoliert betrachtet. Alles davor war Spekulation, weil kein 
Testcode usw. vorhanden war. Mir fehlt das Mitmachverständnis. Es wird 
sich zu viel auf "im Internet gefunden" berufen.

Man kann dem Bus Adressfehler auf den Grund gehen.
Man kann das Programm erweitern bis der Fehler wieder da ist.
Man kann sich im Kreis drehen.

von Kai (ksb)


Lesenswert?

Hallo Veit,

dein Sketch kompiliert bei mir auch ohne Fehler:
1
      Serial.print("I2C device found at address 0x");
2
3
      if (address<16) 
4
5
        Serial.print("0");
6
7
      Serial.print(address,HEX);
8
9
      Serial.println();
10
11
      nDevices++;
12
13
    }
14
15
    else if (error==4) 
16
17
    {
18
19
      Serial.print("Unknow error at address 0x");
20
21
      if (address<16) 
22
23
        Serial.print("0");
24
25
      Serial.println(address,HEX);
26
27
    }

Aber er läuft durch, ohne ein Device zu finden.

aber hier fehlen doch Klammern oder?

      if (address<16)

        Serial.print("0");

viele Grüße
Kai

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Kai schrieb:
> aber hier fehlen doch Klammern oder?
>
>       if (address<16)
>
>         Serial.print("0");

Manche Klammern sind flüssiger als Wasser.
Überflüssig.

Natürlich kann man da Klammern hin machen, wenn man das schöner findet.
Jedem so wie er/sie/es mag....

von Veit D. (devil-elec)


Lesenswert?

Hallo,

ich bin jetzt sprachlos. Du behauptest mein Code funktioniert nicht.
> Leider läuft der Sketch bei mir nicht, weil einige Klammern fehlen.
Daraus schließt man - er funktioniert eben nicht.
Ich habe mir Gedanken gemacht warum er nicht laufen soll. Völlig 
umsonst. Derweile funktioniert er und es dreht sich nur darum das man 
den Syntax nicht versteht. Ich erwarte das eine Formulierung im Forum 
eindeutig ist. Wir haben nur die schriftliche Kommunikation zur 
Verfügung.

Das
1
if (address<16) 
2
  Serial.print("0");
3
Serial.println(address, HEX);
entspricht
1
if (address<16) {
2
  Serial.print("0");
3
}
4
Serial.println(address, HEX);

: Bearbeitet durch User
von Veit D. (devil-elec)


Angehängte Dateien:

Lesenswert?

Hallo,

nimm einmal 3,3 bis 4,7k Pullups an SDA und SCL.
Dann muss der Sketch konstant nur eine Adresse liefern.
Sonst weiß ich nicht mehr weiter.

von Kai (ksb)


Angehängte Dateien:

Lesenswert?

Hallo Veit,

leider gab es in unserer Kommunikation mehrere Missverständnis - 
wahrscheinlich habe ich mich zu kurz gefasst.

1. Deinen Sketch kann ich auch fehlerfrei compilieren.

   Er zeigt alle 5s: Scanning... No I2C devices found.
   Und dies mit und ohne angeschlossenes EEPROM.

   Daran hat sich nichts geändert.
   Da ich dies verwendete Schreinweise noch nie gesehen
   hatte, vermutete ich hier einen Fehler.

2. Mein Sketch liefert mit angeschlossenen EEPROM 0x50 und 0x58.

   Ohne angeschlossenes EEPROM blieb dieser hängen. Ursache war
   ein fehlendes Wire.Begin was vorher wahrscheinlich in einer
   der RTC- oder I2C-Routinen enthalten war.

   Nun liefert dieser ohne angeschlossenes EEPROM:
   No I2C Address found

Ich hänge deine Version (aus deinem Post kopiert) und meine aktuelle 
Version an (mit Wire.Begin und EEPROM löschen am Ende).

Viele Grüße
Kai

von Kai (ksb)


Lesenswert?

Hallo Veit,

der I2C_Adressen_und_Frequenz_Scanner.ino (3,26 KB) liefert mit Pull-Up 
4.7k an SDA und SCL folgendes Ergebnis:
1
addr  100  200  400  kHz   Line
2
0x50   x    x    x         TWI.0
3
0x58   x    x    x         TWI.0

Viele Grüße
Kai

von Klaus (feelfree)


Lesenswert?

Kai schrieb:
> Ich hänge deine Version (aus deinem Post kopiert)

Und die testet halt nach wie vor nur bis Adresse 77 statt 0x77...

von Veit D. (devil-elec)


Lesenswert?

Hallo,

alles gut. Ich dachte du hättest meinen ersten Scannercode korrigiert 
für den Scan Range in
1
for (address = 0x08; address < 0x78; address++ )

Dann sollte er auch was erkennen. Dann müssen alle 3 Sketche ohne EEprom 
nichts erkennen und mit EEprom eine 2. fehlerhafte Adresse.

Unabhängig davon, mich wundern immer noch die 2 erkannten Adressen. Auf 
welche Adresse hast du die EEprom Platine gejumpert?

Kannst du ein leserliches Foto von der EEprom Beschriftung machen?

von Veit D. (devil-elec)


Lesenswert?

Hallo,

wenn man sich die Adressen einmal anschaut
1
0101.0000   0x50
2
0101.1000   0x58
dann unterscheiden sie sich im 3. Bit.  (7 Bit Adressierung)
Laut Datenblatt des EEproms AT24C256 ist das "3. Bit" jedoch 
unveränderbar auf 0.
1
1010. A2 A1 A0 R/W.
2
bzw.
3
0101.0 A2 A1 A0
Die Frage lautet wo kommt die "1" her?

von Helmut H. (helmuth)


Lesenswert?

Die zwei Adressen wurden auch hier beobachtet:
https://docs.buspirate.com/docs/devices/at24c256/
"
This AT24C256 EEPROM is a non-original variant and includes an 
additional address: 0x58 (0xB0/B1). This extra address can be utilized 
to store data, such as a serial number. While this feature is not 
supported in the official AT24C256 chip, it is commonly found in certain 
“AT24C256 boards” sold on platforms like AliExpress.
Just use the 0x50 (0xA0/A1) address for reading and writing data to the 
EEPROM and ignore the 0x58 (0xB0/B1) address.
"

von Christian M. (christian_m280)


Lesenswert?

Am Schluss ist's dann die erste Antwort von Nepomuk!

Gruss Chregu

von Veit D. (devil-elec)


Lesenswert?

Helmut H. schrieb:
> Die zwei Adressen wurden auch hier beobachtet:
> https://docs.buspirate.com/docs/devices/at24c256/
> ...

Moin, na das ist doch einmal eine Top Antwort auf die Problematik. Das 
erklärt alles. Vielen Dank.

Dann sollte jetzt die Einlesefunktion usw. aufgeräumt werden.
Was mir auch gerade auffällt ist sowas. Das return steht falsch.
1
byte read_ext_eeprom(unsigned int addr) {       // Read external eeprom
2
  byte byteToRead;                              // Define byte to read !
3
  if (addr < EXT_EE_SIZE) {                     // Address is valid
4
    Wire.beginTransmission(EXT_EE_ADDR);          // Start transmission Address of external eeprom
5
    Wire.write((byte)(addr >> 8));                // Address high
6
    Wire.write((byte)(addr & 0xFF));              // Address low
7
    Wire.endTransmission();                       // End transmission
8
    Wire.requestFrom(EXT_EE_ADDR, 1);             // Allow multiple requests
9
    byteToRead = Wire.read();                     // Read byte
10
    return byteToRead;                            // Return value
11
  }
12
}

Allgemein werden mir die Datentypen zu wild verwendet. Integer mit bool 
und bool mit Integer.

Diese unbenutzte Funktion ist mir nicht ganz klar.
1
byte bytes_in_page(unsigned int addr) {          // Number of bytes left in page
2
  if (addr < EXT_EE_SIZE) {                     // Address is valid
3
    int page = addr / EXT_EE_PAGE;              // Current page
4
    int border = (page + 1) * EXT_EE_PAGE;      // Next page address = border
5
    int bytes = border - addr;                  // Number of bytes total
6
    int pakets = (bytes / EXT_EE_LIMIT) + 1;    // Number pf pakets
7
    int rest = bytes; int count = 0;            // Rest of bytes
8
9
    if (rest >= EXT_EE_LIMIT) {                 // Enough space for 30 bytes
10
      count = EXT_EE_LIMIT;
11
    }                    //
12
    else if (rest < EXT_EE_LIMIT) {             // Less space than 30 bytes
13
      count = rest;
14
    }                            //
15
    return count;                               // Number of bytes
16
  }
17
  else {
18
    Serial.print(F("Illegal Address"));
19
  }
20
}
Sie soll wahrscheinlich das Page Limit prüfen. Nur wenn ein 'rest' 
größer dem Limit ist, kann kein Platz mehr sein.
1
Ser_Arr[]
 soll der Einlesebuffer sein? Wo wird geprüft ob er noch Platz hat?

Dann steht in test_data_rec_() ein 10ms delay drin. Warum?

Das sind alles so kleine komische Dinge die im Sketch verstreut für 
Ärger sorgen wenn mehr gemacht werden soll wie nur lesen und schreiben 
alleine. Du brauchst eine saubere Einleseroutine mit sauberen Protokoll.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

jetzt habe ich nur immer wieder alleine diese Zeile im Seriellen Monitor 
gesendet.
1
000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20212223242526272829
und es kommt abwechselnd durcheinander.
1
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F  16
2
10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F  32
3
20 21 22 23 24 25 26 27 28 82 D3 2B 2C 2D 2E 2F  48
4
30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F  64
5
40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F  80
6
50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F  96
7
60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F  112
8
70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F  128
9
80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F  144
10
90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F  160
1
30 00 10 20 30 40 50 60 70 80 90 A0 B0 C0 D0 E0  16
2
F1 01 11 21 31 41 51 61 71 81 91 A1 B1 C1 D1 E1  32
3
F2 02 12 22 32 42 52 62 72 D3 D3 2B 2C 2D 2E 2F  48
4
30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F  64
5
40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F  80
6
50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F  96
7
60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F  112
8
70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F  128
9
80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F  144
10
90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F  160

Thema, Einleseroutine überarbeiten, Kontrolle behalten.

von Klaus (feelfree)


Lesenswert?

Veit D. schrieb:
> Einleseroutine überarbeiten, Kontrolle behalten.

Sehe ich auch so.
Einen Bytestrom ohne jede Synchronisierung oder Prüfsumme zu übertragen 
führt früher oder später zu Problemen. Hier halt früher.

Die Erfinder des Intel-Hex-Formats waren da etwas weitsichtiger...

von Nemopuk (nemopuk)


Lesenswert?

Andererseits hat Arduino einen Empfangspuffer von 32 Zeichen, und er hat 
laut log nie mehr als 3 Zeichen im Puffer.

Ich schrieb weiter oben, das Interrupts nicht das Beschreiben der 
Zählvariablen verhindern können. Aber Interrupts können geringer 
priorisierte Interrupts verzögern, und Software kann alle Interrupts 
blockieren. Wenn der (nicht gezeigte und inzwischen entfernte) Code das 
getan hatte, ist es schon denkbar, daß die Serielle Eingabe einzelne 
Zeichen verpasst.

Außerdem könnte ein Stack/Heap Überlauf passieren, wobei ich das hier 
für rekativ unwahrscheinlich halte. Denn der Fehler passiert auch ohne 
die ganzen Debug Ausgaben, für die eine Menge RAM verwendet wird.

Allerdings hat Kai mehrfach geschrieben, dass ganz sicher alle Zeichen 
"im RAM" empfangen wurden. Ich vermute, er meint damit das rx_test 
array. Wenn dem so ist, können wir sowohl die serielle Eingabe als auch 
die Ausgabe ins Eeprom als Fehlerquelle ausschließen. Es bleibt nur noch 
der Algorithmus (Counter und for-Schleife) übrig, den wir alle nicht 
durchblicken.

Doch der müsste im isolierten Test den selben Fehler zeigen. Was nun, 
haben wir doch einen Stack/Heap überlauf? Das sollte kontrolliert 
werden.

Der Algorithmus erfüllt für meinen Geschmack zu viele Aufgaben in einem 
Rutsch. Dadurch ist er kaum zu debuggen. Ich würde diesen Code so 
aufspalten, dass einzelne Teile separat testbar werden. Wenigstens die 
Eingabe und Ausgabe sollte abkoppeln können, damit ein Unit Test 
ermöglicht wird.

: Bearbeitet durch User
von Kai (ksb)


Lesenswert?

Guten Morgen,

und vielen Dank für die vielen Antworten, welche ich gleich im Detail 
beantworten werde.

Wichtigste Frage an Veit:
hast du die erste oder zweite Version meines Sketches verwendet?

Die erste habe ich nur aus dem Reset geprüft.

Die zweite setzt am Ende cnt zurück und löscht das EEPROM. Mit dieser 
Version habe ich viele Male (20-30) hintereinander geprüft und immer 
korrekte Ergebnisse erhalten.

Viele Grüsse
Kai

von Nemopuk (nemopuk)


Lesenswert?

Mir ist noch etwas aufgefallen:

Wenn im EEprom Daten fehlen, sind das immer komplette Bytes, 
entsprechend 2 Zeichen vom seriellen Port. Wenn der Puffer vom seriellen 
Port überlaufen würde, oder wenn Interrupt zu lange blockiert/verzögert 
würden, dann müsste das doch zumindest ab und zu auch mal eine ungerade 
Anzahl von Zeichen betreffen, was bei den folgenden Bytes (im EEprom) zu 
ganz anderen Werten führen würde. Der Fall ist aber kein einziges mal 
beobachtet worden. Außerdem waren die Daten im RAM ja auch immer in 
Ordnung.

Beides bestärkt meine Annahme, dass der serielle Empfang korrekt 
funktioniert. Ich bin ziemlich sicher, dass der Fehler irgendwo 
unterhalb der Zeilen
1
nibble = text2hex(Serial.read());         // read and convert 1. char = high nibble
2
hi = nibble;                              // High nibble  
3
4
nibble = text2hex(Serial.read());         // read and convert 2. char = low nibble
5
lo = nibble;
6
7
val = hi<<4 | lo;                         // Combine hi + low = value  
8
rx_test[ram_idx] = val;                   // DEBUG! store to RAM
9
ram_idx++;

liegt - wenn es nicht ein Stack/Heap Überlauf ist.

: Bearbeitet durch User
von Kai (ksb)


Angehängte Dateien:

Lesenswert?

Hallo Veit,

bezüglich des EEPROM:

alle Adressen sind mit GND verbunden
 1 0 1 0 A2 A1 A0  R/W
 1 0 1 0 0  0  0   0    somit 0x50

WP liegt ebenfalls auf GND (Siehe Microchip Datenblatt 24AA256 Seite 8)
Die Beschreibung zum Reichelt EEPROM mit links und rechts ist falsch.

Viele Grüße
Kai

von Kai (ksb)


Lesenswert?

Hallo Nemopuk,

das vorherige Log ergab, dass im Fehlerfall das cur_byte++ am Ende nicht 
ausgeführt wird:
1
      if ((cur_byte < x/2) &&   // Not all data processed            | -> collect data
2
          (cur_byte < bytes)){  // Below buffer or page-write border |
3
        Ser_Arr[cur_byte]= val; // Add data to page-write buffer     |
4
        cur_byte++;             // Next index                        |

Die folgende Schreibroutine schreibt daher ein Byte zu wenig.
Die Variable cnt hat ebenfalls einen um 1 Byte zu niedrigen Stand.

Viele Grüße
Kai

: Bearbeitet durch User
von Kai (ksb)


Lesenswert?

Hallo Veit,

dein Scanner liefert mit angeschlossenem EEPROM nach Erhöhung des 
Adresszählers auf <0x7F:

I2C Scanner
Scanning...
I2C device found at address 0x50
I2C device found at address 0x58
done

Alle 3 Scanner finden ohne EEPROM keine Adresse.

Viele Grüße
Kai

von Veit D. (devil-elec)


Angehängte Dateien:

Lesenswert?

Hallo,

man muss die Buffer unterscheiden.

Der Arduino I2C Buffer hat Default 32Byte Größe. Kann man notfalls 
ändern, ist aber nicht notwendig. Dieser Buffer puffert die Daten nur 
solange bis man sie abholt beim lesen bzw. über die 
Hardwareschnittstelle rausschreiben lässt. Falls dieser Buffer 
volllaufen sollte, kommt das Abholen nicht hinterher.

Zum Einlesen von Daten für die eigenen Zwecke verwendet man zusätzliche 
seinen eigenen Buffer. Damit hat die Main-Loop Zeit Daten aus dem 
"Arduino I2C Buffer" zu lesen und plausibel vorgeprüft in den eigenen 
Buffer zu schreiben. Wenn das Übertragungsprotokoll seine Endekennung 
erreicht hat, ist diese eine Übertragung zu Ende. Erst dann wird der 
eigenen Buffer verarbeitet. Damit hat man alle Zeit der Welt die Daten 
ins EEprom zu schreiben oder anderweitig zu verarbeiten.

Man bekommt erst ein Zeitproblem wenn die Daten schneller reinkommen wie 
man wegschreiben kann. Wenn man jedoch ständig ein EEprom beschreiben 
muss, dann ist die Wahl auf ein EEprom falsch.

Nemopuk schrieb:
> Allerdings hat Kai mehrfach geschrieben, dass ganz sicher alle Zeichen
> "im RAM" empfangen wurden. Ich vermute, er meint damit das rx_test
> array. Wenn dem so ist, können wir sowohl die serielle Eingabe als auch
> die Ausgabe ins Eeprom als Fehlerquelle ausschließen.

Fehler ausschließen kann ich eben nicht, wenn man es durch einfache 
Wiederholung durcheinanderbringt. Man sieht doch da schon einen Versatz. 
Wo kommt die 30 am Anfang her? Es wird allgemein zu viel vertraut, dass 
es schon irgendwie funktionieren wird. Wenn es einen Versatz gibt, dann 
muss man alle Lese/Schreibroutinen prüfen. Das darf man nicht 
überspringen.

Kai schrieb:
> Wichtigste Frage an Veit:
> hast du die erste oder zweite Version meines Sketches verwendet?

Ich habe deine letzte Version
Beitrag "Re: Arduino: page-write in I2C eeprom verliert sporadisch ein Byte"
verwendet mit cnt=0 und EEprom mit Wire rausgenommen.
Habe sie aktuell bezüglich Warnungen aufgeräumt. Statt der fehlerhaften 
30 kommt jetzt 00 am Anfang doppelt.
Das einlesen ist unsauber.
Lies alles ein bis zur LineFeed Endeerkennung und verarbeite dann erst.

von Nemopuk (nemopuk)


Lesenswert?

Kai schrieb:
> das vorherige Log ergab, dass im Fehlerfall das cur_byte++ am Ende nicht
> ausgeführt wird

Das kann aber nicht sein. Der Mikrocontroller führt den Code exakt so 
aus, wie du ihn geschrieben hast. Interrupts verhindern dort nichts.

Wenn (was zu überprüfen wäre) diese Zeilen das Problem enthalten, sehe 
ich zwei mögliche Gründe:

1) Ein Speicher-Überlauf bewirkt, dass diese Variable überschrieben 
wird. Allerdings wären die anderen Variablen, die direkt davor oder 
dahinter liegen höchstwahrscheinlich ebenso betroffen. Beachte, daß der 
Compiler die Reihenfolge der Variablen im RAM ändern kann. Man kann sich 
ein MAP File erstellen, um das zu kontrollieren.

2) Deine if-Bedingung tut nicht das, was sie soll.

von Veit D. (devil-elec)


Lesenswert?

Kai schrieb:
> Hallo Veit,
>
> dein Scanner liefert mit angeschlossenem EEPROM nach Erhöhung des
> Adresszählers auf <0x7F:
>
> I2C Scanner
> Scanning...
> I2C device found at address 0x50
> I2C device found at address 0x58
> done
>
> Alle 3 Scanner finden ohne EEPROM keine Adresse.
>
> Viele Grüße
> Kai

Danke. Das mit den Adressen passt dann soweit. Dank Erklärung von 
Helmuth bezüglich 2. undokumentierter Adresse. Die 0x58 darfst du damit 
für nichts anderes konfigurieren.

von Kai (ksb)


Lesenswert?

Nemopuk schrieb:

> Außerdem könnte ein Stack/Heap Überlauf passieren, wobei ich das hier
> für rekativ unwahrscheinlich halte. Denn der Fehler passiert auch ohne
> die ganzen Debug Ausgaben, für die eine Menge RAM verwendet wird.

> Ich schrieb weiter oben, das Interrupts nicht das Beschreiben der
> Zählvariablen verhindern können. Aber Interrupts können geringer
> priorisierte Interrupts verzögern, und Software kann alle Interrupts
> blockieren. Wenn der (nicht gezeigte und inzwischen entfernte) Code das
> getan hatte, ist es schon denkbar, daß die Serielle Eingabe einzelne
> Zeichen verpasst.

> Außerdem könnte ein Stack/Heap Überlauf passieren, wobei ich das hier
> für rekativ unwahrscheinlich halte. Denn der Fehler passiert auch ohne
> die ganzen Debug Ausgaben, für die eine Menge RAM verwendet wird.

Hallo Nemopuk,

der Fehler passiert sowohl auf dem Arduino Mega, welcher ohne Logging 
ca. 6 kB freies RAM hat als auch auf dem Arduino Uno, welcher 468 Bytes 
freies RAM hat.

Der Rest des Programmes arbeitet auch im Fehlerfall weiter stabil.
Konkret bedeutet dies, dass alle Funktionen gegeben sind und das LCD in 
allen Menüs fehlerfrei anzeigt.

Vor Erstellung des Minimalprogrammes im Gesamtprogramm hatte ich viele 
Programmteile bis auf das LCD auskommentiert und die Komponenten 
entfernt, was am Fehlerbild nichts geändert hat:

RTC          (wird nur gelesen)
IR-Empfänger (wird zum Zeitpunkt des Fehlers nicht verwendet)
DOT-Matrix   (hat wahrscheinlich leinen Einfluss)
IR-Sender    (wird zurzeit nur bei Empfang eines Kommandos aktiviert)

Noch nicht auskommentiert oder abkeklemmt habe ich bisher:
DHT11 und Piezo

Viele Grüße
Kai

von Nemopuk (nemopuk)


Lesenswert?

Ich würde jetzt trotzdem erst mal auf Stack Überlauf überprüfen, egal 
wie unwahrscheinlich das erscheinen mag. Denn wenn das passiert, läuft 
das Programm eben nicht mehr wie geschrieben ab. Dann wird RAM 
überschrieben und es kann sogar passieren, dass der Programmzähler der 
CPU wild hin und her springt, also Sachen macht, die nirgends im 
Quelltext stehen.

Freies RAM zum Zeitpunkt des Compilierens sagt noch nichts darüber aus, 
wie viel RAM zur Laufzeit frei ist. Durch ein Memory-Leck würde der 
freie Speicher zur Laufzeit schrittweise immer kleiner. Bei Arduino 
droht außerdem eine Fragmentierung des Heap, insbesondere wenn man die 
String Klasse verwendet. Fragmentierung bedeutet: Du hast zwar viele 
kleine Stücke im RAM frei, aber keinen großen Zusammenhängenden Block 
mehr. Auch dann können sich Stack und Heap überlappen, mit den oben 
genannten Konsequenzen.

Es gibt reichlich Artikel im Internet, die Methoden zur Kontrolle 
beschreiben.

Wenn der Test dann wie erwartet (hoffentlich) negativ ausfällt (kein 
Speicherüberlauf), dann würde ich den Algorithmus nochmal ganz genau 
anschauen. Ich würde damit beginnen, das paarweise Einsammeln der 
Zeichen vom Pageing zu trennen (nicht beides in einer For-Schleife 
kombiniert). Dazwischen kommt dann zum Debugging das RAM Array. Dann ist 
zumindest schon mal klar, ob der Fehler vor dem Pageing passiert, oder 
nicht.

Als nächsten Schritt könnte man den Schreibzugriff auf das EEprom durch 
ein weiteres Array im RAM ersetzen (den 5ms delay beibehalten). Dies 
würde die Diskussion um die I2C Kommunikation beenden (denke ich).

: Bearbeitet durch User
von Christian M. (christian_m280)


Lesenswert?

Kai schrieb:
> Die zu schreibenden Daten kommen zurzeit vom seriellen Monitor

Aha. Schon mal mit einem richtigen Terminal probiert?

Gruss Chregu

von Nemopuk (nemopuk)


Lesenswert?

Lesestoff zur Fragmentierung:
https://www.mikrocontroller.net/articles/Heap-Fragmentierung
https://hackingmajenkoblog.wordpress.com/2016/02/04/the-evils-of-arduino-strings/

Wie gesagt: Ich vermute, dass die Problemursache eine andere ist. Aber 
mit Vermutungen kommen wir hier offenbar nicht weiter. Es ist Zeit, sich 
zu vergewissern.

: Bearbeitet durch User
von Nemopuk (nemopuk)


Lesenswert?

Christian M. schrieb:
> Schon mal mit einem richtigen Terminal probiert?

Gute Idee. Beim Hammer Terminal kann man sogar ein Delay zwischen die 
Zeichen einfügen.

von Kai (ksb)


Lesenswert?

Hallo Veit,

Veit D. schrieb:
> Helmut H. schrieb:
>> Die zwei Adressen wurden auch hier beobachtet:
>> https://docs.buspirate.com/docs/devices/at24c256/
>> ...

> Moin, na das ist doch einmal eine Top Antwort auf die Problematik. Das
> erklärt alles. Vielen Dank.

> Dann sollte jetzt die Einlesefunktion usw. aufgeräumt werden.

> Was mir auch gerade auffällt ist sowas. Das return steht falsch.byte
> read_ext_eeprom(unsigned int addr) {       // Read external eeprom
>   byte byteToRead;                              // Define byte to read !
>   if (addr < EXT_EE_SIZE) {                     // Address is valid
>     Wire.beginTransmission(EXT_EE_ADDR);          // Start transmission
> Address of external eeprom
>     Wire.write((byte)(addr >> 8));                // Address high
>     Wire.write((byte)(addr & 0xFF));              // Address low
>     Wire.endTransmission();                       // End transmission
>     Wire.requestFrom(EXT_EE_ADDR, 1);             // Allow multiple
> requests
>     byteToRead = Wire.read();                     // Read byte
>     return byteToRead;                            // Return value
>   }
> }

> Allgemein werden mir die Datentypen zu wild verwendet. Integer mit bool
> und bool mit Integer.

gemäß Arduino-Referenz ist es angeblich kein Problem, unterschiedliche 
Datentypen zu addieren.

> Diese unbenutzte Funktion ist mir nicht ganz klar.

> byte bytes_in_page(unsigned int addr) {          // Number of bytes left in
> page
>   if (addr < EXT_EE_SIZE) {                     // Address is valid
>     int page = addr / EXT_EE_PAGE;              // Current page
>     int border = (page + 1) * EXT_EE_PAGE;      // Next page address =
> border
>     int bytes = border - addr;                  // Number of bytes total
>     int pakets = (bytes / EXT_EE_LIMIT) + 1;    // Number pf pakets
>     int rest = bytes; int count = 0;            // Rest of bytes
>     if (rest >= EXT_EE_LIMIT) {                 // Enough space for 30
> bytes
>       count = EXT_EE_LIMIT;
>     }                    //
>     else if (rest < EXT_EE_LIMIT) {             // Less space than 30
> bytes
>       count = rest;
>     }                            //
>     return count;                               // Number of bytes
>   }
>   else {
>     Serial.print(F("Illegal Address"));
>   }
> }
> Sie soll wahrscheinlich das Page Limit prüfen. Nur wenn ein 'rest'
> größer dem Limit ist, kann kein Platz mehr sein.

Diese Routine ermittelt die Anzahl der Erlaubten bytes für die Schleife:
1
bytes = bytes_in_page(addr + cnt); // Determinate max. number allowed in this page
2
3
if ((cur_byte < x / 2) &&   // Not all data processed  | -> collect data
4
    (cur_byte < bytes)) {   // Below buffer or page-write border   |

> Ser_Arr[]
>  soll der Einlesebuffer sein? Wo wird geprüft ob er noch Platz hat?

Es werden durch cur_byte < bytes maximal 30 Zeichen eingelesen

>
> Dann steht in test_data_rec_() ein 10ms delay drin. Warum?

Es trudeln entgegen meiner Erwartung viel zu wenig Zeichen von der 
seriellen Schnittstelle ein, so dass immer nur ein paar Bytes 
geschrieben werden. Das 10ms Delay ist ein Versuchsballon, welcher diese 
Anzahl erhöht.

Nächste Schritt ist, die Baudrate auf 19200 oder 38400 zu setzen.
Das aber erst, wenn der Fehler im Gesamtprogramm beseitigt ist.

> Das sind alles so kleine komische Dinge die im Sketch verstreut für
> Ärger sorgen wenn mehr gemacht werden soll wie nur lesen und schreiben
> alleine. Du brauchst eine saubere Einleseroutine mit sauberen Protokoll.

OK

Ich hatte vorher eine Software-Flusskontrolle (XON/XOFF sowie Anfangs- 
und Endezeichen verwendet. Hier war immer mein Problem, dass ich die 
Daten nicht schnell genug abholen konnnte.

Viele Grüße
Kai

von Nemopuk (nemopuk)


Lesenswert?

Kai schrieb:
> gemäß Arduino-Referenz ist es angeblich kein Problem, unterschiedliche
> Datentypen zu addieren.

Ich würde mich da ehrlich gesagt lieber auf die Dokumentation des gcc 
verlassen. Die Arduino Doku verheimlicht häufig kleine gemeine Details, 
um einfach aus zu sehen.

von Kai (ksb)


Lesenswert?

Christian M. schrieb:
> Kai schrieb:
>> Die zu schreibenden Daten kommen zurzeit vom seriellen Monitor
>
> Aha. Schon mal mit einem richtigen Terminal probiert?
>
> Gruss Chregu

Hallo Chregu,

auch vielen Dank für deine Antworten.

Ja, mit vielen - vorher hatte ich ja eine SW-Flusskontrolle eingebaut, 
welche aus CMD nicht funktioniert, obwohl Mode es anzeigt.

Zuletzt habe ich Tera Term S verwendet.

Viele Grüße
Kai

: Bearbeitet durch User
von Veit D. (devil-elec)


Lesenswert?

Hallo Kai,

hier findest du die Basics, die man immer noch ausbauen kann.
https://forum.arduino.cc/t/serial-input-basics-updated/382007

Ein Beispiel von mir mit Mehrfachverwendbarkeit.
Man kann sich bei Bedarf noch überlegen 'status' in der Struktur zu 
halten, wenn man zu anderen Zeitpunkten seine Daten verarbeiten möchte.
1
struct Daten {
2
  static const byte READ_BUFFER_SIZE {20};       
3
  char rxBuffer [READ_BUFFER_SIZE + 1] {'\0'}; // Platz für 20 Zeichen + Null-Terminator
4
  byte index {0};
5
  char c {'\0'};
6
};
7
8
Daten meinBuffer;
9
10
void setup()
11
{
12
  Serial.begin(9600);
13
}
14
15
void loop()
16
{
17
  // liefert true wenn das LF eingelesen wurde
18
  if (readLine(Serial, meinBuffer)) {
19
    Serial.print("Eingelesen: ");
20
    Serial.println(meinBuffer.rxBuffer);
21
  }
22
}
23
24
bool readLine(Stream& stream, Daten &daten)
25
{
26
  bool status {false};
27
28
  while (stream.available()) {
29
    daten.c = stream.read();
30
31
    if (daten.c == '\n') {                  // wenn LF eingelesen
32
      daten.rxBuffer[daten.index] = '\0';   // String terminieren
33
      daten.index = 0;
34
      status = true;                        // String fertig eingelesen
35
    }
36
    else if ( (daten.c >= 32) && (daten.index < daten.READ_BUFFER_SIZE)) {  // keine Steuerzeichen und solange noch Platz im Buffer ist
37
      daten.rxBuffer[daten.index++] = daten.c;                              // Zeichen abspeichern und Index inkrementieren
38
    }
39
  }
40
  return status;
41
}

von Nemopuk (nemopuk)


Lesenswert?

Kai schrieb:
> Ich hatte vorher eine Software-Flusskontrolle (XON/XOFF sowie Anfangs-
> und Endezeichen verwendet. Hier war immer mein Problem, dass ich die
> Daten nicht schnell genug abholen konnnte.

Software-Flusskontrolle müsste Bestandteil des Interrupt-Handlers sein, 
ist bei Arduino allerdings nicht vorgesehen.
https://github.com/arduino/ArduinoCore-avr/blob/master/cores/arduino/HardwareSerial_private.h

Du müsstest dazu eine eigene Klasse für den seriellen Port 
implementieren.

Da das EEprom die Pages ja deutlich schneller schreibt, als dein 
serieller Port empfängt, kannst du prinzipiell auch ohne Handshake 
arbeiten. Es wird stabil funktionieren, nachdem die eigentliche 
Problemursache gelöst ist.

Wieso überrascht dich das eigentlich? 9600 Baus sind 960 Zeichen 
(Nibbles) pro Sekunde, also 480 Bytes. Dein EEprom schreibt 64 Bytes 
in 5ms, das sind 1280 Bytes pro Sekunde. Es ist also viel schneller.

: Bearbeitet durch User
von Veit D. (devil-elec)


Lesenswert?

Nemopuk schrieb:
> Kai schrieb:
>> gemäß Arduino-Referenz ist es angeblich kein Problem, unterschiedliche
>> Datentypen zu addieren.
>
> Ich würde mich da ehrlich gesagt lieber auf die Dokumentation des gcc
> verlassen. Die Arduino Doku verheimlicht häufig kleine gemeine Details,
> um einfach aus zu sehen.

Alles Falschaussagen. Wenn man so argumentiert, mit Heap und Stack 
ablenkt, die serielle Kommunikation als Gegeben 'OK' akzeptiert, dann 
wundert mich nichts mehr.

Macht Euer Ding.

von Kai (ksb)


Lesenswert?

Hallo Nemopuk,

Nemopuk schrieb:
> Kai schrieb:
>> Ich hatte vorher eine Software-Flusskontrolle (XON/XOFF sowie Anfangs-
>> und Endezeichen verwendet. Hier war immer mein Problem, dass ich die
>> Daten nicht schnell genug abholen konnnte.
>
> Software-Flusskontrolle müsste Bestandteil des Interrupt-Handlers sein,
> ist bei Arduino allerdings nicht vorgesehen.
> 
https://github.com/arduino/ArduinoCore-avr/blob/master/cores/arduino/HardwareSerial_private.h
> Du müsstest dazu eine eigene Klasse für den seriellen Port
> implementieren.

Die SW-Flusskontrolle hatte ich wie folgt implementiert:
1
void flow_control(int z){           // Flow-control via XOFF and XON
2
3
if (!rx_flag && (z > 20)){          // Buffer size above limit + no XOFF?
4
    rx_flag = 1;                    // Set XOFF status flag
5
    xoff++;                         // Count XOFF
6
    Serial.write(XOFF);}            // Send XOFF to serial
7
8
if  (rx_flag && (z < 10)){          // Buffer size below limit+XOFF sent? 
9
    rx_flag = 0;                    // Clear XOFF status flag
10
    xon++;                          // Count XON
11
    Serial.write(XON);}             // Send XON to serial
12
}
> Da das EEprom die Pages ja deutlich schneller schreibt, als dein
> serieller Port empfängt, kannst du prinzipiell auch ohne Handshake
> arbeiten. Es wird stabil funktionieren, nachdem die eigentliche
> Problemursache gelöst ist.

ja - deshalb habe ich die Flusskontrolle wieder entfernt - hatte auch 
den Nachteil, dass ich mit einem Terminalprogramm arbeiten musste und 
somit der Serielle Monitor nicht zur Verfügung stand.

> Wieso überrascht dich das eigentlich? 9600 Baud sind 960 Zeichen
> (Nibbles) pro Sekunde, also 480 Bytes. Dein EEprom schreibt 64 Bytes
> in 5ms, das sind 1280 Bytes pro Sekunde. Es ist also viel schneller.

Ja, nachdem das Page-Write funktioniert. Mein Test ergab, dass das 
Schreiben von 512 Bytes 180 ms benötigt.

Viele Grüße
Kai

: Bearbeitet durch User
von Nemopuk (nemopuk)


Lesenswert?

Veit D. schrieb:
> Alles Falschaussagen.

Eine Vermutung kann keine Falschaussage sein.

> Wenn man ... die serielle Kommunikation
> als Gegeben 'OK' akzeptiert, dann wundert mich nichts mehr.

Die serielle Kommunikation hat er ja geprüft. Im RAM rx_test[] sind alle 
Daten immer richtig.

: Bearbeitet durch User
von Nemopuk (nemopuk)


Lesenswert?

Kai schrieb:
> Die SW-Flusskontrolle hatte ich wie folgt implementiert:

Dass heißt, du hast reichlich Reserve eingeplant, dass die andere Seite 
verzögert darauf reagiert. Gut.

Aber: Wenn der Rx Interrupt der seriellen Schnittstelle länger als 2 
Bytes blockiert oder verzögert wird, verpasst du trotzdem Empfangene 
Bytes. Das UDR Register wird dann vom Schieberegister überschrieben. Das 
kannst du mit so einem Software-Handshake nicht verhindern. Wenn hier 
der Hase im Pfeffer liegt, nützt dir Software-Handshake nichts. Dann 
wäre der Fehler aber deutlich in rx_test[] sichtbar.

von Kai (ksb)


Lesenswert?

Hallo Veit,

Veit D. schrieb:
> Nemopuk schrieb:
>> Kai schrieb:
>>> gemäß Arduino-Referenz ist es angeblich kein Problem, unterschiedliche
>>> Datentypen zu addieren.
>>
>> Ich würde mich da ehrlich gesagt lieber auf die Dokumentation des gcc
>> verlassen. Die Arduino Doku verheimlicht häufig kleine gemeine Details,
>> um einfach aus zu sehen.
>
> Alles Falschaussagen. Wenn man so argumentiert, mit Heap und Stack
> ablenkt, die serielle Kommunikation als Gegeben 'OK' akzeptiert, dann
> wundert mich nichts mehr.
>
> Macht Euer Ding.

Ich werde versuchsweise alle beteiligten Variablen der Empfangsroutine 
auf den selben Typ ändern. Der Grund für die Verwendung 
unterschiedlicher Typen war, für den Arduino Uno RAM zu sparen.

Für ein 32k-Eprom wäre int gut, besser aber ein unsigned int.
Somit müssten alle Variablen, welche an Berechnungen beteiligt sind, vom 
typ unsigned int sein.

Viele Grüße
Kai

: Bearbeitet durch User
von Kai (ksb)


Lesenswert?

Hallo Nemopuk,

Nemopuk schrieb:
> Kai schrieb:
>> Die SW-Flusskontrolle hatte ich wie folgt implementiert:
>
> Dass heißt, du hast reichlich Reserve eingeplant, dass die andere Seite
> verzögert darauf reagiert. Gut.

Dar HW-Sendepuffer der UART hat 16 Zeichen - für evtl. Zeitverzug habe 
ich das XOFF großzügig früh gesendet.

> Aber: Wenn der Rx Interrupt der seriellen Schnittstelle länger als 2
> Bytes blockiert oder verzögert wird, verpasst du trotzdem Empfangene
> Bytes. Das UDR Register wird dann vom Schieberegister überschrieben. Das
> kannst du mit so einem Software-Handshake nicht verhindern. Wenn hier
> der Hase im Pfeffer liegt, nützt dir Software-Handshake nichts. Dann
> wäre der Fehler aber deutlich in rx_test[] sichtbar.

Richtig rx_test ist immer richtig - daher habe ich den SW-Handshakeals 
Sackgasse verworfen....

Viele Grüße
Kai

von Nemopuk (nemopuk)


Lesenswert?

Kai schrieb:
> Somit müssten alle Variablen, welche an Berechnungen beteiligt sind, vom
> typ unsigned int sein.

Probiere das. Ich erwarte nicht, dass dadurch das Problem gelöst wird. 
Aber der Test ist einfach, es gibt nicht viel zu verlieren.

von Christian M. (christian_m280)


Lesenswert?

Nemopuk schrieb:
> 9600 Baus sind 960 Zeichen (Nibbles) pro Sekunde, also 480 Bytes.

Mit Start- und Stoppbit. Also 800 bzw. 400 Bytes pro Sekunde!

Gruss Chregu

von Klaus (feelfree)


Lesenswert?

Christian M. schrieb:
> Mit Start- und Stoppbit.

Genau, 9600 geteilt durch (1+8+1) macht 960.

> Also 800 bzw. 400 Bytes pro Sekunde!

Äh, nein.

von Hans (ths23)


Lesenswert?

Falk B. schrieb:
> Längeren Quelltext als Anhang, siehe Netiquette.

Wastl schrieb:
> Kai schrieb:
>> Vielen Dank für jegliche Hilfe
>
> Vielen Dank dass du Hinweise zum Posten von Sourcecode ignorierst.

Die Meckerei verstehe ich nicht, er hat doch den Quelltext als Anhang 
gepostet. Habe ich da was übersehen?

von Kai (ksb)


Lesenswert?

Hallo Klaus,

Klaus schrieb:
> Christian M. schrieb:
>> Mit Start- und Stoppbit.
>
> Genau, 9600 geteilt durch (1+8+1) macht 960.
>
>> Also 800 bzw. 400 Bytes pro Sekunde!
>
> Äh, nein.

Ein zu schreibendes Byte besteht aus 2 ASCII-Zeichen von der seriellen 
Schnittstelle - daher kommt diese Aussage.

Viele Grüße
Kai

von Kai (ksb)


Lesenswert?

Hallo Hans,

Hans schrieb:
> Falk B. schrieb:
>> Längeren Quelltext als Anhang, siehe Netiquette.
>
> Wastl schrieb:
>> Kai schrieb:
>>> Vielen Dank für jegliche Hilfe
>>
>> Vielen Dank dass du Hinweise zum Posten von Sourcecode ignorierst.
>
> Die Meckerei verstehe ich nicht, er hat doch den Quelltext als Anhang
> gepostet. Habe ich da was übersehen?

zuerst hatte ich den Quelltext in Code-Tags, weil er mir nicht so lang 
vorkam. Habe ich nachträglich korrigiert.

War also begründet!

Viele Grüße
Kai

von Klaus (feelfree)


Lesenswert?

Kai schrieb:
> Ein zu schreibendes Byte besteht aus 2 ASCII-Zeichen von der seriellen
> Schnittstelle - daher kommt diese Aussage

Ja, deshalb sind es 960 Zeichen oder 480 Bytes pro Sekunde, und nicht 
800 bzw. 400.

von Veit D. (devil-elec)


Lesenswert?

Nemopuk schrieb:
> Veit D. schrieb:
>> Alles Falschaussagen.
>
> Eine Vermutung kann keine Falschaussage sein.

Ohne Kommentar.

>> Wenn man ... die serielle Kommunikation
>> als Gegeben 'OK' akzeptiert, dann wundert mich nichts mehr.
>
> Die serielle Kommunikation hat er ja geprüft. Im RAM rx_test[] sind alle
> Daten immer richtig.

Mein Test sagt etwas komplett anderes. Warum übersieht man das?

von Nemopuk (nemopuk)


Lesenswert?

Veit D. schrieb:
> Mein Test sagt etwas komplett anderes. Warum übersieht man das?

Bei den vielen Test ist inzwischen auch nicht mehr richtig klar, wer was 
womit getestet hat. Der Kai hat damit angefangen: Log-Meldungen von 
einem angeblichem Programmfragment gepostet, das unmöglich diese Ausgabe 
hätte produzieren können.

von Kai (ksb)


Lesenswert?

Hallo Nemopuk,

Nemopuk schrieb:
> Veit D. schrieb:
>> Mein Test sagt etwas komplett anderes. Warum übersieht man das?
>
> Bei den vielen Test ist inzwischen auch nicht mehr richtig klar, wer was
> womit getestet hat. Der Kai hat damit angefangen: Log-Meldungen von
> einem angeblichem Programmfragment gepostet, das unmöglich diese Ausgabe
> hätte produzieren können.

Die Variante mit eingebauten log habe ich nicht gepostet, da sie mir zu 
undurchsichtig erschien - kann ich aber - falls gewünscht - gerne 
nachholen.

Fakt ist, je mehr Logging-Funktionen ich in das Originalprogramm 
einbaue, um so öfter erhalte ich falsche Werte von cnt. Aus meiner Sicht 
zeigt dies in Richtung Stack bzw. Heap. Die erste Übertragung nach 
Neustart ist zurzeit mit 160 Bytes immer richtig.

Das Umstellen auf gleiche Variablentypen (unsigned int) hat das 
Verhalten nicht geändert.

Hat noch jemand meinen Minimal-Sketch getestet?

Zur Beachtung: während das Timeout noch läuft, sollte man kein 2. 
Datenpaket senden - evtl. erklärt das die Fehlfunktion bei Veit?
Ich habe den 2. Minimal-Sketch mittlerweile bestimmt 50 x ohne Fehler 
laufen lassen.

Ich muss jetzt erst einmal wieder meinen Kopf freibekommen und kurz 
etwas anderes machen....

Viele Grüße
Kai

: Bearbeitet durch User
von Kai (ksb)


Angehängte Dateien:

Lesenswert?

Hallo,

anbei die Variante mit Log.

Viele Grüße

Kai

von Veit D. (devil-elec)


Lesenswert?

Hallo,

zum letzten Mal. Programmiere eine saubere Einlesefunktion und eine zum 
verarbeiten der empfangenen Daten. Ohne Timeout. Ich verstehe nicht 
warum dazu alles gesagte, geschriebene und gezeigte ignoriert wird, wenn 
man selbst merkt das es nicht rund läuft. Man kann sich vertun, aber man 
muss auch reagieren können.

von Nemopuk (nemopuk)


Lesenswert?

Kai schrieb:
> Die Variante mit eingebauten log habe ich nicht gepostet, da sie mir zu
> undurchsichtig erschien - kann ich aber - falls gewünscht - gerne
> nachholen.

Das war offensichtlich, und die fehlenden/auskommentierten Zeilen konnte 
man sich denken. Ich wollte nur daran erinnern künftig, besser zu jedem 
Testergebnis auch den Input und das verwendete Programm zu liefern oder 
zu verlinken, um Verwirrung zu vermeiden.

Kai schrieb:
> Fakt ist, je mehr Logging-Funktionen ich in das Originalprogramm
> einbaue, um so öfter erhalte ich falsche Werte von cnt.
> Aus meiner Sicht zeigt dies in Richtung Stack bzw. Heap.

Sehe ich auch so. Überprüfe es.

: Bearbeitet durch User
von Christian M. (christian_m280)


Lesenswert?

Klaus schrieb:
> Äh, nein.

Ups, ja hast Recht!

Gruss Chregu

von Klaus (feelfree)


Lesenswert?

Kai schrieb:
> Aus meiner Sicht
> zeigt dies in Richtung Stack bzw. Heap.

Glaube ich nicht. Da würde es auch mal richtig crashen.
mMn liegt es eher am Timing/Laufzeitverhalten.

: Bearbeitet durch User
von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Kai schrieb:
> je mehr Logging-Funktionen ich in das Originalprogramm einbaue,
> um so öfter erhalte ich falsche Werte von cnt.

Werden Funktionen verwendet, die nicht reentrant sind?

Teilweise verwendet Arduino print m.W. printf et al der AVR-LibC, 
welches nicht reentrant ist.

: Bearbeitet durch User
von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Johann L. schrieb:
> Teilweise verwendet Arduino print m.W. printf et al der AVR-LibC,
> welches nicht reentrant ist.
Nein!
 Und reentrance spielt sowieso keine Rolle solange es nicht um 
präemptives Multitasking oder ISR geht.

Der AVR Ardiono core verwendet kein print oder printf

von Peter D. (peda)


Lesenswert?

Hängt ein Fehler von der Größe des Projektes ab, dann passiert er selten 
dort, wo man ihn sucht. Gerne werden z.B. Arraygrenzen überschritten, 
d.h. man schreibt auf eine Variable, die hinter das Array gelinkt wird.
Man könnte dann Arrays etwas mehr Bytes reservieren, ob sich was ändert.
Auch Pointer sind gerne mal vergiftet, der Compiler kann deren Größe 
nicht mehr überwachen.

von Klaus (feelfree)


Lesenswert?

Abgesehen vom nicht gesicherten Übertragungsprotokoll (das würde ich 
zuerst anpacken) würde ich die Logik ganz extrem vereinfachen.
Also z.B. immer 32/64/WhateverMakesSense Bytes einlesen und dann ins 
Flash schreiben. Ich wette, das geht in weniger als der Hälfte der 
bisher benötigten Zeilen und funktioniert dann auch ohne Probleme.

von Kai (ksb)


Lesenswert?

Hallo Veit,

Veit D. schrieb:
> Hallo,
>
> zum letzten Mal. Programmiere eine saubere Einlesefunktion und eine zum
> verarbeiten der empfangenen Daten. Ohne Timeout. Ich verstehe nicht
> warum dazu alles gesagte, geschriebene und gezeigte ignoriert wird, wenn
> man selbst merkt das es nicht rund läuft. Man kann sich vertun, aber man
> muss auch reagieren können.

Vielen herzlichen Dank für alle Tipps!

Ich habe alle Vorschläge verstanden, die Vor- und Nachteile der 
Umsetzung dieser intensiv überdacht und bin bin keinesfalls 
lernresistent.

Ich hatte bereits angefangen, Veits Beispiel zu integrieren, aber 
gemerkt, dass dieses zu viel Speicher benötigt.

Daher bin ich auf mein Verfahren zurückgekommen.....

Mein Hauptgrund für das von mir verwendete Verfahren ist, dass ich nicht 
ausreichend RAM zum Abspeichern der der später einzulesenden Dateien 
(jetzt ca. 2kB - später können es durchaus auch 10 kB sein) habe. Daher 
muss das Einlesen und Abspeichern "on the fly" passsieren.

Ein Timeout ist deshalb nötig, da der Controller vorher nicht weis, wie 
viele Daten eintreffen werden.

Das Warten auf ein Ende-Zeichen bedeutet ebenfalls, dass ich die Datei 
bis zum Ende ins RAM packen müsste, um sie anschließend zu bearbeiten.

In dem ATMEGA 328P meiner Platine habe ich aber nur 2kB RAM, von welchen 
ich gerne 500 Bytes frei haben möchte.

Mittlerweile bin ich einen grossen Schritt weiter:

zunächst habe ich alle Variablen des Gesamtprogrammes initialisiert, 
aufgrund pedas Vorschlag die Arrays größer als benötigt angelegt und 
soweit möglich auch alle Warnings beseitigt. Das hat keine Verbesserung 
gebracht.

Gerade habe ich als letzte meiner Ideen die Reihenfolge der Definition 
der Variable cur_byte und des Arrays vertauscht und nun hat sich das 
Fehlerbild geändert:

Es wird jetzt zu 90% die richtige Anzahl von 160 Bytes eingelesen - 
einmal hatte ich einen Ausreißer mit 158 Bytes die Bytes fehlen jetzt am 
Ende des EEPROM-Inhaltes 9E, 9F).

Sobald ich mehr weis, werde ich berichten....

Viele Grüße
Kai

: Bearbeitet durch User
von Veit D. (devil-elec)


Lesenswert?

Hallo Kai,

das mit dem Timeout und nicht warten können ist schon Mist. Dann sollte 
man einen anderen µC mit mehr RAM verwenden.
Peters Vorschlag mit dem Array größer wie benötigt dient zum Debuggen. 
Man kann die Größe schon auf das Notwendige anpassen. Man muss bei 
Verwendung auf das Ende prüfen. So wie ich das in
Beitrag "Re: Arduino: page-write in I2C eeprom verliert sporadisch ein Byte"
bspw. zeige
1
else if ( (daten.c >= 32) && (daten.index < daten.READ_BUFFER_SIZE)) {
Damit kann man den Buffer nicht überschreiben.

Wenn du erneut mit Timeout anfängst, hast du erneut keine Kontrolle. 
Möchtest du die Daten aufwendiger mitzählen oder vertraust du erneut 
darauf das immer alles gut geht? Am Ende stehst du wieder da und hast 
ein Problem. Dann verzichte lieber auf Page Write und schreibe alle 
Bytes sofort einzeln. Wenn du dein Bus kurz hälst und sauber 
terminierst, kannste locker 400kHz einstellen.

Überdenke dein Konzept bitte nochmal gründlich.

Und ganz wichtig. Dokter nicht an deinem bestehenden Programm rum. 
Verwirf es und fang bei Null an.

: Bearbeitet durch User
von Hans (ths23)


Lesenswert?

Kai schrieb:
> Habe ich nachträglich korrigiert.
>
> War also begründet!
OK konnte ich natürlich nicht wissen - danke für die Richtigstellung.

von Kai (ksb)


Angehängte Dateien:

Lesenswert?

Sehr geehrte Experten,

sicher habt ihr euch schon gefragt, ob ich aufgegeben habe? Nein, habe 
ich nicht.

Vorab: Schreiben ins RAM mit nachträgliche Page-Write aus dem RAM 
funktioniert theoretisch, hat aber ein Problem am Ende (siehe Ursache 
5).

Da ich immer aber gerne ganz genau weis, weshalb etwas nicht 
funktioniert, habe ich der Reihe nach folgende Punkte abgearbeitet.

1. Zunächst war es mir wichtig, alle Warnungen im realen Programm zu 
beseitigen, um alle möglichen hieraus resultierenden Probleme 
auszuschließen.
   Das hat deshalb etwas länger gedauert, weil hier viele Schreib- und 
Lesevorgänge auf das - Achtung: !interne EEPROM! vorhanden sind,
   deren Funktion immer einwandfrei war (Anmerkungen Es sind hier die 
Einstellungen und alle Kalenderdaten gespeichert).

   Bei jedem hatte ich die Warnung: invalid conversion from 'int' to 
'uint8_t*
   Hier haben sich die Entwickler des Arduino-Framework ja etwas ganz 
besonderes einfallen lassen: es muss ein Pointer auf die Variable 
übergeben werden.
   Egal - ich habe alle geändert und das Schreiben und Lesen scheint 
bisher immer noch fehlerfrei zu sein.
   Ein vollständiger Test dieser Funktionen steht allerdings noch aus. 
Hat aber nichts mit dem aktuellen Problem zu tun.
   Ich reiche an dieser Stelle noch nach, dass das Projekt nach Corona 
seit ca. 2022 ruhte, da ich nicht die Muße fand, eine Platine zu routen.
   Ursprünglich wollte ich die Drähte schön auf einer Grundplatte 
verlegen und das Ganze an die Wand hängen.
   Während dieser Zeit ist das Gerät in seinem unschönen Zustand 
(fliegender Aufbau - Foto kennt ihr ja) gelaufen und hat fehlerfrei 
funktioniert.
   Alle Termine wurden richtig angezeigt und alle Schaltzeiten 
eingehalten.
   Das aber das nur am Rande.....

   Nachdem diese Warnungen beseitigt waren, kamen andere hinzu (zeigt 
die Ide oberhalb einer bestimmten Anzahl nicht mehr alle Fehler an)?
   Dies war die Verwendung vieler unbenutzte Variablen - habe ich 
beseitigt, obwohl es bis auf den RAM-Verbrauch vermutlich irrelevant 
ist.
   Dann kamen interessante Meldungen bezüglich des Vergleiches 
unterschiedlicher Variablentypen - ebenfalls beseitigt.



2. Ich bin dem Hinweis von  Klaus (feelfree) auf ungerades x 
nachgegangen: ist nicht abgedeckt, wird aber dadurch abgefangen, dass 
immer nur gerade Anzahlen gelesen werden.
   Das versuchsweise Abfangen hat am Problem nichts geändert. Es ist 
aber ein Teil des Problems - dazu später.



3. Dann habe ich die Ursachen für das unterschiedliche Verhalten des 
Minimalprogrammes und des realen Programmes gesucht.


   a) Der Sketch von Veit arbeitet ohne externes EEPROM - daher ist das 
Timing völlig anders.... - habe ich daher nicht weiter untersucht.

   b) Die Ermittlung der mittleren Verzugszeit zwischen dem Eintreffen 
der ersten Bytes im seriellen Puffer und dem Aufruf der Empfangsroutine 
ergab 20ms

   c) Und ja: Nach Einfügen eines Delays von mehr als 15ms in die Loop 
kommt auch das Minimalprogramm durcheinander!

   d) Zusätzlich habe ich nun die "echt" eingetroffenen Bytes gezählt: 
der Wert ist immer richtig, auch wenn cnt falsche Werte enthält.


4. Bei nur geringer Anzahl geschriebener Bytes macht sich die Wartezeit 
am Ende des Page-Write stärker bemerkbar - wäre auch noch zu verkraften.


5. Die eigentliche Ursache  für den sporadischen Fehler ist, dass sich 
zur Zykluszeit im ungünstigsten Fall (nur 1 Zeichen eingetroffen) eine 
Weitere von 20ms addiert.
   und genau dann läuft der Puffer über.


6. Es gibt nun mehrere Lösungsansätze: a) als Workaround die 
Empfangsroutine nicht mehr verlassen, bis das 2. Zeichen kommt.
                                       b) Eine intelligente 
Warteroutine.

Im Anhang Programmablaufpläne zur Verdeutlichung.

Viele Grüße
Kai

von Nemopuk (nemopuk)


Lesenswert?

Wo kommen die 20ms Verzögerung her? Ich würde das Problem dort beheben, 
denn das ist nicht normal.

von Helmut H. (helmuth)


Lesenswert?

Schön dass Du nicht aufgibst.
Vielleicht schaust Du dir mal diese Arduino lib an: 
https://docs.arduino.cc/libraries/at24c/
Die kümmert sich um
I2C transfer buffer limit 32 byte
AT24 page size 64 byte
Aufruf mit beliebigem bytecount byc, z.B. beginned ab buffer[4] nach 
EEPROM adr:
1
  const uint8_t bufSiz = 100;
2
  uint8_t buffer[bufSiz];
3
  ...
4
  adr=0x1000;
5
  byc=80;  
6
  eprom.writeBuffer(adr, &buffer[4], byc);

Beitrag #7898537 wurde vom Autor gelöscht.
von Joachim B. (jar)


Lesenswert?

Kai schrieb:
> In dem ATMEGA 328P meiner Platine habe ich aber nur 2kB RAM, von welchen
> ich gerne 500 Bytes frei haben möchte.

dann nutze den ATmega1284p mit 16KB Ram, Leerplatinen gibt es bei OSH 
Park,  fertig aufgebaute leider nicht mehr zu finden.

von Klaus (feelfree)


Lesenswert?

Kai schrieb:
> Es gibt nun mehrere Lösungsansätze: a) als Workaround die
> Empfangsroutine nicht mehr verlassen, bis das 2. Zeichen kommt.
> b) Eine intelligente Warteroutine.

Richtig wäre
c) in der Empfangsroutine immer nur so viele Bytes aufsammeln wie halt 
da sind. Erst wenn sich z.B. 32 Bytes angesammelt haben werden diese 
weggeschrieben.

von Veit D. (devil-elec)


Lesenswert?

Kai schrieb:

> a) Der Sketch von Veit arbeitet ohne externes EEPROM - daher ist das
> Timing völlig anders.... - habe ich daher nicht weiter untersucht.

Deine Argumentation verstehe ich nicht. Versteht bestimmt niemand. Ich 
weiß nicht wie du Fehlersuche betreibst wenn das Offensichtliche 
ignoriert wird. Ich habe gezeigt das deine Einleseroutine fehlerhaft 
ist. Wenn man das ohne EEprom nachvollziehen kann, dann umso besser. Es 
gibt dabei genau so einen Versatz wie Eingangs von dir festgestellt. Wie 
eine saubere Einleseroutine aussehen kann bzw. aussieht habe ich mit 
Link und Code gezeigt. Warum das alles ignoriert verstehe ich echt nicht 
mehr. Das Einlesen muss jederzeit fehlerfrei funktionieren. Dein 
festhalten am paarweisen einlesen ist jedenfalls Mist. Du suchst lieber 
an anderen Baustellen und versaust dir den Code mit Zwangswartezeiten 
u.ä.. Wenn ich dann Ideen lese wie "Empfangsroutine nicht mehr 
verlassen" oder "Warteroutine" ...
Ich denke du hast keine Zeit? Aber möchtest noch mehr blockieren?
Mein wiederholter Rat. Fang nochmal sauber von vorn an.

von Peter D. (peda)


Lesenswert?

Kai schrieb:
> 5. Die eigentliche Ursache  für den sporadischen Fehler ist, dass sich
> zur Zykluszeit im ungünstigsten Fall (nur 1 Zeichen eingetroffen) eine
> Weitere von 20ms addiert.
>    und genau dann läuft der Puffer über.

Dann mach doch das EEPROM Zeugs einfach mit in die schnellen Tasks vor 
der 20ms Wartezeit:
1
void fast_tasks(void)
2
{
3
// ...
4
  eeprom_stuff();
5
}
6
7
int main(void)
8
{
9
// ...
10
  for(;;)
11
  {
12
    fast_tasks();
13
    if (timer_expired())
14
    {
15
      delayed_tasks();
16
    }
17
  }
18
}

Kai schrieb:
> als Workaround die
> Empfangsroutine nicht mehr verlassen

Nein, das ist (fast) immer eine Sackgasse. Eine Task darf alle anderen 
nicht auf unbestimmte Zeit blockieren.
Wenn eine Task gerade auf etwas warten muß, dann verlasse sie einfach 
und setze sie beim nächsten Aufruf fort. Mit einem switch/case kann man 
sich merken, wo weitergemacht werden soll.

Wie oben schon gesagt, ich sammele alle Zeichen in einen Puffer und 
bearbeite sie erst nach dem CR.

von Joachim B. (jar)


Angehängte Dateien:

Lesenswert?

Peter D. schrieb:
> Wie oben schon gesagt, ich sammele alle Zeichen in einen Puffer und
> bearbeite sie erst nach dem CR.

genau so!

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Peter D. schrieb:
> Eine Task darf alle anderen
> nicht auf unbestimmte Zeit blockieren.
Aus dem Grund hat meine I2C Klasse, welche ich vor einiger Zeit in 
diesem Thread vorgestellt habe, einen CallBack um die EEPROM Wartezeit 
eben nicht zu verplempern.

: Bearbeitet durch User
von Kai (ksb)


Lesenswert?

Hallo Nemopuk, Helmut H, Joachim B, Klaus (feelfree) und Veit,

vielen herzlichen Dank für eure hilfreichen Anregungen und Antworten.

Nemopuk:
1
Wo kommen die 20ms Verzögerung her? Ich würde das Problem dort beheben, 
2
denn das ist nicht normal.
Wichtigster Punkt: Die 20ms sind der ausgewogenen Reaktion auf die 
Fernbedienung geschuldet. Nichts nervt hier mehr, als verzögerte 
Reaktion oder wenn Tastendrücke verloren gehen.
Das Gesamtprogramm ist nicht blockierend. Jede Funktion in der Loop wird 
zyklisch aufgerufen und arbeitet dann einen ihre Aufgabe ab.
So kann zum Beispiel das LED-Fading oder eine Melodie ungestört 
weiterlaufen, während beliebige andere Funktionen ausgeführt werden.

Helmut:
1
Vielleicht schaust Du dir mal diese Arduino lib an: https://docs.arduino.cc/libraries/at24c/
2
Die kümmert sich um I2C transfer buffer limit 32 Byte AT24 page size 64 Byte Aufruf mit beliebigem bytecount byc, z.B. beginned ab buffer[4] nach EEPROM adr:
Vielen Dank - super Lib - nur leider ist mein Flash fast voll.....
Beim nächsten Projekt bestimmt.

Joachim:
1
dann nutze den ATmega1284p mit 16KB Ram, Leerplatinen gibt es bei OSH Park, fertig aufgebaute leider nicht mehr zu finden.
Der ATMEGA 1284 ist ein 40-Pinner. Meine Platine mit ATMEGA328P (28-Pin) 
ist bereits gefertigt und funktioniert einwandfrei.
Alle Funktionen bis auf das Einlesen der Dateien sind fertig. Wenn diese 
Funktioniert ist das Projekt abgeschlossen.
Eine Frage am Rande, welche ich dir schon immer mal stellen wollte: Hast 
du eine 1977 eine Ausbildung bei der Deutschen Bundespost in Düsseldorf 
gemacht?

Klaus:
1
Richtig wäre c) in der Empfangsroutine immer nur so viele Bytes aufsammeln wie halt da sind. Erst wenn sich z.B. 32 Bytes angesammelt haben werden diese weggeschrieben.
Genau das meinte ich damit: bestimmen, wie viel Platz für den Page-Write 
bleibt, diese Anzahl abwarten und dann Schreiben.
Es muss aber ein Timeout geben, wenn das Ende der Übertragung erreicht 
ist, aber bis zum Erreichen des Page-Endes noch Daten fehlen.

Veit:

diese Antwort fällt mir schwer - es tut mir leid, wenn ich dir das 
Gefühl vermittelt habe, deine Vorschläge zu ignorieren. Das ist 
definitiv nicht so!
 Nochmal: wirklich vielen Dank für deine Mühe mit dem Testen und 
Verbessern meines Minimal-Sketches!

Ich habe deine Vorschläge nicht ignoriert und bin in den vorherigen 
Antworten auf meine Beweggründe bereits geschildert:

1. Ich habe dein abgespecktes Minimal-Programm (ohne EEPROM-Zugriff 
mittlerweile 100 Mal getestet - es kommt bei mir nie durcheinander.
2. Ich habe deine wirklich sehr elegantes Einlese-Routine getestet. Sie 
funktioniert perfekt. ABER: das verbliebene RAM in der Applikation 
reicht höchstens für 200 Zeichen....


a) Der Sketch von Veit arbeitet ohne externes EEPROM - daher ist das 
Timing völlig anders.... - habe ich daher nicht weiter untersucht.
1
Deine Argumentation verstehe ich nicht. Versteht bestimmt niemand. Ich weiß nicht wie du Fehlersuche betreibst wenn das Offensichtliche 
2
ignoriert wird. Ich habe gezeigt das deine Einleseroutine fehlerhaft ist. Wenn man das ohne EEprom nachvollziehen kann, dann umso besser. Es 
3
gibt dabei genau so einen Versatz wie Eingangs von dir festgestellt.
Antwort siehe oben
1
Wie eine saubere Einleseroutine aussehen kann bzw. aussieht habe ich mit Link und Code gezeigt. Warum das alles ignoriert verstehe ich echt nicht mehr.
Habe ich keinesfalls ignoriert, denn ich habe sie getestet. Sie 
funktioniert perfekt, nur leider habe ich nicht so viel RAM.
Die Dateien sind mindestens 2kB groß, was einer Arraygröße von 2000 
Bytes entspricht.

Hier der Speicherbedarf meines realen Programmes (mit der bisheriger 
Einlese-Routine):
Der Sketch verwendet 32222 Bytes (99%) des Programmspeicherplatzes. Das 
Maximum sind 32256 Bytes.
Globale Variablen verwenden 1580 Bytes (77%) des dynamischen Speichers, 
468 Bytes für lokale Variablen verbleiben. Das Maximum sind 2048 Bytes.

1
Dein festhalten am paarweisen einlesen ist jedenfalls Mist. Du suchst lieber an anderen Baustellen und versaust dir den Code mit Zwangswartezeiten u.ä.. Wenn ich dann Ideen lese wie "Empfangsroutine nicht mehr verlassen" oder "Warteroutine" ...

Ich muss schon 2 ASCII-Zeichen lesen und zu einem Byte zusammensetzen, 
bevor ich es ins EEPROM schreiben kann - siehst du das anders?
Die Zwangswartezeit gibt es nur nach dem ersten Byte - ab hier darf die 
Einlese-Routine nicht verlassen werden, da andernfalls durch die 
doppelte Zykluszeit von 20ms der Puffer überläuft.
Mit "Warteroutine" meinte ich konkret das Warten auf eine ausreichende 
Anzahl von Daten für den Page-Write.
1
Ich denke du hast keine Zeit? Aber möchtest noch mehr blockieren? Mein wiederholter Rat. Fang nochmal sauber von vorn an.
Nicht "mehr blockieren" sondern lediglich an der richtigen Stelle mit 
Timeout auf das 2. ASCII-Zeichen warten.
Habe ich - es gibt dann ein Problem am Ende - der Puffer läuft hier 
über.
Die Ursache ist die Zykluszeit in Verbindung der geringen Anzahl 
geschriebener Daten.

Deshalb die Routine, welche auf das Eintreffen eine ausreichender Anzahl 
von Daten wartet.

Ich hoffe, ich konnte alle Missverständnisse beseitigen und habe 
niemanden mit meinen Antworten "vor den Kopf gestoßen", was keinesfalls 
gewollt wäre!

Viele Grüße
Kai

: Bearbeitet durch User
von Kai (ksb)


Lesenswert?

Hallo zusammen,

es läuft endlich - bisher jedoch nur im Uno getestet, dafür mit mehr 
Daten (512 ASCII-Zeichen = 256 Bytes). Jetzt bin ich mit diesem an der 
Grenze der Logging-Kapazität....

Ein Delay von 50ms in der Loop bringt die Routine nicht durcheinander.
Die Anzahl der zu sammelnden Bytes musste ich oberhalb von 25 Bytes 
begrenzen, da der serielle Puffer andernfalls auf 60 hochläuft, was mir 
zu unsicher war. Mit der Begrenzung liegt der Maximalwert bei 48.
1
+++++++++++-
2
3
Max. buffer fill state: 48 number real received bytes: 256 variable cnt: 256 OK  Char left in serial buffer 0 Remaining -1 Timeout 0 Checksum 0
4
5
1. page-write: cnt 0  cur_byte 24  -> 24 status 0 written 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 16 17   read 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 16 17 
6
2. page-write: cnt 24  cur_byte 8  -> 32 status 0 written 18 19 1A 1B 1C 1D 1E 1F   read 18 19 1A 1B 1C 1D 1E 1F 
7
3. page-write: cnt 32  cur_byte 24  -> 56 status 0 written 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F 30 31 32 33 34 35 36 37   read 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F 30 31 32 33 34 35 36 37 
8
4. page-write: cnt 56  cur_byte 24  -> 80 status 0 written 38 39 3A 3B 3C 3D 3E 3F 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F   read 38 39 3A 3B 3C 3D 3E 3F 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F 
9
5. page-write: cnt 80  cur_byte 16  -> 96 status 0 written 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F   read 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F 
10
6. page-write: cnt 96  cur_byte 24  -> 120 status 0 written 60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F 70 71 72 73 74 75 76 77   read 60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F 70 71 72 73 74 75 76 77 
11
7. page-write: cnt 120  cur_byte 24  -> 144 status 0 written 78 79 7A 7B 7C 7D 7E 7F 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F   read 78 79 7A 7B 7C 7D 7E 7F 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F 
12
8. page-write: cnt 144  cur_byte 16  -> 160 status 0 written 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F   read 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F 
13
9. page-write: cnt 160  cur_byte 24  -> 184 status 0 written A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF B0 B1 B2 B3 B4 B5 B6 B7   read A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF B0 B1 B2 B3 B4 B5 B6 B7 
14
10. page-write: cnt 184  cur_byte 24  -> 208 status 0 written B8 B9 BA BB BC BD BE BF C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF   read B8 B9 BA BB BC BD BE BF C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF 
15
11. page-write: cnt 208  cur_byte 16  -> 224 status 0 written D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF   read D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF 
16
12. page-write: cnt 224  cur_byte 24  -> 248 status 0 written E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF F0 F1 F2 F3 F4 F5 F6 F7   read E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF F0 F1 F2 F3 F4 F5 F6 F7 
17
13. page-write: cnt 248  cur_byte 8  -> 256 status 0 written F8 F9 FA FB FC FD FE FF   read F8 F9 FA FB FC FD FE FF 
18
19
Log of combined values (val): 
20
 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F 60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F 70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF 00
21
22
EEPROM Settings
23
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F  16
24
10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F  32
25
20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F  48
26
30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F  64
27
40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F  80
28
50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F  96
29
60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F  112
30
70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F  128
31
80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F  144
32
90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F  160
33
A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF  176
34
B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF  192
35
C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF  208
36
D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF  224
37
E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF  240
38
F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF  256
39
40
RAM rx_test
41
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F  16
42
10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F  32
43
20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F  48
44
30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F  64
45
40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F  80
46
50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F  96
47
60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F  112
48
70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F  128
49
80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F  144
50
90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F  160
51
A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF  176
52
B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF  192
53
C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF  208
54
D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF  224
55
E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF  240
56
F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF  256
57
58
EEPROM clear
59
60
RX-RAM clear
61
62
BUFFER clear

Ich werde jetzt die Routine zunächst in den fliegenden Aufbau mit 
Arduino Mega integrieren und dann die Datenmenge erhöhen.

Viele Grüße
Kai

von Kai (ksb)


Lesenswert?

Hallo Peter d,

entschuldige, dass ich erst jetzt antworte, aber ich habe noch mit der 
Routine gekämpft.

Auch dir vielen Dank für alle hilfreichen Tipps und Beispiele.
1
Dann mach doch das EEPROM Zeugs einfach mit in die schnellen Tasks vor 
2
der 20ms Wartezeit:
Guter Vorschlag - es gibt aber in dem Sinne keine Wartezeit von 20ms. 
Diese Zeit ist die Zykluszeit aller nacheinander aufgerufenen Funktionen 
der Loop.
1
Nein, das ist (fast) immer eine Sackgasse. Eine Task darf alle anderen 
2
nicht auf unbestimmte Zeit blockieren.
3
Wenn eine Task gerade auf etwas warten muß, dann verlasse sie einfach 
4
und setze sie beim nächsten Aufruf fort. Mit einem switch/case kann man 
5
sich merken, wo weitergemacht werden soll.
6
Wie oben schon gesagt, ich sammele alle Zeichen in einen Puffer und 
7
bearbeite sie erst nach dem CR.
Das Warten passiert nur im Datenübertragungsmenü und das auch nur, wenn 
Mindestens nur ein Zeichen eingetroffen ist. In diesem Fall warte ich 
eine gewisse Zeit auf das 2. Zeichen. Kommt das nicht, wird die 
Wartezeit beendet.

Viele Grüße
Kai

von Joachim B. (jar)


Lesenswert?

Kai schrieb:
> Hallo

selber, was ist mit:

Wichtige Regeln - erst lesen, dann posten!

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

von Falk B. (falk)


Lesenswert?

Kai schrieb:
> Das Warten passiert nur im Datenübertragungsmenü und das auch nur, wenn
> Mindestens nur ein Zeichen eingetroffen ist. In diesem Fall warte ich
> eine gewisse Zeit

Wieviel? Wenn man es richtig macht, wartet man exakt GAR NICHT, sondern 
holt das nächste Zeichen beim nächsten Aufruf der Statemachine ab. 
Siehe Multitasking. Und wenn man den RX Interrupt benutzt, 
wartet man mal sicher nicht.

von Kai (ksb)


Lesenswert?

Hallo Joachim,

Entschuldigung - hatte ich nicht als so lang empfunden - kann es leider 
nicht mehr ändern.

Viele Grüße
Kai

von Kai (ksb)


Lesenswert?

Hallo Falk,
1
Wieviel? Wenn man es richtig macht, wartet man exakt GAR NICHT, sondern
2
holt das nächste Zeichen beim nächsten Aufruf der Statemachine ab.
3
Siehe Multitasking. Und wenn man den RX Interrupt benutzt,
4
wartet man mal sicher nicht.

2 ms

Viele Grüße
Kai

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Kai schrieb:
> 2 ms

Ist deutlich mehr, als "gar nicht".
(eben waren es noch 20ms)

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.