Hallo allerseits, ich habe seit vielen Monaten eine eigene TWI-Implementierung getrennt für Master und Slave für den ATmega328P in Verwendung, die eigentlich gut und stabil läuft. Beide waren komplett interrupt-basierend, mit einer State Machine im TWI-Interrupt. letztens habe ich versucht damit ein DOG XL 240 Grafikdisplay anzusteuern, hätte zwar funktioniert, ich bin aber draufgekommen dass da meine interrupt-Lösung suboptimal ist, u.a. weil sie einen Buffer für das ganze Paket benötigt, und dieser Buffer unnötig ist, weil alle Bytes "on the fly" berechnet werden können (Font rendering) Ich habe nun den Master auf eine interrupt-lose Variante umgestellt, und zu meiner Überraschung festgestellt dass der Code (sowohl der eigentliche i2c-master als auch der "LCD-Treiber") nun besser, schneller, hübscher und effizienter ist. Man lernt ja jeden Tag etwas neues... Nun stelle ich mir die Frage, ob eine solche Umstellung nicht auch meinem Slave gut täte. Hier sehe ich noch nicht so klar... Wichtig am Slave ist, dass der möglichst schnell auf einen "Anruf" vom Master reagiert und diese beantwortet. Ohne Interrupt bedeutet das, dass ich in der main-Schleife recht häufig TWI prüfen müsste, und andere, u.U. zeitlich längere Aktionen das stören könnten. Klassischer Anwendungsfall für einen Interrupt... Die eigentliche TWI-Kommunikation lässt sich aber (wie am Master) eigentlich eleganter "straight-forward" ohne Interrupt abbilden. Und nun bin ich unsicher: a) Slave komplett ohne Interrupt, mit pollen in der main loop? b) Slave reagiert nur auf die Adressierung mit sla+rw per Interrupt, wickelt die restliche Kommunikation aber ohne interrupts direkt in der ISR ab? c) Slave komplett wie gehabt interrupt-basierend Hat darüber schon mal jemand nachgedacht, Vor- und Nachteile zusammengetragen, implementierungen verglichen, etc? Über einen entsprechenden Erfahrungsaustausch würde ich mich sehr freuen! lg Michi
Die Frage ist, ob das Main nur auf I2C wartet oder nebenbei noch was anderes tun soll. Ohne Interrupt kommt man eigentlich nur bei einfachsten Programmen aus.
Peter Dannegger schrieb: > Die Frage ist, ob das Main nur auf I2C wartet oder nebenbei noch was > anderes tun soll. Das Main am Slave macht noch was anderes, je nach Anwendung. Momentan habe ich folgende Fälle: - Ansteuerung eines Grafik-LCD (damit habe ich ein "Parallelport-LCD" in eins mit i2c verwandelt) - Modbus-Interface für meine Heizung / Wärmepumpe - Schrittmotor-Steuerung in allen Fällen ist der Slave auch anderweitig "beschäftigt" > Ohne Interrupt kommt man eigentlich nur bei einfachsten Programmen aus. Das ist sicher richtig, nur beim i2c-master hat sich gezeigt dass es ohne TWI-Interrupt besser und im Endeffekt einfacher geht
Dann musst du eine komische Implementation vom TWI haben. Werde mal etwas konkreter, was denn besser ohne Interrupt läuft? TWI ist halt immer mit bisschen Software verbunden, alles in Hardware und das er erst einen Interrupt auslöst, wenn Daten da sind das wäre mal schön, klappt aber eben nicht. Dort jedesmal ohne Interrupt darauf zu warten, das TWI den nächsten Schritt beendet hat klingt für mich nicht wirklich effizienter. Was wirklich ne Katastrophe bei TWI ist, ist die Fehlererkennung. Keine Ahnung, ob du da auch was in deinem Code dazu hast. Wenn es die Fehlererkennung schwierig macht, dann kann ich Soft-I²C empfehlen, das geht dort wesentlich einfacher. Dennis
Dennis H. schrieb: > Dann musst du eine komische Implementation vom TWI haben. ich glaub nicht dass meine (Master-)Implementierung "komisch" ist, sie orientiert sich weitestgehend am (weit verbreiteten) Code vom Peter Fleury. Die arbeitet nicht mit Interrupts, ich jetzt auch nicht, vorher schon mit großer ISR. Nur damit das klar ist: mit dem master hab ich kein Problem, beim Slave stell ich mir die obigen Fragen...
:
Bearbeitet durch User
Daß Interrupt Master langsamer sein soll, höre ich auch zum ersten mal. Da muß was falsch implementiert sein. Im Master Interrupt darf nirgends gewartet werden! Mit Slave ohne Interrupt würdest Du den Master massiv ausbremsen, der muß dann ständig Däumchen drehen, bis der Slave gnädig das Byte abgeholt hat.
Michael Reinelt schrieb: > ich glaub nicht dass meine (Master-)Implementierung "komisch" ist, sie > orientiert sich weitestgehend am (weit verbreiteten) Code vom Peter > Fleury. Die arbeitet nicht mit Interrupts Man kann nicht einfach eine Routine ohne Interrupts in einen Interrupt rein pappen. Die muß komplett anders aussehen. Sämtliche Warteabfragen müssen den Interrupt verlassen.
Hallo Michael Reinelt, einen TWI-Slave pollend zu konzipieren, halte ich für ziemlich sinnfrei. Warum ? Erstens, weil ich Ereignisse, die einen Interrupt auslösen, vorrangig via Interrupt behandeln würde. Schneller und eleganter geht es nicht. Aber das mag ja noch Geschackssache sein. Zweitens, weil es timing-Probleme geben wird. Wenn der TWI-Takt nur 100 KHz beträgt, dann kommen die Bytes im zeitlichen Abstand von ca. 100us herein. Es muss in entsprechend kleineren Zeitabständen das TWI-Register/Flag gepollt werden. Angenommen, der Controller läuft mit 4 MHz, dann muss das Pollen innerhalb von weniger als 400 Takten erfolgen. Wenn aber Funktionen mehr als 400 Takte benötigen, dann gehen empfangene Bytes verloren. Daher würde ich den Konzept des pollenden TWI-Slaves glatt verwerfen. Michael S.
Michael S. schrieb: > Wenn aber Funktionen mehr als 400 Takte benötigen, dann gehen empfangene > Bytes verloren. Beim I2C kann nichts verloren gehen. Der Slave hält SCL auf low und der Master dreht Däumchen, bis er wieder SCL auf high sieht.
Peter Dannegger schrieb: > Daß Interrupt Master langsamer sein soll, höre ich auch zum ersten mal. > Da muß was falsch implementiert sein. Im Master Interrupt darf nirgends > gewartet werden! Wird auch nicht... Peter Dannegger schrieb: > Man kann nicht einfach eine Routine ohne Interrupts in einen Interrupt > rein pappen. Hab ich nicht. > Die muß komplett anders aussehen. Sämtliche Warteabfragen müssen den > Interrupt verlassen. Tun sie... ich glaub ich hab das etwas mißverständlich formuliert. meine "alte", interrupt-basierte master-Implementierung sah selbstverständlich ganz anders aus als die "neue" ohne Interrupts, ähnlihc dem Fleury-Code. nehmen wir ein Beispiel: ich möchte ein Byte aus einem Register von einem fiktiven Sensor auslesen. Die Sequenz am Master ist ja standardisiert: - START auf den Bus - SLA+W senden - Registeradresse senden - REPEATED START senden - SLA+R senden - Byte empfangen - STOP auf den Bus Die Sequenz läuft einigermaßen flott ab, ich verschenke also nicht extrem viele CPU-Zyklen in der Sequenz. Die könnte ich zwar mit einer Interrupt-basierten Implementierung nutzen, nur - wofür? ich brauch das Ergebnis um weiterarbeiten zu können, müsste also nachdem ich die Interrupt-Variante gestartet habe, sowieso auf irgendein Flag "Transmission finished" welches in der ISR gesetzt wird, warten. Für die ISR brauch ich auch noch ein paar Status-Variablen, einen Buffer für das zu sendende Byte, und die Repeated-Start-Sequenz ist sowieso schwierig zu kapseln. Da ist die Interrupt-lose Variante echt einfacher:
1 | TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN); // START senden |
2 | while (!(TWCR & (1 << TWINT))); // Warten |
3 | TWDR = slarw; // SLA+W |
4 | TWCR = (1 << TWINT) | (1 << TWEN); // Warten |
5 | TWDR = data; // gewünschte Registernummer senden |
6 | TWCR = (1 << TWINT) | (1 << TWEN); // Warten |
7 | TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN); // Repeated START senden |
8 | while (!(TWCR & (1 << TWINT))); // Warten |
9 | ...
|
(Da fehlt jetzt natürlich die ganze Status-prüfung, soll ja nur das prinzip verdeutlichen) Aber - machen wirs mal andersrum: Peter, ich kenne und schätze deine diversen C-Codes über alles. Gibts von dir was zum Thema i2c?
:
Bearbeitet durch User
Peter Dannegger schrieb: > Beim I2C kann nichts verloren gehen. > Der Slave hält SCL auf low und der Master dreht Däumchen, bis er wieder > SCL auf high sieht. Wenn es so wäre wie behauptet, dann würde die gesamte TWI-Kommunikation gestoppt, wenn ein einzelner Slave (genaugenommen die Software darauf) empfangene Bytes nicht abholen würde. Das würde mich wundern, habe es aber explizit nie nicht getestet. Abgesehen davon soll es ja Slaves geben, die das Clock-Stretching nicht unterstützen ... Wobei ich übrigens immer von Hardware-TWI ausgehe. Michael S.
Michael S. schrieb: > Wenn es so wäre wie behauptet, dann würde die gesamte TWI-Kommunikation > gestoppt, wenn ein einzelner Slave (genaugenommen die Software darauf) > empfangene Bytes nicht abholen würde. Das ist so. Wobei "nicht abholen" nicht ganz der richtige Ausdruck ist. Das ist aber nicht unbedingt eine besodnerheit von I2C, sondern von allen Wirde-And-Bussen: Wenn nur ein Teilnehmer den Bus blockiert (indem er bei I2C SDA und/oder SCL auf low hält) geht nix mehr. Michael S. schrieb: > Abgesehen davon soll es ja Slaves geben, die das Clock-Stretching nicht > unterstützen ... Kann man so nicht sagen: Clock-Stretching macht wenn dann nur der Slave (wenn er es nicht unterstützt, passiert gar nix) aber der Master muss es unterstützen im Sinne von "verstehen".
:
Bearbeitet durch User
Michael Reinelt schrieb: > Die Sequenz läuft einigermaßen flott ab, ich verschenke also nicht > extrem viele CPU-Zyklen in der Sequenz. Die könnte ich zwar mit einer > Interrupt-basierten Implementierung nutzen, nur - wofür? Um all die Sachen zu erledigen, die nix mit der I2C-Kommunikation zu tun haben. Wenn es sowas auf deinem Master nicht gibt, dann ist es halt ein SingleTask-Primitivstprogramm, was da läuft. Dafür braucht man dann wirklich keine Interrupts.
Michael Reinelt schrieb: > Kann man so nicht sagen: Clock-Stretching macht wenn dann nur der Slave > (wenn er es nicht unterstützt, passiert gar nix) aber der Master muss es > unterstützen im Sinne von "verstehen". Nicht aufregen, ich habe auch eine ähnliche Lösung - Master ohne Interrupts, aber Slave MUSS mit Interrupts laufen. Warum: Wenn Master 1 Byte (oder 1 Block) sendet, kann er auch die paar Takte bis ACK oder NACK kommt, abwarten - also keine ISR notig. SLAVE muß eben innerhalb bestimmter Zeit antworten. Dafür muß aber SLAVE so schnell wie möglich antworten, deshalb ist beim SLAVE ISR-Empfang die bessere Lösung.
c-hater schrieb: > Um all die Sachen zu erledigen, die nix mit der I2C-Kommunikation zu tun > haben. > > Wenn es sowas auf deinem Master nicht gibt, dann ist es halt ein > SingleTask-Primitivstprogramm, was da läuft. Dafür braucht man dann > wirklich keine Interrupts. Ich überhöre jetzt mal den leicht lächerlich machenden Ton. Ich probiers mal anders: Die Fleury-Lib für I2C-Master scheint am weitesten verbreitet zu sein, auch wenn man hier das Forum durchsucht. Wenn das so "primitiv" ist, welche I2c-master-Implementierungen verwendets ihr dann? Könnte da mal jemand einen Link posten? Aber mir gehts ohnehin nciht um den master, den hab ich ja im Griff (auch wenns "SingleTask-Primitivstprogramm" ist) Wie implementierts ihr den Slave? Gibts da auch sowas wie eine "Standard-Bibliothek"? (Fleury bietet meines Wissens nach ja keinen Slave)
Michael S. schrieb: > Wenn es so wäre wie behauptet, dann würde die gesamte TWI-Kommunikation > gestoppt, wenn ein einzelner Slave (genaugenommen die Software darauf) > empfangene Bytes nicht abholen würde. > Das würde mich wundern, habe es aber explizit nie nicht getestet. Braucht dich nicht zu wundern, es ist so. Low ist Dominant, wenn ein SLAVE die Leitung auf LOW zieht und nicht losläßt, ist die I2C Kommunikation zu Ende.
Marc Vesely schrieb: > Nicht aufregen, ich habe auch eine ähnliche Lösung - > Master ohne Interrupts, aber Slave MUSS mit Interrupts laufen. Danke, das bestätigt mich. Ich überlege nur noch, ob es sinnvoll ist, die gesamte Kommunikation per Interrupt abzuwickeln, oder nur den ersten "Master ruft an" auf die ISR zu legen, und die restliche Kommunikation in der ISR zu machen. Erscheint mir aber wenig sinnvoll, je mehr ich darüber nachdenke. anyway: Marc, darfst du deinen Slave-Code zeigen? Es gibt vergleichswese wenig Slave-Implementierungen da draußen, und ich lerne und vergleiche gerne bestehenden, funktionierenden, bewährten Code.
Michael Reinelt schrieb: > Wie implementierts ihr den Slave? Gibts da auch sowas wie eine > "Standard-Bibliothek"? (Fleury bietet meines Wissens nach ja keinen > Slave) Bei der Kommunikation ohne Interrupt prüfst du TWSR nach der Aktion auf Status. Mit Interrupt ist alles dasselbe wie früher, nur am Anfang der ISR prüfst du TWSR auf Status, damit du überhaupt weisst, warum du in ISR gelandet bist. Es sind, glaube ich 9 Zustände fur SLAVE als Empfänger und 5 Zustände für SLAVE als Sender möglich und alle sind im DaBla gut erklärt. Wenn Status <> OK fur ausgeführte Aktion, FehlerFlag setzen, I2C-Bus so schnell wie möglich freigeben und weiter mit Fehlerbearbeitung. Wenn Status = OK, nächsten Schritt ausführen usw.
Marc Vesely schrieb: > Low ist Dominant, wenn ein SLAVE die Leitung auf LOW zieht und nicht > losläßt, ist die I2C Kommunikation zu Ende. Wobei man im Idealfall diese Situation vom Master aus noch versuchen kann zu bereinigen: TWI abschalten, händisch neun Clock-Impulse rausschreiben, und hoffen dass sich der Slave wieder erholt. Wenn nicht, ist schicht im Schacht. Da hilft nichtmal ein Reboot des Masters (per Watchdog o.ä.)
Marc Vesely schrieb: > Bei der Kommunikation ohne Interrupt prüfst du TWSR nach der Aktion > auf Status. Mit Interrupt ist alles dasselbe wie früher, nur am Anfang > der ISR prüfst du TWSR auf Status, damit du überhaupt weisst, warum du > in ISR gelandet bist. > Es sind, glaube ich 9 Zustände fur SLAVE als Empfänger und 5 Zustände > für SLAVE als Sender möglich und alle sind im DaBla gut erklärt. > Wenn Status <> OK fur ausgeführte Aktion, FehlerFlag setzen, I2C-Bus > so schnell wie möglich freigeben und weiter mit Fehlerbearbeitung. > Wenn Status = OK, nächsten Schritt ausführen usw. Ja, genau so ist mein momentaner Slave ausgeführt. Allerdings sind es schon etwas mehr als 9 Zustände (viele davon kann man aber zusammenfassen) Trotzdem würd ich gerne mal die eine oder andere "fremde" Implementierung sehen. Wenn das nicht möglich ist, poste ich gerne meine Variante, und hoffe auf kritische aber konstruktive Kommentare
Michael Reinelt schrieb: > anyway: Marc, darfst du deinen Slave-Code zeigen? Es gibt vergleichswese Assembler. Aber glaube mir, du brauchst keinen fremden Code, nur TWSR am Anfang der ISR-Routine prüfen... Gerade rumgestöbert, kann es nicht finden, aber im C wäre das etwas ähnliches: switch(TWSR & 0xF8) { case 0x60 : /* Own SLA+W has been received; ACK has been returned */ Action_1 ; /* Flag setzen, Zeiger erhöhen... */ break ; case 0xA8 : /* Own SLA+R has been received; ACK has been returned */ Action_2 ; break ; usw... am Anfang der ISR-Routine und das ist schon die komplette Umstellung.
Marc Vesely schrieb: > Aber glaube mir, du brauchst keinen fremden Code, nur TWSR am > Anfang der ISR-Routine prüfen... Ja, aber da gibts sicher viel (Fehler-) Potential. Anbei meine momentane Implementierung, bitte um Kritik
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.