Forum: Mikrocontroller und Digitale Elektronik I²C (TWI) beim AT90CAN128


von Xine (Gast)


Lesenswert?

Hallo zusammen,

beim Suchen in alten Beiträgen habe ich leider keine Antwort auf mein
Problem gefunden.

Ich arbeite mit zwei Platinen, die mit je einem AT90CAN128 von Atmel 
bestückt sind. Diese verbinde ich unter anderem über I²C und möchte 
lediglich einen einfaches Senden von Daten von einem µC zum anderen 
durchführen. (Die Übertragung werde ich stören und möchte dann am 
Empfänger schauen, was genau an Fehlern auftritt. Aus diesem Grund nur 
ein simples Senden).

Die Kommunikation zwischen den beiden Controllern funktioniert jedoch 
nicht. Weiß von euch jemand, wie das mit den ACKs läuft??? Sendet der 
Empfänger automatisch ein ACK, wenn er die Slave-Adresse und die Daten 
empfangen hat oder muss ich das mit Software realisieren?

In Codevision gibt es vorgefertigte Funktionen für I²C. Hat die schon 
einmal jemand verwendet?! Meine Programme habe ich nämlich selbst 
geschrieben. Vielleicht kann mit zu Verwendung der Funktionen aus der 
Headerdatei i2c.h ja sonst jemand einen Tipp geben? Da sieht man ja 
leider nicht, aus welchem Programmcode diese bestehen...

Vielen Dank,

eine verwirrte Anfängerin.

von Joerg X. (Gast)


Lesenswert?

Zu Codevision kann ich dir nicht helfen (aber evtl. haben die ihre 
Funktionen dokumentiert....)
Zu TWI: Der Slave sendet nur ein ACK wenn: - er eine Addresse hat oder 
auf "General Call" (0x00) reagieren soll; UND das TWEA-Bit gesetzt ist.
Außerdem MUSS auch der Slave das TWINT-Bit bedienen, sonst hält die 
Hardware im Slave die SCK-Leitung auf Low
mein Tipp: nimm dir ein stündchen Zeit und lies das Kapitel "Using the 
TWI" im Datenblatt.
Und schau mal nach den Ateml-App-notes, vl. (bin mir nicht ganz sicher) 
haben die auch was für TWI als slave

hth. Jörg

von Xine (Gast)


Lesenswert?

Hi Jörg,

danke für Deine Antwort.

Den Using the TWI-Teil im Datenblatt habe ich natürlich schon gelesen, 
aber es kann gut sein, dass gerade auf der Slave- (also Empfangs-)seite 
noch der eine oder andere Fehler zu finden ist.

Deine Hinweise werde ich gleich mal berücksichtigen.

Merci, Xine.

von Joerg X. (Gast)


Lesenswert?

Bei den Fehlern können wir (a.k.a das Forum, o.B.d.A ;-) ) dir nur 
helfen, wenn du den Code zeigst.

von Xine (Gast)


Lesenswert?

Okay, dann schicke ich hier mal meine C-Codes. Der erste ist für den 
Master-Transmitter. Die Bitrate wurde zuvor festgelegt auf 400 kBit/s.

1
void iic_init(void)
2
{
3
TWSR=0x00;
4
TWAR=0x00;
5
TWCR=0x05;
6
}
7
8
void iic_transmit(void)
9
{
10
 iic_init();
11
 TWCR=0xA4; // Start-Bedingung senden
12
 while ((TWCR>>7)==1);
13
 delay_us(100);
14
 if ((TWSR & 0xF8)==0x08)
15
        {
16
         TWDR=0xFE;     // Sende Slave-Adresse plus Write-Bit
17
         TWCR=0x84;
18
        }                  
19
 else if ((TWSR & 0xF8)!=0x08) 
20
        {
21
         lcd_gotoxy(0,0);
22
         lcd_putsf("START nicht gesendet"); 
23
         delay_ms(2000);
24
        } 
25
 while ((TWCR>>7)==1); 
26
 delay_us(100);
27
 if ((TWSR & 0xF8)==0x18) 
28
        {
29
         TWDR=0xF0;  // Daten ins Datenregister
30
         TWCR=0x84;
31
        }                
32
 else if ((TWSR & 0xF8)==0x20)
33
        {
34
         lcd_gotoxy(0,1);
35
         lcd_putsf("Adresse nicht gesendet");
36
         delay_ms(2000);
37
        }
38
 else if ((TWSR & 0xF8)==0x38)
39
        {
40
         lcd_gotoxy(0,1);
41
         lcd_putsf("Arbitration lost");
42
         delay_ms(2000);
43
        }
44
 while ((TWCR>>7)==1); 
45
 delay_us(100);
46
 if ((TWSR & 0xF8)==0x28) 
47
        {
48
         TWCR=0x94;
49
        }                
50
 else if ((TWSR & 0xF8)==0x30)
51
        {
52
         lcd_gotoxy(0,2);
53
         lcd_putsf("Daten nicht gesendet.");
54
         delay_ms(2000); 
55
        }
56
 else   {
57
         lcd_gotoxy(0,2);
58
         lcd_putsf("Alles doof.");
59
         delay_ms(2000);                
60
        }
61
}

