PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : m168 - als I²C-Receiver-Sklave, NUR 7Bit Daten möglich!?!



0tes_Gesetz
27.06.2006, 18:56
Hi, hab wieder einmal ein Problem mit meinem ATmega168..

verwendetes Zeugs:
- m168 @8Mhz
- AVRSTudio mit GCC
- Ponyprog
- JTAG ICE mk2
- nen Oszi
- ein VB2005 I²C Terminalprogramm
- I²C-Serial Adapter von robotikhardware.de


Es geht um den I²C Sklave im Receiver Modus.


Der Chip bekommt über den Bus das Adresspaket und 2 Datenpakete, mittels des hier im Shop erhältlichen I²C-Seriell Übersetzers und einem (von mir geschriebenen) VisualBasic2005 I²C Terminalprogramm.
(welches frei nach dem, dem I²C-Seriell Übersetzer beiligenden Programms (war für VB6 oder so) geschrieben wurde)

In folgendem Bild sieht man, was das Terminalprogramm für Signale auf SCL/SDA erzeugt, wenn man eine bestimmte Befehlsfolge startet.
Alles sauber, auch die Antworten vom m168 - meiner Meinung nach.
Er müsste eigentlich alles haben...

"W255;" bedeutet im übrigen - schreibe 255 an Sklave


http://www.is55.de/I2C%20Befehle%20und%20Antworten%20TempSteu.jpg


In den nachfolgenden Bildern hab ich an dieser Befehlsfolge jeweils nur beim 3. Byte den zu übertragenden Wert geändert - Fehler bleibt gleich.

Beachtet bitte die Zustände im Datenregister TWDR, welche ich rot markiert hab.
Es sieht so aus, als ob die empfangenen Daten (des letzen Bytes) nach links geschiftet werden - aber WIESO?



http://www.is55.de/I2C%20Befehle%20und%20Antworten2%20TempSteu.jpg

http://www.is55.de/I2C%20Befehle%20und%20Antworten3%20TempSteu.jpg



Und bei folgendem Bild hab ich die übertragenen Signale (nebst Antworten vom m168) nochmal mit reingepackt.



http://www.is55.de/I2C%20Befehle%20und%20Antworten4%20TempSteu.jpg



