Arduino: page-write in I2C eeprom verliert sporadisch ein Byte

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

Angehängte Dateien:
#7894111
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.

#7894130
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

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

#7894154
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?

#7894158
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
#7894169
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

#7894172
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?

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

#7894185
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

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

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

(Firma: Gast) #7894218
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.

(Firma: Gast) #7894279
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.

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

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

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

#7894345
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

Angehängte Dateien:
#7894397
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.

#7894422
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).

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

Angehängte Dateien:
#7894445
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.

#7894449
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!

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

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

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

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

#7894507
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!

#7894533
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

4
Die 21 fehlt, alle folgenden Bytes sind an der falschen Adresse.
5

6
> 91 92 93 94 95 96 97 97 99 9A 9B 9C 9D 9E 9F FF  160
7

8
Eine der beiden 97 müsste den Wert 98 haben.
9

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

15
Du hast eine leere Textdatei gezippt. Bitte reiche die richtige Datei 
16
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

#7894535
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

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

#7894553
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).

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

#7894575
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

#7894585
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?

#7894596
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

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

(Firma: Gast) #7894614
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.

Angehängte Dateien:
#7894671
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
  1. 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
  1. 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
  1. 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
        }
  1. 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

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

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

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

#7894725
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

#7894772
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

Angehängte Dateien:
#7894777
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.

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

#7894853
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

Angehängte Dateien:
(Firma: Gast) #7895064
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.
#7895094
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

#7895122
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
}
#7895214
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

Angehängte Dateien:
#7895222
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

#7895229
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

#7895244
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

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

(Firma: Gast) #7895248
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.

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

#7895251
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

Angehängte Dateien:
#7895255
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.

#7895259
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. ;-)

#7895264
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

#7895268
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

Angehängte Dateien:
#7895270
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"

#7895277
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

#7895288
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?

#7895302
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

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

#7895307
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?

#7895315
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
}
#7895318
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

#7895321
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

#7895330
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

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

#7895338
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?

#7895344
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

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

#7895355
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?

1
  if (address<16) 
2

3
    Serial.print("0");

viele Grüße Kai

#7895391
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);
#7895423
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

Angehängte Dateien:
#7895436
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?

#7895441
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?

#7895457
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. "

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

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

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

#7895521
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

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

#7895530
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

#7895532
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

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

Angehängte Dateien:
#7895535
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.

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

#7895538
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

#7895539
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).

#7895549
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

#7895551
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

#7895554
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
}
#7895556
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.

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

#7895572
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

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

#7895579
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

#7895583
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

#7895612
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

#7895640
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?

#7895646
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

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

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

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

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

#7895768
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

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

#7898499
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

Angehängte Dateien:
#7898524
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 von einem Moderator gelöscht.
#7898553
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.

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

#7898798
Lesenswert?

Kai schrieb:

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

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren