Advamation-Logo (Druckversion)

Raspberry Pi I2C Clock-Stretching Fehler

Stand:2013-08-17

Zusammenfassung

Der Broadcomm BCM2835 Prozessor, der auf dem Raspberry Pi verwendet wird, enthält einen schwerwiegenden Fehler, der dazu führen kann, dass (a) die Kommunikation mit manchen I2C-Geräten unmöglich wird und (b) falsche Daten gelesen oder geschrieben werden.

I2C-Geräte, die "I2C clock-stretching" verwenden, sollten deshalb nicht direkt mit dem Raspberry Pi oder anderen Broadcomm BCM2835-basierenden Geräten verwendet werden.

Lösung:
keine
Umgehung des Problems:
siehe unten

Details

Laut I2C-Spezifikation dürfen I2C-Slaves während einer Kommunikation die Clock-Leitung auf "low" halten ("clock stretching") falls sie mehr Zeit für die Verarbeitung der Daten benötigen, um den I2C-Master zu bremsen. Nachdem ein I2C-Slave die Clock-Leitung wieder freigibt, muss der Master den Takt wieder an die Clock-Leitung anlegen.

Der Fehler im Raspberry Pi (bzw. eigentlich in dessen ARM-Prozessor Broadcomm BCM2835) besteht darin, dass der I2C-Takt lediglich "maskiert" wird, wenn ein I2C-Slave die Taktleitung auf "low" zieht, und der Raspberry Pi nicht sicherstellt, dass der nächste Takt die volle Länge besitzt. Dadurch können unzulässige "Spikes" auf der Taktleitung entstehen, die evtl. zu kurz sind, um vom I2C-Slave erkannt zu werden. Dies kann zu einer Verschiebung des Slaves gegenüber dem Master um 1 Taktzyklus führen, wodurch beide nicht mehr synchron laufen. Darüber hinaus scheint der Raspberry Pi die Datenleitung zu früh einzulesen, nämlich während der Slave die Taktleitung noch auf "low" hält. Dies bedeutet, dass selbst sehr sehr kurze clock-stretches dazu führen, dass der Raspberry Pi falsche Daten liest.

RPi I2C clock-stretching bug

Der oben genannte Fehler kann dazu führen, dass Daten verfälscht (d.h. andere Daten empfangen als gesendet) werden, oder dass die Kommunikation mit bestimmten I2C-Slaves nicht mehr möglich ist. Der einzige Fall, in dem das "clock-stretching" beim Raspberry Pi korrekt funktioniert, ist, wenn der I2C-Slave den Takt direkt nach der I2C-Read-Acknowledge-Phase um mindestens 1/2 Takt-Periode verlängert.

I2C funktioniert mit dem Raspberry Pi also nur, falls

  • die Slaves kein "clock-stretching" verwenden, oder
  • die Slaves "clock-stretching" nur am Ende der I2C-Read-Acknowledge-Phase (nach dem Lesen des ACK/NACK) verwenden, und ihn dabei um mindestens 1/2 Takt-Periode verlängern.

I2C funktioniert nicht oder kann zu verfälschten Daten führen, falls

  • ein Slave "clock-stretching" am Anfang einer I2C-Write-Acknowledge-Phase verwendet,
  • ein Slave "clock-stretching" am Anfang einer I2C-Read-Acknowledge-Phase verwendet,
  • ein Slave "clock-stretching" am Ende einer I2C-Read-Acknowledge-Phase verwendet, den Takt aber um weniger als 1/2 Takt-Periode verlängert (bereits sehr sehr kurze "stretches" führen zu verfälschten Daten), oder
  • ein Slave "clock-stretching" außerhalb von Acknowledge-Phasen verwendet.

Umgehung des Problems:

  • Nutzen Sie keine I2C-Geräte, die "clock-stretching" verwenden, mit dem Raspberry Pi. Glücklicherweise ist "clock-stretching" bei I2C-Sensoren wenig verbreitet.
  • Falls Sie genau wissen, wie schnell Ihre I2C-Geräte sind, können Sie einen geringeren I2C-Takt wählen, so dass die I2C-Slaves schnell genug sind, um kein "clock-stretching" mehr zu benötigen.
  • Verwenden Sie nur I2C-/SMBus-Geräte, die einen CRC unterstützen.
    (Unsere I2C-Platinen/Sensoren, die einen Mikrocontroller beinhalten, verwenden einen CRC.)
  • Falls Sie das AdvaBoard RPi1 verwenden, können Sie dort einstellen, dass ein Mikrocontroller auf dem AdvaBoard zu kurze clock-stretches während der I2C-Read-Acknowledge-Phase verlängert, so dass damit I2C-Sensoren mit beliebig langen clock-stretches am Ende der I2C-Read-Acknowledge-Phase funktionieren.
  • Falls Sie das AdvaBoard RPi1 verwenden, kann ein Mikrocontroller auf dem AdvaBoard RPi1 als "I2C-Proxy" dienen, und das Problem damit komplett umgehen. Auf diese Weise sollten dann alle I2C-Geräte funktionieren.
  • Falls Sie selbst-programmierte Mikrocontroller als I2C-Slaves verwenden, sollten Sie der Kommunikation evtl. einen CRC hinzufügen. Alternativ könnten Sie eine schnelle Interrupt-Service-Routine für I2C-Writes verwenden, die den Takt nie verlängert, und eine Wartezeit von über 1/2 Clock-Periode zur I2C-Read-Acknowledge-Phase hinzufügen.
  • Verwenden Sie einen SPI->I2C-, RS232->I2C- oder RS485->I2C-Adapter.
    (Wir werden demnächst derartige Adapter anbieten.)

