Hallo, ich habe ein Problem mit dem I²C-Bus in einem embedded System. Der Microcontroller ist ein C161, an dem über besagtem Bus IO-Treiberbausteine, ein EEPROM und ein RTC hängen. Das Programm ist ein C-Programm und läuft sequentiell ab. Durch einen vorgegeben TIMETICK wird mit allen Komponenten über den I²C-Bus kommuniziert. Es funktioniert reibungsfrei, nur in sehr seltenen Zeitpunkten bricht die gesamte Kommunikation plötzlich zusammen und alle Funktionen, die vom Bus abhängen, funktionieren einfach nicht mehr. Das kann direkt nach dem starten des Systems passieren oder nach einem durchlaufenden Betrieb von mehreren Tagen. Hat jemand hier schon so ähnliche Erfahrungen gemacht und eventuell sogar die Ursache rausgefunden? Es darf gemutmaßt werden, womit das zusammenhängen könnte.
> Hat jemand hier schon so ähnliche Erfahrungen gemacht und eventuell > sogar die Ursache rausgefunden? Klar. Das ist systemimmanentes Problem von I2C. Fuer Sachen die 24/7 funktionieren sollen benutzt man besser was anderes. Ursache ist eine Uebertragungsstoerung die eine der Statemaschinen in den Slaves aus dem Tritt bringen. Sowas kann man natuerlich durch vernuenftiges Hardwaredesign verbessern, aber irgendwann gibt es immer mal einen Bitfehler. > Es darf gemutmaßt werden, womit das zusammenhängen könnte Ein weiteres Problem ist vermutlich Unerfahrenheit beim Programmierer. :-) Man muss damit rechnen das der Bus irgendwann haengt. Dann muss man ihn wieder neu aufsetzen. (raustakten einiger Dummybytes) Das hilft oft, aber nicht immer. Es gibt auch Slave die haengen sich so weg das sie einmal kurz ausgeschaltet werden muessen. Olaf
Helmut S. schrieb: > Sendet(taktet) ihr schneller als im Datenblatt erlaubt? Nee, eigentlich nicht. Nur die erlaubten 400 KBit/sec.
Olaf schrieb: >> Hat jemand hier schon so ähnliche Erfahrungen gemacht und > eventuell >> sogar die Ursache rausgefunden? > > Klar. Das ist systemimmanentes Problem von I2C. Fuer Sachen die 24/7 > funktionieren sollen benutzt man besser was anderes. > > Ursache ist eine Uebertragungsstoerung die eine der Statemaschinen in > den Slaves aus dem Tritt bringen. Sowas kann man natuerlich durch > vernuenftiges Hardwaredesign verbessern, aber irgendwann gibt es immer > mal einen Bitfehler. > >> Es darf gemutmaßt werden, womit das zusammenhängen könnte > > Ein weiteres Problem ist vermutlich Unerfahrenheit beim Programmierer. > :-) > > Man muss damit rechnen das der Bus irgendwann haengt. Dann muss man ihn > wieder neu aufsetzen. (raustakten einiger Dummybytes) Das hilft oft, > aber nicht immer. Es gibt auch Slave die haengen sich so weg das sie > einmal kurz ausgeschaltet werden muessen. > > Olaf Danke für die Antwort. Ich habe mir auch schon gedacht, dass es kein richtiger Softwarefehler ist, weil dieser Fehler äußerst selten auftritt und man ihn gar nicht reproduzieren kann. Scheint ein transienter Fehler zu sein. Könntest du eventuell deine Lösungsansätze zur Behebung von Busaussetzern etwas detaillierter beschreiben, möglicherweise könnte man dadurch einen Dauerbetrieb erreichen.
Hi, Die Situation kann folgene sein (Master macht read auf einen Slave) : Einer der Slaves ist noch nicht bereit, dass nächste Byte auf dem Bus zu legen und hat "clock stretching" implementiert. Der Master kennt clock stretching nicht und taktet fleissig weiter. Nun bekommt der Slave ein paar Zyklen der Clock nicht mit. Der Master ist fertig. Der Slave treibt nun die Datenleitung, aber ne Clock fehlt -> Bus hängt sich auf. Frage : Ist die Datenleitung im Fehlerfall "low" ? Wenn ja, im Fehlerfall nacheinander die Datenleitungen an den Slaves unterbrechen -> Damit findest Du den Übeltäter. Fehlerbehebung : Sende ein paar Clock Zyklen (normalerweise sind es 8+1, worst case müßte man also 7+1 clocks senden + STOP) ohne dass der Master SDA asserted (NACK). Die Datenleitung sollte spätestens nach 7 clocks high werden. Damit gibst Du den Bus frei. Die 8. Clock und das Stop sollte das Interface vom Slave resetten. Ganz schlecht wäre es, wenn SCL low bleibt...dann wie mit SDA Übeltäter identifizieren und Hersteller anschreiben...falls Master : Sein Programm mal prüfen :-) Auch durch andere Einflüsse können solche Fehler passieren und u.U. so korregieren. Gruß
Mir ist zwar sowas noch nie passiert. Nichtsdestotrotz: solange das Design es zulaesst, spendiere ich jedem I2C-Device ein p-channel Mosfet, mit dem ich ihm notfalls den Saft abdrehen kann.
@ Hansmeier (Gast) >Danke für die Antwort. Ich habe mir auch schon gedacht, dass es kein >richtiger Softwarefehler ist, weil dieser Fehler äußerst selten auftritt >und man ihn gar nicht reproduzieren kann. Scheint ein transienter Fehler >zu sein. Eben DAS ist ein SEHR gefählicher Fehler, weil er so schlecht reproduzier bar ist. Es könnte ein Fehler im Zusammenhang mit einemInterrupt sein. >Könntest du eventuell deine Lösungsansätze zur Behebung von >Busaussetzern etwas detaillierter beschreiben, möglicherweise könnte man >dadurch einen Dauerbetrieb erreichen. Kann man auch, sooo schlecht ist I2C nicht mal ansatzweise. Aber wie schon gesagt wurde, MUSS die Software so robust sein, und ein gelegentliche Klemmen der Übertragung oder Bitfehler tolerieren, ohne durchzudrehen.
ich hab auf jedem master folgende "hart" i2c-reset-funktion mit drinnen (ist für AVR / ATmega328P)
1 | #define I2C_PORT PORTC
|
2 | #define I2C_DDR DDRC
|
3 | #define I2C_SDA _BV(4)
|
4 | #define I2C_SCL _BV(5)
|
5 | |
6 | #define SCL_LO do { I2C_PORT &= ~I2C_SCL; I2C_DDR |= I2C_SCL; } while (0)
|
7 | #define SCL_HI do { I2C_DDR &= ~I2C_SCL; I2C_PORT |= I2C_SCL; } while (0)
|
8 | |
9 | #define SDA_LO do { I2C_PORT &= ~I2C_SDA; I2C_DDR |= I2C_SDA; } while (0)
|
10 | #define SDA_HI do { I2C_DDR &= ~I2C_SDA; I2C_PORT |= I2C_SDA; } while (0)
|
11 | |
12 | |
13 | static void i2c_reset(void) |
14 | {
|
15 | TWCR = 0; // disable TWI |
16 | |
17 | I2C_DDR &= ~(I2C_SCL | I2C_SDA); // set pins to input |
18 | I2C_PORT |= (I2C_SCL | I2C_SDA); // enable pullups |
19 | |
20 | _delay_us(100); |
21 | |
22 | // toggle clock 9 times
|
23 | for (uint8_t i = 9; i > 0; i--) { |
24 | SCL_LO; // pull SCL low |
25 | _delay_us(5); |
26 | SCL_HI; // release SCL |
27 | _delay_us(5); |
28 | }
|
29 | |
30 | // manually send a STOP condition
|
31 | _delay_us(5); |
32 | SCL_LO; |
33 | _delay_us(5); |
34 | SDA_LO; |
35 | _delay_us(5); |
36 | SCL_HI; |
37 | _delay_us(5); |
38 | SDA_HI; |
39 | |
40 | _delay_ms(10); |
41 | TWCR = _BV(TWEN); // enable TWI to standby state |
42 | _delay_ms(10); |
43 | }
|
Funktioniert bei mir seit Jahren problemlos. Zusätzlich ist es hilfreich, in die i2c-routinen einen Error-Callback einzubauen, der Kommunikationsfehler z.B. per UART rausschreibt.
Michael Reinelt schrieb: > #define SCL_LO do { I2C_PORT &= ~I2C_SCL; I2C_DDR |= I2C_SCL; } while > (0) > #define SCL_HI do { I2C_DDR &= ~I2C_SCL; I2C_PORT |= I2C_SCL; } while > (0) > > #define SDA_LO do { I2C_PORT &= ~I2C_SDA; I2C_DDR |= I2C_SDA; } while > (0) > #define SDA_HI do { I2C_DDR &= ~I2C_SDA; I2C_PORT |= I2C_SDA; } while > (0 Hi, so eine Code werde ich auch mal basteln, um den Bus wieder zum laufen zu bringen. Wenn der Bus hängt, gibt es eindeutige Zeichen, die man während der Softwarelaufzeit abfragen kann und kann dann die RESET-Funktion aufrufen. Was mich aber hier wundert, ist do{}while(0). Sicher funktioniert das, aber für mich sieht es so aus, als ob die Funktion nur einmal aufgerufen wird und eigentlich keiner Schleife bedarf, weil die Schleife nie ein zweites mal durchlaufen wird. Das sind ja Tricks hier, völlig unkonventionell.
>völlig unkonventionell
War früher, als es noch keine Inline-Funktionen gab durchaus so üblich.
Falk Brunner schrieb: > @ Hansmeier (Gast) > >>Danke für die Antwort. Ich habe mir auch schon gedacht, dass es kein >>richtiger Softwarefehler ist, weil dieser Fehler äußerst selten auftritt >>und man ihn gar nicht reproduzieren kann. Scheint ein transienter Fehler >>zu sein. > > Eben DAS ist ein SEHR gefählicher Fehler, weil er so schlecht > reproduzier bar ist. Es könnte ein Fehler im Zusammenhang mit > einemInterrupt sein. > Ja, der TimeTick ist Interrupt gesteuert und wird bei kritischen Codesequenzen wie das auslesen und schreiben auf IIC gesperrt, aber wieso sollte das denn Bus stören? Der bekommt ja seine Befehle vom internen Timer.
Mehmet Kendi schrieb: > Mir ist zwar sowas noch nie passiert. Nichtsdestotrotz: solange > das > Design es zulaesst, spendiere ich jedem I2C-Device ein p-channel Mosfet, > mit dem ich ihm notfalls den Saft abdrehen kann. Das ist ja ein sehr harter Reset, das würde sicher die Probleme erschlagen, aber hhmm, naja ich weiss nicht.
Marvin schrieb: > Fehlerbehebung : > Sende ein paar Clock Zyklen (normalerweise sind es 8+1, worst case müßte > man also 7+1 clocks senden + STOP) ohne dass der Master SDA asserted > (NACK). Die Datenleitung sollte spätestens nach 7 clocks high werden. > Damit gibst Du den Bus frei. Die 8. Clock und das Stop sollte das > Interface vom Slave resetten. Sollte während des Clockens die SDA vom Master high oder low sein? Ich denke mal high, weil so wir wollen ja den BUS am ende auch wieder vom low-Zustand befreien.
Hansmeier schrieb: >> #define SCL_LO do { I2C_PORT &= ~I2C_SCL; I2C_DDR |= I2C_SCL; } while >> (0) >> #define SDA_HI do { I2C_DDR &= ~I2C_SDA; I2C_PORT |= I2C_SDA; } while >> (0 > Was mich aber hier wundert, ist do{}while(0). Sicher funktioniert das, > aber für mich sieht es so aus, als ob die Funktion nur einmal aufgerufen > wird und eigentlich keiner Schleife bedarf, weil die Schleife nie ein > zweites mal durchlaufen wird. > Das sind ja Tricks hier, völlig unkonventionell. Das mit dem do-while hat einen einfach Hintergrund, erstens benötigst du einen ; als Abschluss und der code funktioniert auch in if-bodies ohne klammern, etc. Das ist eine übliche Programmierweise bei Makros. Ich zitiere dazu mal: http://stackoverflow.com/questions/154136/do-while-and-if-else-statements-in-c-c-macros
Wie bereits gesagt wurde, das do-while Konstrukt in Makros ist durchaus üblich. Was mich bei sowas immer stört sind die fehlenden Klammern. Hier handelt es sich ja eindeutig um ein function-like Makro, das anstelle einer inline-Funktion genutzt wird. Also bitte auch so kennzeichnen! Ansonsten siehts aus wie ein Statement, das nichts bewirkt. Sowas kann den "inneren Parser" sehr durcheinander bringen...
ben schrieb: > Hier handelt > es sich ja eindeutig um ein function-like Makro, das anstelle einer > inline-Funktion genutzt wird. Also bitte auch so kennzeichnen! Wie meinst du, "kennzeichnen"? Generell habts ihr aber recht: "heutzutage" sollte man das besser als inline-Funktion lösen. Ich muss mal mit meinem Unterbewusstsein und dessen gewohnheiten ein ernstes Wort reden ;-)
:
Bearbeitet durch User
Michael Reinelt schrieb: > Wie meinst du, "kennzeichnen"? SCL_LO() statt SCL_LO. Dadurch wird sofort ersichtlich dass hier "etwas passiert", du das Makro eben "function-like" verwendest.
ben schrieb: > Michael Reinelt schrieb: >> Wie meinst du, "kennzeichnen"? > > SCL_LO() statt SCL_LO. Dadurch wird sofort ersichtlich dass hier "etwas > passiert", du das Makro eben "function-like" verwendest. Ok, hast recht...
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.