PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : I²C und der Deadlock bei Atmel 8Bitter mega. Ein Lösungsvorschlag



oberallgeier
10.12.2012, 09:25
Es kommt selten vor, dass elektronische Bauteile ausfallen, aber es kommt vor.

Durch Störung oder Ausfall eines I²C-Slave ist es möglich, dass der Buspegel auf einer oder beiden Leitungen auf low - zumindest von high weg - gezogen wird. Bei bekannten I²C-Slave-Routinen kann dadurch der Master in der Abfrage

unsigned char i2c_start(unsigned char address)
TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN); // wait until transmission completed
while (!(TWCR & (1<<TWINT)));
...hängen bleiben und in der Folge hängt die ganze Ausrüstung.

Anmerkung: eine denkbare Störung, die leicht simuliert werden kann, ist ein Slave an der Busleitung, aber ohne Spannungsversorgung.

Hängt der Bus wegen eines Teilnehmers (I²C-Adressaten), dann ist auch die Kommunikation mit anderen I²C-Baugruppen nicht möglich. Wäre es also vergebliche Mühe einen solchen Deadlock als Problem anzusehen und eine Lösung zu überlegen?

Ich meine schon. Wenn der Master hängt, dann hängt ja schließlich die ganze Apparatur - egal welche es auch ist. Wenn ein solches Hängenbleiben in der I²C-Routine verhindert werden kann, dann könnte wenigstens noch eine Störungsmeldung oder Ähnliches vom Master ausgegeben werden, es liegt ja nach der Abfrage ein entsprechendes 0/1-Signal vor. Daher die Frage ob es eine Lösung gibt und wie die aussieht?

Ja, es gibt sie, sie ist einfach, eine Möglichkeit folgt jetzt.

Die while-Schleife wird ersetzt durch eine Schleife

#define nrep 3000 //
...
for (uint16_t m = 0; m<=nrep; m++)
{ if ((TWCR & (1<<TWINT))) break; } //
Wenn eine positive Antwort existiert, wird die Schleife sofort abgebrochen - es ist also zu der while-Variante kein Nachteil (signifikant nach Codelänge oder Zeitbedarf) entstanden. Damit wird die Abfrage aber in ihrer Anzahl begrenzt, sie kann nicht endlos ausgedehnt werden und der Master erhält AUF JEDEN FALL eine Antwort - selbst wenn es nur eine negative ist. Der Zähler nrep soll dazu dienen evtl. auftretende Stretches und evtl. auftretende andere Signale (Multimaster o.ä.) abzufangen. Theoretisch reichen an die 150 Wiederholungen (bei einem 20 MHz-CPU-Takt) um in die zeitliche Spezifikation eines sehr langsamen Taktes - 10 kHz - zu kommen. Allerdings ist der I²C-Bus nach der offiziellen Darstellung, siehe

