Forum: Mikrocontroller und Digitale Elektronik twi: wo ist das datenbyte?


von scales (Gast)


Lesenswert?

Ich hab ein Problem mit dem Hardware-TWI bei einem Mega32.
ich will von einem I2C Slave Daten empfangen, das versuche ich wie 
folgt:


- START senden  //TWCR: (1<<TWINT) | (1<<TWSTA) | (1<<TWEN)
- warten bis START gesendet wurde (warten bis TWINT gesetzt ist)
- überprüfen ob erfolgreich (TWSR auslesen)

- Slave-Adresse mit Read Bit senden: 0b00111011 (Die Slave Adresse ist 
0011101)  //TWCR: (1<<TWINT) | (1<<TWEA) | (1<<TWEN)
- warten bis die Slave-Adresse gesendet wurde (warten bis TWINT gesetzt 
ist)
- überprüfen ob erfolgreich (TWSR auslesen)

aber wie gehts hier nun weiter?
am Ende kommt natürlich noch das STOP-Signal, aber wo ist das Datenbyte 
das ich empfangen will?
In TWDR ist immernoch die gesendete Slave-Adresse.
Muss ich noch irgendwas senden um letzendlich das Datenbyte zu bekommen?

von winne (Gast)


Lesenswert?

Immer brav weiter takten, dann findet sich das datenbayte im twdr ein.

Am einfachsten ein FF schicken und nachschauen was hernach in TWDR steht

wenn es kein WW ist ist's ne Antwort vom Slave

von scales (Gast)


Lesenswert?

Weis genau du mit weiter takten meinst weis ich nicht.
Aber ich hab versucht einfach danach nochmal ein Byte zu schicken, wenn 
ich das mach setzt der controller aber hinterher TWINT nicht mehr.

Das Datenblatt verwirrt mich auch an der Stelle:
"When SLA+R have been transmitted and an acknowledgement bit has been 
received, TWINT is set again and a number of status codes in TWSR are 
possible. Possible status codes in master mode are $38, $40, or $48. The 
appropriate action to be taken for each of these status codes is 
detailed in Table 75. Received data can be read from the TWDR Register 
when the TWINT Flag is set high by hardware. This scheme is repeated 
until the last byte has been received."

Da steht wenn ich die Slave-Adresse übertragen hab soll ich warten bis 
TWINT gesetzt wird, dann ist die Slave-Adresse übertragen.
Und dann soll ich direkt nochmal warten bis TWINT gesetzt wird, ohne was 
dazwischen zu machen, dann ist das Byte da.
Das macht doch keinen Sinn, das TWINT ist ja eh schon von der 
Slave-Adressen-Übertragung gesetzt.

von Thilo M. (Gast)


Lesenswert?

Weitertakten brauchst du nicht (ist beim SPI so), das sollte der 
'Gesprächspartner' übernehmen .
Im TWDR steht immer das zuletzt gesendete/Empfangene Byte. Also wenn was 
empfangen wurde (TWINT ausgelöst), dann sollte auch das Datenbyte vom 
Empfänger drinstehen.

von scales (Gast)


Lesenswert?

dass das Datenbyte am Ende in TWDR ist ist klar, aber ich denk eben, 
dass man nachdem man die Slave-Adresse gesendet hat und TWINT wieder 
gesetzt ist noch irgendwas machen muss, damit das Datenbyte in TWDR 
landet.
Zumindest müssste einem ja irgendein Mechanismus Bescheid sagen sobald 
es da gelandet ist.

von Thilo M. (Gast)


Lesenswert?

Guck' dir mal das Datenblatt Seite 185 'Data Transfer in Master Receiver 
Mode' an, da ist es recht gut beschrieben.

von scales (Gast)


Lesenswert?

grade das hab ich mir doch angeguckt, von da hab ich doch oben zitiert 
und gesagt welcher teil mir daran unklar ist.

von winne (Gast)


Angehängte Dateien:

Lesenswert?

Schau mal in den Anhang, ich hatte mir damals die Mühe gemacht das zu 
übersetzen, wenn ich's wiederfinde stelle ich Dir auch noch meine 
12C-Versuche rein war genau dein thema aber schon 3 Jahre her.

von John S. (linux_80)


Lesenswert?

Hi,
nachdem die Slaveadresse gesendet ist, und der neue Status abgefragt 
wurde, muss irgendwas geschehen, damits bei TWI weitergeht, also mit 
TWINT setzen, damit das Byte vom Slave empfangen werden kann.
Erst danach ist das Byte da.

Mit den Grafiken im DB sollte es doch recht gut hinzubekommen sein, auch 
was man alles machen kann je nach Status, ist in schönen Tabellen 
aufgeführt, welche Bits man setzen kann/soll damit die nächste 
TWI-Aktion gemacht wird.


Ein wenig mit Programmtext ist hier zu finden:
http://www.roboternetz.de/wissen/index.php/TWI_Praxis
Ist zwar in Basic, aber nix spezielles von Bascom verwendet, damit man 
das Prinzip erkennen können sollte.

von winne (Gast)


Lesenswert?


von scales (Gast)


Lesenswert?

den Thread kenn ich, ich mach das im Prinzip wie in dem Code da, aber es 
tut trotzdem nicht.

Im Anhang ist ein Diagramm aus dem Datenblatt des Slave-Bausteins 
(LIS3LV02DQ) den ich ansprechen will, wie die Übertragung aussehen soll.

das hier sollen die einzelnen Abkürzungen bedeuten:
ST - Start
SR - Repeated Start
SAK - Slave Acknowledge
NMAK - Not Master Acknowledge
SAD+W - Slave-Adresse mit Write-Bit
SAD+R - Slave-Adresse mit Write-Bit
SUB - Die Adresse des Registers dass man von dem Baustein auslesen 
möchte

bis zu der mit dem roten Pfeil markierten Stelle komme ich ohne 
Probleme, ich bekom auch immer ein NACK vom Slave zurück.

Ich hab dann also die Slave Adresse mit Read-Bit gesendet und hab ein 
NACK darauf zurückbekommen, an dem Punkt ist der rote Pfeil.
Anschließen versteh ich aber nicht was genau ich machen muss um die 
Daten zu lesen, die ja noch kommen sollen.
Ich hab versucht einfach 0x00 o.ä. auf den Bus zu schreiben und dann zu 
warten bis TWINT gesetzt ist, aber das passiert nie.

von scales (Gast)


Angehängte Dateien:

Lesenswert?

anhang vergessen...

von winne (Gast)


Lesenswert?

wenn du ein NACK zurück bekommst ist schon etwas schief gelaufen. Du 
mußt ein ACK zurück bekommen, erst dann wird dir auch das gewünschte 
Datum ausgegeben werden!

von scales (Gast)


Lesenswert?

achso, aber was sind mögliche Ursachen dafür, dass man ein NACK bekommt?
Der SCL Takt müsste eigentlich stimmen, ich benutz einen 16Mhz Quarz und 
hab in TWBR 72 stehen, sollten also 100Khz rauskommen.

von winne (Gast)


Lesenswert?

Leider sind meine Erstversuche mit I2C in Assembler auf tiny12 verloren 
gegangen (hd chrash).

aber ich schau noch mal nach meinen CVAVR Anfängen eventuell hab ich 
dort noch was dazu.

Stell doch mal deine Quellcodeschnipsel ein. Dann weiß man woran man 
doktorn muss. So ist alles ziemlich abgehoben von der Realität. Ebenso 
ein Plan vom betreffenden HW Ausschnitt ich denke da sind noch keine 
Geheimnisse drin ;-) oder?


Zu deiner Frage ein NACK bekommst du immer, wenn sich niemand findet der 
ein ACK generiert. Das erzeugt  der Sender selbst. Nur wenn sich ein 
Busteilnehmer angesprochen fühlt macht der durch zupfen an SDA aus dem 
NACK ein ACK.

