Forum: Mikrocontroller und Digitale Elektronik Probleme mit TWI (ATmega8)


von Chris K. (ati13)


Lesenswert?

Hallo,

ich bin gerade dabei, eine Datenübertragung zwischen zwei ATmega8 
mittels dem TWI zu realisieren. Leider stoße ich dabei auf ein paar 
Probleme, die wahrscheinlich auf mein Halbwissen zum Thema TWI 
zurückzuführen sind ;-)

Ich habe mich schon auf einigen Seiten im Netz belesen und unter anderem 
mit Hilfe des Datenblattes ein einfaches Programm geschrieben. Der 
Master soll dabei ein Byte an den Slave senden. Mehr ist es bisher 
nicht.
Guckst du hier:

Master-Transmitter:
1
#define F_CPU 3686400
2
#include <avr\io.h>
3
#include <avr\interrupt.h>
4
5
#define Slave_Adresse_W 0x02  // Slave-Adresse inklusive WRITE-Bit (0)
6
7
char daten = 0x04;
8
9
//----Hauptprogramm--------------------------------------------------------
10
int main (void)
11
{
12
    DDRD = 0x1C; // LED's zum Auslesen von Fehlern (4-rot,8-gelb,16-grün)
13
    
14
    TWBR = 10; // Bitrate von 10 ergibt ein SCL von ca. 100kHz 
15
    sei(); // globale Interrupts freigeben
16
   
17
    void twi_Start();
18
    void twi_Adresse();
19
    void twi_Daten();
20
    void twi_Stop();
21
         
22
    while (true)      // Mainloop
23
    {    
24
     PORTD = 0x08;  // gelbe LED               
25
      twi_Start();
26
      PORTD = 0x08;  // gelbe LED  
27
      twi_Adresse();
28
      PORTD = 0x08;  // gelbe LED  
29
      twi_Daten();
30
      PORTD = 0x08;  // gelbe LED  
31
     twi_Stop();       
32
    }  
33
    return 0;
34
}
35
//-------------------------------------------------------------------------
36
37
//----Funktionen-----------------------------------------------------------
38
void twi_Start()
39
{
40
    TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN); // Start senden
41
    while (!(TWCR & (1<<TWINT))); // warten
42
    if ((TWSR & 0xF8) != 0x08){  // Überprüfen, ob Senden erfolgreich 
43
      PORTD = 0x04; // rote LED (Fehlerbehandlung)
44
        waitMs(500);
45
    } else {
46
        PORTD = 0x10; // grüne LED
47
        waitMs(500);  
48
    }
49
    PORTD = 0x00;
50
    waitMs(250);
51
}
52
      
53
void twi_Adresse()
54
{
55
    TWDR = Slave_Adresse_W; // Slave-Adresse in Adressregister übertragen
56
    TWCR = (1<<TWINT) | (1<<TWEN); // Slave-Adresse und WRITE-Bit senden 
57
    while (!(TWCR & (1<<TWINT))); // warten  
58
    if ((TWSR & 0xF8) != 0x18){  // Überprüfen, ob Senden erfolgreich 
59
      PORTD = 0x04; // rote LED (Fehlerbehandlung)
60
        waitMs(500); 
61
    } else {
62
        PORTD = 0x10; // grüne LED
63
        waitMs(500);
64
    }
65
    PORTD = 0x00;
66
    waitMs(250); 
67
}
68
      
69
void twi_Daten()
70
{
71
    TWDR = daten; // zu sendendes Datenbyte in Datenregister uebertragen 
72
    TWCR = (1<<TWINT) | (1<<TWEN); // Datenbyte senden 
73
    while (!(TWCR & (1<<TWINT))); // warten 
74
    if ((TWSR & 0xF8) != 0x28){   // Überprüfen, ob Senden erfolgreich 
75
        PORTD = 0x04; // rote LED (Fehlerbehandlung)
76
        waitMs(500); 
77
    } else {
78
        PORTD = 0x10; // grüne LED
79
        waitMs(500);
80
    }
81
    PORTD = 0x00;
82
    waitMs(250); 
83
}
84
   
85
void twi_Stop()
86
{
87
    TWCR = (1<<TWINT) | (1<<TWSTO) | (1<<TWEN); 
88
    while (!(TWCR & (1<<TWINT))); // warten
89
    PORTD = 0x1C; // alle drei LED's 
90
    waitMs(500);
91
    PORTD = 0x00;
92
    waitMs(250); 
93
}
94
//-----------------------------------------------------------------------------

und Slave-Receiver:
1
#define F_CPU 1000000
2
#include <avr\io.h>
3
#include <avr\interrupt.h>
4
5
#define Slave_Adresse_GC 0x02 // Slave-Adresse ist 1, GCEN ist 0 (uninteressant)
6
7
char daten = 0;
8
9
//----TWI-Interrupt-Routine----------------------------------------------------
10
ISR(TWI_vect){
11
   if (TWSR == 0x60) // Adresse erkannt und ACK zurückgesendet
12
      PORTD = 0x04;
13
14
   if (TWSR == 0x88) // wenn Daten erfolgreich empfangen
15
      PORTD = 0x10;
16
      //daten = TWDR; // empfangene Daten sichern
17
}
18
//-----------------------------------------------------------------------------
19
20
//----Hauptprogramm------------------------------------------------------------
21
int main (void)
22
{
23
   DDRD = 0x0C;  // LEDs zur Anzeige der Status
24
25
   TWAR = Slave_Adresse_GC; // Slave-Adresse ins Adress-Register schreiben
26
   TWCR = (1<<TWEA) | (1<<TWEN) | (1<<TWIE);
27
   sei();
28
   
29
   while (true)
30
   {
31
      //PORTD = daten; // empfangene Daten auf PORTD ausgeben
32
   }
33
return 0;
34
}
35
//-----------------------------------------------------------------------------