.. und zugeuter Letzt noch den Code-Schnipsel, mit der TWI-Interrupt-Sequenz (glaub nach der Anleitung im GCC-Tutorial oder wars ein dem GCC beiligendes Beispiel?..




ISR (TWI_vect) // I²C Interrupt - 8Bit Solltemperatur einlesen (ohne Fehlercheck!!!)
{
TWSTATUS++; // Schalter um eins erhöhen
switch (TWSTATUS)
{
case 1: // erster Durchlauf
{
if (TW_STATUS != TW_SR_SLA_ACK) { TWSTATUS = 0;} // wenn erkannt weiter, ansonsten Reset
}
case 2:
{
// I²C Temperaturvorgabe gültig oder Analoge Temperaturvorgabe nutzen?
if (TWDR > 0) { I2C_TEMPSOLL = 1;} // der I²C Temp-Vorgabewert gilt
else { I2C_TEMPSOLL = 0;} // Analoger Temp-Vorgabewert gilt, I²C gibt Steuerung ab
}
case 3:
{
if (I2C_TEMPSOLL == 1) // I²C Temp-Vorgabe ist möglich, also los..
{
temp_soll = TWDR; // Soll-Temperatur übernehmen
}
/* else // I²C gibt Temp NICHT mehr vor!!
{
__asm__ __volatile__ (" nop \n" :: ); // nichts tun...
}
*/ TWSTATUS = 0; // Reset des Schalters, Ende erreicht
}
}
// TWI wieder empfangsbereit machen (TWINT-Bit setzen)
// I²C: Sklave, TWI-Enable, TWI-Interrupt-Enable (Pin27 = SDA & Pin28 = SCL)
TWCR = (1<<TWINT)|(1<<TWEA)|(0<<TWSTA)|(0<<TWSTO)|(0<<TWWC)|(1<<TWEN)|(1<<TWIE);
}

int main (void)
{
// Pin11(D7)<-I_Anteil ON/OFF, Pin10(D6)<-D_Anteil ON/OFF
// Pin9(D5)->T0_PWM, Pin2(D4)<-T0_CLK, Pin1(D3)->ALIVE-Signal, Pin32(D2)<-INT0,
// Pin31(D1)->Heizen/Kühlen, Pin30(D0)<-Laserdiode oder nicht
DDRD = (0<<DDD7)|(0<<DDD6)|(1<<DDD5)|(0<<DDD4)|(1<<DDD3)|(0<<DDD2)|(1<<DDD1)|(1<<DDD0);

// Pin27(C4)/28(C5)<-SDA/SCL für I²C,
DDRC = (0<<DDC6)|(0<<DDC5)|(0<<DDC4)|(0<<DDC3)|(0<<DDC2)|(0<<DDC1)|(0<<DDC0);

// Pin7,8 & 16,17(B4-B7) für I²C Adresse, Pin15(B3)->T0_CLK, Pin12(B0)<-ICP1,
DDRB = (0<<DDB7)|(0<<DDB6)|(0<<DDB5)|(0<<DDB4)|(1<<DDB3)|(0<<DDB2)|(0<<DDB1)|(0<<DDB0);

...

// I²C: Sklave, TWI-Enable, TWI-Interrupt-Enable (Pin27 = SDA & Pin28 = SCL)
TWCR = (1<<TWINT)|(1<<TWEA)|(0<<TWSTA)|(0<<TWSTO)|(0<<TWWC)|(1<<TWEN)|(1<<TWIE);

// ************************************************** *********************************************
// nur in der folgenden Zeile Änderungen an der I²C-Adresse vornehmen!!!
TWAR |= (0<<TWA6)|(0<<TWA5)|(1<<TWA4)|(0<<TWGCE); // Setzen der ersten 3 (TWA) Bits der I²C-Adresse
TWAR = 254;
// in diesem Fall werden die Chips mit den Adressen: [xxx]0000 bis [xxx]1111 erreicht!!!
// die letzten 4 Bits werden durch die hardwareseitige Beschaltung der Pins 7,8, 16 & 17 bestimmt!
// ************************************************** *********************************************

...

}
return (0);
}


Also nochmal: Wieso bekomm ich per Interrupt aus dem Datenregister des TWI nur 7Bit - anstatt der 'üblichen' 8Bit?

Das LSB in TWDR ist immer Zero, außer nach dem einschalten des m168.
"LSB" heißt least significant Bit, "MSB" entsprechend most .. (für Neulinge)

Grüße und besten Dank
0tes_Gesetz

PS: wenn noch irgendwelche Angaben fehlen.. bitte schreibts, ich liefere gerne nach, wenn nur irgendjemand einen Rat hat.. :(

PicNick
27.06.2006, 19:25
Ich bin erschlagen *pfuuu*

Zwischenfrage von einem Doofen: du willst vom PC was an den Slave senden ? Ich seh da oben eine Slave ID von "127" . Das heißt aber doch vom Slave Lesen ?

0tes_Gesetz
27.06.2006, 19:58
Sorry, dass das gleich so viel war... wenn das so gehen würde wie gedacht hätt ich niemanden "genervt" ;)

zu deinen Fragen:

1) Ja, ich sende via Serieller Com Port -> I²C Umetzer über die SCL/SDA Leitungen Daten an den Atmel Prozessor (hier mega 168).
Allerdings derzeit nur mit dem PC, weil ich da "spielen" kann und sich auch mal schnell was ändern lässt ;)

2) Das I²C-Adressierungsformat lautet folgendermassen (8Bit):