Für den Slave-Receiver habe ich folgendes Programm:
init() beinhaltet dabei:
TWSR=0x00; TWAR=0xFF (Slave-Adresse 0x7F und General Call Recognition) 
TWCR=0x44 (ACK enabled, TWI enabled).
1
void iic_receive(void)
2
{
3
 char iic_data;
4
 iic_init();
5
 if ((TWSR & 0xF8)==0x60)
6
        {
7
         TWCR=0xC4;
8
        }               
9
 else   {
10
         lcd_putsf("Fehler bei Adresse");
11
         delay_ms(2000);
12
        }
13
while ((TWCR>>7)==1); 
14
delay_us(50);
15
 if ((TWSR & 0xF8)==0x80)
16
        {
17
         TWCR=0xC4;
18
        }  
19
 else if ((TWSR & 0xF8)==0x88) 
20
        {
21
         TWCR=0xC5;
22
        }              
23
 else   {
24
         lcd_putsf("Fehler bei Daten");
25
         delay_ms(2000);
26
        } 
27
 if ((TWSR & 0xF8)==0xA0)
28
        {
29
         lcd_putsf("STOP erhalten");
30
         delay_ms(2000);
31
        }         
32
}

Falls sich jemand trotz 30 Grad Hitze und Sonnenschein erbarmen würde, 
diesen Code durchzuschauen: dickes Danke! :)

Xine

von Joerg X. (Gast)


Lesenswert?

Bist du mal auf die gewagte Idee gekommen, in die Doku der 
I2C-Funktionen von Codevision zu schauen?
Die sind zwar nur für Master-Betrieb, aber die würden deinem Code doch 
guttun - obwohl da nicht explizit drinsteht, ob die TWI-hardware genutzt 
wird.
 Außerdem gibt's die Appnotes "AVR311: Using the TWI module as I2C 
slave", und AVR315 mit C-Code ( 
http://www.atmel.com/dyn/products/app_notes.asp?family_id=607 ).
zu deinem Code, für den Slave:
1
 
2
while ((TWCR>>7)==1);  //macht wohl nicht exakt, das was du erwartest
3
/* besser: warten solange das Bit _nicht_ gesetzt ist: */
4
while(!TWCR & (1<<TWINT));
- erst auf das TWINT (bit 7) warten, DANN TWSR lesen, andersrum ist 
witzlos.
- die Delays beim slave sind unnötig, dafür ist das Hardware TWI da.
(-der wizard-erzeugte Code ist besch---en zu lesen, hab nicht so recht 
lust die hex-Zahlen für die Register aufzu'dröseln')

hth. -Jörg

von Xine (Gast)


Lesenswert?

Sooo... Habe mich Deiner Tipps mal angenommen.

Was das TWINT-Flag betrifft, kriege ich langsam einen Knoten ins Hirn, 
auch wenn es logisch ist. ;) Fakt ist doch, dass nach einer 
erfolgreichen Übertragung das TWINT-Flag gesetzt (also null) wird. Also 
muss ich doch, um TWSR im Anschluss zu checken, warten, bis TWINT (das 
7. Bit im TWCR) gesetzt ist, also nicht mehr 1 ist. Was doch dem hier 
entspricht, oder nicht:
1
 while ((TWCR>>7)==1);
 Danach kann er doch schön das TWSR checken. Die folgende Schreibweise 
versteht CodeVision nämlich nicht:
1
 while(!TWCR & (1<<TWINT));


An welcher Stelle beim Slave habe ich Deiner Meinung nach das TWSR 
gelesen bevor ich auf TWINT gewartet habe? Bevor ich die Slaveadresse 
empfange, muss ich ja nicht aufs TWINT warten. Wenn er die richtige 
Adresse bekommen hat und ein Adress-ACK schicken will, dann wird beim 
Senden des ACKs das TWINT gelöscht und ich muss danach doch erst warten, 
dass es wieder gesetzt wird. Beim Daten-ACK das Gleiche.

