Hallo, ich habe ein kleines Testsystem mit einem Arduino mega 2560 (Atmel ATmega640) aufgebaut. Ich habe mehre Sensoren, die über I2C kommunizieren, angeschlossen. Ich verwende die Standard Arduino libraries, somit die I2C library von Todd Krein ( https://github.com/arduino/Arduino/tree/master/libraries/Wire ). Grundsätzlich funktioniert alles tadellos. Jedoch soll auch bei einem Fehlerfall mein System kontrollierte Befehle ausführen. Die Fehlerfälle sind: SCL Leitung hat einen Wackelkontakt, Slave erhalten keine Versorgungsspannung bzw. SDA Leitung und SCL Leitung sind gebrückt. Tritt eines dieser Fehler auf, wird das gesamte Programm neu gestärt. Daraufhin habe ich mir die library von Todd Krein angesehen, sowie den TWSR Register ausgelesen und habe folgende Vermutung was in twi.c passiert. https://github.com/arduino/Arduino/blob/master/libraries/Wire/utility/twi.c Bei einem dieser o.g. Fehler wird der TWI Interrupt ausgelöst (Zeile 363) und der TWSR Register ausgelesen. Dieser ist bei einem Fehlerfall A0 und die Zeile 462 (stop or repeated start condition received) wird ausgeführt. Daraufhin wird die void twi_stop() (Zeile 333) aufgerufen und die Stop-Condition wird in TWCR Register geschrieben. Darunter ist auch das TWSTO Bit. Laut Atmel Datenblatt Seite 262 (http://www.atmel.com/images/atmel-2549-8-bit-avr-microcontroller-atmega640-1280-1281-2560-2561_datasheet.pdf) sollte dieses automatisch wieder zurückgesetzt werden (When the STOP condition is executed on the bus, the TWSTO bit is cleared automatically.). Daraufhin bleibt das Programm in der While-Schleife (Zeile 340-342) stecken, da dies scheinbar nicht funktioniert, siehe Code. while(TWCR & _BV(TWSTO)){ continue; } Damit das Programm weiterläuft, wurde ein Abbruchkriterium (Idee aus WWW) für alle 5 while-Schleifen in twi.c eingesetzt, siehe Code. twi_tout(1); while(TWCR & _BV(TWSTO)){ if (twi_tout(0)) return; continue; } … //Nirea. Time Out static volatile uint32_t twi_toutc; uint8_t twi_tout(uint8_t ini) { if (ini) twi_toutc=0; else twi_toutc++; if (twi_toutc>=100000UL) { twi_toutc=0; twi_init(); return 1; } return 0; } Dies Hilft aus der while-Schleife nach wenigen ms zu springen. Wird jedoch in Folge wieder ein Slave über den Befehl wire.requestFrom() angesprochen, wird die Funktion uint8_t twi_readFrom (…) in twi.c in Zeile 115 aufgerufen. Darin befindet sich ein Start-Condition die in TWCR geschrieben werden soll (Zeile 159), TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT) | _BV(TWSTA);. Und hier wird der TWI Bus nicht mehr wissen was er machen soll, da ja voraussichtlich das TWSTO Bit noch gesetzt ist. Die Idee TWSTO vorab programmtechnisch auf 0 zu setzen, habe ich gehabt. Leider ohne Erfolg. Ich suche nach einem möglichen Rückgabewert bzw. ein Möglichkeit Bus-Stop auszuführen, ohne das mein Programm neustartet. Hat jemand eine Idee? Beste Grüße Jörg
Jörg Thomas schrieb: > Zeile 115 aufgerufen. Darin befindet sich ein Start-Condition die in > TWCR geschrieben werden soll (Zeile 159), > TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT) | _BV(TWSTA);. > > Und hier wird der TWI Bus nicht mehr wissen was er machen soll, da ja > voraussichtlich das TWSTO Bit noch gesetzt ist. Die Idee TWSTO vorab Wie gesetzt ? Du hast ihn mit obigem Befehl gerade auf Null gesetzt. Es ist nicht TWCR |=, sondern TWCR = Entschuldige wenn ich da was falsch verstehe...
Hallo Marc, meine C# Kenntnisse sind nicht die besten aber ich verstehe unter den | Operator eine bitweise ODER-Verknüpfung, z.B. 1100 1010 ---- 1110 Der Operator |= kann man ja auch anderes schreiben, z.B. X|=Y entspricht X= X|Y. Somit gehe ich von der Zeile TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT) | _BV(TWSTA); davon aus, dass die TWCR-Registerbits TWEN, TWEIE, TWEA, TWINT und TWSTA auf "1" gesetzt werden, zumal das Makro _BV folgende Bedeutung hat: #default _BV(bit) \ (1<<(bit)) Beste Grüße
>Der Operator |= kann man ja auch anderes schreiben, z.B. >X|=Y entspricht X= X|Y. Irgendwie habt Ihr beide recht und gleichzeitig auch nicht. Bei X=bb|cc|dd|ee; bleibt vom ursprünglichen X nichts über bei X|=bb|cc|dd|ee; bleiben ursprüngliche Bits verschont. Also: X ist 0b10001000 X =0b00000100|0b00000001; --> X = 0b00000101 X |=0b00000100|0b00000001; --> X = 0b10001101 X=X|0b00000100|0b00000001; --> X = 0b10001101
Danke für deinen Beitrag. Dies würde meine Thoerie bestätigen und nach der TWCR = .... Anweisung das Bit TWSTO auf "1" gesetzt werden.
:
Bearbeitet durch User
Jörg Thomas schrieb: > Somit gehe ich von der Zeile > TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT) | _BV(TWSTA); > davon aus, dass die TWCR-Registerbits TWEN, TWEIE, TWEA, TWINT und TWSTA > auf "1" gesetzt werden, zumal das Makro _BV folgende Bedeutung hat: > #default _BV(bit) \ (1<<(bit)) Ja, nur wird TWCR hier erst mit Null geladen und dann OR. Beim TWCR |= bla, bla, wird zuerst TWCR gelesen und dann OR. Somit interessiert dich TWSTO nicht, weil der mit obigem Befehl automatisch auf Null geht.
Jörg Thomas schrieb: > Danke für deinen Beitrag. Dies würde meine Thoerie bestätigen und nach > der TWCR = .... Anweisung das Bit TWSTO auf "1" gesetzt werden. ??? Wie ?
Und warum bleibt er bei einem o.g. Fehlerfall in der folgenden while-Schleifewhile (TWCR & _BV(TWSTO)) stehen, wenn er nach deiner Meinung auf "0" steht?
Amateur schrieb: > Irgendwie habt Ihr beide recht und gleichzeitig auch nicht. wo bin ich im unrecht ?
Hallo Marc, hier nochmal der Code: void twi_stop(void) { // send stop condition TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT) | _BV(TWSTO); // wait for stop condition to be exectued on bus // TWINT is not set after a stop condition! while(TWCR & _BV(TWSTO)){ continue; } ... Bei einem Fehlerfall bleibt das Programm in der while-Schleife stehen, somit gehe ich davon aus das im TWCR Register das TWSTO Bit auf "1" steh. Oder habe ich einen Denkfehler?
Jörg Thomas schrieb: > Und warum bleibt er bei einem o.g. Fehlerfall in der folgenden > while-Schleifewhile (TWCR & _BV(TWSTO)) stehen, wenn er nach deiner > Meinung auf "0" steht? Auweia. Hier wird TWSTO gesetzt (von dir): // send stop condition TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT) | _BV(TWSTO); Hier wartest du, dass die STOP-condition ausgeführt wird: // wait for stop condition to be exectued on bus // TWINT is not set after a stop condition! while(TWCR & _BV(TWSTO)){ continue; } STOP-condition: SDA-Leitung von LOW- auf HIGH-Pegel, SCL Leitung befindet sich auf High-Pegel. Low-Pegel ist Dominant, wenn irgendeine Device SDA-Leitung nicht freigibt, oder die Leitung im Schluss ist, kann die STOP-condition NIEMALS auftretten. Deswegen kommst du auch in einem Fehlerfall nie aus der Schleife heraus und deswegen ist diese Schleife auch (mE) schlecht geschrieben. Klar soweit ?
Somit komm ich aus der Misere bei einem o.g. Fehlerfall auch mit einem Abbruchkriterium nie raus, oder? Seh ich das richtig, das ich am besten eine Fehlerrückgabe in der While-Schleife vornehmen müsste?
Marc Vesely schrieb: > Deswegen kommst du auch in einem Fehlerfall nie aus der Schleife > heraus und deswegen ist diese Schleife auch (mE) schlecht geschrieben. > > Klar soweit ? Okay. Nun ist dein Programm mit Hilfe des Abbruchkriteriums aus der Schleife heraus. TWSTO ist immer noch auf 1 Jörg Thomas schrieb: > Zeile 115 aufgerufen. Darin befindet sich ein Start-Condition die in > TWCR geschrieben werden soll (Zeile 159), > TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT) | _BV(TWSTA);. Deine Zuweisung ist '=", also nimmt der Compiler einen Register - irgendeinen, hängt vom Compiler ab, und setzt den erst mal aufs Null. Danach wird dieser Register OR mit: _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT) | _BV(TWSTA); Und dann wird TWCR mit diesem Register geladen. Wenn aber deine Zuweisung " |=" ware, wurde der Compiler erst TWCR einlesen, Register mit diesen Wert laden und weiter wie oben. Klar soweit ?
Klar soweit aber mit dem geänderten Operator ist TWSTO immernoch "1". TWCR|= _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT) | _BV(TWSTA); Das ist somit nur ein noch besseres Abbruchkriterium, oder?
Jörg Thomas schrieb: > Somit komm ich aus der Misere bei einem o.g. Fehlerfall auch mit einem > Abbruchkriterium nie raus, oder? Ja, wieso ? Jörg Thomas schrieb: > Seh ich das richtig, das ich am besten eine Fehlerrückgabe in der > While-Schleife vornehmen müsste? Nee, wieso ? Spaß beiseite, es ist doch gar nicht so schlimm - eher schlimmer. Fall 1: Du kommst aus der Warteschleife zurück, TWSTO = 0. Alles in Ordnung, weiter im Programm. Fall 2: Du kommst aus der Warteschleife zurück, TWSTO = 1. Keine STOP-condition aufgetretten, SDA Leitung auf Level überprüffen - wenn SDA = 0, dann ist weiteres vorgehen im Programm sowieso sinnlos - weitere Kommunikation auf dem Bus ist unmöglich. LED an, Meldung über RS232, USB, SPI, Feuerwehr benachrichtigen...
Hallo Zusammen, wie bekannt ist, ist meine I2C-Kommunikation bei einer Fehlersimulation (Wackelkontakt SDA- bzw. SCL-Leitung oder SDA- und SCL-Leitung gebrückt) ausgefallen. Mit Hilfe des Oszilloskops habe ich erkannt, dass dann die SDA-Leitung dauerhaft auf LOW liegt. Dieser LOW-Pegel wurde hervorgerufen durch den entsprechende Slave und/ oder des Masters, siehe Signalverlauf im Anhang. Fehlerbehebung konnte erfolgen durch: Slave-Seite: Einbau eine bilateralen Switch! Bei einem Fehlerfall wurde der Slave kurzzeitig abgeschalten. Master-Seite: Sobald das TWEN: TWI Enable Bit im TWCR Register gesetzt wurde ist kein weiteres Zugriff auf die SCL & SDA I/O Pins möglich. Dieses Bit wird sobald der Befehl „wire.begin();“ aufgerufen wird dauerhaft gesetzt. Da bei einem Fehler ein Problem im I2C-Modul vorliegt, wollte ich versuchen einen Urzustand herzustellen. Dies konnte ich ermöglichen indem ich die wire library mit den Änderung von https://github.com/steamfire/WSWireLib vorgenommen und zusätzlich folgendes im twi.cpp file geändert (Fett Markierung): void twi_init(void) { // initialize state twi_state = TWI_READY; twi_sendStop = true; // default value twi_inRepStart = false; TWCR=0; delay(1); pinMode(SDA , OUTPUT); pinMode(SCL , OUTPUT); digitalWrite(SDA, 0); digitalWrite(SCL, 0); delay (200); digitalWrite(SDA, 1); digitalWrite(SCL, 1); delay(200); // initialize twi prescaler and bit rate cbi(TWSR, TWPS0); cbi(TWSR, TWPS1); TWBR = ((F_CPU / TWI_FREQ) - 16) / 2; /* twi bit rate formula from atmega128 manual pg 204 SCL Frequency = CPU Clock Frequency / (16 + (2 * TWBR)) note: TWBR should be 10 or higher for master mode It is 72 for a 16mhz Wiring board with 100kHz TWI */ // enable twi module, acks, and twi interrupt TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA); } Sobald auf Seiten des Master die SDA-Leitung runtergezogen wird, kann das I2C-Modul rückgesetzt werden. Dies wird ermöglicht durch den Codezusatz twi_tout () in den entsprechenden while-Schleifen. Natürlich könnte man jetzt verschiedene Fehlerrutinen zusätzlich programmiert. Ich hoffe den einen oder anderen Hilft diese Information. Beste Grüße Jörg
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.