Der Master soll anhand der 3 LEDs den aktuellen Status anzeigen, der 
Slave soll anzeigen, ob die letzte Aktion erfolgreich war.
Bisherige Beobachtungen: Master blinkt 2 mal hintereinander grün auf, 
bleibt dann auf gelb stehen (zwischen dem Senden der Adresse und den 
Daten).
Beim Slave leuchtet nach dem zweiten grünen Blinken am Master die LED an 
PIN D2 auf (signalisiert, dass der Slave angesprochen und daraufhin ein 
ACK gesendet wurde).

Wieso hängt der Master fest? Ist der Code für den Slave zum Empfangen 
ausreichend oder hab ich da was falsch verstanden (sehr wahrscheinlich)?

: Verschoben durch User
von Chris K. (ati13)


Lesenswert?

Ist es richtig, dass man auf Empfängerseite das Acknowledge immer 
manuell (also per Software) zurücksenden muss? Das ist mir noch 
unklar...

von Stefan F. (Gast)


Lesenswert?

> Ist es richtig ...
Ja

Bevor du Master und Slave ohne Erfahrung programmierst, solltest du 
eines der beiden Geräte durch eins ersetzen, das bekanntermaßen 
funktioniert.

Nimm zum Beispiel einen PCF8574 als SLave.

Und benutze den seriellen Port, um Debug Meldungen an ein Terminal 
auszugeben, so wie hier beschrieben: 
http://stefanfrings.de/avr_hello_world/index.html

Es könnte auch sehr hilfreich sein, den I2C Bus mit einem 
Speicheroszilloskop oder Logikanalyzer zu beobachten.

Hast du pull-up Widerstände eingebaut? Welchen Pegel haben die beiden 
Leitungen in dem Moment, wo es hängt? Und welchen Pegel haben sie nach 
Reset der beiden Controller VOR der ersten Übertragung?

von Chris K. (ati13)


Lesenswert?

> Bevor du Master und Slave ohne Erfahrung programmierst, solltest du
> eines der beiden Geräte durch eins ersetzen, das bekanntermaßen
> funktioniert.

Genau das wäre sehr hilfreich! Ich hatte bisher keine Idee, wie man 
beispielsweise nur den Master (Sender) testen könnte. Wenn man Sender 
und Empfänger gleichzeitig aufbaut und testen will, ist die Fehlersuche 
natürlich enorm schwer.

Der PCF8574 gibt im Empfangsmodus ein Byte direkt an den I/O-Ports aus, 
richtig?

Die Sache mit den Debug-Meldungen muss ich mir nochmal genauer 
anschauen. Ich bin mir nicht sicher, ob ich das mit meiner 
"Programmierausrüstung" hinbekomme...

Einen Logikanalysator habe ich leider ebensowenig zur Verfügung, wie ein 
Oszilloskop.

Die SCL- und die SDA-Leitungen hab ich mit pull-ups versehen (4,7kohm). 
Lassen sich die Pegel nur mit einem Oszilloskop betrachten oder gibt's 
da auch andere Wege?

von Karl H. (kbuchegg)


Lesenswert?

Chris K. schrieb:
>> Bevor du Master und Slave ohne Erfahrung programmierst, solltest du
>> eines der beiden Geräte durch eins ersetzen, das bekanntermaßen
>> funktioniert.
>
> Genau das wäre sehr hilfreich! Ich hatte bisher keine Idee, wie man
> beispielsweise nur den Master (Sender) testen könnte. Wenn man Sender
> und Empfänger gleichzeitig aufbaut und testen will, ist die Fehlersuche
> natürlich enorm schwer.

Du könntest deinen Sender zb erst mal mit einer als funktionierend 
bekannten Implementierung ersetzen. Wie zb die Fleury TWI (I2C) Routinen

http://homepage.hispeed.ch/peterfleury/avr-software.html

von Stefan F. (Gast)


Lesenswert?

> Der PCF8574 gibt im Empfangsmodus ein Byte direkt an den
> I/O-Ports aus, richtig?

Ja. Du sendest einfach die Adresse und dann ein Byte.
Die Ausgänge sind (fast) Open-Collector, also wenn du da LED's 
anschließst, dann zwischen VCC und den I/O Pins.

> Lassen sich die Pegel nur mit einem Oszilloskop betrachten oder
> gibt's da auch andere Wege?

Der andere Weg wäre eben ein Logikanalyzer. Dafür sind diese Geräte 
gemacht.

Da man I2C beliebig langsam takten kann, kann man die Signale 
prinzipiell auch mit zwei LED's, Stift und Papier aufzeichnen.

ich weiss jetzt allerdings nicht auswendig, wie hoch die niedrigste 
Frequenz ist, die so ein Atmega8 mit seinem TWI Taktgeber unterstützt.

Schonmal erwägt, für den Anfang Soft-i2C zu benutzen? Da könntest du 
beliebig _delay_ms() Pausen einbauen.

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.