Danke, so hatte ich auch gedacht/vermutet - mit leichtem Kopfwiegen und der Hoffnung, dass die devantech-Leute mehr von I2C verstehen als ich.
Dann werd ich das Ganze mal wieder hervorholen.
Werbung
Danke, so hatte ich auch gedacht/vermutet - mit leichtem Kopfwiegen und der Hoffnung, dass die devantech-Leute mehr von I2C verstehen als ich.
Dann werd ich das Ganze mal wieder hervorholen.
Ciao sagt der JoeamBerg
Tja. Wenn ich dieses Detail nur kapieren würde.... Dann werd ich das Ganze mal wieder hervorholen.
Im Internet hatte ich folgende Anleitung gefunden:Mein Code lautet (und ich bin sicher, dass ich damit die Anleitung NICHT realisiert habe (sorry):Code:Busy Test geht so: 1. Start senden 2. Adresse + W senden 3. ACK lesen 4. if ACK gehe zu 7. 5. Stop senden 6. gehe zu 1. 7. Stop senden Endemit i2c_readAck/~Nak laut PFleuryCode:cli(); tmr1 = 0; sei(); // Timer 1 starten // i2c_start (Sadd + I2C_WRITE); // Starte Slave lesen i2c_write ( 0x00 ); // write address = dieses Byte soll gelesen werden i2c_stop(); // i2c_start (Sadd + I2C_READ); // Slave bereit zum Lesen? lobyte = i2c_readNak(); // Byte lesen... NAK i2c_stop(); // cli(); Timer1 = tmr1; sei(); uputs0 ("\r\t## Timer1 = "); uputs0u ( Timer1 ); uputs0 ("\r\tlobyte = "); uputs0u ( lobyte );mit dem Ergebnis:Code:/************************************************************************* Read one byte from the I2C device, request more data from device Return: byte read from I2C device *************************************************************************/ unsigned char i2c_readAck(void) { TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWEA); while(!(TWCR & (1<<TWINT))); return TWDR; }/* i2c_readAck */ /************************************************************************* Read one byte from the I2C device, read is followed by a stop condition Return: byte read from I2C device *************************************************************************/ unsigned char i2c_readNak(void) { TWCR = (1<<TWINT) | (1<<TWEN); while(!(TWCR & (1<<TWINT))); return TWDR; }/* i2c_readNak */
Das Ergebnis bedeutet, dass ich a) mehr als zwischen 100 µs und 150 µs fürs abarbeiten gebraucht habe und dass b) das gelesene Byte die ominöse 6 ist. Der Fall a) würde bedeuten, dass die zum Lesen vom SRF02 benötigten 75 ms (MILLI !) nicht erreicht wurden. In < 150 µs läuft der Schall ganz grob 5 cm, der Sensor hat aber rund 2 m bis zum Echo . . .Code:## Timer1 = 2 lobyte = 6
Daher bitte ich um weitere Hilfe, danke.
Nachtrag/Erklärung: Wieso steht daWeil sich der controller aufhängt, wenn ich ein i2c_readAck(); reinschreibe (ich hab ja geschrieben, dass ich das nicht kapiere :-/ )Code:lobyte = i2c_readNak(); // Byte lesen... NAK
Ciao sagt der JoeamBerg
Hallo oberallgeier,
mir fallen 3 Dinge auf.
1)
Im deinem oben angegebenen ersten Code liest du in der while(1)-Schleife immer ZWEI Bytes aus.
Laut Doku bzw. Musterprogramm aus dem PDF von Robotikhardware (http://www.robotikhardware.de/download/srf02doku.pdf) Seite 8 wird dort aber nur das Register 0 gelesen. (Auch so im Text erwähnt.)
Dein Code mit meinen Kommentaren:
2)Code:while ( 1 ) { i2c_start( Sadd + I2C_WRITE ); i2c_write( 0x00 ); // HIER OK. Adresse 0 anzusprechen i2c_stop(); i2c_start ( Sadd + I2C_READ ); byte_0 = i2c_readAck(); // OK, um das Firmware-Register geht es byte_1 = i2c_readNak(); // NICHT OK, ein weiteres Byte zu lesen? i2c_stop(); if ( byte_0 == 0 ) break; }
In deiner while(1)-Schleife ist KEIN wait enthalten.
In dem Beispiel ist ein 'Wait' mit Bascom als "Waitms 1" angegeben.
Ich bin nicht sicher, aber ein "Dauerfeuer" auf dem Bus ist nicht erlaubt.
Hier solltest du doch eines deiner eigentlich zu sparenden waits auch mal einbauen.
3)
In deinem "... NICHT realisierten ..." Codestück liest du zwar nicht mehr das zweite Byte vom Register, aber dort loopst du auch nicht mehr.
Warum?
Viele Grüße und vor allem Erfolg
Sternthaler
Lieber Asuro programieren als arbeiten gehen.
Soo schwer ist I2C nicht. Ich benutze dazu keine Libraries, die "paar" Zeilen mach ich selber (dann sind auch Fehler im Code meine eigenen).
Ich versuch mal eine Erklärung:
Für jedes Byte werden 9 Bit übertragen. Wenn der Master schreibt, schickt er die ersten 8 Bit (die Daten) und der Slave quitiert beim 9. Takt mit ACK, bzw. quitiert nicht also NAK.
Wenn der Master liest, taktet er 8 Bit ein (der Master gibt immer den Takt vor) und liefert mit dem 9. Takt entweder ACK oder NAK.
Dann kann der Master auf dem Bus noch eine Startkondition oder eine Stopkondition anlegen. Das sind feste Zustände. Und als letztes: treibt der Master den Bus nicht und sind SCL und SDA high, ist der Bus idle.
Die passenden Timingdiagramme gibt es zuhauf in den diversen Datenblättern von I2C Chips oder hier. Das ist eigentlich schon alles über den Datentransfer auf dem I2C Bus.
Und jetzt zu dem Ablauf von oben:
Busy Test geht so:
1. Start senden
Eigentlich sollte man zuerst prüfen, ob der Bus idle ist, also SDA und SCL == 1. Wenn ja, eine Startkondition erzeugen. Ist häufig einfach ein Bit im Controler setzen.
2. Adresse + W senden
Das erste Byte nach dem Start muß das Adressbyte sein, oberste 7 Bit die I2C-Adresse des Slave, unterste Bit das R/W bit. Das wird typisch ins Datenregister des Chips geschrieben, der erzeugt dann die erforderlichen 9 Takte und liefert das ACK bzw. NAK in einem Bit zurück. Bei einem NAK muß die Übertragung mit Stop abgebrochen werden
3. ACK lesen
4. if ACK gehe zu 7.
5. Stop senden
Das ist hier passiert
6. gehe zu 1.
Slave not ready, weiter probieren. Es könnte aber auch bedeuten, Slave garnicht da, hat sich ausgelötet, Adresse ist falsch etc
7. Stop senden
Das müßte man nicht machen, der Slave ist da und bereit, man könnte jetzt die Registeradresse schreiben und dann erst Stop anlegen.
Ein "I2C read" ist hier nicht vorgekommen, daher halte ich die Verwendung einer Funktion mit "read" im Namen für falsch (ich kenne die Library nicht). Ich vermute aber mal so ins Blaue: die Funktion i2c_start() liefert als Returnwert den Status des ACK-Bits.
Ich hoffe, das hilft etwas weiter.
@Sternthaler
Ein "Dauerfeuer" wie du es nennst, ist selbstverständlich erlaubt. Man muß sich nur an die Setup und Holdzeiten der Chips halten und das sind ganz wenige µs eher Nanosekunden. So schnell kann dein Programm garnicht laufen. Und Chips, die eine lange Leitung haben, quitieren nicht (was dann ein NAK ist) solange sie nicht fertig sind.In deiner while(1)-Schleife ist KEIN wait enthalten.
In dem Beispiel ist ein 'Wait' mit Bascom als "Waitms 1" angegeben.
Ich bin nicht sicher, aber ein "Dauerfeuer" auf dem Bus ist nicht erlaubt.
MfG Klebwax
Strom fließt auch durch krumme Drähte !
Hallo oberallgeier,
nochmals ich.
Ich rechne mal etwas anders als du.
Wenn der Schall 300 Meter pro Sekunde schafft, dann sind das 300 * 100cm / 1000ms * 75ms-Messdauer = 2250 cm Weg.
Unser Schall muss hin und zurück laufen. Somit vom SRF02 zum Hinderniss die Hälfte: 1125 cm Weg.
Die Reichweite vom Sensor wird mit 600 cm angegeben und entspricht somit ca. der Hälfte 'meiner' Rechnung.
Wenn also der Piepston mit einer Dauer von NULL erzeugt würde, könnte der Sensor in der Zeit die doppelte Entfernung schaffen.
Ich gehe also davon aus, dass auch das Piepen selbst schon Zeit benötigt![]()
Evenuell wird auch zwei mal 'gepiept' um eine Mittelwertmessung zu machen.
cu Sternthaler
- - - Aktualisiert - - -
Hallo Klebwax,
jepp, du hast Recht mit deinen Zeitangaben.
Ich hatte im Hinterkopf, dass im dicken AVR-Handbuch so eine Zeitangabe zwischen STOP und nächstem START angegeben ist.
Nun wiedergefunden im Kapitel "Electrical Characteristics" und Unterkapitel "Two-wire Serial Interface Characteristics" aus einer Atmel-Doku zum ATmega48A bis 328P:
Bus free time between a STOP and START condition
fSCL < 100kHz 4.7 – μs
fSCL > 100kHz 1.3 – μs
oberallgeier ist beim I2C mit 400 kHz unterwegs und somit sind 1.3 μs nun nicht wirklich die Welt. Aber trotzdem schaffen seine 20 MHz dann schon 26 1-Takt Befehle!
Und nun könnte man mal schauen, was zwischen i2c_stop() und dem nächsten i2c_start() an Maschinenbefehlen steht. (In der Loop gibt es das 2 mal.)
Gruß Sternthaler
Lieber Asuro programieren als arbeiten gehen.
Erstmal vielen Dank Klebwax und Sternthaler für eure Geduld und Ausdauer.
Klar, das ist der richtige Ansatz. Bisher hatte ich die Anleitungen dazu in der Dokumentation von Fleury undoder von Atmel schon nach den ersten Zeilen wegen massiven Nichtverstehens weggelegt. Sorry. Das hatte mir schon vor vielen Monaten Schwierigkeiten gemacht - damals konnte ich nie schnellere I2C-Taktraten als 100 kHz störungsfrei fahren :-/ Hier bin ich mittlerweile mit so ner Art full speed (ok ok, da gibts natürlich Bremser vom Slave) störungsfrei *lichtblick*Soo schwer ist I2C nicht. Ich benutze dazu keine Libraries, die "paar" Zeilen mach ich selber ...
Grundlagen: Dokumentation zum mega1284 : 8272D–AVR–05/12 und die verwendete Bibliothek von PFleury : i2cmaster.zip
Euer Entgegenkommen nutze ich mal noch mehr aus und versuche mal langsam mein Nicht-Verständnis abzubauen. Es fängt ja schon mit der von mir verwendeten Bibliothek an (Peter Fleurys I2C Master library).
Hier fehlt >für mich< schon mal der Wert für TW_STATUS. In der Atmeldokumentation steht dazu TWSR z.B. auf Seite 220 bei Check value of TWI Status Register :Code:Erklärung in der Dokumentation zur Bibliothek (siehe obigen Link "... Master lib..") : unsigned char i2c_start ( unsigned char addr ) Issues a start condition and sends address and transfer direction. Parameters: addr address and transfer direction of I2C device Return values: 0 device accessible 1 failed to access device Hier die Startsequenz, stimmt im Wesentlichen mit der Dokumentation zum mega1284 überein. /************************************************************************* Issues a start condition and sends address and transfer direction. return 0 = device accessible, 1= failed to access device *************************************************************************/ unsigned char i2c_start(unsigned char address) { uint8_t twst; // send START condition TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN); // wait until transmission completed while(!(TWCR & (1<<TWINT))); // check value of TWI Status Register. Mask prescaler bits. twst = TW_STATUS & 0xF8; if ( (twst != TW_START) && (twst != TW_REP_START)) return 1; // send device address TWDR = address; TWCR = (1<<TWINT) | (1<<TWEN); // wail until transmission completed and ACK/NACK has been received while(!(TWCR & (1<<TWINT))); // check value of TWI Status Register. Mask prescaler bits. twst = TW_STATUS & 0xF8; if ( (twst != TW_MT_SLA_ACK) && (twst != TW_MR_SLA_ACK) ) return 1; return 0; }/* i2c_start */ /*************************************************************************
und pfleury schreibt dazuCode:if ((TWSR & 0xF8) != START) ERROR();In myfile.lls finde ich dazu:Code:// check value of TWI Status Register. Mask prescaler bits twst = TW_STATUS & 0xF8;und weiter oben:Code:// check value of TWI Status Register. Mask prescaler bits. twst = TW_STATUS & 0xF8; 25c: 80 91 b9 00 lds r24, 0x00B9 260: 88 7f andi r24, 0xF8 ; 248 if ( (twst != TW_START) && (twst != TW_REP_START)) return 1; 262: 88 30 cpi r24, 0x08 ; 8 264: 21 f0 breq .+8 ; 0x26e <i2c_start+0x22> 266: 80 31 cpi r24, 0x10 ; 16 268: 11 f0 breq .+4 ; 0x26e <i2c_start+0x22> 26a: 81 e0 ldi r24, 0x01 ; 1 26c: 08 95 retUnd da setzts bei mir aus - ob das TWIStatusregister TWSR richtig angegeben ist. Und .. ich finde in den Quellen kein define für das TWSR . . .Code:void i2c_init(void) { /* initialize TWI clock: 100 kHz clock, TWPS = 0 => prescaler = 1 */ TWSR = 0; /* no prescaler */ 240: 10 92 b9 00 sts 0x00B9, r1 TWBR = ((F_CPU/SCL_CLOCK)-16)/2; /* must be > 10 for stable operation */ 244: 8c e0 ldi r24, 0x0C ; 12 246: 80 93 b8 00 sts 0x00B8, r24 }/* i2c_init */ 24a: 08 95 ret
Einfach wäre es jetzt im fleury-code das TWI_TW_STATUS durch TWSR zu ersetzen. Aber es wäre eben experimentelle Softwareentwicklung :-/
Danke im Voraus!
Nachtrag:
in der m1284def.inc steht bei "... TWI ..." nur:Code:; TWSR - TWI Status Register .equ TWPS0 = 0 ; TWI Prescaler .equ TWPS1 = 1 ; TWI Prescaler .equ TWS3 = 3 ; TWI Status .equ TWS4 = 4 ; TWI Status .equ TWS5 = 5 ; TWI Status .equ TWS6 = 6 ; TWI Status .equ TWS7 = 7 ; TWI Status
Geändert von oberallgeier (30.09.2014 um 09:53 Uhr) Grund: Auszug aus der m1284def.inc zitiert
Ciao sagt der JoeamBerg
Meine Fehler sind ja noch schlimmer - im R N Wiki steht ein Beispielprogramm für den SRF02 MIT der Sequenz Warteaufmessung (in Bascom) die wohl genau das tut, was ich eigentlich suche:
Code:'------------- Hilfsfunktionen für SRF02 ---------- Function Srf02_firmware(byval Slaveid As Byte) As Byte Local Firmware As Byte Local Slaveid_read As Byte slaveid_read = Slaveid + 1 I2cstart I2cwbyte Slaveid I2cwbyte 0 'Leseregister festlegen I2cstop I2cstart I2cwbyte Slaveid_read I2crbyte Firmware , Nack I2cstop Srf02_firmware = Firmware End Function Function Srf02_entfernung(byval Slaveid As Byte) As Integer Local Lob As Byte Local Hib As Byte Local Firmware As Byte Local Temp As Byte Local Slaveid_read As Byte slaveid_read = Slaveid + 1 'Messvorgang in starten I2cstart I2cwbyte Slaveid I2cwbyte 0 I2cwbyte 81 'in Zentimetern messen I2cstop Warteaufmessung: Waitms 1 Firmware = Srf02_firmware(slaveid) If Firmware = 255 Then Goto Warteaufmessung I2cstart I2cwbyte Slaveid I2cwbyte 2 'Leseregister festlegen I2cstop I2cstart I2cwbyte Slaveid_read I2crbyte Hib , Ack I2crbyte Lob , Nack I2cstop Srf02_entfernung = Makeint(lob , Hib) End Function End
Ciao sagt der JoeamBerg
Ich mache mit PICs rum und kenne mich daher mit AVR nicht so aus. Ich hab mir daher das Datenblatt von Atmel mal angesehen. Dafür hat Atmel weder einen Preis für Didaktik noch für Chipdesign verdient. Jetzt kann ich verstehen, weswegen viele I2C für kompliziert halten.
Aber in deinem geposteten Code findet sich das wieder, was ich oben geschrieben habe. In i2c_start() wird alles nötige gemacht.
also:
So sollte es gehen.Code:if(i2c_start(Adresse und Write) == 0) { Chip ist fertig mit der Messung und bereit zum Lesen i2c_write(Registeradresse) i2c_stop() if(i2c_start(Adresse und Read) != 0) { eben war der Chip doch noch bereit, was soll das jetzt? Selbstzerstörung einleiten?? } ErsteByte = i2c_readAck() ZweiteByte = i2c_readAck() . . . . LetzteByte = i2c_readNak() i2c_stop() } else { Chip noch nicht bereit, mach erstmal was anderes und versuchs später noch einmal }
Und zu deinem Problem mit TW_STATUS: wenn es nicht definiert wäre, würde der Compiler maulen. In dem meisten IDEs kann man sich irgendwie die Macroexpansion anzeigen lassen, da findet man dann auch wo das steht. Aber man kann da natürlich auch TWSR schreiben, das ist kein Experiment sondern aus dem Datenblatt. TW_STATUS hat sich auch nur jemand ausgedacht (weil es sich schöner liest?).
etwas OT:
Das ganze ist halt ein muchtiges Design. Das eigentlich Statuswort steht in den 5 oberen Bits von TWSR, kann man so machen, ist halt die Hardware. Aber in der Status-Codeliste immer die unteren Bits mitzuschleppen ist Krampf.
Eigentlich müßte man schreiben:
TwiStatus = TWSR >> 3
Und dann kann man mit if oder case die Auswertung auf TwiStatus machen.
Da Start und Stop in Hardware erzeugt werden und im Code darauf gewartet wird, das sich der Status ändert, spielt das keine Rolle.Aber trotzdem schaffen seine 20 MHz dann schon 26 1-Takt Befehle!
MfG Klebwax
Strom fließt auch durch krumme Drähte !
Seit einigen Wochen denke ich, dass PICs vielleicht die bessere Alternative sind: mein Program mer, der erste, der mir wirklich sehr gut gefällt, hat nen PIC. Der SRF02, der ja ziemlich gut funktioniert, hat auch nen PIC.Ich mache mit PICs rum ... Datenblatt von Atmel ... weder einen Preis für Didaktik noch für Chipdesign verdient. Jetzt kann ich verstehen, weswegen viele I2C für kompliziert halten ...
Ok, ich habe jetzt mal rumbastelt am Quellcode und dies hierin allerlei Variationen ausgekostet. Fazit: dieses Byte nimmt im Verlauf der Messung ansteigende Werte an bis die Messung (vermutlich) fertig ist, danach wird die Versionsnummer - aktuell 6 - gelesen.Code:// 30. Sept 2014, 14:ff // Hier der Code von oben, mit Modifikationen (siehe Klebwax) cli(); tmr1 = 0; sei(); // Timer 1 starten i2c_start (Sadd + I2C_WRITE); // Starte Slave lesen i2c_write ( 0x00 ); // write address = dieses Byte soll gelesen werden i2c_stop(); // for ( ; ; ) { // i2c_start (Sadd + I2C_READ); // Slave bereit zum Lesen? lobyte = i2c_readNak(); // Byte lesen... ACK i2c_stop(); // if ( lobyte > 200 ) break; uputs0 ("\r\tlbt "); uputs0u ( lobyte ); .....
WENN ich z.B. bei >200 abbreche, dann fange ich schon mal viele Messungen ab, der Timer hat dann auch Werte, die zur Laufzeit "hin und zurück" passen.
WENN ich bei 6 abbreche, dann hat üblicherweise die Messung nicht wirklich angefangen, dann läuft der Timer bis so etwa 2 bis 4 mal (ein Timerschritt sind 50µs). Die Variante "Abbruch bei 6 - Neustart lesen - Abbruch bei 6" funktioniert dann eher. Offensichtlich läuft dieses Hochtickern von Null bis zum Endwert und da kommt man eben an der Sechs schon beim Hochtickern vorbei. Die Versionsnummer ist 6.
WENN ich bei 255 abbreche - abbrechen möchte - dann läuft der Controller sofort ins Koma. Irgendwie ominös, diesen Wert hatte ich nie gesehen, allenfalls etwas bei 240 und knapp drüber.
Die Versuche leiden natürlich durch Zeitprobleme. Wenn ich zu schnell abfrage dann gibst Fehlfunktionen aber ich hoffe, dass ich diese Geschichten, die meist reproduzierbar waren, ordentlich interpretiert habe.
Jedenfalls weiß ich jetzt, dass ich nicht die genannten 65 ms warten muss. Eine Messung geht teilweise deutlich schneller und passt gut in meine sonstigen, teils noch geplanten Aktivitäten, zumal ich zwei Ultraschallsensoren alternierend abfrage.
Ciao sagt der JoeamBerg
@oberallgeier
Du mußt den Returnwert von "i2c_start (Sadd + I2C_WRITE)" auswerten. So wie ich das in meinem Pseudocode geschrieben habe
if(i2c_start(Adresse und Write) == 0) {
.....1 heißt, das Device ist nicht ansprechbar, hat nicht mit ACK geantwortet.Code:Erklärung in der Dokumentation zur Bibliothek (siehe obigen Link "... Master lib..") : unsigned char i2c_start ( unsigned char addr ) Issues a start condition and sends address and transfer direction. Parameters: addr address and transfer direction of I2C device Return values: 0 device accessible 1 failed to access device
Wer schreibt eigentlich den Code, den du da so postest, z.B. den Bascom Code. Ich sehe an keiner Stelle, daß ACK bzw NAK ausgewertet wird. Da wird überall blind in den Bus hineingerufen, ob ein Device nun antwortet oder gar nicht da ist. Das wird dann auch noch als Beispielcode veröffentlicht.
MfG Klebwax
Strom fließt auch durch krumme Drähte !
Lesezeichen