von winne (Gast)


Angehängte Dateien:

Lesenswert?

so habe da noch 2 Dateien gefunden,
direkt zu verwenden unter CVAVR

zuerst eine twi.h

und im nächsten Beitrag eine twi.lib

welche ich auf einen Atmega128 verwende

In der twi.lib sind die zeitlichen Abläufe innerhalb der Funktionen 
erkennbar.

für die Nutzung der Routinen suche ich auch noch was raus.

von winne (Gast)


Angehängte Dateien:

Lesenswert?

hier die twi.lib

von winne (Gast)


Lesenswert?

hier noch ein paar Codeschnipsel welche meine Funktionen verwenden
viel Spaßß beim verstehen ;-)
1
void rexeeprom(unsigned char bhigh, unsigned char blow, unsigned char bufferlen)
2
{    
3
delay_ms(10);
4
TWI_STARTCONDITION(); 
5
TWI_SLA_W (exteeprom_SLA_W);
6
TWI_SENDDATA (bhigh);
7
TWI_SENDDATA (blow);
8
TWI_RSTARTCONDITION(); 
9
TWI_SLA_R (exteeprom_SLA_R);
10
for (n=0;n<bufferlen-1;n++)
11
putchar1(TWI_RECIVEDATA_ACK ());
12
putchar1(TWI_RECIVEDATA_NACK ());
13
TWI_STOPCONDITION();  
14
}
1
void timeread() // DS3231
2
TWI_STARTCONDITION(); 
3
TWI_SLA_W (clock_SLA_W);
4
TWI_SENDDATA (0x00);
5
TWI_RSTARTCONDITION(); 
6
TWI_SLA_R (clock_SLA_R);
7
s0=TWI_RECIVEDATA_ACK ();
8
m0=TWI_RECIVEDATA_ACK ();
9
h0=TWI_RECIVEDATA_NACK ();
10
TWI_STOPCONDITION();
1
void timeset() // DS3231
2
{
3
s0=0x0;
4
h0=(settime[0]&0x03)*0x10+(settime[1]&0x0f);
5
m0=(settime[3]&0x07)*0x10+(settime[4]&0x0f); 
6
TWI_STARTCONDITION(); 
7
TWI_SLA_W (clock_SLA_W);
8
TWI_SENDDATA (0x00);
9
TWI_SENDDATA (s0);
10
TWI_SENDDATA (m0);
11
TWI_SENDDATA (h0);
12
TWI_STOPCONDITION();  
13
UART1_Write_Strf("\n\r");
14
UART1_Write_Str (settime);   
15
UART1_Write_Strf("\n\r");
16
UART1_Write_Strf("time will be set.\n\r"); 
17
delay_ms(15);
18
}
1
void dateset()//DS3231  
2
{  
3
wd0=1;           
4
if (setdate[0]=='M' | setdate[0] =='m') if (setdate[1]=='o') wd0=1;
5
if (setdate[0]=='D' | setdate[0] =='d') if (setdate[1]=='i') wd0=2;
6
if (setdate[0]=='M' | setdate[0] =='m') if (setdate[1]=='i') wd0=3;
7
if (setdate[0]=='D' | setdate[0] =='d') if (setdate[1]=='o') wd0=4;
8
if (setdate[0]=='F' | setdate[0] =='f') if (setdate[1]=='r') wd0=5;
9
if (setdate[0]=='S' | setdate[0] =='s') if (setdate[1]=='a') wd0=6;
10
if (setdate[0]=='S' | setdate[0] =='s') if (setdate[1]=='o') wd0=7;
11
12
13
14
d0=(setdate[3]&0x03)*0x10+(setdate[4]&0x0f);
15
mo0=(setdate[6]&0x07)*0x10+(setdate[7]&0x0f);
16
y0=(setdate[11]&0x0f)*0x10+(setdate[12]&0x0f);
17
18
TWI_STARTCONDITION(); 
19
TWI_SLA_W (clock_SLA_W);
20
TWI_SENDDATA (0x03);
21
TWI_SENDDATA (wd0);
22
TWI_SENDDATA (d0);
23
TWI_SENDDATA (mo0);
24
TWI_SENDDATA (y0);
25
26
TWI_STOPCONDITION();  
27
UART1_Write_Str (setdate);   
28
UART1_Write_Strf("\n\r");
29
UART1_Write_Strf("date will be set.\n\r"); 
30
delay_ms(15);
31
}

