Hallo allerseits, ich versuche gerade folgende Situation anhand des Datenblattes zu verstehen, was mir nicht richtig gelingen will: zwei ATmega328P über I2C verbunden. Der Slave ist eigentlich reiner "Befehlsempfänger", liefert also keine Daten. Aber er benötigt für gewisse empfangene Befehle eine (unbekannte) Zeit, diese zu verarbeiten, kann solange also keinen weiteren Befehl über I2C empfangen (um genau zu sein: den nächsten schon, den übernächsten quittiert er per NAK) Um das "flüssig" zu gestalten, habe ich mir eine Art "Busy-Flag" überlegt: die einzige Lese-Operation die der Slave kennt, ist das busyFlag lesen. bevor der Master also einen Befehl absendet, macht er zuerst ein i2c-read, prüft ob das busy-Flag gesetzt ist, und wiederholt diese Prüfung solange, bis der Slave bereit ist. Um Overhead zu sparen, würde ich das gerne als fortlaufende Leseoperation ausführen. also eine START-Condition, viele Lese-Operationen, solange bis BusyFlag = 0, danach ein RESTART im Write-Mode, und befehl senden. Das ist jetzt etwas knifflig, was das ACK/NAK des Slaves betrifft: Der master muss eigentlich immer ein ACK senden, da er erst nachher das empfangene Byte prüfen kann. Der Slave muss daher auch immer ein ACK erwarten. Wenn jetzt das Busy-Flag gelöscht ankommt, will der Slave eigentlich weitersenden, da der Master vorher ein ACK gesendet hat. Ich würde jetzt aber gerne ein RESTART+WRITE senden. Wenn ich das tue, bleibt mir irgendwie der Bus hängen. Leider finde ich diese Kombination im Datenblatt so nicht, das einzige was irgendwie passen würde wäre ein Bus Error. Das scheint es aber nicht zu sein. bevor ich mühsam mit dem Oszi auf den Bus losgehe, und beginne Bits zu zählen: Hat jemand eine Idee, woran das liegen kann, bzw. wie man das sauber macht? Danke, Michi
Michael R. schrieb: > Der Slave ist eigentlich reiner > "Befehlsempfänger", liefert also keine Daten. Michael R. schrieb: > bevor der Master also einen Befehl absendet, macht er zuerst ein > i2c-read, prüft ob das busy-Flag gesetzt ist, und wiederholt diese > Prüfung solange, bis der Slave bereit ist. verstehe ich noch nicht ganz wenn der AVR I2C slave busy senden soll dann ist er kein reiner Empfänger. Ich vermute mal es ist ein "ähnliches" Problem wie ich am I2C EEPROM hatte. Alle AT24C32/64 Libs arbeiteten nach einem write mit delay_ms(5); das nervte irgendwie. Dann dachte ich an i2c clock stretching, aber wie feststellen und fand den Weg das der I2C solange blockiert ist also bei weiterer Abfrage ein error liefert und wartete bis der error weg war, das verkürzte Schreiboperationen im I2C EEPROM deutlich. Also statt auf NotBusy zu warten, warte doch einfach bis ein Zugriff keinen Error mehr liefert bzw. statt warten eine ISR mit Flagabfrage state machine
Wenn beide Seiten bei I2C mit Interrupts arbeiten geht das auch wesentlich einfacher: Der Slave schaltet seinen I2C Interrupt ab, wenn er nicht mehr bereit ist, und ein, wenn er es wieder ist. Der Master bleibt dann ggf. im Clock Stretching hängen. Dank Interrupts verschwendet niemand dabei Zeit.
Michael R. schrieb: > Wenn jetzt das Busy-Flag gelöscht ankommt, will der Slave eigentlich > weitersenden, da der Master vorher ein ACK gesendet hat. Ich würde jetzt > aber gerne ein RESTART+WRITE senden. Schon mit STOP + START statt REPEATED START versucht?
Joachim B. schrieb: > wenn der AVR I2C slave busy senden soll dann ist er kein reiner > Empfänger. Gut erkannt, wobei ein I2C-Slave immer gesendet wird. Von sich aus tut der gar nichts ;-) Michael R. schrieb: > die einzige Lese-Operation die der Slave kennt, ist das > busyFlag lesen.
Joachim B. schrieb: > wenn der AVR I2C slave busy senden soll dann ist er kein reiner > Empfänger. Stimmt. "reiner Empfänger" war bezogen auf Datenaustausch: er muss nie Daten zurückliefern. Joachim B. schrieb: > clock stretching ist gefühlsmäßig der falsche Weg: Clock stretching kommt eher zum Einsatz, wenn der Slave nicht mit der Datenrate mithalten kann. Joachim B. schrieb: > Also statt auf NotBusy zu warten, warte doch einfach bis ein Zugriff > keinen Error mehr liefert War auch meine erste idee, hat aber einen Schönheitsfehler: Damit kann ich nicht mehr zwischen "Slave busy" und "Slave tot" unterscheiden A. K. schrieb: > Wenn beide Seiten bei I2C mit Interrupts arbeiten geht das auch > wesentlich einfacher: Der Slave schaltet seinen I2C Interrupt ab, wenn > er nicht mehr bereit ist, und ein, wenn er es wieder ist. Der Master > bleibt dann ggf. im Clock Stretching hängen. Dank Interrupts > verschwendet niemand dabei Zeit. siehe oben, "busy != tot". Clock stretching hilt nix, ist auch softwaremäßig schwer umsetzbar (oder versteh ich da was falsch? Wie mach ich clock stretching am Slave?) A. K. schrieb: > Schon mit STOP + START statt REPEATED START versucht? Ja, hängt auch. Für eine detaillierte Analyse ist es mir im moment zu heiß :-). Was jedenfalls funktioniert: "single read" d.h. Master will ein byte, Slave quittiert ein byte, läuft halt auf eine unnötige Adressierung hinaus. Wobei das mit STOP+START als auch mit REP.START funktioniert. Was auch funktionieren sollte: Sobald der Slave ein "busy=0" liefert, fordert der Master noch ein dummy byte mit "expect NAK" an, was der Slave auch liefern könnte. Aber ich glaube ich habe einen Denkfehler; irgendwas in meiner State machine im Slave ist krank. ich muss mir hier debug-möglichkeiten schaffen...
Michael R. schrieb: > softwaremäßig schwer umsetzbar (oder versteh ich da was falsch? Wie mach > ich clock stretching am Slave?) Passiert automatisch in der Hardware des Slave. Die hängt SCL automatisch so lange auf low, bis die State Machine von der Software aktualisiert wird. Ohne dieses Verfahren sind AVR-Slaves bei einigen MHz Takt nicht sicher in der Lage, einen I2C Takt von 100kHz per C-Interrupt zu verarbeiten, zumal wenn noch andere Interrupts im Spiel sind. Mit Clock Stretching löst sich das Problem in Luft auf, weil völlig selbsttätig abgebremst wird, bis die ISR reagiert. Voraussetzung ist jedoch, dass der Master Clock Stretching korrekt berücksichtigt. Der RPi beispielsweise tut es nicht, der Bus Pirate ebensowenig. Ein AVR Master hingegen schon.
So, ich hab mich jetzt "durchdebuggt", und hatte zwar keinen Fehler in der Implementierung, aber einen Denkfehler: Wenn ich ein busy-Flag empfange, hab ich ja vorher schon ein weiteres Byte angefordert (per ACK), und diesesjenige welche ist auch schon unterwegs. Also kann ich hier natürlich nicht mitten in die bereits laufende Übertragung hinein ein STOP senden. Der AVR ignoriert das auch, quittiert aber nichtmal mit irgendeinem Statuscode. Die Lösung ist eigentlich einfach: Sobald ich ein BusyFlag von 0 empfange, muss ich das nächste "dummy-mäßig" auch noch empfangen, aber mit NAK quittieren, abwarten und dann kann ich sauber ein STOP machen. Allerdings hat mir das hinten und vorne nicht wirklich geholfen, die ganze BusyFlag-idee war von anfang an Topfen. ich machs jetzt ganz normal per NACK-Erkennung beim Adressieren, und das tut wunderbar. Manchmal verrennt man sich halt :-)
Schau Dir mal an, wie das in EEPROMs (24Cxx) gemacht wird. Wenn die mit Schreiben beschäftigt sind, senden die einfach ein NACK auf die Adresse.
Peter D. schrieb: > Schau Dir mal an, wie das in EEPROMs (24Cxx) gemacht wird. Wenn die mit > Schreiben beschäftigt sind, senden die einfach ein NACK auf die Adresse. Was in Wirklichkeit heißt, sie tun garnichts. Ein NAK braucht man nicht senden, der Pullup am Bus macht das NAK. MfG Klaus
Peter D. schrieb: > chau Dir mal an, wie das in EEPROMs (24Cxx) gemacht wird. Wenn die mit > Schreiben beschäftigt sind, senden die einfach ein NACK auf die Adresse. Genauso mach ichs jetzt. Klaus schrieb: > Was in Wirklichkeit heißt, sie tun garnichts. Ein NAK braucht man nicht > senden, der Pullup am Bus macht das NAK. Richtig, und das ist auch das was mich ein bisschen daran stört: man kann nicht unterscheiden ob busy oder tot oder gar nicht vorhanden oder.... aber - irgendeinen Tod muss man sterben ;-)
Michael R. schrieb: > man > kann nicht unterscheiden ob busy oder tot oder gar nicht vorhanden > oder.... Doch. Wenn die max Schreibzeit mit 10ms angegeben ist und er sich dann immer noch nicht meldet, ist keiner angeschlossen.
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.