Forum: Mikrocontroller und Digitale Elektronik Arduino + ESP 32 = I2C Repeated Start?


von Ulrich P. (uprinz)


Lesenswert?

Hallo zusammen!

Ich versuche mit der Wire.h und den üblichen installierten Paketen für 
Arduino und ESP32 einen speziellen Chip über I2C ans Laufen zu bekommen.

Der Chip benötigt einen mehr-byte Kommando-Block und antwortet darauf 
ggf mit Daten. Der Trick ist, dass man die Sequenz nicht unterbricht, 
sondern zuerst Schreibt und dann mit Repeateted-Start auf Lesen 
umschaltet um die erwarteten Daten abzuholen.

Ich habe einige Varianten ausprobiert, aber keine davon hat funktioniert
1
  if ( (0 < rx_sz) && (NULL != rx_buf)) {
2
    stop = false;
3
  }
4
5
  if ( (0 < tx_sz) && (NULL != tx_buf)) {
6
7
    rc = wire.writeTransmission( i2cAddr, tx_buf, tx_sz, stop);
8
9
    if (I2C_ERROR_OK != rc) {
10
      error = ModulatorError_I2C_WRITE_NO_ACK | rc;
11
      goto exit;
12
    }
13
  }
14
  if ( (0 < rx_sz) && (NULL != rx_buf)) {
15
16
    rc = wire.readTransmission( modulator->i2cAddr, rx_buf, rx_sz, true, &count);
17
18
    if (I2C_ERROR_OK != rc) {
19
      error = ModulatorError_I2C_READ_NO_ACK | rc;
20
      goto exit;
21
    }
22
  }

Allerdings scheint hier die Kommunikation nicht mal zu starten.
Verwende ich stattdessen
1
    wire.beginTransmission(modulator->i2cAddr);
2
    count = wire.write( tx_buf, (size_t)tx_sz);
3
    rc = wire.endTransmission(stop);
4
5
...
6
7
    wire.beginTransmission(modulator->i2cAddr);
8
    wire.requestFrom(modulator->i2cAddr, rx_sz, true);
9
10
    count = 0;
11
    while (wire.available()) {
12
      rx_buf[count++] = wire.read();
13
    }
Dann gehen die Daten beim write raus, aber es kommen keine mehr zurück 
beim darauffolgenden read(), unabhängig davon, ob ich die erneute 
beginTransmission einbaue oder nicht.

Bevor ich jetzt das Scope in die Schaltung hinein klöppele, kennt jemand 
eine funktionierende S -> TX -> RS -> RX -> P implementation für den 
ESP32?

Danke schon mal im Voraus!

von W.S. (Gast)


Lesenswert?

Ulrich P. schrieb:
> Ich habe einige Varianten ausprobiert, aber keine davon hat funktioniert

Eigentlich ist es ganz einfach:
Start-Cond ist, wenn SDA H-->L geht, während SCL H ist.
Stop-Cond ist, wenn SDA L--H geht, während SCL H ist.
Für einen wiederholten Start mußt du also SDA auf H bringen, ohne daß 
SCL auf H steht, um eine Stop-Cond zu vermeiden. Danach kannst du ganz 
einfach eine Start-Cond applizieren.

Was du vermutlich bislang probiert hast, sind Versuche, mit 
irgendwelchen Arduino-Bibliotheken zu Potte zu kommen und da entweder 
eine wiederholte Start-Cond dort nicht vorgesehen ist oder du selbige 
nicht gefunden hast, geht es eben nicht.

Fazit: Mach es besser. Also selbst machen. Ohne Arduino-Funktionen 
sofern nötig.

W.S.

von EAF (Gast)


Lesenswert?

W.S. schrieb:
> und da entweder
> eine wiederholte Start-Cond dort nicht vorgesehen ist
Das ist vorgesehen!
https://github.com/espressif/arduino-esp32/blob/master/libraries/Wire/src/Wire.h#L116

W.S. schrieb:
> Mach es besser. Also selbst machen.
Besser machen, als Wire?
Das was auf so vielen µC bestens funktioniert?
(bekommst DU nicht so universell hin)

