Raspberry, I²C und Mikrocontroller als Slave

Erst mal nur als Idee: Am Raspberry PI kann man am I²C keinen AVR als I²C-Slave fehlerfrei betreiben: Sowohl der Hardwaretreiber „i2c-bcm2835“ als auch der Softwaretreiber „i2c-gpio“ funktionieren nicht richtig.

Pardon! Mit diesen Einstellungen in der config.txt sind alle Fehler (beim Raspberry 3b) verschwunden, und das Oszillogramm deutet auf Verhalten (2) hin.

# Diese Zeilen rein:
dtparam=i2c1=on
dtparam=i2c_arm=on,i2c_arm_baudrate=400000

# Diese Zeilen raus:
dtoverlay=i2c-bcm2835…
dtoverlay=i2c-gpio…

RPi-I2C-ClockStretcher

Die Hardware selbst hat einen Fehler bei der korrekten Implementierung der Clock-Stretch-Auswertung. Zur Erinnerung: Der I²C-Slave darf durch Low-Halten von SCK den Takterzeuger beliebig lange aufhalten. (Beim darauf aufbauenden Standard SMB gelten Zeitlimits.) Der Implementierungsfehler ist, dass der darauf folgende Taktimpuls beliebig kurz werden kann. Weil man vergessen hat, den I²C-Master-Taktgenerator anzuhalten.

Funktioniert nicht weil der Raspberry keine Probiertaktimpulse ausgibt! Alles für die Katz'. Die Voruntersuchung mit Serienwiderständen 470 Ω zeigt wer SCL und SDA gerade treibt.

Screenshots und Entwürfe

Die Idee ist nun, den Strom in der SCL-Leitung vom AVR zum Raspberry PI zu erfassen: Wenn dieser vom Pull-Up des Raspberry zum AVR fließt, liegt slave-seitiges Clock-Stretch vor. Dieser wird nun so lange verlängert, bis der Raspberry „mal wieder“ Low ausgibt. Damit wird der Clock-Stretch mit dem Raspberry-I²C-Takt (im Datenblatt: BSC, Seiten 28~37, klingt wie BSE) synchronisiert. Die Schottky-Diode D1 leitet SCL normal (d.h. vom Master zum Slave) weiter. Auf der AVR-Seite befinden sich an SCL und SDA Pullup-Widerstände. Bei Clock-Stretch und SCL' = H fließt Strom durch T1 und sein Kollektoranschluss öffnet T2 und schließlich T3. Wobei T3 parallel zum Ausgangstransistor am SCL-Anschluss des AVR liegt: Die so realisierte Thyristorstruktur bleibt so lange gezündet, bis die Stromquelle an SCL' (der Raspberry-seitige Pullup-Widerstand) unwirksam wird, indem SCL' wieder auf L gezogen wird.

Als Randproblem „sieht“ der Raspberry während Clock-Stretch 0,6 .. 0,7 V statt 0 V. T3 sollte ein MOSFET sein, damit diese Spannung nicht weiter steigt; ansonsten kan man für T2 und T3 auch Bipolartransistoren mit Basisvorwiderständen oder Digitaltransistoren verwenden. Oder einen 74LVC2G07: Die 2 Widerstände müssen dann beide nach 3P3 gehen. Ein LM393 geht nicht (für 400 kHz Takt), ist zu langsam.
Sollte die Transistorkombination wegen zu kurzer Überlappung von AVR-Clock-Stretch und High-vom-Raspberry, kommt ein nur leicht gekürzter, nahezu voller Taktimpuls zum AVR durch.

Seltsam: 230418 funktioniert I²C ohne „i2c-bcm2835“ einfach so. (Zumindest mit dem Prüfadapter mit den Serienwiderständen.) Als ob ein Configware-Update über den Raspberry gelaufen ist. Allerdings muss man read() und write() bei errno==121 wiederholen, wie früher auch. Der Bus verklemmt sich nie, der AVR kommt nicht in die Verlegenheit, bei Dauer-Lo auf SDA den I²C-Slave rücksetzen zu müssen. CRC-Fehler sind verschwunden. Die Fehler haben sich buchstäblich in Luft aufgelöst.

Softwarelösung

Der Treiber „i2c-gpio“ unterstützt zwar Clock-Stretch korrekt, aber gelegentlich sind Taktimpulse „unerklärlich“ kurz. Selbst neu geschrieben: Der gleiche Fehler! Auch __sync_synchronize() hilft nicht. Auch Kontroll-Lesen des SCL-Pins hilft nicht. Liegt es am Delay? Mittels std::chrono realisiert.

Oszibilder

Wichtig: Die o.g. Hardware funktioniert nicht mit einem Softwaretreiber, sondern blockiert dann den I²C-Bus!

USB-Adapter

Anstatt sich mit dem Raspberry heumzuärgern besteht die Möglichkeit, einen der käuflich erwerbbaren Konverter einzusetzen oder einen selbst zu basteln. Für letzteres sind ein „Pro Micro“ mit ATmega32U4 sowie eine PIC16F1454 zwei gute Ausgangspunkte.

Da Low-Speed genügt, tut's auch V-USB auf einem 8-poligen ATtiny45. Das Interface habe ich so gestaltet, dass mit einem Control-Transfer das Lesen von einer bestimmten Subadresse möglich ist, mithin ist I²C-Schreiben und Lesen zusammengebunden. Das Interface ist an EZUSB AN2131 / CY7C68013A angelehnt.