Forum: Mikrocontroller und Digitale Elektronik I2C mal wieder


von Rene H. (ballibou77)


Lesenswert?

Hallo,

ich müsste 5 AVRs miteinander verbinden.

Als Master dient ein ATMega128A mit 16Mhz externem Quarz

Als Slaves gibt es

1 ATMega8 mit 1 Mhz interner Quarz
2 ATMega32 mit 1Mhz interner Quarz

Hinzu kommt ein eDIPTFT70-ATP was 400khz I2C ebenfalls beherscht. In 
diesem ist auch ein ATMega128 verbaut.

Eine kommunikation zwischen dem Master und Display habe ich schon. Wenn 
das Display Daten hat legt es einen PIN auf LOW und man liest die Daten 
aus zerlegt den String und hat seine Daten :)

Wie aber funktioniert nun die Kommunikation? Das Display hat $DE (lesen) 
$Df (schreiben) als feste adressen.

Die adressierung ist nicht das problem, vielmehr wie man die 
kommunikation aufbaut.

Sagen wir Daten von Slave 1 müssen an Slave 2. Bisher habe ich immer nur 
daten vom Master an den Slave geschrieben, nie gelesen.

Was ich nicht verstehe, wie lese ich die Daten vom Slave?

Der Slave muss diese ja bereitstellen etc. und da verstehe ich es nicht 
mehr.

Muss der master erst an den slave schreiben (ich brauche die daten xyz) 
dann arbeitet der Slave diesen befehl ab, tut die Daten "irgendwo" hin 
und dann redet der Master nochmal mit dem Slave mit "gib jetzt her die 
daten"?

Wie sieht das dann in C aus?

Ich danke euch schonmal.

von Sascha (Gast)


Lesenswert?

Hallo,
also ersteinmal die Grundlagen vom I2C-BUS Philips bzw. NXP genau lesen, 
wenns geht auch noch verstehen.

Focus: Start-Bit ... Stop-Bit Re-Start und für Multimaster 
BUS-Arbitration.


Immer der, der von anderen lesen oder schreiben will wird sozusagen der 
Master.
Es ist ein multimaster system.
Alle Devices die nicht gerade Master sein wollen sind Slaves und die 
Adresse, also Device-Adresse wird auf der Hardware ins Register 
geschrieben.
Bei gleicher Adresse kommt dan ein Interrupt und das entsprechende Slave 
Device muss ganz schnell die Kommunikation mit einem ACK bestätigen.
Danach können die Daten vom Master gelesen werden.
Nun kann der Master über ein Stopbit die Kommunikation oder über ein 
NACK oder auch der Slave über ein NACK die Kommunikation beenden.
Beim Master unterscheidet sich das ob er gerade lesen oder schreiben 
tut.
Den wenn der Master ließt muss er ja das ACK oder NACK ausgeben.

So weit mal nur das ganz Grobe.
Weiteres Thema SCL erzwungene Clock Phase Verlängerung durch Slave.

Gruß Sascha

von Rene H. (ballibou77)


Lesenswert?

Hi,

danke schonmal.

Also eine I2C Kommunikation mit dem Display habe ich schon hinbekommen.

Auch habe ich es hinbekommen auf einem ATMega8 eine PWM mittels I2C zu 
steuern. Also der Master hat dem Slave gesagt PWM auf 100 und dann ging 
daa.

Jedoch war es so das ich mit dem Master an den Slave eine "zahl" 
geschrieben habe und das war dann eben der PWM Wert. Das klappte 
perfekt.

Nun ist aber der Fall das der Slave 3 PWMs hat. Dort muss ich dem Slave 
ja auch mitteilen welche PWM auf den Wert eingestellt wird der nun vom 
Master kommt.

Nun könnte ich folgendes machen.

1001 oder 1100 senden und diese Zahl auf dem Slave zerlegen 1001 in 1 
und 001 das wäre dann 1 für die PWM Nummer 001 der PWM Wert gleiches mit 
2090 oder 1050 oder 3100 usw.

Ok.