- die ersten 7 Bit sind die Adresse des Chips, also von 0-127
- das letzte Bit (8tes) ist die Schreib(0)/Lese(1) Info

Damit wird 's127w'="Sklave mit ID 127, BEschreiben" zu: 1111.1110

- das ganze wirde gefolgt von einem SCL-Impuls des Masters, bei dem der Sklave die SDA-Leitung auf Masse ziehen muss -> ACKnowledged.

An den Oszi-Aufnahmen sieht man sehr schön, wie der Sklave gleich nach dem 8. SCL-Impuls die SDA-Leitung runterzieht und so lange GÜLTIG hält, wie der Master den 9. SCL-Impuls anliegen hat, damit der das ACK auch sicher empfangen kann (das Read Data Feld im Terminal-Prog zeigt dann ja auch das ACK jeweils an ;)).
Dann lässt der Sklave die SDA-Leitung wieder los und der Master macht entweder mit Daten weiter oder aber zum Schluss das STOP.

Ich hab mir die Zeitverläufe auch genauer angesehen (hatte Verdacht, dass der Sklave es nicht richtig macht und ungültige SDA-Werte verursacht ), aber dem ist nicht so.. ist halt nur recht fix der Kerl.
Daran sollte es jedenfalls nicht liegen, dass bei der Datenübertragung in TWDR nur die letzten 7Bit vom gewünschten vollen Byte stehen. :(

Irgendwie sieht das so aus, als ob mit dem 9. SCL-Impuls die Bits in TWDR noch eins weiter geschoben werden...

Muss morgen mal nen Test mit Nicht-ACK machen, vielleicht steht dann zufällig eine 1 an der letzten Stelle in TWDR.. hm.. :(

Grüße
0tes_Gesetz

linux_80
27.06.2006, 21:26
Hallo,

ich weiss jetzt nicht, ob ich hier alles gelesen hab,
aber das mit dem das TWDR ein Bit zu weit nach links geschoben ist, liegt meistens daran, das man erst nach dem Stopbit da rein schaut.
Das Stopbit kommt auch über den Bus, und wird wie alle anderen Daten ins TWDR geschoben, nur das danach schluss ist, und somit das Byte das zuletzt in TWDR stand dann eins nach links geschoben ist.
Deswegen muss das Byte aus TWDR immer schon ausgelesen werden, bevor man das ACK oder NACK zurückgibt, und es am Bus weitergeht mit der Übertragung.

0tes_Gesetz
27.06.2006, 21:38
Das mit dem STOP-Bit könnte ich ja gleich morgen ausprobieren, indem ich meine Befehlszeile ohne 'STOP;' ausführen lasse, Danke.

Muss ich dann wohl aber doch noch ein wenig tiefer in die Materie rein, da ich bis jetzt erst in den Interrupt komme, NACHDEM das ACK vom Sklaven gesendet wurde - automatisch... :(
Ich seh schon, das wird noch ne lustige Angelegenheit, bis ich das im Kasten hab.

Grüße
0tes_Gesetz

PicNick
28.06.2006, 07:02
@linux , leichter Widerspruch: Wenn du den HW-TWI aktivierst, dann mit ack-enable oder -disable. Zwischen Daten und ack kommst du da nicht hin, das macht er intern elektrisch. Oder umgekehrt, du kriegst gesendet+Ack empfangen oder gesendet+Nicht Ack
Ich mißtrau' aus dem Bauch eher dem PC Programm.

0tes-Gesetz
28.06.2006, 13:51
Hab leider mein PW nicht dabei.. deswegen mit anderem Login ;)


Ich weiß jetzt woran es liegt: an der Stop-Sequenz!

Hier das "Beweisfoto" :D


http://www.is55.de/I2C%20Befehle%20und%20Antworten5%20TempSteu.jpg


Es ist also so, wie linux_80 gesagt hat, die TWI-Unit des m168 interpretiert das STOP als den Beginn einer weiteren Übertragung und beginnt schon wieder mitzuschreiben und versaut dadurch die Daten im TWDR (Schieberegister)...

vermutliche Lösung: Abschalten der TWI-Unit des m168, gleich nachdem er das ACK gesendet hat.. weiß der Geier wie das gehen soll :(

Vielen Dank bis hierher - falls jemand ne Idee haben sollte, wie ich der TWI-Unit ins Getriebe greifen kann?!? Bin für alles offen ;)

Grüße
0tes-Gesetz

PS: ich meld mich wieder, wenn ich ne Lösung hab..

linux_80
28.06.2006, 19:16
Hallo miteinander,

schaut mal hier im Wiki vorbei -> https://www.roboternetz.de/wissen/index.php/TWI_Praxis
für jeden Fall ein kleines Beispiel.
Hatte selber schon ein paar Experimente gemacht, und rausgefunden das es an dem Stopbit liegt.

Und man kann natürlich auf das TWDR zugreifen bevor man das ACK/NACK abgibt, denn es muss erst das richtige Flag in TWCR gesetzt werden, damits überhaupt weiter geht, automatisch gehts auf jeden Fall nicht.

Das TWI-Modul deaktivieren ist schon mal die schlechteste Idee, weil die Ports des AVR wieder für normale IO umgeschaltet werden, wer weiss was da auf dem Bus los ist, kennt sich kein Slave mehr aus was läuft.

Hat da einer das DB nicht im Kopf :-)

Edit:
noch vergessen,
Guter Satz: ;-)


