PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : [ERLEDIGT] SRF02. Wie ohne Timer Messwert-Abfrage steuern



oberallgeier
28.09.2014, 09:49
Hallo alle,

ich habe meine Probleme, aktuell mit dem SRF02, meinem Englisch und der Bedienungsanleitung des SRF02 (http://www.robot-electronics.co.uk/htm/srf02techI2C.htm). Dort steht nämlich zu Checking for Completion of Ranging:

... ... Therefore, if you try to read from the SRF02 ... then you will get 255 (0xFF) whilst ranging ... As soon as the ranging is complete ... its not 255 (0xFF) anymore ... ...

Also programmiere ich unbedarft:
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Messung starten auf Sensor Sadd
// - - - - - - - - - - - - - - -
if (!(i2c_start( Sadd + I2C_WRITE ))) // Slave bereit zum schreiben?
{ // dann Werte schreiben
i2cdmy = i2c_write ( 00 ); // Buffer Startadresse setzen
i2cdmy = i2c_write ( 0x51 ); // Startbefehl SRF02
i2c_stop(); // Zugriff beenden
} //
else // Weiter mit if (!(i2c_start( Sadd + I2C_WRITE )))
{ //
uputs0("\r\n\t## SRF_rdat\r\n");
wms ( 1000);
return 9901; // Return mit Fehlercode
} // Ende if (!(i2c_start( Sadd + I2C_WRITE )))

cli(); tmr1 = 0 ; sei();

while ( 1 )
{ //
i2c_start( Sadd + I2C_WRITE ); // Starte Slave lesen
i2c_write( 0x00 ); // write address = das angepeilte Byte
i2c_stop(); //

i2c_start ( Sadd + I2C_READ ); // Slave bereit zum Lesen?
byte_0 = i2c_readAck(); // Byte 0 lesen... ab laddr = 0
byte_1 = i2c_readNak(); // Byte 1 lesen... NAK
i2c_stop(); //

if ( byte_0 == 0 ) break; // Messung beendet
} // Ende while ( 1 )

cli(); Timer1 = tmr1; sei();
uputs0 ("\r\t####\t Timer1 [tupsi] = "); uputs0u ( Timer1 );

uputs0 ("\r\tbyte_0 = "); uputs0u ( byte_0 );... um die Messung abzuwarten UND um mir zu Testzwecken den Timerwert für die Messdauer ausgeben zu lassen. Pustekuchen. Je nach Abwandlung des Codes : der Controller hängt sich auf, eine Variation der Abfrage ergibt den WErt 6 - vermutlich die Versionsnummer (so stehts in der Dokumentation) - aber eine Abfrage z.B. auf 255 führt zum Koma. Wenn ich in einer Variante die Abfrage hinkriege, dann steht der Timer auf 0, das heißt bei diesem Timer: Zeitbedarf <50 µs -- bei einem Messabstand von rund 150 cm nicht glaubhaft weil viel zu wenig. Ich kann also den SRF02 nicht ohne Timer betreiben, wie es in der Bedienungsanleitung in Aussicht gestellt wird.

Fragen:
Erkennt jemand meine Fehler?
Hat jemand ne Lösung mit der er den Busystatus - oder den Readystatus - eines SRF02 abfragen kann?

Danke im Voraus für die Hilfe.

Klebwax
28.09.2014, 22:46
Der Text ist für mich etwas widersprüchlich. Ich versuche mal zu interpretieren.

Es wird gesagt, der SRF02 reagiert nicht auf den I2C, solange er misst. Das klingt erstmal einfach. Dann wird aber gesagt, man solle den Chip, am besten ein Register mit bekanntem Wert auslesen, und solange der 0xFF sei (der Bus wenn er nicht getrieben wird) ist der Chip noch nicht fertig.

Wenn der SRF nicht auf I2C Kommandos reagiert, solange er misst, kann man aber eigentlich auch kein Register auslesen. Er liefert dann nämlich kein ACK auf das Adressbyte. Ich kann nur vermuten, daß der Schreiber des Textes davon ausgeht, daß der Anwender einfach blind den I2C Bus liest und keine Fehlerauswertung macht. Ob aber die jeweils verwendete I2C Library oder HW das 0xFF des Buses liefert, oder der Wert bei einem NACK undefiniert ist, kann ich nicht sagen.

Ich würde es mal so probieren (wie es auch in Anleitungen zu anderen Bausteinen beschrieben wird): solange bis der SRF mit ACK antwortet die Adresse mit Write senden. Reagiert er, kann man dann auch Register auslesen.

MfG Klebwax

oberallgeier
29.09.2014, 08:00
... für mich etwas widersprüchlich. ... solange er misst, kann man aber eigentlich auch kein Register auslesen ...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.

oberallgeier
29.09.2014, 16:13
... Dann werd ich das Ganze mal wieder hervorholen.Tja. Wenn ich dieses Detail nur kapieren würde.

Im Internet hatte ich folgende Anleitung gefunden:
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
EndeMein Code lautet (und ich bin sicher, dass ich damit die Anleitung NICHT realisiert habe (sorry):
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 i2c_readAck/~Nak laut PFleury
/************************************************** ***********************
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 */
mit dem Ergebnis:

## Timer1 = 2
lobyte = 6Das 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 . . .

Daher bitte ich um weitere Hilfe, danke.

Nachtrag/Erklärung: Wieso steht da
lobyte = i2c_readNak(); // Byte lesen... NAKWeil sich der controller aufhängt, wenn ich ein i2c_readAck(); reinschreibe (ich hab ja geschrieben, dass ich das nicht kapiere :-/ )

Sternthaler
29.09.2014, 20:39
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:




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;
}


2)
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

Klebwax
29.09.2014, 22:52
Tja. Wenn ich dieses Detail nur kapieren würde.