Jetzt kommt aber noch dazu das der Slave einen DS18S20 und einen 
Fotowiderstand auswertet. Die Daten stehen dann im Slave in einer 
Variable.

Wie nun komme ich vom Master da dran? Also wie sage ich dem Slave mit 
welche Daten der Master nun haben will?

Danke euch.

von Carl D. (jcw2)


Lesenswert?

Schau dir einfach mal das *Protokoll einer RTC oder eines kleinen 
EEProms an. Da werden Sklaven ausgelesen.

*im Datenblatt

von Oliver S. (oliverso)


Lesenswert?

TWI mit einem AVR ist für alles außer einfachem 
Master-Slave-Verbindungen zu anderen Bausteinen problematisch.

Nimm lieber SPI.

Oliver

von Karl H. (kbuchegg)


Lesenswert?

Rene H. schrieb:

> 1001 oder 1100 senden und diese Zahl auf dem Slave zerlegen 1001 in 1
> und 001 das wäre dann 1 für die PWM Nummer 001 der PWM Wert gleiches mit
> 2090 oder 1050 oder 3100 usw.

Wenn du mir jetzt noch erklärst, wie du mit 1 Byte eine 16 Bit Zahl 
übertragen willst, dann sind wir fast dort.

Ja. In dem Fall schreibst du eben 2 Bytes nacheinander zum Slave. Das 
erste Byte kann man als Kommando auffassen, das zweite als der zu 
schreibende Wert. Genausogut kann man eine Kommunikation nach dem Muster 
aufbauen: Erst mal schreibt der Master wovon oder wohin er 
lesen/schreiben will und dann kommt ein Reead oder Write hinten nach, 
mit dem dann der Wert übermittelt wird.
Genauso wie du es im täglichen Leben auch machst.

Wenn ein Slave nur einen Wert verwalten muss (1 Byte) dann ist alles 
klar. Denn dann gibt es keine Unterscheidungen. Mit dem Lesen ist alles 
klar was gelesen oder geschrieben werden soll. Gibt es mehrere Werte, 
dann muss halt mal irgendwer mitteilen welcher der 5 es sein soll.

von Rene H. (ballibou77)


Lesenswert?

Also für das Display habe ich folgenden Code:
1
unsigned char SendDisplayData(char *buf)
2
{
3
    unsigned int len = strlen(buf);
4
    unsigned char i, bcc, ret;
5
  int daten = 0;
6
    ret = i2c_start(DISPLAY_I2C_ADDR+I2C_WRITE);       // set device address and write mode
7
    if ( ret ) {
8
        /* failed to issue start condition, possibly no device found */
9
        i2c_stop();
10
        /* do some error reporting.. led or whatever */
11
    } else {
12
        /* issuing start condition ok, device accessible */
13
        i2c_write(0x11);                            // send DC1
14
        bcc = 0x11;
15
        i2c_write(len);
16
        bcc = bcc + len;
17
        for(i=0; i < len; i++) // Send buf
18
        {
19
            i2c_write(buf[i]);
20
            bcc = bcc + buf[i];
21
        }
22
        i2c_write(bcc); // Send checksum                      
23
        i2c_start(DISPLAY_I2C_ADDR+I2C_READ);       // set device address and read mode
24
        ret = i2c_readNak();                        // read one byte
25
        if (ret == DISPLAY_I2C_ACK)
26
        {
27
            daten = 1;
28
        } else {
29
            daten = 0;
30
        }
31
        i2c_stop();
32
    }
33
  return daten;
34
}
35
36
37
38
    unsigned char ReadDisplayData(void)