UM10204 I2C-bus specification and user manual
(http://www.nxp.com/documents/user_manual/UM10204.pdf)Rev. 4 — 13 February 2012 von NXP
Seite 32 unten

bis auf 0 Hz festgelegt. Der hier genannte Wert von 3000 dürfte für fast alle praktisch vorkommenden Fälle ausreichend sein. Werden deutlich langsamere Antworten erwartet, kann der Wert nrep entsprechend höher genommen werden oder eine Verlängerung der Art


for (uint16_t m = 0; m<=60000; m++)
{ if ((TWCR & (1<<TWINT))) break; // I2C-Device hat sich gemeldet
for (uint8_t n = 0; n<= tttt; n++) // wenn nicht
{ tggl = 29 + 2; } // => Zeit schinden
}
eingebaut werden. Dabei bringt die Übernahme von TWBR in die Zeitschleife eine Anpassung an unterschiedlich gewählte I²C-Takte. Weitere Anpassungen sind natürlich noch möglich. So nutze ich diese Variante beispielsweise, um zu Beginn der Arbeit mit einem I²C-Bussystem alle möglichen Adressen abzufragen und nachzusehen, ob und welche Adressen besetzt sind. Der Vergleich mit einer vorgegebenen Liste ist dabei natürlich möglich.

Danke Sternthaler für die fruchtbare Diskussion.

PicNick
10.12.2012, 10:41
Ist sicher eine einfache und empfehlenswerte Möglichkeit.
Ich mach das so, dass ich die ganze TWI-Geschichte für Interrupts auslege, und für den gesamten Lese- oder Schreibbefehl ein (äusseres) Limit setze, d.h. es ist mir gleich, was die Aktion im Einzelnen am Fertigwerden hindert, also ob der Bus blockiert wird oder irgendein Slave/Master spinnt (deadlock bei multimaster)
Vorteil ist, dass der sonstige Ablauf so oder so nicht (mässig) behindert werden kann.

Klebwax
11.12.2012, 02:31
Hängt der Bus wegen eines Teilnehmers (I²C-Adressaten), dann ist auch die Kommunikation mit anderen I²C-Baugruppen nicht möglich. Wäre es also vergebliche Mühe einen solchen Deadlock als Problem anzusehen und eine Lösung zu überlegen?

Ich meine schon. Wenn der Master hängt, dann hängt ja schließlich die ganze Apparatur - egal welche es auch ist. Wenn ein solches Hängenbleiben in der I²C-Routine verhindert werden kann, dann könnte wenigstens noch eine Störungsmeldung oder Ähnliches vom Master ausgegeben werden, es liegt ja nach der Abfrage ein entsprechendes 0/1-Signal vor. Daher die Frage ob es eine Lösung gibt und wie die aussieht?

1. Daß der I2C Bus kein Timeout hat, ist eine prinzipielle Schwäche. Dies ist beim SMBus "nachgerüstet" worden. Da wird von einer minimalen Busfrequenz von 10k ausgegangen.

2. Auf eine Statusänderung ohne Timeout zu warten, ist eigentlich ein grober Programmierfehler. Ich muß aber zugeben, aus reiner Faulheit mache ich das auch oft. Auf einem PC ist das häufig nicht ganz so schlimm, neue Console aufmachen und Task killen. Es bleibt aber Schlamperei und ich mach das auch nur, wenn es keiner sieht.

Außer einem Hardwarefehler gibt es noch andere Ursachen für einen hängenden I2C Bus. Häufig passiert sowas beim Debuggen, Breakpoint irgendwo in der Mitte des I2C Codes, Programm wird neu gestartet oder neu geladen und ein Slave hängt. Nach einem Powercycle geht alles wieder. Da kommt man raus, wenn man 8 (oder waren es 9?) mal mit SCL wackelt und dann ein Stop ausgibt.

Was du tust, mußt du aus Systemsicht entscheiden. Was soll dein System tun, wenn durch einen Hardwarefehler der I2C Bus hängt? Diesen Fall kann man möglicherweise auch schon früher erkennen. Am Anfang die Pins für SCL und SDA als Input schalten, und schauen ob beide High sind. Wenn ja ist das ein gutes Zeichen, wenn nein, brauchst du einen Plan B. Wenn dein Bus später (im Betrieb) hängt, brauchst du auch einen Plan. Macht es Sinn, den Bus noch mal zu initialisieren (siehe oben) oder sollte das System in eine Art Notbetrieb gehen oder sich ausschalten.

Da du der Chef in deinem System bist, und ein I2C Timeout Sache des Masters ist, würde ich mir nicht viele Gedanken um die Länge des Timeouts aus I2C Sicht machen. Hier ist eigentlich wieder die Systemsicht gefragt. Ein Beispiel: ein Roboter fährt, ein Kollisionssensor wird über I2C abgefragt. Wie lange kann man auf die Antwort vom Sensor warten, bevor man eine Notbremsung einleitet. Das wären für mich so Kriterien für das längste Timeout.

MfG Klebwax

oberallgeier
15.12.2012, 11:55
... 2. Auf eine Statusänderung ohne Timeout zu warten, ist eigentlich ein grober Programmierfehler ...Hmmmmm. Soweit ich es sehe, huscht diese "while (Bedingung)"-Version aber eifrig durch unsere Wohnzimmer. Z.B. bei PFleurys I²C-Routine, vermutlich auch bei anderen. Wie kommerzielle Routinen aussehen weiß ich nicht. Die Konsequenzen dieses Abfrageroutine waren mir mit meiner eher geringen Programmiererfahrung garnicht klar. Egal - jetzt klappts für meine Bedürfnisse ganz gut. Übrigens ist in der FleuryLibrary auch im stop-Befehl (und anderen) eine solche while-Schleife . . .

Klebwax
15.12.2012, 22:18
Nun ja, der Teufel steckt wie überall im Detail.

Nehmen wir mal ein UART. Dort wird oft gewartet, bis ein Byte fertig gesendet ist. Dies könnte man auch mit einem Delay erledigen, dazu sollte man aber schlauerweise die Zeit über die Baudrate ausrechnen. Besser ist natürlich, den UART selbst zu benutzen, also auf den Status zu warten. Kann es einen externen Grund geben, daß das Status-Bit nicht kommt? Nein, ein UART sendet auch in eine defekte Hardware. Kann es einen internen Grund geben, daß das Bit nicht kommt? Schon, wenn man vergisst, den UART zu intialisieren. Will man diesen Fall in seine Fehlerbehandlung mit einbeziehen (wenn man z.B. eine Library schreibt, totale Noobs sie einsetzen um sie dann Schrott zu nennen) wäre ein Timeout eine Möglichkeit (es gibt aber mehr). Ist man sicher, daß dieser Fall nicht eintritt, ist es sicher legitim ohne timeout zu loopen.

Wie intensiv man sich solchen Überlegungen widmen sollte, hängt natürlich vom Projekt ab. Ob die Software bei einem kleinen Robotor hängt, oder ob ein 400kg Kettenfahrzeug (aus einem anderen Thread) außer Kontrolle gerät macht schon einen Unterschied.

MfG Klebwax

oberallgeier
17.12.2012, 10:18
... UART ... oft gewartet ... könnte man ... Delay ... über die Baudrate ausrechnen ...Ne Abstimmumg der Wartezeit mit der Kommunikationsgeschwindigkeit hatte ich beim I²C schon versucht - die oben erwähnte Warteschleife mithilfe des TWBR dimensioniert. Da ja Störungen nur sehr schwer realistisch zu simulieren sind, weiß ich nicht ob das besser ist.

Dummerweise hatte ich mich sowieso zu früh über meine for(..-Schleifenlösung gefreut.
... Auf eine Statusänderung ohne Timeout zu warten, ist eigentlich ein grober Programmierfehler ...Das würde ich auch so sehen. Aber (wie gesagt, ich hatte zu schnell meine Deadlocklösung gepostet) Atmel siehts anders, Beispiel Wait for TWINT Flag set :
... while (!(TWCR & (1<<TWINT))) ; ...Und ich denke, dass diese Leute schon deutlich besser C können als ich. Dumm! Nun glaube ich die Lösung zum Deadlock bei I²C eher in der Art wie Robert es vorschlägt - einen Störfall in dieser Umgebung besser mit einer externen Maßnahme, z.B. einem äußeren Limit, abfangen . . .

Schade, der Schnellschuss war zu früh, läuft u.a. auf meinen eigenen Ratschlag raus: rtfm. Und eben VORHER lesen, dann posten.