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.
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
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.
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
voidiic_init(void)
2
{
3
TWSR=0x00;
4
TWAR=0x00;
5
TWCR=0x05;
6
}
7
8
voidiic_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
elseif((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
elseif((TWSR&0xF8)==0x20)
33
{
34
lcd_gotoxy(0,1);
35
lcd_putsf("Adresse nicht gesendet");
36
delay_ms(2000);
37
}
38
elseif((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
elseif((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
voidiic_receive(void)
2
{
3
chariic_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
elseif((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
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
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...
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
..."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
>> 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.
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. ;-)