Wenn ein fertiges Werkzeug da ist, sollte man zumindest die Doku lesen.
Die Arduino Wire Doku.
Und wenn es Besonderheiten gibt, wie alternative Pins, auch die zur 
Boarddefinition gehörende Doku.

von Ulrich P. (uprinz)


Lesenswert?

Danke schon mal für die verschiedenen Hinweise.

Es war bisher eigentlich kein Problem mit der vorhandenen Implementation 
von Wire auf ESP32 zu arbeiten. Daher bin ich von den Komplikationen 
etwas überrascht. Allerdings hat der Hersteller des Chips, den es zu 
steuern gilt, auch nur einen Windows, eine Linux und eine Treiber für 
einen "Rahmen-Treiber" falls diese beiden Systeme nicht zutreffen...

Wie auch immer, ich habe eine Kommunikation aufgebaut:
1
  tw_sys.beginTransmission(modulator->i2cAddr);
2
  tw_sys.write( tx_buf, (uint8_t)tx_sz);
3
  count = tw_sys.endTransmission(true);
4
  rc = tw_sys.lastError();
Frage 1:
Ich würde erwarten, dass das Senden umgehend beginnt, wenn ich 
endTransmission aufrufe. Auch wenn es für eine re-start Kondition mit 
(false) aufgerufen wird.
Das tut es aber nicht. Es passiert einfach nix.
Frage 2:
Obwohl alle bytes gesendet wurden, ist count == 0, ich dachte es müsste 
bei Erfolg tx_sz == count sein?
lastError() ist aber 0, daher kann man das mal so hinnehmen...
1
  tw_sys.beginTransmission(modulator->i2cAddr);
2
  tw_sys.requestFrom(modulator->i2cAddr, (uint8_t)rx_sz, true);
3
4
  count = 0;
5
  while (tw_sys.available()) {
6
    rx_buf[count++] = tw_sys.read();
7
  }
8
  tw_sys.endTransmission(true);
