Hallo zusammen, ich hab mit meinem ATmega128P ab und zu folgendes Problem: Das Ding arbeitet als I2C Slave, und steuert ein LCD an. Wenn nix zu tun ist, schicke ich das teil in den "extended standby", da arbeitet TWI slarw match weiter, und der AVR sollte in 6 Takten wieder voll einsatzfähig sein. Taktquelle ist ein externer Quarz mit 16 MHz. Nun bekomme ich am Master (ein zweiter AVR) ab und zu (alle paar Minuten) ein NAK auf das erste gesendete Datenbyte. Sieht für mich so aus, als würde der Slave nicht schnell genug aufwachen, um das Datenbyte mit ACK zu quittieren. Das SLA+RW wird aber erfolgreich geACKt (aber das macht die Hardware, nicht die ISR). Sobald ich den Sleep-Mode deaktiviere, funktioniert alles problemlos. am Slave laufen nur noch zwei sehr einfache und kurze Timer2-ISRs. hat da jemand eine Erklärung dafür? und was man dagegen tun kann? Ausmessen mit Oszi ist eher mühsam, wenn es nur alle paar tausend I2C-Übertragungen zum Fehler kommt...
Eine niedrigere Bitrate könnte helfen. Zumindest für das erste Byte nach der Slave-Adresse.
Ich habe auch schon ein I2C Slave mit einem Attiny85 programmiert. Der Tiny hat jedoch ein USI Interface, und man muss ziemlich viel per Software machen, damit der I2C funktioniert. Ich hatte damals auch Probleme, dass die ISR mit der Datenverarbeitung des I2C zuviel Zeit brauchte, da wahren nur etwa 4 oder 5 Takte Spielraum. Je nach dem, wenn der uC noch andere Sachen erledigen sollte, und somit die ISR des I2C verzögerte, hatte ich genau das gleiche Problem (der Slave antwortetet mit NAK, weil er nicht nachkam). Ich hatte dann die ISR soweit optimiert, so das ich minimum 20 Takte Spielraum hatte. Nun hat der Tiny auch genügend Zeit um in die ISR zu springen. Das Problem ist nie wieder aufgetaucht. Ich denke, bei dir ist das Problem ein ähnliches. Ich kenne zwar das TWI interface des Atmega128 nicht.
Michael L. schrieb: > Ich hatte damals auch Probleme, dass die ISR mit der Datenverarbeitung > des I2C zuviel Zeit brauchte I2C ist völlig unkritisch, die I2C-Hardware hält SCL solange auf low, bis der Interrupt abgearbeitet ist. Man darf nur das Interruptflag nicht zu früh löschen, sondern erst am Ende des Interrupts.
Danke euch. nachdem ich noch einige lästige Probleme am Master nun hoffentlich gelöst habe (richtige Behandlung von Bus Error, manueller I2C-Reset, ... der Beispielcode von ATmel ist an der Stelle manchmal "fragwürdig") ist das problem immer noch da. Momentan hab ich es so gelöst, dass seitens des masters bei einem NAK auf Daten ähnlich wie beim NAK auf sla+rw einfach nochmal versucht wird. Scheint bisher stabil zu laufen. Allerdings hab ich immer noch keine Erklärung woher das Problem kommt.
Michael Reinelt schrieb: > Das SLA+RW wird aber erfolgreich geACKt (aber das > macht die Hardware, nicht die ISR). Das ACK macht immer die Hardware. Du müßtest mal debuggen, welchen Status Code der Slave hatte vor dem NACK. Bei NACK müßte es ja 0x88 sein. Und Du müßtest TWEA auf 0 gesetzt haben.
Peter Dannegger schrieb: > Michael Reinelt schrieb: >> Das SLA+RW wird aber erfolgreich geACKt (aber das >> macht die Hardware, nicht die ISR). > > Das ACK macht immer die Hardware. Ja schon, ich meinte auf das erste ACK auf die Adressierung habe ich wenig Einfluss, für alle weiteren Datenbytes kann ich's mir aussuchen ob ACK oder NAK. > Du müßtest mal debuggen, welchen Status Code der Slave hatte vor dem > NACK. Ja, wenn das so einfach wäre :-) > Bei NACK müßte es ja 0x88 sein. eigentlich Nachher, nicht vorher. (das "vorher" und "nachher" bei der TWI-StateMachine bringt mich auch immer durcheinander). In die State-Machine kommt er ja immer erst nach dem Quittieren. > Und Du müßtest TWEA auf 0 gesetzt haben. Nein, eigentlich nicht. Hier mal der relevante Ausschnitt aus der ISR:
1 | case 0x60: // own SLA+W has been received; ACK has been returned |
2 | I2C_Busy = 1; // transmission started |
3 | I2C_RxIndex = 0; // reset packet index |
4 | TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT) | _BV(TWEA); // send ACK |
5 | break; |
6 | |
7 | case 0x80: // previously addressed with own SLA+W; data has been received; ACK has been returned |
8 | case 0x88: // previously addressed with own SLA+W; data has been received; NAK has been returned |
9 | index = I2C_RxIndex; // local cache |
10 | if (index < I2C_PACKETSIZE) { |
11 | I2C_RxBuffer[index++] = TWDR; // store received data |
12 | I2C_RxIndex = index; // write back cache |
13 | }
|
14 | if (index < I2C_PACKETSIZE - 1) { |
15 | TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT) | _BV(TWEA); // send ACK |
16 | } else { |
17 | TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT); // last byte, send NAK |
18 | }
|
19 | break; |
(I2C_PACKETSIZE ist eine Konstante (#define) mit Wert 64, also groß genug) Da der Slave auf die Adressierung noch mit ACK geantwortet hat, müsste er mit 0x60 in die ISR gekommen sein. Dort sag ich ihm, er soll das nächste Byte mit ACK quittieren. Dann kommt das erste Datenbyte, dieses wird aber offensichtlich NICHT mit ACK quittiert. Also müsste 0x88 im TWSR stehen. Es gibt keinen Code-Pfad, der TWEA nicht auf 1 setzt, nach erfolgreicher Adressierung. Die einzige Möglichkeit, die ich sehe, ist dass das TWI in Hardware die Adressierung quittiert, danach versucht die ISR aufzurufen, wo TWEA auf 1 gesetzt würde, aufgrund des sleeps das aber zu langsam / zu spät erfolgt. Da der AVR aber angeblich in 6 Takten aufwacht, und mit 16 MHz läuft, müsste er ja fast unendlich zeit dafür haben...
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.