... das macht er intern elektrisch...
hab mir schon sowas gedacht, weil ich noch nie damit an der Takstelle war und meine AVRs trotzdem laufen :-)
:lol:

0tes_Gesetz
28.06.2006, 22:32
...schaut mal hier im Wiki vorbei -> https://www.roboternetz.de/wissen/index.php/TWI_Praxis
für jeden Fall ein kleines Beispiel.
Hatte selber schon ein paar Experimente gemacht, und rausgefunden das es an dem Stopbit liegt.

Es liegt nicht _nur_ am Stopbit bzw. ist das Stopbit im letzten Byte nur ein Symptom..
Ich bekomm das TWDR auch nicht zwischendurch mit dem richtigen Byte ausgelesen, sondern immer nur das nachfolgende, weil das ACK eben schon gesendet wurde und der Master (berechtigterweise) fröhlich weiter macht...


Und man kann natürlich auf das TWDR zugreifen bevor man das ACK/NACK abgibt, denn es muss erst das richtige Flag in TWCR gesetzt werden, damits überhaupt weiter geht, automatisch gehts auf jeden Fall nicht.

Was ebend so nicht richtig sein kann, denn sonst würde ja auch das Stop-Bit zum Schluss nicht stören ;)

Ich werd mal meine derzeitige Auffassung der Dinge geben:

Das, was der TWI machen soll, wenn Daten/Adressen kommen sagt man ihm mit dem TWCR.
In dem Basom-Beispiel hat man nichts anderes gemacht als ich auch, nur dass ich zusätzlich noch 'nen Interrupt aufrufen lasse, in dem ich dann eigentlich das TWDR auslesen wollte..

Bascom-Bsp:

Twcr = &B01000100 ' TWI aktivieren, ACK einschalten

ich:

TWCR = (1<<TWINT)|(1<<TWEA)|(0<<TWSTA)|(0<<TWSTO)|(0<<TWWC)|(1<<TWEN)|(1<<TWIE);
// Flag gelöscht (TWI gestartet), ACK senden anstatt No-ACK nach Empfang, Pins an TWI angeschlossen und TWI-Interruptvektor
*) ob ich nun TWINT beschreibe oder nicht, dürfte null Auswirkung haben..

Interessant wäre nun die Diskussion um das TWEA-Bit..
Das Datenblatt des m168 sagt dazu folgendes:

• Bit 6 – TWEA: TWI Enable Acknowledge Bit
The TWEA bit controls the generation of the acknowledge pulse. [highlight=orange:5ea0e0b877]If the TWEA bit is written to
one, the ACK pulse is generated on the TWI bus if the following conditions are met:[/highlight:5ea0e0b877]
1. The device’s own slave address has been received.
2. A general call has been received, while the TWGCE bit in the TWAR is set.
[highlight=orange:5ea0e0b877]3. A data byte has been received in Master Receiver or Slave Receiver mode.[/highlight:5ea0e0b877]
By writing the TWEA bit to zero, the device can be virtually disconnected from the 2-wire Serial
Bus temporarily. Address recognition can then be resumed by writing the TWEA bit to one
again.

Meiner Auffassung und meinem Erleben nach passiert folgendes (in vollkommener Übereinstimmung mit dem hervorgehobenen):
Die TWI Unit empfängt ein Byte und quitiert den Empfang dann während des 9ten SCL Impulses vom Master entweder mit einem ACK oder einem No-ACK und dann kommt schon wieder das nächste Bit, weil der Master ja zurecht annimmt, dass der Sklave weiter machen will. Dazwischen kann man nix machen, das läuft AUTOMATISCH so ab!
Was ich eigentlich bräuchte, wäre das der Sklave die SCL-Leitung gegen Masse zieht und somit dem Master mittteilt, dass der noch auf das ACK oder No-ACK warten muss...

Nur wie mache ich das, wenn ich doch mit dem TWCR Register nur sagen kann, dass er nach dem Empfang eines Bytes ACK oder No-ACK machen soll, aber nicht, dass er die SCL-Leitung auf LOW ziehen soll?
Ich sehe diese Möglichkeit nirgends!!


Ich hab gestern und heut schon 2 Chips gegrillt, weil mein Aufbau mit dem JTAG ICE mk2 und dem Seriell-I²C-Übersetzer irgend wie Mist bauen - zusammen mit dem Notebook und dem Netzteil..
Und das I²C-Terminal-Progg ist auch irgendwie instabil geworden seit neuestem.. :( :) :(

Morgen werd ich, um meine 2 verbliebenen m168 zu schonen, erstmal tiny2313 nehmen müssen und versuch dann mal nen reines I²C Prog zum testen, weil mein Code von oben bisher in nem etwas größeren Temperatursteuerungsproggie steckt..

Ihr hört von mir..

Grüße
0tes_Gesetz


PS: vielleicht sollte ich meine Daten lesen, sobald ich Case 1: erreicht hab, also die Adresse erkannt wurde, ACK gesendet wurde und schon die richtigen Daten im TWDR stehen, nur eben eine Runde zu früh (nach meiner Auffassung)..
Hm.. das macht auf eine ganz verrückte Art sogar irgendwie einen verdrehten Sinn.. oder?

Master sagt:
SklaveXXX, ich beschreibe dich!

SklaveXXX:
oh, ich bin gemeint, Geht klar Master!

Master schreibt Datenbyte:
Blablabla..bla..

SklaveXXX:
alle Daten in den TWDR und Flagge setzen wenn fertig, Bing!

Das Interrupt im SklavenXXX findet daraufhin in TWDR das erste Datenbyte und im Statusregister TWSR steht drin, dass sich der SklaveXXX erkannt hat und alles bestens ist (Auslöser des Interrupts war die Adresserkennung!!)

Nun kann der Interrupt in TWCR die Flagge zurücksetzen & löst damit ein "Geht klar Master!" als Meldung an den Master aus, so dass dieser das nächste Datenbyte raufschiebt...

Hm..
Hoffentlich geht das so..
Die Kausalität die dahintersteckt ist mir zwar irgendwie noch suspekt, aber eine andere Möglichkeit seh ich ehrlich gesagt nicht mehr..

Man liest die Daten aus, obwohl man sie eigentlich noch gar nicht erwartet (das im Hinblick auf TWSR, welches ja den "eigentlich" vorherigen Status enthält).. das macht Kopfschmerzen, hoffentlich gewöhnt man sich daran..

linux_80
28.06.2006, 23:20
Nur wie mache ich das, wenn ich doch mit dem TWCR Register nur sagen kann, dass er nach dem Empfang eines Bytes ACK oder No-ACK machen soll, aber nicht, dass er die SCL-Leitung auf LOW ziehen soll?
Ich sehe diese Möglichkeit nirgends!!


Lese mal ein paarmal durch, was bei der Beschreibung von TWINT steht, bei mir auf Seite 213 unten (beim M168).
Nur immer diesen Absatz lesen, und erst zum lesen aufhören wenns Klick gemacht hat !!

Evtl. mit meinem Beispiel, auf der von mir oben genannten Seite, vergleichen.
:-)