9
  if (count != rx_sz) {
Frage 3:
Spätestens hier hätte ich dann erwartet, dass die Routine die komplette 
Sequenz abfährt, wenn man das vorhergehende Senden mit 
endTransmission(false); für eine repeated-start Sequenz offen gelassen 
hat.

Es funktioniert nur, wenn ich wirklich zwei komplette Transfers daraus 
mache.

Was mache ich da falsch?

von EAF (Gast)


Lesenswert?

Ulrich P. schrieb:
> tw_sys.beginTransmission(modulator->i2cAddr);
>   tw_sys.requestFrom(modulator->i2cAddr, (uint8_t)rx_sz, true);
>   count = 0;
>   while (tw_sys.available()) {
>     rx_buf[count++] = tw_sys.read();
>   }
>   tw_sys.endTransmission(true);
>   if (count != rx_sz) {

Was ist das?
Arduino?

Dann:
Du kannst/darfst kein requestFrom mit einer Transmission umrahmen.
Das ist hanebüchener Unsinn.(sorry)

Das kann nicht das tun, was du beabsichtigst.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

für ein Repeat darf die Verbindung nicht mit Default 'true' gestoppt 
werden. Soweit logisch oder?
https://www.arduino.cc/reference/en/language/functions/communication/wire/endtransmission/
Also muss der Parameter "false" Verwendung finden. Du möchtest 
sicherlich
1
  Wire.beginTransmission(addr);
2
  Wire.write(data);     
3
  Wire.endTransmission(false);
4
  Wire.requestFrom(addr, ANZAHL);
5
  if (Wire.available() )       
6
  {
7
    ... = Wire.read();
8
    ...
9
  }

: Bearbeitet durch User
von Stefan F. (Gast)


Lesenswert?

Ulrich P. schrieb:
> Ich würde erwarten, ...
> Spätestens hier hätte ich dann erwartet

Kleine Tipp: Benutze eine anständige IDE wo du mit Strg-Mausklick die 
Quelltexter der verwendeten Methoden aufrufen kann, und schau sie dir 
an.

Gerade bei Arduino Cores die nicht von Arduino.cc kommen halte ich das 
für sehr nützlich denn die verhalten sich oft ein bisschen anders als 
(bei Arduino) üblich. Und selbst wenn nicht, sind deren Quelltexte oft 
aufschlussreich, so dass man man nicht so viel erwarten und vermuten 
muss, sondern gezielt vorgehen kann.

von EAF (Gast)


Lesenswert?

Stefan ⛄ F. schrieb:
> und schau sie dir an.

In diesem Fall reicht es die originale Arduino Wire Doku zu lesen um die 
Beispiele zu kapieren.
Dann ist alles gut!

Ja, bei den ESP gibt es Besonderheiten (Instanziierung und 
Wire::begin()), aber die liegen hier nicht in der Waagschale.

von Ulrich P. (uprinz)


Lesenswert?

Freunde bitte,

natürlich habe ich die Doku gelesen und auch im Netz vorhandene 
Beispiele abgearbeitet. Normalerweise programmiere ich Embedded Software 
unter Linux, VxWorks und Bare-Metal auf Cortex M, -R und -A arbeite 
immer Eclipse. Daher nutze ich die Sloeber IDE für Arduino erfolgreich 
mit Arduino Nano, ESP8266, ES32 und BluePill Boards. Auch OTA Updates 
und solche Extras.

Außerdem hängt inzwischen ein Scope mit Bus-Analyzer im Aufbau.

Bei dem aktuellen Hobby-Projekt ist es so, dass ich dazu ein Frontpanel 
habe, das über drei PCF8574 ein LCD, 8 Tasten und 5 LEDs ansteuert. Sie 
hängen am gleichen Bus wie der Spezial-Chip, der mir Kopfzerbrechen 
bereitet. Aber bis auf einen Bug in der LCD Implementierung 
funktionieren die Standard-Libs für diese Aufgaben tadellos. Aber sie 
nutzen eben fertige libs. Dazu sind die ja da, man will ja nicht immer 
alles komplett selber machen.

Da es für meinen IC keine Lib gibt, ich mit dem IIC Protokoll aber 
durchaus vertraut bin, habe ich versucht einen Kompromiss zwischen 
fertiger Treiber und "alles selber schreiben" zu finden und dabei bin 
ich auf das Problem mit dem IIC des ESP32 gestoßen:
1
  Wire.beginTransmission(addr);
2
  Wire.write(data);     
3
  Wire.endTransmission(false);
4
  Wire.requestFrom(addr, ANZAHL);
5
  if (Wire.available() )       
6
  {
7
    ... = Wire.read();
8
    ...
9
  }
führt dazu, dass KEIN Transfer stattfindet.
Aber...
1
  Wire.beginTransmission(addr);
2
  Wire.write(data);     
3
  Wire.endTransmission(true);
funktioniert und sendet einen Block zu Chip.

Würde der Fehler an dem Chip liegen, dann müsste nach 
Wire.endTransmission(false) zumindest der zu sendende Block abgearbeitet 
werden, da erst nach der Sendung der Chip über ACK mitteilen kann, dass 
ihm was nicht passt. Es kommt aber nicht einmal die Adresse des Chips 
auf den Bus. Laut Scope ist der Bus aber in einem sauberen State.

Auch das hier habe ich getestet...
1
  Wire.beginTransmission(addr);
2
  Wire.write(data);     
3
  Wire.endTransmission(false);
4
  Wire.requestFrom(addr, ANZAHL);
5
  if (Wire.available() )       
6
  {
7
    ... = Wire.read();
8
    ...
9
  }
10
  Wire.endTransmission(true);   <<---
funktioniert aber auch nicht. Obwohl wir beim Lesen das Protokoll mit 
NACK beenden müssten... Naja kann man so oder so sehen.

Trotzdem untersuche ich gerade, ob der Chip anders tickt als gedacht und 
man ihm tatsächlich komplett separate Sequenzen senden muss. Also zuerst 
ein reines TX-Telegramm und dann ein komplettes RX-Telegramm. So erhalte 
ich schon mal einige Daten vom Chip, obwohl noch nicht alles stimmig zu 
sein scheint.

Aber danke schon mal für die verschiedenen Hinweise. Ich werde das 
Ergebnis hier rein schreiben.

von EAF (Gast)


Lesenswert?

Ulrich P. schrieb:
> natürlich habe ich die Doku gelesen und auch im Netz vorhandene
> Beispiele abgearbeitet.

Dann weißt du auch, dass die Methoden requestFrom und endTransmission 
melden, was ihnen nicht passt.

Mein Rat:
Wenn man Fehlermeldungen bekommen kann, sollte man die Wahrnehmung 
darauf richten.



Ulrich P. schrieb:
> Auch das hier habe ich getestet...  Wire.beginTransmission(addr);
>   Wire.write(data);
>   Wire.endTransmission(false);
>   Wire.requestFrom(addr, ANZAHL);
>   if (Wire.available() )
>   {
>     ... = Wire.read();
>     ...
>   }
>   Wire.endTransmission(true);   <<---
> funktioniert aber auch nicht.
Dass das unsinnig ist, habe ich schon versucht zu sagen....
Aber wenn du es besser weißt, trotz Doku lesen und Beispiele 
durcharbeiten, dann kann ich dir auch nicht mehr helfen.

Tipp:
Kennst du den, mit dem verlorenen Autoschlüssel?

von W.S. (Gast)


Lesenswert?

EAF schrieb:
> W.S. schrieb:
>> und da entweder
>> eine wiederholte Start-Cond dort nicht vorgesehen ist
> Das ist vorgesehen!

> W.S. schrieb:
>> Mach es besser. Also selbst machen.
> Besser machen, als Wire?
> Das was auf so vielen µC bestens funktioniert?
> (bekommst DU nicht so universell hin)

Kein Mensch will hier eine universelle eierlegende Dingsbums schreiben, 
sondern eben nur etwas, das auf genau diesem Chip das tut, was 
beabsichtigt ist.

Allerdings habe ich bei sowas
Ulrich P. schrieb:
>   Wire.beginTransmission(addr);
>   Wire.write(data);
>   Wire.endTransmission(false);

so meine Bedenken, denn das sieht nicht so aus, wie man es vom I2C her 
gewohnt ist, wo zuerst der Bus akquiriert und der Slave nebst Read oder 
Write aktiviert werden muß und dann die Daten übertragen und dabei auf 
ACK oder NAK reagiert werden muß. Bei dieser offenbar hochgelobten 
Bibliothek geht das offenbar anders. Nun ja. Dann kannst du dem TO ja 
mal erzählen, wie das mit dieser Bibliothek geht.

W.S.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

was ist daran unsinnig? Mach ich genauso für einen 10Bit Adresszugriff.
1
  Wire.beginTransmission(addr);
2
  Wire.write(data);     
3
  Wire.endTransmission(false);
4
  Wire.requestFrom(addr, ANZAHL);
5
  if (Wire.available() )       
6
  {
7
    ... = Wire.read();
8
    ...
9
  }

Das vom TO am Ende eingefügte Wire.endTransmission(true) ist nicht 
notwendig. Sollte aber auch nicht stören, weil die Kommunikation zu dem 
Zeitpunkt schon zu Ende ist.

Irgendein ACK oder NACK muss doch auf dem Bus zu sehen sein? Die Frage 
ist was will der unbekannte IC in welcher Reihenfolge wirklich? Stimmt 
das mit dem überein was programmiert ist? Und wenn ein Repeat benötigt 
wird muss an passender Stelle ein Wire.endTransmission(false) erfolgen.

Die Überlegungen mit dem Repeat habe ich auch schon durch.
Beitrag "NXP PCA8574 hat Phänomen mit 2 Adressen"
Nur deswegen konnte ich zeigen was ich am Ende gemacht habe.

Ich bin gespannt wie das anders, richtiger, gemacht wird.

: Bearbeitet durch User
von Ulrich P. (uprinz)


Lesenswert?

So, ich bedanke mich an dieser Stelle mal bei allen für den Support.

Niemand kann in einem Forum, das so allgemein gehalten ist abschätzen, 
auf welchem Level ein Fragesteller ist und wie tief er bereits in das 
Thema hinein verwurstelt ist. Das macht das Spektrum der Antworten immer 
sehr breit.

Eine Analyse der ganzen Geschichte hat nun ergeben, dass der wire Stack 
vom ESP32 recht gut funktioniert. Ich bin da an mancher Stelle noch 
nicht ganz überzeugt, das muss ich aber erst analysieren.

Fakt ist, das Protokoll, das mein spezieller Chip hier unterstützt hat 
es so aussehen lassen, als wäre es der ESP schuld. Mit einem Repeated 
Start kann der Chip nix anfangen, schweigt aber auf alles, was danach 
kommt.
Er will zuerst einen Write mit Command und passender Payload gefolgt von 
STOP. Dann eine neue Adressierung START Adresse, Read und  passender 
Datenabfrage gefolgt von STOP.
Und weil mehrere Commands unterwegs sein können, gibts da auch noch eine 
passende Sequence-Number.

Dröselt man das so auseinander, läuft alles recht gut.

Bei bestimmten Commands gibt es noch einen Error 2, also "ACK". Da muss 
ich noch herausfinden, warum das passiert. Das liegt aber sehr 
wahrscheinlich an dem Chip und nicht am ESP.

Danke!
Ulrich

von Veit D. (devil-elec)


Lesenswert?

Hallo,

klingt doch ganz positiv. Den Rest findest du auch noch raus. Ist 
manchmal Puzzlearbeit.  ;-)

von Veit D. (devil-elec)


Lesenswert?

Hallo,

ich hatte eigentlich von EAF und W.S. noch eine Antwort erwartet wie sie 
es programmieren würden.

von EAF (Gast)


Lesenswert?

Was willst du von mir hören?

Offensichtlich hat sich erwiesen, dass von Anfang an, von der irrigen 
Annahme ausgegangen wurde, dass der ultra geheime Chip, von dem keiner 
was wissen darf, der so geheim ist, dass noch nicht mal der TO eine Doku 
dazu gelesen hat, dass genau dieser Chip, die "Repeated Start Condition" 
korrekt(?) abhandelt.

Tut er aber nicht!

Also ist der Fehler nicht in deinem Code, welchen ich auch überhaupt 
nicht kritisiert habe. (bin ja genauso ahnungslos, was den geheimen Chip 
betrifft)

Auch die Fehler im Code des TO waren wohl nicht so ganz tragisch, auch 
wenn diese von derber Verwirrung, und etwas von Leseblockade, zeugen.

Um es mal zusammen zu fassen:
1. Alle Hilfestellungen zum Repeated Start mit der Wire Lib sind 
gegenstandslos, da der Chip es angeblich/offensichtlich nicht kann.
2. Alle Kritik an der Wire Lib, wegen angeblich fehlerhaftem Repeated 
Start, sind gegenstandslos, da diese hunderttausendfach im praktischen 
Einsatz beweist, dass sie es kann.
3. Der ganze Thread basiert auf einer falschen Annahme!

Die Probleme:
1. Leider gab es nie einen vollständigen testbaren Code! Nur 
verstümmelte Fragmente
2. Leider gab es nie ein Datenblatt zu dem geheimen Chip, welches sein 
Verhalten hätte evtl. erklären können.

Damit ist klar, dass man von Außen, also hier im Forum, nie die Ursache 
für das Fehlverhalten erkennen konnte, die falsche Annahme, als solches 
identifizieren konnte.

Im Prinzip:
Die Repeated Start Geschichte ist schon sehr wichtig. Gerade auch im 
Multimaster Betrieb.

Veit D. schrieb:
> von EAF und W.S. noch eine Antwort erwartet wie sie
> es programmieren würden.

Also keine Ahnung was du jetzt noch von mir noch erwartest!
Oder welchen Stock du verschluckt hast.......

Veit D. schrieb:
> W.S.
Zu ihm kann man fest halten, dass ihm in Sachen Arduino und seinen 
Libs/APIs herzerfrischend kenntnisfrei ist. Was ihn aber nicht davon 
abhält reichlich lauwarmen Schaum (ohne jede greifbare Substanz) 
abzusondern.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

okay, dann war das ein Missverständnis von mir. Ich dachte mein Code ist 
noch fehlerhaft und funktioniert nur zufällig. Ins Geheim dachte ich mir 
aber das kann irgendwie nicht sein.  ;-)