Muss ich vorm Erhalt der STOP-Bedingung auch noch TWINT checken mit 
while(...)? Im Datasheet stehts nicht.

Mein Code funktioniert jetzt besser. Nur irgendwie doch noch nicht wie 
gewollt...

von Jörg X. (Gast)


Lesenswert?

Das TWINT wird gesetzt! (1), wenn auf dem I2C-Bus was passiert ist, 
worauf das Programm reagieren soll, für den slave wäre das:
- Adresse und R oder W empfangen: TWINT löschen und TWEA setzen, wenn 
der slave mehr als ein Byte emfangen soll
- Daten empfangen: TWDR lesen und weiter wie oben
- Daten gesendet und ACK emfangen: TWDR beschreiben, TWINT löschen
- Daten gesendet und NAK emfangen: TWINT löschen
- Stop-condition empfangen: TWINT löschen
 (Als Slave-Receiver(SR) gilt das TWEA immer für das nächste Byte das 
kommt, nicht für das, welches gerade empfangen wurde)
Im Datenblatt gibt's  4 Tabellen (master / slave ; transmitter / 
receiver) mit den Status codes und Vorschlägen, wie man reagieren kann.
1
 while(!TWCR & (1<<TWINT)); // da fehlen noch klammern, sorry:
2
//besser: "Warte hier solange Bit Nummer TWINT in TWCR nicht gesetzt ist"
3
while(!(TWCR & (1<<TWINT)))
4
    ;

Wenn bei irgendeinem AVR, der am I2c-Bus hängt, das TWINT gesetzt ist 
(und TWI aktiviert ist ;) ) blockiert dieser AVR den gesamten Bus!

hth. Jörg

von Xine (Gast)


Lesenswert?

..."writing a one to TWINT clears the flag."... Daraus verstehe ich 
doch, dass es mit einer "0" gesetzt ist, oder nicht? Also kann ich 
nichts machen, solange es Null ist.

Muss ich TWSR auslesen, während TWINT gesetzt ist?

An den Tabellen für Master-Transmitter und Slave-Receiver habe ich mich 
orientiert mit meinen Programmen.

Wenn die Programmierumgebung "TWINT" nicht kennt und ich nicht jedes Bit 
mit Namen benennen möchte, das ich benutze, dann kann ich doch zu
1
 while(!(TWCR & (1<<TWINT))); // äquivalent schreiben: 
2
while(!(TWCR & (1<<(TWCR>>7)))); // oder nicht?

Dank Dir, Xine

von Jörg X. (Gast)


Lesenswert?

>> Daraus verstehe ich doch, dass es mit einer "0" gesetzt
Das TWINT kann nur von der Hardware gesetzt werden, wenn du da eine 0 
hinschreibst passiert nichts. Schau mal hier in's Tutorial nach den 
Interrupt-Flags, die werden (fast) immer mit einer 1 gelöscht und können 
nicht vom Programm gesetzt werden.
1
 TWCR & (1<<(TWCR>>7))
Das ist ungünstig, weil der AVR nur 1 Bit  oder 4 Bit in einem 
(Asm-)Befehl schieben kann, und der Compiler kann das nicht 
wegoptimieren, (1<<TWINT) ist eine Konstante und taucht normalerweise 
gar nicht als Schiebe-Befehl im Programm auf.
Beim GCC sind die Namen der Bits (entsprechend denen im Datenblatt) in 
der jeweiligen io.h deklariert, es wäre merkwürdig wenn der 
Codevision-Compiler die nicht kennt.
1
// du kannst die Namen auch selber definieren:
2
#define TWINT 7
3
//etc.

hth. Jörg

von Xine (Gast)


Lesenswert?

Codevision kennt TWINT tatsächlich nicht. Die Registernamen (TWCR, ...) 
ja, aber die einzelnen Bits darin nicht. Aber mit dem #define ... 
funktionierts. Die Sendeseite erzählt nun, dass alles übertragen wird. 
Auf der Empfangsseite kommt wohl auch die richtige Adresse an. Nur die 
Daten nicht. Daran hängts nun noch und das wird heute wohl den Tag 
füllen.
Is schon nicht einfach, als kompletter Neuling da manchmal den Überblick 
zu behalten... Besten Dank deswegen,
die kleine Anfängerin. ;-)

von Xine L. (xine)


Lesenswert?

Läuft mittlerweile, thx!

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.