Hallo oberallgeier.
Gratulation zum Erfolg bei diesem Teil-Problem.
Gruß Sternthaler
So, nun ist meine Stimmung doch deutlich gestiegen. Ich habe endlich einen halbwegs realistischen Testaufbau - drei Controller. Dazu ein fast zweieinhalb Meter langes 10fach-Flachkabel. Und I2C läuft mit TWBR = 5. Master wie vor mit 20 MHz. Da machts einfach gute Laune, wenn der Master bei einem Rundruf in seine - hier noch unbekannte I²C-Welt (...for ( uint8_t look=0x00; look <= 0xFC; look = look + 2 )...) auch die tatsächlich vorhandenen Controller erkennt , siehe Dump vom Terminal :... I2C mit maximaler Geschwindigkeit ... TWBR = 10 ...
Ach so - jedes Minus oben im Slave-Suchabschnitt ist eine nicht-antwortende Adresse.Code:C501 R5M_x15-16 m1284p/20MHz 28Nov2012 16:46 I2C >>400kHz [t05], I2C mit Taste [gelb], dann [OK] zu MoCo328 Motoren rauf+runter mit Taste [P100] Gute Funktion mit extINT2 für RC-5 Initialisierung ADC auf ADC5/PA5=Poti UbattADC5 Min = 2 , Mess = 678 Es folgt Aufruf i2clook Suche vorhandene I²C-Devices von 0x00 bis 0xFE ----------------------------------------------------------------- Slave erkannt auf 130 dez = 0x82 Slave erkannt auf 132 dez = 0x84 ------------------------------------------------------------ Rückkehr von i2clook Beginn Schleife/RC-5-lesen in ~r1n15~ Aktiv : Taste [MIX], [P100], [9] und [gelb] Bitte um Aktion _
Ciao sagt der JoeamBerg
Hallo oberallgeier.
Gratulation zum Erfolg bei diesem Teil-Problem.
Gruß Sternthaler
Lieber Asuro programieren als arbeiten gehen.
Das Thema ist erfolgreich abgeschlossen, im ersten Posting (klick) ist ein Inhaltsverzeichnis der relevanten Beiträge.
Ciao sagt der JoeamBerg
Viel Unverständnis zu diesem Zustand (passt halt nur so ungefähr in diesen Thread):
I2C-Lesen braucht ne Pause.
Vorgeschichte: Bei meinem Archie hängen an einem I²C-Master (mega1284/20MHz) mehrere Slaves, einer davon eine Motor-Steuerplatine (mega328/20MHz) mit Treiberplatine für zwei Motoren je max 60W. Die Befehle für die Motoransteuerung der beiden Motoren gehen vom Master zur Motor-Steuerplatine per I²C, das läuft prächtig. Lesen des Slavepuffers - z.B. Encoderstand oder Ist-Speed - ging oft, meist, führte bisher stehts nach 1 bis etwa 20 (evtl. mehr?) Lesevorgängen zum Blackout des Masters. Blackout: TWI-Übertragung stoppte (z.B. belegt durch testweise Ausgabe per UART), aber ISR (TIMER2_COMPA_vect) lief weiter mit 20kHz-Interrupt, erkennbar am Toggeln der 1-sec-Heartbeat-LED.
Sonstige Angaben: 2 UARTkanäle laufen per Interrupt - 1x 115,2 kBd fürs Terminal und 1x 57,6 kBd für die PingPong-LED-Anzeigetafel, ISR (TIMER2_COMPA_vect) mit 20 kHz, ADC im free running mit rund 15 kHz, External Interrupt 20 für RC5, I2C nach PFleury mit 100 kHz.
Fazit bisher: Telegramme werden an die Slaves per I²C nur gesendet, es wird nichts zurückgelesen. Ein ziemlich unbefriedigender Zustand (aber Archie lief ziemlich gut).
Versuch einer Nachbesserung heute, dazu zuerst Versuch einer Identifizierung der Abbruchstelle. Um die Absturzstelle des Codes zu identifizieren wurden zuerst kurze UART-Telegramme in die I²C-Lesesequenz eingefügt. Die erste im Master gleich nach dem Schreiben der Adresse des ersten zu lesenden Pufferbytes. Und schon läufts klaglos. Ein einziges Millisekundenwait tat den gleichen Nutzen, mittlerweile tuts ein wait von 100 Mikrosekunden. Kontrolle: ohne Wait bekomme ich den Master nach 1 bis 15 Lesevorgängen ins Koma.
Derzeit wurde als Testsequenz tausend Mal auf den Puffer zugegriffen und stets jeweils die gleichen fünf Bytes der Motorplatine ausgelesen, Slaveadresse 130 dez = 0x82, Startbyte zum Lesen = 23. No Problem, mit UART-Ausgabe und einem Wait von 50 ms in der Leseschleife eine Sache von gut 60 sec (die ersten fünf brauche ich um den Befehl per RC5 an den Master zu senden). Das sieht zum Schluss am Terminal so aus, die Sekunden ist die aktuelle Boardzeit mit Start beim Power on:
Fazit: diese tausend Lesevorgänge lassen vermuten, dass zuküftig keine Störungen mehr auftauchen.PHP-Code:
I2CMrid # 131/23 O-AL 23 O-AH 24 O-BL 25 O-BH 26 985 Zeit 65 sec
I2CMrid # 131/23 O-AL 23 O-AH 24 O-BL 25 O-BH 26 986 Zeit 65 sec
I2CMrid # 131/23 O-AL 23 O-AH 24 O-BL 25 O-BH 26 987 Zeit 65 sec
I2CMrid # 131/23 O-AL 23 O-AH 24 O-BL 25 O-BH 26 988 Zeit 65 sec
I2CMrid # 131/23 O-AL 23 O-AH 24 O-BL 25 O-BH 26 989 Zeit 65 sec
I2CMrid # 131/23 O-AL 23 O-AH 24 O-BL 25 O-BH 26 990 Zeit 65 sec
I2CMrid # 131/23 O-AL 23 O-AH 24 O-BL 25 O-BH 26 991 Zeit 65 sec
I2CMrid # 131/23 O-AL 23 O-AH 24 O-BL 25 O-BH 26 992 Zeit 65 sec
I2CMrid # 131/23 O-AL 23 O-AH 24 O-BL 25 O-BH 26 993 Zeit 65 sec
I2CMrid # 131/23 O-AL 23 O-AH 24 O-BL 25 O-BH 26 994 Zeit 65 sec
I2CMrid # 131/23 O-AL 23 O-AH 24 O-BL 25 O-BH 26 995 Zeit 65 sec
I2CMrid # 131/23 O-AL 23 O-AH 24 O-BL 25 O-BH 26 996 Zeit 65 sec
I2CMrid # 131/23 O-AL 23 O-AH 24 O-BL 25 O-BH 26 997 Zeit 66 sec
I2CMrid # 131/23 O-AL 23 O-AH 24 O-BL 25 O-BH 26 998 Zeit 66 sec
I2CMrid # 131/23 O-AL 23 O-AH 24 O-BL 25 O-BH 26 999 Zeit 66 sec
I2CMrid # 131/23 O-AL 23 O-AH 24 O-BL 25 O-BH 26 1000 Zeit 66 sec
Ende test841
Hier zur Erläuterung der Leseabschnitt der Funktion void I2CMrid ( void ) // I2C, Motordaten auslesen über I²C
Frage/Bitte:Code:// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Lesen vom Slave if(!(i2c_start(SLAVE_MoCo+I2C_WRITE))) //Slave bereit zum schreiben/lesen? { // i2c_stop(); // Dies scheint notwendig, um den Lesepointer korrekt // zu positionieren i2c_start(SLAVE_MoCo+I2C_WRITE); // Slave bereit zum schreiben/lesen? // i2cdmy = i2c_write( 0x15 ); // Bufferadresse 15hex/21dez zum Lesen // i2cdmy = i2c_write( 0x04 ); // Bufferadresse 04hex/04dez zum Lesen i2cdmy = i2c_write( laddr ); // Lese Buffer ab Adresse laddr i2c_stop(); // //uputs0 ("\r\tLabel nach Start "); // Nach Einfügen dieser Zeile lief es gut! // wms ( 1); // daher statt der UART-Ausgabe das wait wmus ( 100); // kurzes Wait, ca. 0,1 ms i2cdmy = i2c_start(SLAVE_MoCo+I2C_READ); // <<<### Lesen beginnen ipwm12 = i2c_read (ACK); // Bytes lesen... soll12 = i2c_read (ACK); // ipwm34 = i2c_read (ACK); // soll34 = i2c_read (ACK); // i2cdmy = i2c_read (NAK); // letztes Byte lesen, NAK i2c_stop(); // Zugriff beenden } // else // Wenn Fehler, dann nelde jetzt: { // Lesefehler, dazu Fehlerblinken uputs0("\r\n\t### Kein Lesen möglich.\r\n"); // i2cerr = 0b00000001; // Fehlercode zu i2c-read nicht möglich } // Ende if(!(i2c_start(SLAVE_MoCo+I2C_WRITE)))
Kann mir jemand bitte erklären wo ungefähr die Leseroutine sich durch eine zu knappe zeitliche Bemessung aufhängen könnte? Ich dachte, dass das
Ciao sagt der JoeamBerg
Das Problem ist offensichtlich gelöst - nach der Methode "Auch ein blindes Huhn ...". Eine Erklärung, warum die Verzögerung nötig ist habe ich nämlich nicht. Vielleicht werde ich mit einem DSO noch Messungen am I²C-Bus machen, aber das hat erstmal Zeit.
Was geschah?
Die kritische Stelle der I²C-Leserei hatte ich im obigen Posting wohl recht gut getroffen. Da die konstanten wait-Vorgaben doch unbefriedigend sind, hatte ich mal versucht die Stati des Busses abzufragen. Das klappte nicht wirklich, ist auch eine etwas seltsame Lösung.
Mittlerweile habe ich den Code geändert, der Befehl "..rep_start.." steht in einer while-Schleife. Und nun tuckerts klaglos, aktuell mit 800 kHz, das ist ein TWBR von 4 (eigentlich 4,5), PFleury nennt ein Minumum von 10 für stabilen I²C-Lauf.
Anmerkung: der "..rep_start.." in der PFleury-Bibliothek ist nur ein 1:1 umgeleiteter "... i2c_start ...", der ja mit dem Ergebnis einer Bus-Statusprüfung zurückkommt ;.-.)
Anmerkung2: Das Datenblatt des m1284 nennt als TWI-Frequenz eine obere Grenze von Slavetakt / 16. Diesen kann ich nicht erreichen. Mit TWBR 2 gehts nämlich schon nicht mehr ! . . .
Ärgerlich für mich ist nur, dass diese Änderung wieder mal ne experimentelle Softwareentwicklung ist, weil mir der tatsächliche Grund für die Notwendigkeit dieser Verzögerung nicht klar ist.Code:// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Lesen (read back) vom Slave ... mit der Routine aus fleury´s lib i2c_start_wait(SLAVE_MoCo+I2C_WRITE); // set device address and write mode i2c_write( laddr ); // write address = laddr, das angepeilte Byte i2c_stop(); // while ((i2c_rep_start(SLAVE_MoCo+I2C_READ))) {}// Slave bereit zum Lesen? //i2c_rep_start(SLAVE_MoCo+I2C_READ); // set device address and read mode ipwm12 = i2c_readAck(); // Bytes lesen... ab laddr = 0x33/51 soll12 = i2c_readAck(); // ipwm34 = i2c_readAck(); // soll34 = i2c_readNak(); // letztes Byte lesen, NAK i2c_stop(); //
Getestet wurde in diesem Zusammenhang in meinem aktuellen Archie-Aufbau mit Master (m1284/20MHz), drei Slaves: 2x1284/20MHz und 1x328/20MHz und einer rund 1,5 m langen I²C-Leitung mit zwei Zwischensteckern. Master und Slaves sind durch mehrere Interrupts "verseucht", der Motorcontroller z.B. durch Regelungsroutine ( > 2 x 100 Hz ), Encoder (2 Stk, bis > 500 Hz), Heartbeat+Boardtimer 20 kHz und UART-115kBd. Ähnlich auch der Master durch Heartbeat+Boardtimer, RC-5-Decoder, UART 1 x mit 115kBd und 1 x mit 57kBd. . .
Geändert von oberallgeier (13.07.2014 um 11:31 Uhr) Grund: Interruptverschmutzung der Test-Targets
Ciao sagt der JoeamBerg
@oberallgeier
Eigentlich schon. Manche Slaves, z.B.EEPROMS, zeigen an, daß sie nicht zum Lesen bereit sind, indem sie ihre Adresse nicht mit ACK bestätigen. Das nennt man glaube ich ACK-Polling. Ob dein Slave so reagiert, kann ich jetzt nicht sagenEine Erklärung, warum die Verzögerung nötig ist habe ich nämlich nicht.
Ich verwende "Repeated Start" nicht (Repeated Start braucht man nur bei Multimaster-Betrieb), sondern mache einen Stop und fange dann mit Start neu an. Und ich werte bei jedem Byte das ACK aus. Kommt das ACK nicht wie erwartet, breche ich den Vorgang mit Stop ab. Wenn danach der Bus nicht Idle wird, was vor dem Anlegen der nächsten Start-Condition geprüft wird, versuche ich den Slave mit bis zu 8 Pulsen auf der SCL-Leitungen zu resetten, d.h. den Bus in den Idle-Zustand zu bringen. Wenn das nicht klappt, ist die Hardware kaput (oder ich habe den Slave selbst programmiert )
Wie das irgendwelche Libraries machen, weiß ich nicht. Wenn man I2C Hardware benutzt, kann man die paar Registerzugriffe auch selber programmieren. Und selbst SW-I2C ist nicht wirklich kompliziert.
MfG Klebwax
Strom fließt auch durch krumme Drähte !
Danke für diese Beschreibung. Ich sehe das hier bei mir auch so. Mittlerweile habe ich ja auch die Bibliothek einigermassen durchgehechelt - denn selbst diese Hardware-I²C braucht ja ein bisschen Software - und diese paar Zeilen stehen ja eigentlich fast identisch im Datenblatt. So blicke ich allmählich durch und verstehe was da abläuft. Und offensichtlich ging da bei mir einiges fehl, weil ich beim I²C-Start nicht immer prüfte, ob ich auch Antwort bekomme.... Manche Slaves, z.B.EEPROMS, zeigen an, daß sie nicht zum Lesen bereit sind, indem sie ihre Adresse nicht mit ACK bestätigen ...
Da scheint bei einigen Beispielen der Hase im Pfeffer zu liegen. Und meine Probleme traten sicher dadurch auf, dass ich die Beispiele ohne Verständnis abgekupfert hatte. Egal - wie erwähnt geht mir langsam ein Licht auf. Und danke für den Trick mit den acht Pulsen (hatte ich wohl schon irgenwo mal gelesen - aber wenn man nicht weiß was da geht . . . ).... was vor dem Anlegen der nächsten Start-Condition geprüft wird ...
Den Repeated Start hatte ich ja schon identifiziert (siehe oben) als das identische Teil wie der "normale" Start (neee, eigentlich kein Teil sondern ein drübergestülptes "Mehr"), nur dass damit die "..rep_start.."-Routine aufgerufen wird - die mit demselben Parameter die "... i2c_start ..."-Routine aufruft. Also einfach nur Routinen-Overhead-Verplemperei.
Ciao sagt der JoeamBerg
Gut, wer viel misst misst Mist. Kenne ich. Trotzdem hatte ich nach Einführung der Bereitschaftsprüfung beim I²C-lesen (danke Klebwax für den Ausdruck "ACK-Polling") mal die benötigten Zeiten wissen wollen. Hier sind diese Werte, bei denen vier (4) Bytes aus dem I²C-Puffer des Slaves gelesen wurden, natürlich mit den Zeiten für den zugehörigen Overhead :
Δt1 ist Messung EINSCHLIEßLICH UART-Ausgabe, Δt2 ist das wirklich interessierende Ding – NUR I²C-Übertragung, siehe Code im Kasten unten. Die Zeitangabe ist in tupsi, meiner Board-Zeiteinheit von 50 µs. Die angegebene I²C-Rate ist der Wert aus "#define SCL_CLOCK" in meiner twima_hze.c, real sind also z.B. 800 kHz; wegen der Rundung beim Berechnen von TWBR = ((F_CPU/SCL_CLOCK)-16)/2; sind das schon 833 kHz. Hier ist dann TWBR 4 -- und damit deutlich unter der verbreiteten Grenzangabe in den Libraries von 10. Trotzdem läuft die Chose bei mir klaglos.Code:I²C Δt1 Δt2 kHz tupsi tupsi ( 1 tupsi sind 50 µs) 100 22..26 14..17 400 13..15 4..6 800 11..13 2..4
Woher dieser Grenzwert stammt weiß ich noch nicht. Ebenso ist (mir, noch) unklar das Timing der I²C-Lese-Hardware der 8bittigen ATMEL Megas. Aber - abwarten.
Overhead rausmessen, sprich: NUR die reine Datenübertragung zu messen, wäre möglich. Aber wozu? Immerhin geben diese Werte einen Anhalt über den tatsächlichen Zeitaufwand bei mittelschnellem I²C.Code:// ============================================================================= = // Lesen Daten von der Motorplatine, Slaveadresse 0x82/130dez; // Ausgabe auf UART0 // - - - - - - - - - - - - - - - - void I2CMrid ( void ) // I2C, Motordaten auslesen über I²C // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - { // // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - u8 ipwm12 = 99; // Read-back-Byte vom Slave u8 soll12 = 98; u8 ipwm34 = 97; u8 soll34 = 96; u8 laddr = 0x33; // Leseaddresse = 51dez u8 i2cttt = 0; // Lokal gemessene Zeit i2ctim = 0; // Sendedauer - siehe ~com~.h // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Lesen (read back) vom Slave ... mit der Routine aus fleury´s lib i2c_start_wait(SLAVE_MoCo+I2C_WRITE); // set device address and write mode i2c_write( laddr ); // write address = laddr, das angepeilte Byte i2c_stop(); // while ((i2c_start(SLAVE_MoCo+I2C_READ))) {} // Slave bereit zum Lesen? //while ((i2c_rep_start(SLAVE_MoCo+I2C_READ))) {}// Slave bereit zum Lesen? //i2c_rep_start(SLAVE_MoCo+I2C_READ); // set device address and read mode ipwm12 = i2c_readAck(); // Bytes lesen... ab laddr = 0x33/51 soll12 = i2c_readAck(); // ipwm34 = i2c_readAck(); // soll34 = i2c_readNak(); // letztes Byte lesen, NAK i2c_stop(); // i2cttt = i2ctim; // Timerwert übernehmen if ( ipwm12 <= 1 ) i2cRct++; // Wenn Lesebyte fehlerhaft scheint // I2C-ERRorcounter hochzählen // - - - - - - - - - - - - - - - - - - - - // Ausgabe der Daten über UART - Fortsetzung der Startsequenz uputs0 ("\r\tI²Crd # "); // Kennung dieser Routine uputs0i(SLAVE_MoCo+I2C_READ); uputs0("/"); uputs0i ( laddr ); uputs0("\tP12 "); uputs0u(ipwm12); uputs0("\tsp1 "); uputs0u(soll12); uputs0("\tP34 "); uputs0u(ipwm34); uputs0("\tsp4 "); uputs0u(soll34); //uputs0("\ttmr "); uputs0u(i2ctim); // Timerwert ausgeben MIT UART uputs0("\tttt "); uputs0u(i2cttt); // Gestoppte Zeit NUR I2C // // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // uputs0("\tEnde I2CMrid"); return; // } // === Ende void I2CMrid ( void ) // ============================================================================= =
Ciao sagt der JoeamBerg
Lesezeichen