@ TO
Du könntest das Datenblatt von deinem Chip angeben und deine 
funktionierende Code Sequence dazu. Dann wäre allen klar was los ist.

von EAF (Gast)


Lesenswert?

Veit D. schrieb:
> Ich dachte mein Code ist
> noch fehlerhaft und funktioniert nur zufällig.

Der ist schon OK.
Trifft nur die hier scheinbar nötigen Anforderungen nicht. Nur aus der 
Sicht ist er falsch.
In 99% der anderen Anwendungen/i2cChips ist das Prinzip OK und 
funktioniert wie man es erwarten darf.

von Veit D. (devil-elec)


Lesenswert?

Okay alles klar alles gut.  ;-)

von Ulrich P. (uprinz)


Lesenswert?

Huiuiui...

Also ich habe meine ursprüngliche Anfrage schon so formuliert, dass ich 
ein Eigenverschulden grundsätzlich nicht ausgeschlossen habe. "Etwas 
funktioniert nicht" ist bei mir auch grundsätzlich nicht "Du Idiot hast 
Sch... gebaut!"

Der Chip ist nicht ultra-geheim aber man muss für diesen DVB-T, -C, 
ISDBC Encoder und Modulator eben erst eine NDA unterschreiben, bevor man 
irgendwas dazu in die Hand bekommt. Finde ich auch blöde, ist nun aber 
so.