Im Internet hatte ich folgende Anleitung gefunden:
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
Ende

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 (http://www.nxp.com/documents/user_manual/UM10204.pdf). 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

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.

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.

MfG Klebwax

Sternthaler
30.09.2014, 00:58
## Timer1 = 2
lobyte = 6
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 . . .


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 - - -



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.

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

oberallgeier
30.09.2014, 09:33
Erstmal vielen Dank Klebwax und Sternthaler für eure Geduld und Ausdauer.

Soo schwer ist I2C nicht. Ich benutze dazu keine Libraries, die "paar" Zeilen mach ich selber ...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*

Grundlagen: Dokumentation zum mega1284 : 8272D–AVR–05/12 (http://www.atmel.com/Images/Atmel-8272-8-bit-AVR-microcontroller-ATmega164A_PA-324A_PA-644A_PA-1284_P_datasheet.pdf) und die verwendete Bibliothek von PFleury : i2cmaster.zip (http://homepage.hispeed.ch/peterfleury/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 (http://homepage.hispeed.ch/peterfleury/group__pfleury__ic2master.html)).


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 */

/************************************************** ***********************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 :


if ((TWSR & 0xF8) != START)
ERROR();und pfleury schreibt dazu
// check value of TWI Status Register. Mask prescaler bits
twst = TW_STATUS & 0xF8;
In myfile.lls finde ich dazu:
// 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 weiter oben:
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

Und da setzts bei mir aus - ob das TWIStatusregister TWSR richtig angegeben ist. Und .. ich finde in den Quellen kein define für das TWSR . . .

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:
; 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

oberallgeier
30.09.2014, 13:00
Meine Fehler sind ja noch schlimmer - im R N Wiki steht ein Beispielprogramm (http://rn-wissen.de/wiki/index.php/Ultraschallsensor_SRF02_am_RN-Board) für den SRF02 MIT der Sequenz Warteaufmessung (in Bascom) die wohl genau das tut, was ich eigentlich suche:

'------------- 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

Klebwax
30.09.2014, 13:11
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:


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
}

So sollte es gehen.

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.


Aber trotzdem schaffen seine 20 MHz dann schon 26 1-Takt Befehle!
Da Start und Stop in Hardware erzeugt werden und im Code darauf gewartet wird, das sich der Status ändert, spielt das keine Rolle.

MfG Klebwax

oberallgeier
30.09.2014, 17:18
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 ...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.

Ok, ich habe jetzt mal rumbastelt am Quellcode und dies hier
// 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 );
.....in 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.

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.

Klebwax
30.09.2014, 21:11
@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) {
.....





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

1 heißt, das Device ist nicht ansprechbar, hat nicht mit ACK geantwortet.

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

oberallgeier
30.09.2014, 22:47
... Du mußt den Returnwert von "i2c_start (Sadd + I2C_WRITE)" auswerten ... if(i2c_start(Adresse und Write) == 0) { ...Sorry, ja, danke. Zu Beginn dieser Litanei, den ich nicht zitiert hatte, hab ich es ja noch richtig:
// Messung starten auf Sensor Sadd
// - - - - - - - - - - - - - - -
if (!(i2c_start (Sadd + I2C_WRITE))) // Slave bereit zum schreiben?
{ // dann Werte schreiben
i2cdmy = i2c_write ( 00 ); // Buffer Startadresse setzen
i2cdmy = i2c_write ( 0x51 ); // Startbefehl SRF02
i2c_stop(); // Zugriff beenden
} //
else // Weiter mit if (!(i2c_start( Sadd + I2C_WRITE )))
{ //
uputs0("\r\t## SRF_rdat\r");
wms ( 10);
return 9901; // Return mit Fehlercode
} // Ende if (!(i2c_start( Sadd + I2C_WRITE )))Und danach - hat das dauernde Ändern zu Schlamperei geführt. Hast ja Recht.


... Wer schreibt eigentlich den Code, den du da so postest, z.B. den BASCOM Code ...BASCOM sind Beispiele aus dem RN Wi ki (http://rn-wissen.de/wiki/index.php/Ultraschallsensor_SRF02_am_RN-Board), das kann ich nicht. Die C-Sequenzen sind von mir - sicher mehr schlecht als recht :-/ . Die I²C-Bibliothek ist von PFleury.

Sternthaler
30.09.2014, 23:24
Hallo zusammen,

natürlich weitere Unterstützung (leider auch Fragen) von mir.


@oberallgeier

Die Defines für die TWI-Schnittstelle sind angegeben in twi.h im Verzeichnis ...\GCC-COMPILER\... \avr\include\util\

#define TW_STATUS_MASK (_BV(TWS7)|_BV(TWS6)|_BV(TWS5)|_BV(TWS4)|_BV(TWS3) )

#define TW_STATUS (TWSR & TW_STATUS_MASK)

Und die Form der hier benutzten Definition passt dann natürlich auch zu den 'Gegenstücken' um in einer case()-Anweisung die case-Sprungstellen zu finden.
Die 'Gegenstücke' sind also sinnvollerweise auch in der Datei twi.h zu finden.
Beispielsweise mit:
#define TW_MT_SLA_ACK 0x18

Somit ist es sehr wohl relevant, ob man nun die vorhandenen Defines nutzt oder "TwiStatus = TWSR >> 3".

switch (TW_STATUS) {
case TW_MT_SLA_ACK: xxx
wird funktionieren.

Aber
TwiStatus = TWSR >> 3;
switch (TwiStatus) {
case TW_MT_SLA_ACK: xxx
wird nicht mehr funktionieren, da nun die Bits mit >> 'versetzt' zu den 'Gegenstück'-Define stehen.

Genauso wie es schon wichtig ist, dass man NICHT das Register TWSR in seinem switch() benutzt, da dann ja immer noch die letzten beiden Bits TWPS0 und TWPS1 zum Einstellen vom Prescaler enthalten sind, und dann auch nicht mehr zu den 'Gegenstücken' in der case-Orgie passen werden.


Dein letzter Beitrag deutet ja darauf hin, dass während der Messung dieses 'merkwürdige' Versionsregister 0 eben nicht das macht was in der Doku steht.
Nicht gut.



@Klebwax

Tut mir leid, jetzt kommen eher Fragen ;)

Und was passiert tatsächlich mit folgenden beiden C-Codezeilen?

Erzeuge STOP mit sofort folgendem START in der TWI-Hardware:

TWCR = (1<<TWSTO)|(1<<TWINT) | (1<<TWEN)|(1<<TWIE);
TWCR = (1<<TWSTA) |(1<<TWINT) | (1<<TWEN)|(1<<TWIE);


Der Compiler macht daraus:
4d6: 85 e9 ldi r24, 0x95 ; 149
4d8: 80 93 bc 00 sts 0x00BC, r24 <<== Register für TWI-Hardware im mega168 und anderen AVRs.

4dc: 85 ea ldi r24, 0xA5 ; 165
4de: 80 93 bc 00 sts 0x00BC, r24


Hier liegen wir garantiert unter den 1.3 us und nun stellen sich mir folgenden Fragen:
Was macht die Hardware tatsächlich, wenn sie STOP'en und sofort wieder START'en soll?
Wird dann der Softwareteil beim START'en verzögert? Kann ich mir beim besten Willen nicht vorstellen.
OK, nach dem START wird es etwas dauern, bis im TWSR das Bit TWINT wieder gesetzt wird. Klar, das macht die Hardware.

Wenn ich die TWI-Hardware benutze, dann sollte ich natürlich nicht dein angegebenes "... und im Code darauf gewartet wird. ..." dahingehend wörtlich nehmen, dass ich nun eine Schleife im Code bauen soll, die zwischen STOP und START prüft, "... das sich der Status ändert ..."?

Kannst du deine Erklärung genauer formulieren, bzw. mir eine Erklärung geben, die ich verstehe?


Da die TWI-Hardware, nach einem durch die Software angegebenem STOP-Kommando an das Register TWCR, sich nicht mehr per Interrupt meldet, da nämlich kein Status-Wert für diese Aktion definiert ist im TWSR-Register (ODER habe ich diese Info noch nicht gefunden!), kann ich somit den nächsten START-Aufruf tatsächlich nur ohne Interrupt-Kontrolle erzeugen.
Also auch im Extremfall wie oben angegeben.


Im Handbuch finde ich folgendes: (mega48 bis 328 )

Im Kapitel "Overview of the TWI Module" Unterpunkt "Bus Interface Unit":
"When in Transmitter mode, the value of the received (N)ACK bit can be determined by the value in the TWSR."
Also hier noch keine Angabe, was im TWSR nach einem STOP steht.

Im Kapitel "Using the TWI" ist das Bild "Interfacing the Application to the TWI in a Typical Transmission" angegeben, welches die Aktionen von Software und Hardware sehr schön im (typischen) Zusammenspiel zeigt.
Hinter dem STOP wird hier nichts mehr angegeben.
Hast du hier eine Info, ob, und wie, der STOP-Zustand auf dem BUS im TWSR 'zu sehen' ist?

Und das dazu wohl wichtigste Kapitel:
"Overview of the TWI Module" Unterpunkt "Control Unit" ist zu finden, wann das Bit TWINT im TWSR gesetzt wird um anzuzeigen, das die Hardware fertig ist.
• After the TWI has transmitted a START/REPEATED START condition.
• After the TWI has transmitted SLA+R/W.
• After the TWI has transmitted an address byte.
• After the TWI has lost arbitration.
• After the TWI has been addressed by own slave address or general call.
• After the TWI has received a data byte.
• After a STOP or REPEATED START has been received while still addressed as a Slave.
• When a bus error has occurred due to an illegal START or STOP condition.
Dort ist KEINE ANGABE vorhanden, das ein STOP das Bit setzt.
Aus meiner Sicht ist somit nicht zu prüfen, wann das STOP 'fertig' ist. --> Alles eben beim ATmega und nicht in einem PIC.


Gruß aus dem frühen neuen Tag
Sternthaler


Nachtrag:
Vielleicht doch ne' Lösung vorhanden?

In "Transmission Modes" Unterkapitel "Miscellaneous States" steht natürlich noch etwas zu diesem "illegal START or STOP condition"
Table 22-6. Miscellaneous States

0x00 Bus error due to an illegal START or STOP condition
No TWDR action 0 1 1 X
Only the internal hardware is affected, no STOP condition is sent on the bus. In all cases, the bus is released and TWSTO is cleared.

schorsch_76
01.10.2014, 07:31
Hier
TWCR = (1<<TWSTO)|(1<<TWINT) | (1<<TWEN)|(1<<TWIE);
TWCR = (1<<TWSTA) |(1<<TWINT) | (1<<TWEN)|(1<<TWIE);

muss man eigentlich warten bis das STO wieder zurück esetzt wurde (siehe Tabelle in DS).

Seite 229 Atmega328 Doku: Table 21-2. Status codes for Master Transmitter Mode
bzw. Table 21-3 für Master Receive.

Man kann auch STO und STA gleichzeitig setzen. Siehe die genannten Tabellen.

oberallgeier
01.10.2014, 09:36
Erstmal danke für Eure Mühe, Geduld, Hilfe und Erklärungen.


... Du mußt den Returnwert von "i2c_start (Sadd + I2C_WRITE)" auswerten
... Da wird überall blind in den Bus hineingerufen ... auch noch als Beispielcode ...Nochmal zum Auswerten der Busanfrage. Derzeit, im Aufbau- und Teststadium habe ich nur zwei baugleiche Sensoren auf 0xE0 und 0xE2 angeschlossen. Wenn ich I²C fahre, dann prüfe ich standardmässig immer auf aktuell angeschlossene/vorhandene Devices in der Startphase des Controllers/Programms. Manchmal über den ganzen, meist aber nur über einen begrenzten Adressbereich. Ausgabe aufs Terminal.
SRF02_look ( ); // Suche angeschlossene Sensoren 0xE0..0xFE SRF
while ( 1 ) //
//for ( u16 such=0xE0; such <= 0xFC; such = such + 2 )
{ // Teste nur I2C-Adressbereich der US-Sensoren
if(!(i2c_start(such))) // Slave bereit zum schreiben?
{ //
i2c_stop(); // Zugriff beenden
// - - - - - - - - - - - - - - - -
uputs0 ("\r\tUSdev addr\t"); // Melde zugriffbereiten Slave
uputs0u (such); uputs0hex (such); // ..mit dezimaler und Hex-Adresse
jaflag = 55; //
} //
else // Melde jetzt: Kein Byte geschrieben
{ // und Fehlermelde-Strich
if ( jaflag == 55 ) // Flag "Slave erkannt" ??
{ //
uputs0 ("\r\t"); // .. dann neue Zeile für Nein-Strich(e)
} // Ende if ( jaflag = 55; )

jaflag = 0; // Flag zurücksetzen
uputs0 ("-"); // Nein-Strich
// uputs0 ("\r- "); //
// uputs0hex (such); // ..mit dezimaler und Hex-Adresse
i2c_stop(); // Zugriff im Fehlerfall beenden, sonst SDA low
} // Ende if(!(i2c_start(such)))
//
if ( such == 0xFE ) break;
if ( such <= 0xFC ) such = such + 2;

} // Ende while ( 1 ), war for ( uint8_t such=0xE0; such <= 0xFE;Das kommt dann so aufs Terminal (dazu die Meldung zum Start-Kalibrieren - das eigentlich unnötig ist) :
C506x21-32(319) CiCo babyo328/20MHz 30 Sep 2014 19:42
CIR: irLED=PB1, CIR PC0ff ...
UART0 115 kBd RxTx-buff 64/64.

Init I²C ok

I²C-Dev 0xE0-0xFE NoDev =: '-'
USdev addr 224=0xE0
USdev addr 226=0xE2
-------------- End 254=0xFE

Kal SRF02 224=0xE0 End
Kal SRF02 226=0xE2 End

In der Entwicklungsphase neige ich dazu (mehr als sonst) leichtfertig zu programmieren, sprich: Busverfügbarkeit weglassen. Damit kann man manchmal schon frühzeitig Fehler provozieren. Dass die Leichtfertigkeit sich auch in den fertigen Code reinschmuggelt - kommt bei mir vor :-/

ABER : aktuell sind nur die beiden Sensoren am Bus, es ist ein Master-Slave-Betrieb und aktuell nur der Timerinterrupt vom Boardtimer mit Heartbeat-LED. Da sollte der Bus sauber sein, es sei denn, ich habe irgendwo ein i2c_stop vergessen oder so was Ähnliches.

Beispielcodes. Stimmt, das ist ein eigenes Thema. Ich hatte ne ganze Weile Mühe mit dem Code für den Master aus dem R N - Wissen zu TWI Slave mit avr-gcc. Der wird als getestet signiert - lief aber bei mir nicht (stand ja auch nix von "erfolgreich getestet"). Aber meist sind die Beispielcodes schon recht hilfreich und ohne die hätte ich viel mehr Mühe gehabt in C einzusteigen. Und in die ganze Controllertechnik.

Klebwax
01.10.2014, 10:12
@Klebwax

Tut mir leid, jetzt kommen eher Fragen ;)

Und was passiert tatsächlich mit folgenden beiden C-Codezeilen?

Erzeuge STOP mit sofort folgendem START in der TWI-Hardware:

TWCR = (1<<TWSTO)|(1<<TWINT) | (1<<TWEN)|(1<<TWIE);
TWCR = (1<<TWSTA) |(1<<TWINT) | (1<<TWEN)|(1<<TWIE);


Der Compiler macht daraus:
4d6: 85 e9 ldi r24, 0x95 ; 149
4d8: 80 93 bc 00 sts 0x00BC, r24 <<== Register für TWI-Hardware im mega168 und anderen AVRs.

4dc: 85 ea ldi r24, 0xA5 ; 165
4de: 80 93 bc 00 sts 0x00BC, r24


Hier liegen wir garantiert unter den 1.3 us und nun stellen sich mir folgenden Fragen:
Was macht die Hardware tatsächlich, wenn sie STOP'en und sofort wieder START'en soll?
Wird dann der Softwareteil beim START'en verzögert? Kann ich mir beim besten Willen nicht vorstellen.
OK, nach dem START wird es etwas dauern, bis im TWSR das Bit TWINT wieder gesetzt wird. Klar, das macht die Hardware.

Wenn ich die TWI-Hardware benutze, dann sollte ich natürlich nicht dein angegebenes "... und im Code darauf gewartet wird. ..." dahingehend wörtlich nehmen, dass ich nun eine Schleife im Code bauen soll, die zwischen STOP und START prüft, "... das sich der Status ändert ..."?

Kannst du deine Erklärung genauer formulieren, bzw. mir eine Erklärung geben, die ich verstehe?


Da die TWI-Hardware, nach einem durch die Software angegebenem STOP-Kommando an das Register TWCR, sich nicht mehr per Interrupt meldet, da nämlich kein Status-Wert für diese Aktion definiert ist im TWSR-Register (ODER habe ich diese Info noch nicht gefunden!), kann ich somit den nächsten START-Aufruf tatsächlich nur ohne Interrupt-Kontrolle erzeugen.
Also auch im Extremfall wie oben angegeben.


Im Handbuch finde ich folgendes: (mega48 bis 328 )

Im Kapitel "Overview of the TWI Module" Unterpunkt "Bus Interface Unit":
"When in Transmitter mode, the value of the received (N)ACK bit can be determined by the value in the TWSR."
Also hier noch keine Angabe, was im TWSR nach einem STOP steht.

Im Kapitel "Using the TWI" ist das Bild "Interfacing the Application to the TWI in a Typical Transmission" angegeben, welches die Aktionen von Software und Hardware sehr schön im (typischen) Zusammenspiel zeigt.
Hinter dem STOP wird hier nichts mehr angegeben.
Hast du hier eine Info, ob, und wie, der STOP-Zustand auf dem BUS im TWSR 'zu sehen' ist?

Und das dazu wohl wichtigste Kapitel:
"Overview of the TWI Module" Unterpunkt "Control Unit" ist zu finden, wann das Bit TWINT im TWSR gesetzt wird um anzuzeigen, das die Hardware fertig ist.
• After the TWI has transmitted a START/REPEATED START condition.
• After the TWI has transmitted SLA+R/W.
• After the TWI has transmitted an address byte.
• After the TWI has lost arbitration.
• After the TWI has been addressed by own slave address or general call.
• After the TWI has received a data byte.
• After a STOP or REPEATED START has been received while still addressed as a Slave.
• When a bus error has occurred due to an illegal START or STOP condition.
Dort ist KEINE ANGABE vorhanden, das ein STOP das Bit setzt.
Aus meiner Sicht ist somit nicht zu prüfen, wann das STOP 'fertig' ist. --> Alles eben beim ATmega und nicht in einem PIC.


Gruß aus dem frühen neuen Tag
Sternthaler


Nachtrag:
Vielleicht doch ne' Lösung vorhanden?

In "Transmission Modes" Unterkapitel "Miscellaneous States" steht natürlich noch etwas zu diesem "illegal START or STOP condition"
Table 22-6. Miscellaneous States

0x00 Bus error due to an illegal START or STOP condition
No TWDR action 0 1 1 X
Only the internal hardware is affected, no STOP condition is sent on the bus. In all cases, the bus is released and TWSTO is cleared.

Wenn ich deinen Text richtig verstehe, willst du mir sagen, zwischen Stop und Start muß eine Delay gemacht werden, ich liege falsch wenn locker sage "das kann man gleich hintereinander machen". Das mag bei den AVRs wohl sein, glaub ich aber eher nicht.
Bei mir geht sofort eine große Warnleuchte an, wenn ich ein Delay im Code sehe. Ist dieselbe Leuchte, die angeht, wenn ein neues Gerät von Panzerband zusammengehalten wird.

muss man eigentlich warten bis das STO wieder zurück gesetzt wurde (siehe Tabelle in DS).
Das ist so ein gängiges Verfahren, ich kann aber nicht sagen, ob das hier klappt. In diesem Fall ist noch etwas anders, der TWI-Controler wartet mit einem Start, bis der Bus idle ist, und am Ende eines Stops wird der Bus idle.

Was aber sicher nicht geht ist

TWCR = (1<<TWSTO)|(1<<TWINT) | (1<<TWEN)|(1<<TWIE);
TWCR = (1<<TWSTA) |(1<<TWINT) | (1<<TWEN)|(1<<TWIE);

da wird in der zweiten Zeile TWISTO auf Null gesetzt, und in der Zeit ist ein Stop nicht erzeugt. Möglicherweise hat der TWI-Controler noch garnicht mitbekommen, daß TWISTO gesetzt war, er läuft ja nicht mit dem selben Takt, wie die CPU, sondern mit dem TWI-Clock oder einem vielfachen davon.

MfG Klebwax

oberallgeier
01.10.2014, 17:04
Vielleicht plage ich mich noch mit dem alten 20 MHz-DSO an die Sache, aber ich glaube, da bekomme ich vertrauenswürdige Ergebnisse zum Start/Stop- bzw. Stop-Start-Problem nur deutlich unter meinem aktuellen Bustakt (explizit nenne ich 500 kHz, TWBR ist dann 12). Übrigens nennt das Datenblatt 8272D–AVR–05/12 aus Seite 340:

tBUF Bus free time between a STOP and fSCL = 100kHz 4.7 – µs
START condition fSCL > 100kHz 1.3 – µs

oberallgeier
02.10.2014, 10:48
@oberallgeier ... Du mußt den Returnwert von "i2c_start (Sadd + I2C_WRITE)" auswerten ...Wie oben geschrieben, das geschieht normalerweise. Und jetzt ist (wieder mal) Anlass darüber nachzudenken. Grundlage ist ordnungsgemäßes Programmieren von sicherem und schnellen Code für eine Singel-Master-I²C-Übertragung.

MUSS ich - und wenn ja, WARUM, beim I²C-Master zu Beginn einer I²C-Übertragung den Returnwert auswerten? ICH (also mein Controller) sendet. Als Master. Da hat doch niemand den Bus lahmzulegen und ich müsste die früheren Aktionen ordnungsgemäß abgeschlossen haben. Ausnahme ist Mister Murphy-Law, der schon mal, wie sein Kollege Wackel-Kontakt und andere, dreinpfuschen kann. Aber normalerweise sollte doch der I²C-Bus zu Beginn der Übertragung nicht besetzt sein - ist ja kein Multimaster.

Beim anschließenden Schreiben oder Lesen wird/kann das sinnvoll sein. Aber auch da überlege ich noch.

Nicht dass es mir auf die paar zusätzlichen Maschinenbefehle für das "if" ankäme. Ich finde nur überflüssige Befehle sind ebenso unschönes Programmieren wie falsche.

RoboHolIC
02.10.2014, 15:39
MUSS ich - und wenn ja, WARUM, beim I²C-Master zu Beginn einer I²C-Übertragung den Returnwert auswerten? ICH (also mein Controller) sendet.

Weil dir eventuell seit dem letzten PowerUp ein EMP-Puls auf dem I2C-Bus den Slave aus dem Tritt gebracht hat und dieser Slave vielleicht keinen Reseteingang besitzt, über den du ihn programmtechnisch wieder zur Ordnung rufen könntest.
Du kannst die Abfrage auch weglassen und stattdessen den Controller regelmässig vorbeugend eine I2C-Zauberformel sprechen lassen. Das ist irgendwas wie "mindestens 8x einen H-L-Wechsel auf der Clockleitung, während die Datenleitung H-Pegel hat". Könnte in der Philips-Doku (bzw. NXP) über den I2C-Bus stehen, müsste ich aber erst suchen.

Konkretes Beispiel gefällig?
Mein aktuelles Projekt mit I2C-Bus hängt nach dem Flashen häufig.
Ein einziger Controller als Master und ein einziger Gesprächspartner, der nur Slavemodus kann; alles ganz elementar und minimal.
Mein eigener, ursprünglicher Ansatz, den Bus in einen geordneten Zustand zu bringen war, zu Beginn jedes Datenaustauschs das I2C-Modul des Controllers zu deaktivieren, wieder zu aktivieren und dann einen I2C-Startzustand auf den Bus zu bringen.
Nette Idee, hilft aber leider nicht. Ein PowerUp dagegen hilft immer. Vermutlich liegt das Problem darin begraben, dass das Flashen den Controller aus einer laufenden I2C-Kommunikation rausreisst, der Slave mangels Reset-Eingang aber in undefiniertem Zustand verharrt. Ich habe das noch nicht weiterverfolgt, weil ich bei der aktuellen Anwendung die Windows-Option habe: Power OFF :MistPC , dann Power ON :Strahl.

Klebwax
02.10.2014, 20:36
MUSS ich - und wenn ja, WARUM, beim I²C-Master zu Beginn einer I²C-Übertragung den Returnwert auswerten? ICH (also mein Controller) sendet. Als Master. Da hat doch niemand den Bus lahmzulegen und ich müsste die früheren Aktionen ordnungsgemäß abgeschlossen haben.

Der Beginn einer I2C-Übertragung ist das Herstellen einer Start-Kondition. Nun ist es aber so, daß die Funktion i2c_start() wesentlich mehr macht. Sie ist schon der erste Teil der Übertragung, denn sie sie sendet auch noch das Adressbyte. Daß man das in einer Funktion zusammengefaßt ist, hat der Programmierer dieser Funktion zu verantworten. Ich würde das nicht so machen, sondern daraus zwei Funktionen bauen, auch einer der Gründe das selbst zu machen.

Und jetzt zu dieser Funktion selbst. Als erstes setzt sie das TWISTA-Bit (sorry, wenn ich die Bitnamen mal etwas falsch schreibe, ich müßt sonst jedesmal nachschlagen). der TWI-Controler macht dann laut Datenblatt folgendes: er wartet, bis der Bus idle wird, und setzt dann Start. Da macht sogar schon die Hardware einen Check. Geht dabei etwas schief, meldet die Funktion i2c_start() einen Fehler. Dann sendet die Funktion die Adresse, wird sie nicht mit ACK quitiert, meldet die Funktion den gleichen Fehler (schlecht). Antwortet ein Slave nicht auf seine Adresse mit ACK, muß die Transaktion mit einem Stop abgebrochen werden!. Also muß der Returnwert ausgewertet werden. Dies erstmal zum Speziellen.


Ausnahme ist Mister Murphy-Law, der schon mal, wie sein Kollege Wackel-Kontakt und andere, dreinpfuschen kann. Aber normalerweise sollte doch der I²C-Bus zu Beginn der Übertragung nicht besetzt sein - ist ja kein Multimaster
Oder das Device ist Busy, das EEPROM schreibt gerade, ein US-Sensor misst, dein selbstgemachter Slave ist noch nicht hochgelaufen oder, oder ...


Beim anschließenden Schreiben oder Lesen wird/kann das sinnvoll sein. Aber auch da überlege ich noch.

Nicht dass es mir auf die paar zusätzlichen Maschinenbefehle für das "if" ankäme. Ich finde nur überflüssige Befehle sind ebenso unschönes Programmieren wie falsche.

Es ist also eher umgekehrt, beim Verbindungsauf ist es Pflicht. Aber, wenn ich bei einer Datenübertragung nun schon eine minimale Rückmeldung bekomme, sollte man sie auch verwenden. Das ist jetzt das Inhaltliche.

Prinzipiell gilt aber:
Errorcodes sind dazu da, ausgewertet zu werden! Ein Code, der das nicht macht, fällt durch jedes Audit. Und ja, es führt dazu daß Fehlerbehandlung manchmal den größten Teil des Codes ausmacht. Und eigentlich sollte eine Library wie z.B. die I2C Library, die von fremden Programmieren verwendet wird, jeden Übergabeparameter auf gültige Werte überprüfe, und im Fall ungültiger Werte mit einem Fehlercode zurückkehren. Schau dir mal einige Funktionen der libc oder andere professionelle Libraries an, da ist die Beschreibung der Errorcodes häufig länger als die Funtionsbeschreibung.


Weil dir eventuell seit dem letzten PowerUp ein EMP-Puls auf dem I2C-Bus den Slave aus dem Tritt gebracht hat und dieser Slave vielleicht keinen Reseteingang besitzt, über den du ihn programmtechnisch wieder zur Ordnung rufen könntest.
Du kannst die Abfrage auch weglassen und stattdessen den Controller regelmässig vorbeugend eine I2C-Zauberformel sprechen lassen. Das ist irgendwas wie "mindestens 8x einen H-L-Wechsel auf der Clockleitung, während die Datenleitung H-Pegel hat".
Das ist eher "unschlau". Dazu müßte man erstmal die I2C Hardware abschalten. Und dann dauert die Sequenz natürlich viel länger, als den Controler auf idle prüfen zu lassen, und einfach den Status auszulesen.
Und es ist mitnichten eine "Zauberformel" sondern leicht zu erklären. Wenn der Slave mit Read adressiert worden ist, wartet er auf 8 Clocks um Datenbits zu liefern und ein neuntes mit dem ACK. Führt der Host diese Sequenz nicht vollständig durch (Absturz, Reset o.ä.), hängt der Slave (es gibt kein Timeout bei I2C). Das Toggeln von SCL führt das zuende und das High auf SDA ist ein NAK. Das bricht dann die Übertragung ab.


Konkretes Beispiel gefällig?
Mein aktuelles Projekt mit I2C-Bus hängt nach dem Flashen häufig.
Ein einziger Controller als Master und ein einziger Gesprächspartner, der nur Slavemodus kann; alles ganz elementar und minimal.
Mein eigener, ursprünglicher Ansatz, den Bus in einen geordneten Zustand zu bringen war, zu Beginn jedes Datenaustauschs das I2C-Modul des Controllers zu deaktivieren, wieder zu aktivieren und dann einen I2C-Startzustand auf den Bus zu bringen.
Nette Idee, hilft aber leider nicht. Ein PowerUp dagegen hilft immer.
Das passiert nicht nur beim Flashen sondern auch beim Debuggen. Single Step oder Breakpoint, Register anschauen .. noch mal von vorne, Peng!
Und genau hier kommt die Resetsequenz ins Spiel. Wenn beim Init nach Reset der Bus nicht idle ist, schick die 8 Clocks. Ich mache das, bevor ich überhaupt die I2C Hardware anschmeiße. SDA und SCL als Input und auf High abfragen. Wenn nicht, SCL als Output und toggeln. Dann wieder Input. Wenn sie dann nicht high sind, Errorcode ausgeben und Halt (embedded Bluescreen).

MfG Klebwax

Sternthaler
02.10.2014, 22:44
Hallo große Runde.



Hier
TWCR = (1<<TWSTO)|(1<<TWINT) | (1<<TWEN)|(1<<TWIE);
TWCR = (1<<TWSTA) |(1<<TWINT) | (1<<TWEN)|(1<<TWIE);

muss man eigentlich warten bis das STO wieder zurück esetzt wurde (siehe Tabelle in DS).

Seite 229 Atmega328 Doku: Table 21-2. Status codes for Master Transmitter Mode
bzw. Table 21-3 für Master Receive.

Man kann auch STO und STA gleichzeitig setzen. Siehe die genannten Tabellen.
Hallo schorsch_76,

ja, die beiden Tabellen kenne ich. (Gleiches gilt auch für die Slave-Seite.)
In den Master-Tabellen kann ich nicht erkennen, dass dort etwas dazu geschrieben wird, wie eben die Beendigung vom STOP anhand der Status-Codes aus dem TWSR-Register abzulesen ist. Ich hatte ja schon auf das 'Schaubild' "Interfacing the Application to the TWI in a Typical Transmission" hingewiesen bei dem eben auch keine Aktion nach dem STOP beschrieben ist.

Und auch TWSTO und TWSTA gleichzeitig zu setzen ist mir bekannt um eben ein REP-START zu erzeugen.
Das ist hier aber bewusst nicht angesprochen, da es ja um das Timing zwischen STOP und START geht.


Aber nun kommt dein 'eigentlich' in's Spiel, denn genau um diese Spitzfindigkeit geht es mir.
Wie kann ich erkennen, dass das TWSTO-Bit zurückgesetzt wurde? (Eher der Mut, dies zu fragen, den ich nun durch dein 'eigentlich' bekommen habe!)

Wenn man nun zum Kapitel "TWCR - TWI Control Register" (bei mir auf Seite 235 im Kapielt 22.9.2) geht, und (nochmal, nochmal, nochmal, AHHH, endlich hab ich's nun auch gefunden) dort zum Bit TWSTO kommt, dann ist klar geschrieben was zu tun ist:

• Bit 4 – TWSTO: TWI STOP Condition Bit
Writing the TWSTO bit to one in Master mode will generate a STOP condition on the 2-wire Serial Bus. When the
STOP condition is executed on the bus, the TWSTO bit is cleared automatically. In Slave mode, setting the
TWSTO bit can be used to recover from an error condition. This will not generate a STOP condition, but the TWI
returns to a well-defined unaddressed Slave mode and releases the SCL and SDA lines to a high impedance state.


Also bleibt tatsächlich folgendes zu tun:
TWCR = (1<<TWSTO)|(1<<TWINT) | (1<<TWEN)|(1<<TWIE);
while (TWCR & (1<<TWSTO) /* oder while ( ! (TWCR & (1<<TWSTO)) da müsste ich jetzt echt nachdenken ;) */
;
TWCR = (1<<TWSTA) |(1<<TWINT) | (1<<TWEN)|(1<<TWIE);

Und so ist auch das unbedingt verpönte 'Panzerband' von Klebwax endlich beseitigt und es ist kein wait()-Statement mit geratener Zeitangabe notwendig.

Gruß an alle, ich werde das mal nächste/übernächste Woche mit "Dauerfeuer" testen. Klar, mit und ohne while().

Gruß Sternthaler


@Klebwax
Tut mir Leid, wenn ich an dich nur Fragen habe.
Womit debug'st du?
Ich habe für meine xx4'er megas das JTAGICE3 und bin ganz zufrieden mit dem Teil. Steuernder Client ist natürlich das Atmel Studio 6.1
Unwissenheit besteht bei mir in der Nutzung von externen simulierten Signalen. Ansonsten komme ich bis in ISR-Funktionen und kann dort auch meine Fehler suchen.

- - - Aktualisiert - - -

By the way.
Ich habe gerade eine weitere Version vom 'Sammel'-Handbuch zu den ATmega48A bis ATmega328P 'entdeckt'.
Meine letzte Version war 8271E-AVR-07/2012 mit popeligen 555 PDF-Seiten
Es gibt mittlerweile 8271G-AVR-02/2013 aufgebläht auf 660 PDF-Seiten
Hier zu finden: www.atmel.com/images/atmel-8271-8-bit-avr-microcontroller-atmega48a-48pa-88a-88pa-168a-168pa-328-328p_datasheet.pdf (http://www.atmel.com/images/atmel-8271-8-bit-avr-microcontroller-atmega48a-48pa-88a-88pa-168a-168pa-328-328p_datasheet.pdf)

oberallgeier
02.10.2014, 23:37
Oh Leute, da komme ich vor lauter andächtigem Lesen garnicht nach mit dem Dazulernen! Bitte macht weiter, so kann man (kann ich) das I²C-Thema mal lernen ... und noch ein bisschen C dazu.


... Errorcodes sind dazu da, ausgewertet zu werden! ... Fehlerbehandlung manchmal den größten Teil des Codes ausmacht ...Wie war das noch? Es kann nur die Anwesenheit, nicht aber die Abwesenheit von Fehlern sicher festgestellt werden. Und das schlägt auch hier mit voller Wucht zu. (Und hat seine Konsequenzen). Ansonsten muss ich sagen - können tu ich I²C noch nicht besser, aber ich lerne grad sehr viel dazu. Und in so einer tiefschürfenden Runde macht das Lernen viel mehr Spass als das Datenblatt so vor sich hinzulesen.

Klasse macht ihrs!

Sternthaler, danke für den Link zum 8271G; mein letztes ist das 8271E. Nun ist das Dutzend Datenblätter der 48-168-328-Familie voll.

Klebwax
03.10.2014, 00:40
@Klebwax
Tut mir Leid, wenn ich an dich nur Fragen habe.
Womit debug'st du?

Ich schrub (oder heißt das schriebte ;) ), daß ich mit PICs rummache. Da gibt es den PICkit 3 und für kleineres Geld Clones. Damit kann man Programmieren und Debuggen. Fast so, wie man es auf dem PC gewöhnt ist. Je nach Chip einen oder mehrere HW-Breakpoints setzen, Variablen ansehen und ändern, Watchwindows etc. Bei den teureren Debuggern gibts wohl auch einen Trace, bin mir da aber nicht sicher. Das bedeutet zwar, daß man zwei Pins und den MRST dafür freihalten muß, das nehme ich aber in Kauf.

"externe simulierte Signale" verstehe ich nicht. Externe Signale sind da, nicht simuliert.

MfG Klebwax

schorsch_76
03.10.2014, 08:29
@Sternthaler:
Das mit dem TWI Stop hab ich so implementiert:



385 void
386 i2c::wait_for_twi_stopped() const
387 {
388 // make sure stop was already sent
389 while(TWCR & (1<<TWSTO));
390 }



Die sende/lese Routine stellt sicher, dass der Bus frei ist wenn ich was machen will.


449 void
450 i2c::write_to_slave(unsigned char address, const char* data, unsigned char len)
451 {
452 // when the i2c is busy, we need to wait
453 while (is_busy()) _delay_ms(1);
454
455 // make sure stop was already sent
456 wait_for_twi_stopped();
457
458 // set the mode and prevent others from interfering with the registers
459 // the irq routine will clear it again to idle
460 m_mode = master_transmit;




475 void
476 i2c::read_from_slave(unsigned char address, const char* data, unsigned char tx_len, unsigned char rx_len)
477 {
478 // when the i2c is busy, we need to wait
479 while (is_busy()) _delay_ms(1);
480
481 // make sure stop was already sent
482 wait_for_twi_stopped();
483
484 // set the mode and prevent others from interfering with the registers
485 // the irq routine will clear it again to idle
486 m_mode = master_receive;


is_budy ist nur eine Abfrage auf meie interne i2c Statemachine


529 bool
530 i2c::is_busy() const
531 {
532 return m_mode != idle;
533 }


Da das ganze in C++ und in einer Klasse implementiert ist (alle Hilfsfunktionen private), kann ich so sicherstellen, das wirklich die Reihenfolge eingehalten wird und das Interface anch aussen sehr simpel ist.



8 class i2c
9 {
10 public:
11 enum error_code_t
12 {
13 no_error = 0,
14
15 tx_no_answer_tw_start,
16 tx_no_answer_tw_mt_sla_ack,
17 tx_no_answer_tw_mt_data_ack,
18
19 rx_no_answer_tw_start,
20 rx_no_answer_sla_mt_or_sla_mr_ack,
21 rx_no_answer_tw_mt_data_ack,
22 rx_no_answer_tw_rep_start,
23 rx_no_answer_tw_mr_sla_ack,
24 rx_no_answer_tw_mr_data_ack,
25 };
26
27 i2c();
28 void init();
29
30 // handle the IRQs
31 void handle_irq();
32
33 // read/write something from/to a slave
34 void
35 write_to_slave(unsigned char address, const char* data, unsigned char len);
36 void
37 read_from_slave(unsigned char address, const char* data, unsigned char tx_len, unsigned char rx_len);
38
39 // status information
40 unsigned char rx_size() const;
41 unsigned char tx_size() const;
42 error_code_t is_error() const;
43 bool is_busy() const;
44
45 // get received data
46 i2c& operator >> (char& data);
47 private:


Bei interesse kann ich den Code gerne ganz hochladen. :)

oberallgeier
03.10.2014, 08:39
... Das mit dem TWI Stop hab ich so implementiert: ... Bei interesse kann ich den Code gerne ganz hochladen.Bitte hochladen! Hier ist ja ein informatives Stück I²C-Wissen aus den Tiefen der Busaktivitäten zusammengetragen, da wäre so eine Ergänzung sicher sinnvoll. Ich habe auch gerade eben den Moderator gebeten, den Titel um "I²C" zu ergänzen, damit der Thread bei allfälligen Suchen nach "I²C" nicht in den Tiefen des Servers verstaubt. Bei 25 Klicks pro Antwort sicher nicht unbegründet.

schorsch_76
03.10.2014, 09:39
29147
29148
29150

Seltsamerweise läst mich das Forum keine cpp Dateien hochladen. Es benennt auch meine i2c.c Datei nach i.c um...

Die Funktion handle_irq() wird aus dem IRQ Handler aufgerufen. Ganz unten in "i.c" bzw. i2c.cpp.

Mein Makefile sieht so aus:



################################################## ##########
# Projekteinstellungen
################################################## ##########

# MCU
MCU=atmega328p
F_CPU=16000000
LFUSE=0xde
HFUSE=0xd7
EFUSE=0x01

# Die Sources
SOURCES = util.cpp eeprom.cpp i2c.cpp \
menu.cpp main.cpp \
timer1.cpp scheduler.cpp \
rtc.cpp display.cpp adc.cpp relay.cpp storage.cpp r-to-t.cpp \
core.cpp

# temporary deactivated parser.cpp rs232.cpp
# Die Zieldateien
ELF = heat.elf
HEX = heat.hex
MAP = heat.map

################################################## ##########
# Compiler Flags. Muss vermutlich nicht angepasst werden
################################################## ##########
CFLAGS=-g -DF_CPU=$(F_CPU) -Wall -Os --std=c++11 -mcall-prologues
LFLAGS=-lprintf_flt
################################################## ##########
# Die Toolchain. Muss vermutlich nicht angepasst werden
# Die AVRDUDE Zeile muss an den Programmer und
# die Ziel CPU angepasst werden
################################################## ##########
AVRDUDE=avrdude -p m328p -c avrispmkii -P usb

CC=avr-g++
RM=rm -f
OBJCOPY=avr-objcopy
AVRSIZE=avr-size
OBJDUMP=avr-objdump

################################################## ##########
# Ab hier muss nichts mehr angepasst werden
################################################## ##########
# OBJ = Sources mit ersetzter Dateiendung
OBJ=$(SOURCES:%.cpp=%.o)

all: hex

hex: elf
$(OBJCOPY) -R .eeprom -O ihex $(ELF) $(HEX)

elf: $(OBJ)
$(CC) -mmcu=$(MCU) $(CFLAGS) $(LFLAGS) -o $(ELF) -Wl,-Map,$(MAP) -Wl,-u,vfprintf $(OBJ)

%.o: %.cpp
$(CC) -mmcu=$(MCU) $(CFLAGS) -c $<

.phony: flash
flash: hex
$(AVRDUDE) -e -U flash:w:$(HEX)

# Ziele ohne Abhängigkeiten
clean:
$(RM) $(OBJ) $(ELF) $(HEX) $(MAP)

readfuse:
$(AVRDUDE) lfuse:r:-:i -v

writefuse:
$(AVRDUDE) -U lfuse:w:$(LFUSE):m -U hfuse:w:$(HFUSE):m -U efuse:w:$(EFUSE):m

size: $(ELF)
$(AVRSIZE) -B $(ELF)

dump:
$(OBJDUMP) -d -S --demangle $(ELF)

read-eeprom:
$(AVRDUDE) -U eeprom:r:eeprom.bin:i


- - - Aktualisiert - - -

Benutzen tu ich das ganze so:



#include "project.h"
#include "i2c.h"

extern avr::project sys;
extern avr::i2c twi;

extern bool check_twi(unsigned char& state);

// -----------------------------------------------------
// relay
// -----------------------------------------------------
namespace relay {
unsigned char state = 0;
bool
handle()
{
switch (state)
{
case 0:
g_buffer[0] = ~sys.relay_state;
twi.write_to_slave(avr::relay_address, g_buffer, 1);
state++;
return false;
// check that twi transmitted successful
case 1:
return check_twi(state);
case 2:
state = 0;
return true;
// 100: Error
case 100:
// reenter. try again
state = 0;
break;
default:
break;
}

return true;
}
}; // namespace relay





// -----------------------------------------------------
// helper functions for statemachines
// -----------------------------------------------------

bool
check_twi(unsigned char& state)
{
if (twi.is_busy())
return false; // not yet done
else if (twi.is_error() != avr::i2c::no_error)
{
state = 100; // we have an error
}
else
{
// everything ok
state++;
}
return true;
}


- - - Aktualisiert - - -

Der Scheduler ruft das ganze dann so auf:



// -----------------------------------------------------
// scheduler
// -----------------------------------------------------
namespace scheduler {

unsigned char state_i2c = 0;
unsigned char state_spi = 0;
unsigned char state_uart = 0;

void
i2c()
{
bool next_step = true;
switch (state_i2c)
{
case 0:
next_step = rtc::handle();
break;
case 1:
next_step = storage::handle();
break;
case 2:
next_step = menu::handle();
break;
case 3:
next_step = relay::handle();
break;
case 4:
next_step = display::handle();
break;
}

if (next_step) state_i2c += 1;;
if (state_i2c >= 5) state_i2c = 0;
}
}; // namespace scheduler


Der Scheduler selbst hängt wieder im timer1 IRQ.



/*
Copyright (c) 2014 "Georg Gast <georg@schorsch-tech.de>"

This file is part of thermocontrol.

thermocontrol is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include <avr/interrupt.h>
#include "units.h"
#include "timer1.h"
#include "project.h"
#include "scheduler.h"

extern avr::project sys;

// we count 250 cycles until TOV1 (normal mode)
static const int start_tcnt = 65536-250;

namespace avr {
namespace timer1 {

void
init()
{
// start timer with 1 ms interval
// normal mode
TCCR1A = 0;
TCCR1B = (1 << CS11) | (1 << CS10); // prescale factor 64
TIMSK1 |= (1 << TOIE1);
TCNT1 = start_tcnt;
}

} // namespace timer1
} // namespace avr

// -----------------------------------------------------
// IRQ Handler are outside of our namespace
// -----------------------------------------------------
ISR(TIMER1_OVF_vect)
{
/*
Timer 1, Normal mode, runs up and
and then TOV1 irq when 0xffff to 0x0000
(Timer Counter Overflow 1) irq
*/

// to measure time, set let when we are in here
TCNT1 = start_tcnt;
sys.elapsed_time++;

scheduler::i2c();
scheduler::spi();
scheduler::uart();
}

RoboHolIC
03.10.2014, 23:31
Und es ist mitnichten eine "Zauberformel" sondern leicht zu erklären. Wenn der Slave mit Read adressiert worden ist, wartet er auf 8 Clocks um Datenbits zu liefern und ein neuntes mit dem ACK. Führt der Host diese Sequenz nicht vollständig durch (Absturz, Reset o.ä.), hängt der Slave (es gibt kein Timeout bei I2C). Das Toggeln von SCL führt das zuende und das High auf SDA ist ein NAK. Das bricht dann die Übertragung ab.


@klebwax
Wie unterscheidet man nach dem Controller-Reset einen Bus im Idle-Zustand (SCL und SDA, beide H) vom wartenden Slave, der gerade eine "1" übertragen will ?

Und was, wenn der Slave mit write adressiert wurde - macht das einen Unterschied?
Was, wenn die Adressierung mit bitgebangten Einsen erst eine gültige Adressierung bewirkt?
Was unterscheidet von Slave kommende Einsen von NAK (oder was es das ACK???) ?

Ich sehe einfach noch nicht die geschlossenen Lösung für den Bus-Reset: Müssen jetzt acht getaktete Einsen gesendet werden, oder maximal acht und nach jedem geprüft? Ich krieg das nicht zusammen. Ich habe mich halt bisher nicht in die I2C-Diagramme hineingedacht, weil es bei mir mit einem PIC16 auf der Ebene der Steuerbits und des IRQ-Flags schönwettermäßig recht bald funktionierte. Auch der neuerliche Erhellungsversuch mit RN-Wissen und dem relevanten Teil der Contollerdoku war erfolglos.

Kannst du das mit Fallunterscheidung mal wasserdicht darlegen - du scheinst I2C ja fundiert zu kennen. Das wäre super, auch im Sinne von oberallgeier's Anregung zum geballten I2C-Know How-Thread.

Gruß
RoboHolIC

Klebwax
04.10.2014, 09:36
@klebwax
Wie unterscheidet man nach dem Controller-Reset einen Bus im Idle-Zustand (SCL und SDA, beide H) vom wartenden Slave, der gerade eine "1" übertragen will ?
garnicht. Du bekommst dann später einen Fehler. Du müßtest das Problem dann bei der Reinitialisierung in deiner Fehlerbehandlung beseitigen. Am einfachsten ist, vor der Initialisierung generell die Clocks zu liefern.

Und was, wenn der Slave mit write adressiert wurde - macht das einen Unterschied?
Bei einem Write treibt nur der Master den Bus, nie der Slave. Der Slave kann also den Bus nicht blockieren.

Was, wenn die Adressierung mit bitgebangten Einsen erst eine gültige Adressierung bewirkt?
Der I2C Bus ist Open Collector, man kann keine 1 Senden. Und was hat "gültige Adressierung" mit "Bus ist nicht Idle" zu tun?

Was unterscheidet von Slave kommende Einsen von NAK (oder was es das ACK???) ?
Ein High auf dem Bus beim ersten bis achten Takt ist eine 1, beim neunten ist ein NAK.

Ich sehe einfach noch nicht die geschlossenen Lösung für den Bus-Reset: Müssen jetzt acht getaktete Einsen gesendet werden, oder maximal acht und nach jedem geprüft?
Wenn ich mich recht erinnere, steht "mindestens 8" in den I2C Spec. Und eigentlich kann man keine 1 Senden. Der I2C Bus ist Open Collector, man kann die Leitung nur loslassen. Wenn in der Spec 1 oder High steht, ist gemeint, daß der Master den Bus nicht treibt. Deswegen habe ich ja oben geschrieben: ich schalte vor der Initialisierung SDA auf Input (der Pin ist dann hochohmig und das ist dann equivalent zu einem nicht angesteuerten Transistor im Open Collector Triber), SCL auf Output, liefere die Clocks, schalte dann SCL auf Input und prüfe auf Bus Idle.

Ich krieg das nicht zusammen. Ich habe mich halt bisher nicht in die I2C-Diagramme hineingedacht
Das wäre aber zum Verständniss hilfreich. Ebenso die Lektüre der Spec, die NXP (vormals Philips) kostenfrei zur Verfügung stellt. Wenn man sich auf Single Master sowie Standard und Fast Mode beschränkt, gehört sie zu den einfachsten und kürzesten Protokollbeschreibungen, die ich kenne.

weil es bei mir mit einem PIC16 auf der Ebene der Steuerbits und des IRQ-Flags schönwettermäßig recht bald funktionierte
Da geht dann die Arbeit richtig los. Wie ich an anderer Stelle gesagt habe, kann der Schlechtwettercode viel länger sein.

- du scheinst I2C ja fundiert zu kennen.
Nun ja, in einem System mit zwei Open Colector Treiber können sich nicht viele Dinge verbergen, insbesondere wenn es so alt ist, daß die ersten Slaves mit Sicherheit reine Hardware waren, und die meisten heute noch sind.

MfG Klebwax

RoboHolIC
05.10.2014, 23:10
@ klebwax
Zunächst vielen Dank für deine ausführliche Antwort und deine Bemühung, mir Verständnis zu verschaffen. Leider blieb das große Achsoo noch aus, da hab ich noch weiter gesucht:

Zunächst habe ich ein Zitat aus der mir vorliegenden Philips-I2C-Spec.: ( Ver. 2.1, Jan. 2000 )

I2C-bus compatible devices must reset their bus logic
on receipt of a START or repeated START condition
such that they all anticipate the sending of a slave
address, even if these START conditions are not
positioned according to the proper format.
Das besagt ja, dass eine START Condition -auch zu Unzeit angewendet- eine Resetierung der Buslogik im konformen I2C-Slave bewirkt.

Über den Busreset habe ich dort mit dem Suchbegriff "reset" nichts Einschlägiges gefunden,
dafür aber bei Freescale folgendes:

The sequence for a Bus Reset is as follows:
• Disable the host MCU IIC controller
• Create a START condition
• Clock SCL for at least nine clocks
• Check for SDA high
• Create a STOP condition
• Enable the host MCU IIC controller
Dieser Vorschlag legt nahe, dass der Reset des Slaves auch beim STOP erfolgt.

Zusammengenommen verstehe ich einen möglichen zielgerichteten Resetvorgang daher so:
"Der Controller erzeugt bei nicht-getriebener (floating) SDA-Leitung wiederholt Clocksignale und prüft SDA, bis er den Zustand SDA = H vorfindet, der ihm erlaubt, ein START-Signal auf dem Bus durchzusetzen, das den Slave resettet, gefolgt von Dummy-Bit und abschliessendem STOP."
Möglicherweise genügt (s.o.) auch die STOP Condition alleine, sobald SDA = H.
Das Dummybit ist ggf. nötig, weil Die Abfolge START Condition - STOP Condition nicht erlaubt ist.

Trifft diese Formulierung den Punkt?

Ein paar Worde noch zu deiner ausführlichen Antwort:


Bei einem Write treibt nur der Master den Bus, nie der Slave. Der Slave kann also den Bus nicht blockieren.
OK, Denkfehler meinerseits.


Und was hat "gültige Adressierung" mit "Bus ist nicht Idle" zu tun?
Szenario: Controllerreset während der Chip- oder Registeradressierung. Ein Clearance-Versuch sieht SDA und SCL floating. Was tun? Busy oder nicht? Acht oder neun Clockpulse vervollständigen evtl. eine gültige Adressierung mit READ-Bit, die restlichen Clocks gehen für den Rest der Registeradresse drauf oder takten bereits die Antwort des Slave - genaues über den Zustand des Protokolls kann der Controller m.E. nicht aus SDA und SCL ablesen.
Beliebig viele weitere Clocks sind auch nicht statthaft, das kann kollidieren mit einer reservierten Sonderadresse (1111 1111).
Alles m.E. fragwürdig im Sinne der I2C-Spezifikation. Vielleicht liege ich auch voll daneben - das ist eben mein Rätselraten, jedenfalls nicht völlig abseits jeglicher Logik.


Nun ja, in einem System mit zwei Open Colector Treiber können sich nicht viele Dinge verbergen
Eines ist schon eines zuviel.

Gruß
RoboHolIC

oberallgeier
06.10.2014, 08:47
... vielen Dank für ... Antwort und deine Bemühung, mir Verständnis zu verschaffen. Leider blieb das große Achsoo noch aus ...RoboHolIC mir gehts genauso. Ich gehe diesen Thread durch und suche mir in meiner Datenblattsammlung die entsprechenden Passagen der Atmel-Application-docs, in Datenblättern, Wikis und so. Nachgucken, versuchen zu verstehen oder zu begreifen. Das bringt schon immer wieder so ein kleines "Aha!". Dann springt man - springe ich - auch mal in die THE I2C-BUS SPECIFICATION von Philips, VERSION 2.1 JANUARY 2000. Da gibts dann Dinge wie:

1.3 Version 2.1 - 2000
Version 2.1 of the I2C-bus specification includes the following minor modifications:
· After a repeated START condition in Hs-mode, it is possible to stretch the clock signal SCLH
(see Section 13.2 and Figs 22, 25 and 32).und anderes das ich nie wusste und "nie brauchte". Irgendwann ist dann der Kopf so voll, dass ich wieder im Wald stehe und von vorn anfangen muss. Dann leg ich das für den Tag beiseite. Aber manchmal komm ich doch ein Stück weiter.

Dumm, jede PWM hat AUCH ihre Register, ihre Wünsche und Vorschriften - und die hatte "man" ja schon früher einigermassen geschafft. Also wirds schon mal werden.

Klebwax
07.10.2014, 02:47
Irgendwie hab ich Probleme, ein Zitat im Zitat zu erzeugen, hier mein Versuch:

Zunächst habe ich ein Zitat aus der mir vorliegenden Philips-I2C-Spec.: ( Ver. 2.1, Jan. 2000 )

I2C-bus compatible devices must reset their bus logic
on receipt of a START or repeated START condition
such that they all anticipate the sending of a slave
address, even if these START conditions are not
positioned according to the proper format.
Das besagt ja, dass eine START Condition -auch zu Unzeit angewendet- eine Resetierung der Buslogik im konformen I2C-Slave bewirkt.

Über den Busreset habe ich dort mit dem Suchbegriff "reset" nichts Einschlägiges gefunden,
dafür aber bei Freescale folgendes:

The sequence for a Bus Reset is as follows:
• Disable the host MCU IIC controller
• Create a START condition
• Clock SCL for at least nine clocks
• Check for SDA high
• Create a STOP condition
• Enable the host MCU IIC controller
Dieser Vorschlag legt nahe, dass der Reset des Slaves auch beim STOP erfolgt.

Hier wird mehrfach das erzeugen einer Start/Stop condition genannt. Das Problem dabei ist, daß das nicht geht, wenn der Slave SDA auf low hält. Ansonsten klingt der Freescale Vorschlag bekannt. Ich bild mir aber ein, ähnliches auch bei NXP gesehen zu haben.

Nach meiner praktischen Erfahrung gibt es zwei Probleme, die sich mit den gängigen I2C Funktionen (start(), stop(), write(), readACK(),readNAK() ) nicht leicht auflösen lassen:


Lesen von einem Slave, der das Lesen von mehreren Bytes zulässt, ohne das letzte Byte mit einem NAK (sondern einem ACK) zu quitieren
Abbruch eines READ durch einen Reset/Debugger/Absturz/... im Master
In beiden Fällen gewinnt der Slave die Kontrolle über SDA und der Master kann, wenn SDA low ist, weder Start noch Stop erzeugen.

29167

Deswegen ist
• Create a START condition etwas fragwürdig. Bisher bin ich mit der Strategie: SDA freigeben, SCL 8 mal takten gut gefahren. Bevor es dann richtig losgeht, kommt pflichtgemäß sowieso ein Start.


Beliebig viele weitere Clocks sind auch nicht statthaft, das kann kollidieren mit einer reservierten Sonderadresse (1111 1111).
Reserviert heißt ja nicht, daß die Adresse nicht auf dem Bus sein darf. Es heißt ja nur, daß kein Slave sie verwenden darf. Und es wird sich daher auch keiner angesprochen fühlen, das passt doch. Es sei denn, du hast eines dieser "Reserved for future purposes" Devices am Bus. Es geht ja hier nicht um einen gültigen I2C Datentransfer sondern um das Auflösen einer Fehlersituation ohne Poweronreset.

Eines ist schon eines zuviel.
Das wirst du nie vermeiden können. Es gibt sicher einige tausend I2C Devices, und daher ebensoviele oder mehr Entwickler, die die Spec für sich interpretiert haben.


Ich gehe diesen Thread durch und suche mir in meiner Datenblattsammlung die entsprechenden Passagen der Atmel-Application-docs, in Datenblättern, Wikis und so. Nachgucken, versuchen zu verstehen oder zu begreifen. Das bringt schon immer wieder so ein kleines "Aha!". Dann springt man - springe ich - auch mal in die THE I2C-BUS SPECIFICATION von Philips, VERSION 2.1 JANUARY 2000. Da gibts dann Dinge wie:
[CODE]Code:
1.3 Version 2.1 - 2000
Version 2.1 of the I2C-bus specification includes the following minor modifications:
· After a repeated START condition in Hs-mode, it is possible to stretch the clock signal SCLH
(see Section 13.2 and Figs 22, 25 and 32).[/CODE)
@oberallgeier
Das wird aus zwei Gründen keine wirkliche Bedeutung haben: Hs-mode wird nicht vorkommen, und die Änderung ist "minor".

Aber zusätzlich zum Lesen hilft (nicht nur hier) manchmal: Machen und Messen. Ich hab mal I2C in SW gemacht, ging zuerst garnicht. Dann in fremder SW geschmökert und neu gemacht. Und dabei erkannt, worauf es ankommt. Wenn man dann die Spec noch mal liest, versteht man sie besser. Heute mit den billigen DSOs oder LAs und den eingebauten Protokolldekodern geht das noch leichter.

MfG Klebwax

RoboHolIC
16.10.2014, 00:03
Nachtrag:

Ich habe jetzt endlich meine eigene Variante des I2C-Busresets
(SCL-Flanken fallend + steigend per TriState-Register samt Wartezeit; das Ganze so oft, bis SDA high ist)
angewendet und sie funktioniert mit einem Drucksensor MPL3115A2 von Freescale als einzigem Slave am PIC16F886.
Der I2C-Bus ist auch nach zig Warmstarts bis jetzt nie blockiert geblieben.

Klebwax
16.10.2014, 08:50
Der I2C-Bus ist auch nach zig Warmstarts bis jetzt nie blockiert geblieben.

Gratulation !

MfG Klebwax