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?

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.