39
    {
40
        unsigned char bcc, ret, rettemp;
41
        unsigned char len = 2;
42
        ret = i2c_start(DISPLAY_I2C_ADDR+I2C_WRITE);      // set device address and write mode
43
        if ( ret ) {
44
            /* failed to issue start condition, possibly no device found */
45
            i2c_stop();
46
            /* do some error reporting.. led or whatever */
47
        } else {
48
            /* issuing start condition ok, device accessible */
49
            i2c_write(DISPLAY_I2C_DC2); // send DC2
50
            i2c_write(0x01);            // send 1
51
            i2c_write(0x53);            // send S
52
            bcc = DISPLAY_I2C_DC2 + 0x01 + 0x53;                // Set checksum
53
            i2c_write(bcc);            // Send checksum 
54
            bcc = 0;
55
            i2c_start(DISPLAY_I2C_ADDR+I2C_READ);      // set device address and read mode
56
            ret = i2c_readAck();                        // read ack byte
57
            if (ret == DISPLAY_I2C_ACK)
58
            {
59
                rettemp = i2c_readAck();
60
                bcc = bcc + rettemp;
61
                ret = i2c_readAck();            // read len
62
                bcc = bcc+ret;
63
                unsigned char *buf = malloc(ret * sizeof(char));
64
                len += ret;
65
                for (int i = 0; i < ret; i++)
66
                {
67
                    buf[i] = i2c_readAck(); // read data bytes
68
                    bcc = bcc + buf[i];
69
                }
70
                ret = i2c_readNak();    // read len
71
                if (ret != len) {
72
                    // Checksum not matching
73
                }
74
                return buf;
75
            } else {
76
                // No Ack
77
            }
78
            i2c_stop();
79
           
80
        }
81
        return 0;
82
    }
83
84
void DisplayReset(void)
85
  {
86
    PORTD &= ~(1<<PD3);
87
    PORTD &= ~(1<<PD3);
88
    PORTD &= ~(1<<PD3);
89
    PORTD |= (1<<PD3);
90
    
91
  }
92
93
unsigned char ReadDisplayBufferStatus(void)
94
  {
95
    int DisplayBufferStatus;
96
    if ( !(PIND & (1<<PIND2)) )
97
      {
98
        DisplayBufferStatus = 1;
99
      }
100
      else
101
      {
102
        DisplayBufferStatus = 0;
103
      }
104
      
105
    return DisplayBufferStatus;
106
  }

Und damit kann ich ganze Strings senden.

So in der Art aber auch zum lesen zwischen zwei ATMegas wäre schön.

von Rene H. (ballibou77)


Angehängte Dateien:

Lesenswert?

So,

ich habe mich dann mal am I2C versucht. Und was passiert? Nichts 
passiert.

Es soll eigentlich nur was von Master zum Slave geschrieben werden, also 
ein recht kurzer Code. Jedoch passiert nichts. Laut Oszi bleibt der I2C 
Bus auch dauerhaft auf High. Den Bus ziehe ich mit 4,7k PullUps und 5V 
auf High.

Ich habe die Codes mal angehangen.

ATMega8 und ATMega32 sind Slaves

ATMega128 ist der Master

Ich hoffe mir kann einer weiter helfen.

von Rene H. (ballibou77)


Lesenswert?

Achso, was vielleicht noch wichtig ist,

der ATMega8 sowie der ATMega32 laufen mit dem internen Oszilator mit 
1Mhz.

Der ATMega128 läuft mit einem 16Mhz externem Quarz.

von Rene H. (ballibou77)


Lesenswert?

Jetzt hab ich viel gelesen und keinen Schritt weiter -.- Also manchmal 
steht was von Pullups zu klein. Größere verwenden. Dann wieder, ne, 4,7k 
sind gut.

Dann wieder was von Kapazitiver BUS und je nach Anzahl der devices muss 
der Pullup größer / kleiner. -.-

Ich habe dann mal die PINS mit einem µC selber auf LOW gezogen und ich 
habe immernoch knapp 2V auf dem Bus. Ist der Pullup hier wirklich zu 
klein und ich muss diesen erhöhen?

von Dennis R. (dennis_r93)


Lesenswert?

Dein ic muss die Spannung gegen den pullup auf einen niedrigen 
spannungslevel ziehen.
Wenn bei einem pullup auf 5v mit 4.7 kohm dann noch 2V übrig bleiben 
wird vermutlich ein anderer busteilnehmer den bus auf high ziehen.
Prüfe ob alle anderen Teilnehmer den Port als tristate konfiguriert 
haben.

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.