Zum schluss hört sich das schon gut an was Du in kursiv geschrieben hast, jetzt nur noch in Software umsetzen.

0tes-Gesetz
06.07.2006, 09:59
Problem gelöst :) (schon vor ner Woche, wollt nur noch "Lösung" posten)

Also..

Es lag an meinem AVR-C-Code.. ich hatte in dem InterruptCode
für das TWI-Modul eine Switch-Anweisung stehen, so dass ich meine I²C Übertragung Protokollgemäss abarbeiten konnte (Switch/Zählvariable).

Dummerweise hatte ich bei den einzelnen "Case x: { ... }" das "break;" vergessen :D
So wurde zwar immer der richtige Case zuerst angesprungen, aber dann auch alle nachfolgenden ausgeführt, da ich ja kein "break;" nach jedem Case drin hatte..

...

Desweiteren um der Verwirrung hilfesuchender Mitleser vorzubeugen - das TWI-Modul des AVR funktioniert im Slave-Receiver/Transmitter-Modus folgendermassen (und zwar tadellos ;) ):

Slave Receiver:

Master: sendet Slave Adress + W, ... wartet auf ACK/noACK

Slave: erkennt das und springt wenn enabled in die TWI-ISR, hält dabei SCL auf LOW -> Master muss warten!, in TWDR ist dann Sla+W drin!!
Nun kann man die TWI-Flag löschen und damit erst wird ACK/noACK gesendet (SCL losgelassen)...

Master: empfängt ACK/noACK und sendet Datenbyte, wartet auf ACK/noACK

Slave: empfängt 8Bit/1Byte un springt wieder in die TWI-ISR, SCL LOW (Master muss warten).. in TWDR ist nun das Byte..

usw...

im Slave Transmittermodus:

Master: sendet Slave Adress + R, ... wartet auf ACK/noACK

Slave: erkennt das und springt wenn enabled in die TWI-ISR, hält dabei SCL auf LOW -> Master muss warten!, in TWDR ist dann Sla+R drin!!
In TWDR kommt nun das zu sendende Byte!
Nun kann man die TWI-Flag löschen und damit erst wird ACK/noACK gesendet (SCL losgelassen)...

Master: empfängt empfängt Datenbyte und sendet ACK/noACK

Slave: empfängt ACK/noACK und springt wieder in die TWI-ISR, SCL LOW (Master muss warten).. in TWDR kommt nun das nächste Byte..

usw...


Die restlichen Feinheiten wie TWSR (Status Register vom TWI) und dessen Auswertung ist recht simpel...

Hier noch ein Screenshot wo der Ablauf Slave-Receiver mit dem Auftauchen des TWI-ISR anhand des blauen Signals (Pin High/ISR Start, Pin LOW/ISR Ende) schön zu sehen ist.

http://www.is55.de/I2C%20Befehle%20und%20Antworten6%20TempSteu.jpg

Grüße
0tes_Gesetz

PS: mein VB-Terminal Prog funzt übrigens super @ PicNick ;)