von scales (Gast)


Lesenswert?

Danke für die Mühe mit der Library, aber ich bräuchte ja wenn dann den 
Quellcode.
Ich will das TWI selber zusammengebastelt bekommen und nicht den Code 
von jemand anders benutzen.

Aber langsam glaub ich auch mein Code ist richtig, ich hab mir ein 
Assembler-, ein Basic- und ein C-Beispiel angeguckt und ich machs 
eigentlich genau so.

In den Anhang hab ich mal meinen Schaltplan getan, vielleicht ist da was 
verkehrt.
(An den beiden ICs ist natürlich noch mehr dran, VCC hab ich nur mit 
eingezeichnet, damit man erkennt, dass das eine ein 5V und das andere 
ein 3,3V Gerät ist.)

Wenn sich da kein Fehler finden lässt, post ich mal meinen Code (den 
muss ich erst so umschreiben, dass man den einigermaßen lesen kann :) )

von scales (Gast)


Angehängte Dateien:

Lesenswert?

Anhang mal wieder vergessen...

von Thilo M. (Gast)


Lesenswert?

Das sieht ja alles extrem Hochohmig aus.
Bei zwei 5V-Teilnehmern nehme ich normalerweise 4k7 als Pullup, den 
Spannungsteiler 100k und 200k würde ich drastisch verkleinern und direkt 
vor den 3V3-Teilnehmer setzen. Die Pullups vor die AVR-Leitungen.

von spess53 (Gast)


Lesenswert?

Hi

Das kann nicht gehen. I2C-Ausgänge sind faktisch Open-Kollektor-Stufen. 
D.h. der H-Pegel wird durch die Pull-Up-Widerstände bestimmt. An deiner 
Stelle würde ich die Widerstände bis auf die Pull-Ups entfernen. Der 
Pegel auf den Leitungen ist dann maximal 3,3V. Der AVR sollte das noch 
als H-Pegel erkennen. Ist allerdings nicht ganz sauber.

MfG Spess

von scales (Gast)


Lesenswert?

Aber wenn an den AVR-Ausgängen High-Pegel anliegt und ich den 
Spannungsteiler komplett wegmache, dann liegen doch auch an dem 
LIS3LV02DQ 5V an.
Der Reset Zustand des AVRs ist ja das überall High anliegt, d.h. bei 
jedem Start und bei jedem Programmiervorgang würden an dem LIS3LV02DQ 5V 
anliegen.
Ich glaub nicht dass der das lange ausshält.


@ Thilo M.:
das Problem ist das in dem Datenblatt von dem LIS3LV02DQ nicht steht 
wieviel A ich aus den SCL und SDA ziehen darf, wenn ich jetzt einen zu 
kleinen Spannungsteiler nehm geht der ja überlastet.

von Thilo M. (Gast)


Lesenswert?

Je größer die Pullups desto kleiner die maximale Datenrate.
Du kannst auch mal probieren, die Datenrate extrem klein zu machen.
Aber ich denke, 1..5mA sollten die Ausgänge treiben können.

von spess53 (Gast)


Lesenswert?

Hi

Der AVR muss SDA/SCL auf Masse ziehen können. Und das kann deine 
Schaltung nicht. Im Reset-Zustand sind die AVR-Ports als Eingang 
geschaltet. Liefern also auch keine Spannung.

MfG Spess

von scales (Gast)


Lesenswert?

alles klar jetzt tuts, hab den spannungsteiler komplett weggemacht und 
die Ports im Programm auf Eingang gelassen.
Vielen Dank für die Hilfe!

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.