Leider löscht das Internet auch keine irrelevanten oder veralteten 
Fakten oder stuft sie zumindest in den Suchergebnissen viel weiter 
hinten ein. Und so bin ich über ein paar Keywords in der Suche zu 
Berichten, Implementationen und Hilfe-Seiten geführt worden, auf denen 
stand, dass Repeated-Start (noch) nicht richtig implementiert sei. 
Natürlich habe ich auch das Datum der Posts gesehen und mir gedacht, 
dass das sicher inzwischen anders ist. Aber es ist nun mal auch so, dass 
man viel liest über das was nicht geht, aber sobald es geht, hört man 
nie wieder was.

Dass der Chip garkein Repeated-Start haben will hat mich kalt erwischt, 
denn ich habe schon zig I2C Chips an zig verschiedene Prozessoren 
angebunden, aber alles eher im Bereich Sensorik und Speicher oder MII 
eben. Im Datenblatt und dem Programming-Manual steht dazu nichts... 
Naja, dass in dem einen IIC Timing Diagramm keine RS Sequenz vorkommt, 
hätte mich indirekt stutzig machen können...

Ich bin den hier angeratenen Weg gegangen und habe das ganze Projekt in 
Sloeber integriert und dort dann meine gewohnten "Moves" für Reverse 
Engineering aka fremden Code lesen und verstehen angewandt. Damit war 
das Problem auch recht schnell beim Hersteller zu lokalisieren.

Aktuell schreibe ich Software halt gerne oben im Wohnzimmer bei der 
Familie, aber Löten und Messen das tue ich im Keller, denn da stehen die 
Regale mit allem was dazu nötig ist. Und ehe ich da alles auseinander 
rupfe um Scope und DVB Analyzer nach oben zu schleppen oder PC und 
Monitore in den Keller zu tragen und die Familie auch noch am Abend 
allein zu lassen, habe ich gefragt, ob jemand Probleme mit dem IIC des 
ESP32 bekannt sind.

Von meiner Seite aus hat der ESP keine Probleme, der IIC Analyzer im 
Agilent Scope sagt auch, dass der ESP keine Probleme hat. Alle sind 
zufrieden.

von Interessierter (Gast)


Lesenswert?

Ulrich P. schrieb:
> Huiuiui...
Willkommen im Forum.

Nimm regelmäßig nen anderen Gastaccount, ignoriere die 90% 
Scheißeschmeißer und nutze die 10% realen Informationsgehalt. Dann perlt 
der Dreck hier an Dir ab wie Wasser.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.