Von der Raspberry Pi Foundation selbst oder von Broadcomm ist kaum Hilfe bzgl. dieses Problems zu erwarten. Das Problem ist ihnen mittlerweile (zumindest teilweise) bekannt; sie haben es aber leider weder dokumentiert, noch können sie eine Lösung bieten.

Messdaten

Hier sind einige Messdaten mit dem Raspberry Pi als I2C-Master und einem Mikrocontroller (SiLabs C8051F353) als I2C-Slave. Der Mikrocontroller war dabei schnell genug, um sogar die Clock-Spikes zu erkennen. Da der Raspberry Pi die Datenleitung jedoch zu früh liest, wurden die Daten dennoch verfälscht.

Es wurden verschiedene clock-stretching-Zeiten verwendet, und dabei SCL und SDA mit einem Oszilloskop vermessen. Zu beachten ist, dass nur das clock-stretching-Verhalten während der I2C-Acknowledge-Phase getestet wurde.

I2C-Read

Konfiguration:

  • I2C-Read
  • I2C-Slave-Adresse: 0x4F
  • 2 Daten-Bytes: 0x00 0x00
  • unterschiedliche ACK/NACK-Verzögerungen

Ergebnisse:

  • I2C clock-stretching funktioniert nur am Ende der I2C-Read-Acknowledge-Phase (nach dem Lesen des ACK/NACK), nicht am Anfang.
  • I2C clock-stretching funktioniert am Ende der I2C-Read-Acknowledge-Phase nur, falls die Verzögerung über 1/2 I2C-Takt-Periode beträgt. (siehe Verzögerungen/Bild-Nummern >= 11)
  • Verzögerungen, die kürzer als 1/2 I2C-Takt-Periode sind, führen zu verfälschten Daten, sogar wenn die Verzögerungen sehr kurz sind, und obwohl der Slave alle zu kurzen Clock-Pulse erkannte. (siehe Verzögerungen/Bild-Nummern 1..10)
komplette Kommunikation:
OK (0x4F 0x00 0x00) Fehler (0x4F 0x00 0x80)
OK (0x4F 0x00 0x00) (zum Vergrößern anklicken) Fehler (0x4F 0x00 0x80) (zum Vergrößern anklicken)
Details:

Im Folgenden ist derjenige Ausschnitt aus den Messdaten genauer dargestellt, in dem der Lese-Fehler auftritt:

relevanter Ausschnitt
relevanter Ausschnitt (zum Vergrößern anklicken)

Animierte Bildsequenz des Ausschnitts mit den Verzögerungen 1..42: read_01-42.gif (GIF, 2.8 MB)
Einige interessante Bilder aus der Bildsequenz, inkl. Markierung des relevanten, auf die Verzögerung folgenden SCL-Pulses (rot=Fehler, grün=OK):

SDA/SCL bei Delay 1 SDA/SCL bei Delay 7 SDA/SCL bei Delay 10 SDA/SCL bei Delay 13 SDA/SCL bei Delay 25 SDA/SCL bei Delay 26

(zum Vergrößern anklicken)

Weitere animierte Bildsequenz, die das fehlerhafte Verhalten verdeutlicht:
komplette Kommunikation (GIF, 1.4 MB), relevanter Ausschnitt (GIF, 1.1 MB)

I2C-Write

Konfiguration:

  • I2C-Write
  • I2C-Slave-Adresse: 0x4F
  • 1 Daten-Byte: 0xAA
  • unterschiedliche ACK/NACK-Verzögerungen

Ergebnis:

  • I2C clock-stretching funktioniert nicht am Anfang einer I2C-Write-Acknowledge-Phase. Dies wäre aber der relevante Zeitpunkt, da hier der I2C-Slave entscheiden muss, ob er ein ACK oder ein NACK sendet.
  • Ob clock-stretching am Ende einer I2C-Write-Acknowledge-Phase funktioniert wurde noch nicht getestet.
Daten:

Animierte Bildsequenz mit den Verzögerungen 0..45: i2c_write_00-45.gif (GIF, 5.1 MB)
Einige interessante Bilder aus der Bildsequenz (inkl. roter Markierung der fehlerhaften Clock-Pulse):

SDA/SCL bei Delay 0 SDA/SCL bei Delay 7 SDA/SCL bei Delay 39

(zum Vergrößern anklicken)