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):
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.
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.
Nemopuk schrieb:> Offenbar fehlt ein Handshake. Dein Sender sendet die Daten schneller an> den Mikrocontroller, als dieser sie verarbeiten kann. Du brauchst eine> bidirektionale Kommunikation, damit der Mikrocontroller dem Sender ein> "mach mal pause" Signal senden kann.
Oder genügend buffer um die Daten in RAM zwischenzuspeichern.
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
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.
Funktioniert es stets 10 Mal und beim 11. Mal nicht?
Ist es immer die 0x65, die fehlt?
Was sind die Werte der Debug-Ausgaben, wenn es funktioniert, und wenn
nicht?
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?
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:
Zähle mal, wie oft page_write() aufgerufen wird und gebe die Zahl tzum
Schluss aus. Wo ist die Dokumentation (oder besser noch der Quelltext
von page_write()?
Kai schrieb:> Dar Ram-Inhalt ist richtig aber die EEPROM-Schreibroutine verschluckt> ein Byte.
Dann solltest Du uns den dafür notwendigen Programmteil zeigen.
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
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?
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.
for(unsignedinti=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
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.
Kai schrieb:> void page_write(unsigned int addr, unsigned int number)
Das wird so nicht gehen, da die Wire-Klasse nicht weiss dass
sie auf die Programmierung des EEPROMs Rücksicht nehmen muss.
Nemopuk schrieb:> vermute ich einen Fehler in der Berechnung der Adressen> (Positionen im EEprom)
Nö. Einfach nicht lang genug gewartet sondern EEPROM "überfahren".
Wastl schrieb:> Einfach nicht lang genug gewartet sondern EEPROM "überfahren".
Ich habe gerade mal in ein zufällig gewähltes DB geschaut, da werden 5ms
als maximale Zeit genannt. Also das sollte man mal mit dem RICHTIGEN
Datenblatt abgleichen und sicherheitshalber ein bisschen länger warten.
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 ....
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.
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.
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.
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.
Die Wire-Schreibroutine wird ja nur mit der erlaubten Anzahl von Daten
aufgerufen. Die Zeit werde ich kontrollieren. Die verwendete Angabe habe
ich aus einem Beispiel übernommen.
3. Arduino F:
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
Kai schrieb:> Das Einfügen einer Debug-Ausgabe ist nicht ganz einfach,> da diese das Timing stark verändert.
Nicht, wenn du die Daten über SPI ausgibst und mit dem Logikanalyzer
mitliest. Die SPI-Ausgange kannst du deutlich schneller als die 9600Bd
laufen lassen.
Kai schrieb:> Das Einfügen einer Debug-Ausgabe ist nicht ganz einfach,> da diese das Timing stark verändert. Versuche ich gleich auf jeden Fall> mal.
Falsch. Man muss halt die Debugausgaben passend puffern und dann
ausgeben, wenn genügend Zeit ist.
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):
for(unsignedinti=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:
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.
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).
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.
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.
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!
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.
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.
Helmut H. schrieb:> So wird endTransmission() im Fehlerfall einfach nochmal aufgerufen,> keine Ahnung was dann passiert - macht nix und gibt 0 zurück ??
Damit hast Du voll recht, das ist ein grober Fehler.
I2C kennt kein Retry, wie z.B. der CAN-Bus. Man muß daher die
fehlerhafte Aktion komplett wiederholen.
Nemopuk schrieb:> Ein Logic Analyzer wäre auch am I2C Bus hilfreich.
I2C Kommunikation erfordert wegen ACK mehr Zusammenarbeit zwischen den
beiden Kommunikationsteilnehmern. Macht der Arduino des TO den Handshake
im Hintergrund per Hardware?
Peter D. schrieb:> Man muß daher die fehlerhafte Aktion komplett wiederholen.
Wen man endTransmission() erneut aufruft, passiert genau das.
Rainer W. schrieb:> Macht der Arduino des TO den Handshake im Hintergrund per Hardware?
Ja
Helmut H. schrieb:> Wire.write() schreibt die Daten nur in den transmit buffer
Korrekt!
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.
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.
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!
Ich würde immer noch versuchen, einen großzügigen Delay (mehr als im
9
Datenblatt steht) einzubauen. Nur zur Probe, ob dort der Knackpunkt
10
liegt. Du kannst zusätzlich die Baudrate herabsetzen, wenn nötig
11
(Pufferüberlauf).
12
13
Du hast eine leere Textdatei gezippt. Bitte reiche die richtige Datei
14
nach.
Die doppelt vorhandene 97 habe ich gestern Abend tatsächlich übersehen
und leider offenbar auch das Ergebnis des Logging nicht in die geöffnete
Datei kopiert. Die Daten sind leider weg - ich wiederhole es gleich.
Helmut:
1
Wire.endTransmission() überträgt die Daten aus dem transmit buffer und
2
meldet Misserfolge, das muss ausgewertet werden:
3
0: success.
4
1: data too long to fit in transmit buffer.
5
2: received NACK on transmit of address.
6
3: received NACK on transmit of data.
7
4: other error.
8
5: timeout
Vielen Dank für diese Information, welche mir bisher fehlte.
Peda:
1
Nun, vor dem Schreiben oder Lesen mußt Du doch erstmal die Adresse
2
senden. Damit ist automatisch gesichert, daß der EEPROM ein vorheriges
3
Schreiben beendet hat.
4
Ein EEPROM der busy ist, stellt sich tot, d.h. antwortet mit NACK auf
5
seine Adresse. Ich versuche es dann einfach in einer Schleife nochmal,
6
die bei 400kHz I2C-Takt bis zu 10ms wartet.
7
8
Man muß natürlich vor jedem Byte abtesten, ob ein Pageüberlauf erfolgt
9
und dann erstmal die aktuelle Page wegschreiben.
Deine I2C-Lib ist natürlich die Königsklasse - wahrscheinlich würde ich
mir bei der Umsetzung für meine Applikation aber viele neue Probleme
einhandeln (RTC, LCD)?
Bedeutet dies, dass das Warten auf ein ACK am Ende des Schreibvorganges
nicht erforderlich ist?
Zunächst werde ich versuchen, ein ähnliches Ergebnis wie gestern zu
loggen.
Eine Anmerkung noch:
a) zwecks möglichst schnellen Abholens der seriellen Daten und
b) damit mir auf dem I2C-Bus nichts dazwischenfunkt
wird während der gesamten Übertragung die while-Schleife nicht
verlassen.
Viele Grüße und einen schönen Sonntag
Kai
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
Peter D. schrieb:> Kai schrieb:>> byte Ser_Arr[31];>> Sicher, daß eine Page 31 Byte groß ist?>> Ich kenne nur EEPROMs mit 32 Bytes per Page.
Das verwendete hat offensichtlich 64 Byte per Page.
Der Wire Buffer hat nur 32 Byte
Unser Klient möchte nur 30 pro Transfer übertragen.
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.
Arduino F. schrieb:> Unser Klient möchte nur 30 pro Transfer übertragen.
Dann muß aber "page_write" einen Pageüberlauf mit behandeln.
Schade, das "page_write" nicht gezeigt wird, das wäre schon sehr
wichtig.
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).
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.
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
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?
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
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.
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.
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) !
2. Jeweils 2 Zeichen werden in eine Hexadezimalzahl gewandelt.
1
for(inti=0;i<x/2;i++){// Process total number of bytes (=2 nibbles) received 2 4 6 8
2
3
bytey=Serial.available();// Current buffer state = no of char!
4
if(y>serbuf){serbuf=y;}// Buffer max size
5
6
nibble=text2hex(Serial.read());// read and convert 1. char = high nibble
7
hi=nibble;// High nibble
8
9
nibble=text2hex(Serial.read());// read and convert 2. char = low nibble
10
lo=nibble;// Low nibble
11
12
val=hi<<4|lo;// Combine hi + low = value
3. Die maximal erlaubte Anzahl von Bytes (Puffer- oder Page-Grenze) wird
berechnet:
1
bytes=bytes_in_page(addr+cnt);// Determinate max. number allowed in this page
4. Solange keine der Grenzen (Wire-Puffer, Page) erreicht ist, werden
die Daten in ein Array geschrieben (x ist die Anzahl der verfügbaren
Zeichen:
1
bytes=bytes_in_page(addr+cnt);// Anzahl der max. erlaubten Bytes
2
3
if((cur_byte<x/2)&&// Not all data processed | -> collect data
4
(cur_byte<bytes)){// Below Buffer or page-write border |
5
Ser_Arr[cur_byte]=val;// Add data to page-write buffer |
6
cur_byte++;// Next index |
7
}
5. Sobald eine der Grenzen (Wire-Puffer, Page) erreicht ist, werden die
Daten ins EEPROM geschrieben
1
if((cur_byte==x/2)||// current count = received bytes | -> page write
2
(cur_byte==bytes)){// Buffer or page-write border reached |
3
page_write(addr+cnt,cur_byte);// Perform page-write to ext ee |
4
//debug_page_wr(addr); // DEBUG! show page write status |
5
cnt=cnt+cur_byte;// Increase address by bytes added | -> prepare next packet
6
cur_byte=0;// No Bytes left |
7
}
8
}
Beim Schreiben fallen mir nun folgende mögliche Fehlerursachen ein:
a) da der Timeout aktuell nur am Anfang der Übertragung gesetzt wird,
könnte dieser vor dem Ende der Übertragung ablaufen.
b) da die Abfrage 1 auf Werte >= 2 abfragt, könnte die Wandlung 2)
fehlschlagen.
c) Es könnte irgendein Interrupt die korrekte Adressberechnung stören.
Zurzeit arbeite ich daran, ein Logging der zu schreibenden und der
tatsächlich geschriebenen Daten zu implementieren.
Erstes Zwischenergebnis ist, dass das wahrscheinlich immer erste Byte
eines neuen Datenpaketes falsch geschrieben wird.
Zur Verifikation möchte ich aber erst einmal eine größere Anzahl von
Übertragungen analysieren.
Viele Grüße
Kai
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.
Peter D. schrieb:> Was> bedeutet "sends a restart message". Wird bei einem Fehler das ganze> Paket nun nochmal gesendet oder was?
Nein, das Flag entscheidet ob Wire.endTransmission(flag) eine Stopp
oder Repeated-Start Condition auf dem Bus setzt.
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.
Peter D. schrieb:> Es ist also keinen Deut schneller, als meine Lib mit SW-I2C.
Das ist wohl so. Allerdings haben die Pins (soweit ich weiß)
Entstörfilter, von denen eine Software Implementierung mit Bitbanging
nicht profitieren kann. Was je nach Anwendung völlig egal ist.
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.
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
Kai schrieb:> Der Arduino Mega hat gemäß Dokumentation 10k-Pullups.Kai schrieb:> Es befinden sich 10k-Pullup in der Mitte des Busses.
Aha. Viel zu hoch! Und nimm Zwei!
Gruss Chregu
Christian M. schrieb:> Kai schrieb:>> Es befinden sich 10k-Pullup in der Mitte des Busses.>> Aha. Viel zu hoch! Und nimm Zwei!
Jain. Die meisten Sachen laufen auch mit 10k Pull Ups und 100kHz. Bei
allem was man hier so gelesen hat, liegt der Fehler zu 99% in der
Software.
I2C ist mit max. 3mA bei LOW definiert, also min. 1,6k bei 5V. 2-4,7k
sind OK.
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
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.
Nochmal: Es scheitert nicht an den EEprom Zugriffen, sondern an dem
Algorithmus für die Zähler und Adressen. Der Fehler steckt in dem
undurchsichtigen Algorithmus.
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.
Wenn ich das richtig sehe hat jedes der Module und der Mega 10k Pullup.
Das macht insgesamt 2k. Evtl. etwas wenig.
Ist auf dem RTC Board nicht noch ein EEPROM?
Im bitteren Fall gar mit der gleichen Adresse?
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.
Arduino F. schrieb:> oder Repeated-Start Condition auf dem Bus setzt.
Das ist ja blöd, was soll das bringen?
Man muß doch im Fehlerfall das ganze Paket nochmal senden.
Es müßte also eine Funktion geben, die die Daten im Puffer wieder als
gültig markiert.
Ansonsten muß man ja von hinten durch die Brust ins Auge alles nochmal
präparieren.
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.
Arduino F. schrieb:> Du hältst es also für blöd diese Bedingungen setzen zu können?
Ja.
Ich wüßte nicht, wie ein Repeated-Start bei einem Fehler weiter helfen
soll.
Es setzt einfach nur den Bus auf blockiert.
Das Repeated-Start wird nur benötigt, um nach der EEPROM-Adresse auf
Lesen umzuschalten, also wenn eben kein Fehler aufgetreten ist.
Peter D. schrieb:> Das Repeated-Start wird nur benötigt, um nach der EEPROM-Adresse auf> Lesen umzuschalten, also wenn eben kein Fehler aufgetreten ist.
Damit widersprichst du dir selber!
1. Du findest es blöd
2. Du hältst es für notwendig
Die irrsinnige Fantasie, dass das repteated in der Doku irgendwas mit
Fehlern zu tun hat, existiert nur in deinem Kopf.
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
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
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:
> 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?
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:
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
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
Kai schrieb:> Meine Vermutung geht in Richtung irgendwelcher Interrupts, welche das> letzte hochzählen von cur_bytes verhindert....
Interrupts verhindern keine Schreibzugriffe auf das RAM.
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.
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.
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
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.
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,
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.
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
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
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.
Veit D. schrieb:> ich ging davon aus das man alle I2C Decives für den High Speed Modus> erst gezielt ansprechen muss um diesen Modus zu aktivieren
Kann es sein, dass du das mit SD Karten durcheinander bringst?
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. ;-)
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
Veit D. schrieb:> Reagiert dennoch mit einer Antwort auf seine 2. Adresse,> was auch normal ist.
Normal ist das im Datenblatt beschriebene Verhalten.
Wenn das da drin beschrieben ist, ist alles ok.
Ist es nicht beschrieben, ist da ein Problem!
Wo siehst du den Fehler in dieser Logischen Schlussfolgerung?
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
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"
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?
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:
Veit D. schrieb:> dein Scan Code zeigt an meinem nackten Arduino Mega 2560 63 gefundene> Adressen an.
Lass mich raten: Weil du keine Pull-Up Widerstande an I2C Bus hast (?)
Nemopuk schrieb:> Veit D. schrieb:>> dein Scan Code zeigt an meinem nackten Arduino Mega 2560 63 gefundene>> Adressen an.>> Lass mich raten: Weil du keine Pull-Up Widerstande an I2C Bus hast (?)
Nein. Auf dem Board sind Arduinoische 10k. Dafür reicht das. Außerdem
zeigt mein Scan Code korrekt an. Ich möchte wissen was er bei Kai
ausgibt.
Und du verwendest keine guten Kabel/Steckverbinder! Das sind doch
wahrscheinlich alles ausgeleierte Eisendrähte. Geh da mal mit einem
Magneten ran, dann weisst du, ob es Kupfer oder Eisen ist. Löte lieber
anständige Kabel dran.
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
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
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 ....
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?
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.
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
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
Hallo,
wenn dein Scanner Adresse 0x50 und 0x58 findet und mein Code bis 0x77
nichts, sollte man stutzig werden.
Ändere meinen Code auf < 128 ab. Was zeigt er an?
Ändere deinen Code auf < 78 ab. Was zeigt er an?
Laut Spezifikation ist im 7 Bit Bereich an Adresse 77 Ende.
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
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.
> 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?
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
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.
Kai schrieb:> aber hier fehlen doch Klammern oder?>> if (address<16)>> Serial.print("0");
Manche Klammern sind flüssiger als Wasser.
Überflüssig.
Natürlich kann man da Klammern hin machen, wenn man das schöner findet.
Jedem so wie er/sie/es mag....
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
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
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?
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.
"
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.
Allgemein werden mir die Datentypen zu wild verwendet. Integer mit bool
und bool mit Integer.
Diese unbenutzte Funktion ist mir nicht ganz klar.
1
bytebytes_in_page(unsignedintaddr){// Number of bytes left in page
2
if(addr<EXT_EE_SIZE){// Address is valid
3
intpage=addr/EXT_EE_PAGE;// Current page
4
intborder=(page+1)*EXT_EE_PAGE;// Next page address = border
5
intbytes=border-addr;// Number of bytes total
6
intpakets=(bytes/EXT_EE_LIMIT)+1;// Number pf pakets
7
intrest=bytes;intcount=0;// Rest of bytes
8
9
if(rest>=EXT_EE_LIMIT){// Enough space for 30 bytes
10
count=EXT_EE_LIMIT;
11
}//
12
elseif(rest<EXT_EE_LIMIT){// Less space than 30 bytes
13
count=rest;
14
}//
15
returncount;// 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.
Veit D. schrieb:> Einleseroutine überarbeiten, Kontrolle behalten.
Sehe ich auch so.
Einen Bytestrom ohne jede Synchronisierung oder Prüfsumme zu übertragen
führt früher oder später zu Problemen. Hier halt früher.
Die Erfinder des Intel-Hex-Formats waren da etwas weitsichtiger...
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.
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
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.
Hallo Veit,
bezüglich des EEPROM:
alle Adressen sind mit GND verbunden
1 0 1 0 A2 A1 A0 R/W
1 0 1 0 0 0 0 0 somit 0x50
WP liegt ebenfalls auf GND (Siehe Microchip Datenblatt 24AA256 Seite 8)
Die Beschreibung zum Reichelt EEPROM mit links und rechts ist falsch.
Viele Grüße
Kai
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
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.
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.
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.
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
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).
Christian M. schrieb:> Schon mal mit einem richtigen Terminal probiert?
Gute Idee. Beim Hammer Terminal kann man sogar ein Delay zwischen die
Zeichen einfügen.
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
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.
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
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
structDaten{
2
staticconstbyteREAD_BUFFER_SIZE{20};
3
charrxBuffer[READ_BUFFER_SIZE+1]{'\0'};// Platz für 20 Zeichen + Null-Terminator
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.
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.
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
voidflow_control(intz){// Flow-control via XOFF and XON
2
3
if(!rx_flag&&(z>20)){// Buffer size above limit + no XOFF?
> 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
Veit D. schrieb:> Alles Falschaussagen.
Eine Vermutung kann keine Falschaussage sein.
> Wenn man ... die serielle Kommunikation> als Gegeben 'OK' akzeptiert, dann wundert mich nichts mehr.
Die serielle Kommunikation hat er ja geprüft. Im RAM rx_test[] sind alle
Daten immer richtig.
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.
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
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
Kai schrieb:> Somit müssten alle Variablen, welche an Berechnungen beteiligt sind, vom> typ unsigned int sein.
Probiere das. Ich erwarte nicht, dass dadurch das Problem gelöst wird.
Aber der Test ist einfach, es gibt nicht viel zu verlieren.
Nemopuk schrieb:> 9600 Baus sind 960 Zeichen (Nibbles) pro Sekunde, also 480 Bytes.
Mit Start- und Stoppbit. Also 800 bzw. 400 Bytes pro Sekunde!
Gruss Chregu
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?
Hallo Klaus,
Klaus schrieb:> Christian M. schrieb:>> Mit Start- und Stoppbit.>> Genau, 9600 geteilt durch (1+8+1) macht 960.>>> Also 800 bzw. 400 Bytes pro Sekunde!>> Äh, nein.
Ein zu schreibendes Byte besteht aus 2 ASCII-Zeichen von der seriellen
Schnittstelle - daher kommt diese Aussage.
Viele Grüße
Kai
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
Kai schrieb:> Ein zu schreibendes Byte besteht aus 2 ASCII-Zeichen von der seriellen> Schnittstelle - daher kommt diese Aussage
Ja, deshalb sind es 960 Zeichen oder 480 Bytes pro Sekunde, und nicht
800 bzw. 400.
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?
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.
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
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.
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.
Kai schrieb:> Aus meiner Sicht> zeigt dies in Richtung Stack bzw. Heap.
Glaube ich nicht. Da würde es auch mal richtig crashen.
mMn liegt es eher am Timing/Laufzeitverhalten.
Kai schrieb:> je mehr Logging-Funktionen ich in das Originalprogramm einbaue,> um so öfter erhalte ich falsche Werte von cnt.
Werden Funktionen verwendet, die nicht reentrant sind?
Teilweise verwendet Arduino print m.W. printf et al der AVR-LibC,
welches nicht reentrant ist.
Johann L. schrieb:> Teilweise verwendet Arduino print m.W. printf et al der AVR-LibC,> welches nicht reentrant ist.
Nein!
Und reentrance spielt sowieso keine Rolle solange es nicht um
präemptives Multitasking oder ISR geht.
Der AVR Ardiono core verwendet kein print oder printf
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.
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.
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
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
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.
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
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:
Kai schrieb:> In dem ATMEGA 328P meiner Platine habe ich aber nur 2kB RAM, von welchen> ich gerne 500 Bytes frei haben möchte.
dann nutze den ATmega1284p mit 16KB Ram, Leerplatinen gibt es bei OSH
Park, fertig aufgebaute leider nicht mehr zu finden.
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.
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.
Kai schrieb:> 5. Die eigentliche Ursache für den sporadischen Fehler ist, dass sich> zur Zykluszeit im ungünstigsten Fall (nur 1 Zeichen eingetroffen) eine> Weitere von 20ms addiert.> und genau dann läuft der Puffer über.
Dann mach doch das EEPROM Zeugs einfach mit in die schnellen Tasks vor
der 20ms Wartezeit:
1
voidfast_tasks(void)
2
{
3
// ...
4
eeprom_stuff();
5
}
6
7
intmain(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.