Forum: Mikrocontroller und Digitale Elektronik I2C Ablauf so korrekt?


von Tobias N. (silberkristall)


Lesenswert?

Ich habe mich jetzt mal in I2C eingelesen und frage mich ob ich so 
richtig denke.

Also, ich erteile dem Slave eine Adresse, nun habe ich das wie folgt 
verstanden:

Master:

I2C Start
I2C Schreibe Adresse + Write
I2C Schreibe byte
I2C Stop

Das ist ja die Übertragung.

Nun habe ich wie folgt verstanden das, sobald der Slave seine Adresse 
"verstanden" hat in einen ISR springt und dort dann die register, eins 
nach dem anderen einliest.

Ist das so richtig?

Demach könnte ich als "erstes" Byte eine länge der Daten senden und auf 
dem Slave dann eine schleife mit der Anzahl der länge laufen lassen, 
richtig?

Also auf dem Master:

Sende start
Sende adresse+write
Sende länge
Sende bytes in schleife
Sende checksumme
Sende stop

Und auf dem slave:
Empfange adresse
Springe ISR
Lese erstes byte (länge)
Führe schleife in anzahl der länge aus
Lese checksumme
Führe programm aus

Habe ich das so richtig verstanden? Dann muss ich nur noch gucken wie es 
vom slave zum master geht.

Danke euch schonmal.

von Klaus R. (klara)


Lesenswert?

Hallo Tobias,
auf Anhieb würde ich sagen, Du hast hier einen speziellen Fall 
beschrieben. Das Protokoll für einen PCF8574 ist etwas einfacher. Wenn 
Du Dich da praktisch einarbeiten möchtest, dann fang mit dem PCF8574 an. 
Der ist ziemlich simpel und unkritisch.
mfg klaus

von Tobias N. (silberkristall)


Lesenswert?

Ich möchte halt atmega <-> atmega

Ich prügel mich da schon durch g

Mir ging es nur darum ob der "ablauf" so richtig ist!?

von Klaus R. (klara)


Lesenswert?

Schau mal bei TI nach, MSP430. Da gibt es auch Beispiele für I2C 
Master-Client Komunikation. Aber im Prinzip müsste Dein Ansatz so OK 
sein. Du erstellst das Protokoll ja selber.
Ansonsten ähneln sich die Protokolle der I2C-Bausteine. Aber in jedem 
Datenblatt findest Du trotzdem dazu eine spezielle 
Protokollbeschreibung.
mfg Klaus

von Karl H. (kbuchegg)


Lesenswert?

Tobias N. schrieb:
> Ich möchte halt atmega <-> atmega
>
> Ich prügel mich da schon durch *g*
>
> Mir ging es nur darum ob der "ablauf" so richtig ist!?


Richtig ist das was du als richtig definierst.
Einem Telefon ist es auch egal, ob du reinpfeifst oder deine Nachricht 
in Form von Rülpsern übermittelst. Das Telefon stellt den 
Übertragunskanal bereit, wie deine Botschaften dann aussehen, ist dein 
Bier (so etwas nennt man ein Protokoll)

Also: wenn du das so machen willst, dann mach das so. Nur das man beim 
Client keine Schleife machen wird. Wenn per I2C was reinkommt, dann 
'benachrichtigt' die Hardware ds Programm mit einem Interrupt. In diesem 
Interrupt entscheidest du dann, was das gerade eingetroffene Byte für 
eine Bedeutung hat und was du damit machst. Ist es das erste Byte in der 
Übertragung, dann speicherst du es dir als Länge der noch zu 
empfangenden Bytes ab, ist es nicht das erste Byte, dann speicherst du 
das Byte als Datenbyte ab und erniedrigst diesen Längenzähler um 1. Wird 
so der Längenzähler zu 0, dann weisst du, dass damit die Nachricht 
komplett übertragen wurde.

In den vielen Fällen muss man in der µC Programmierung weg von dieser 
'Ich mach was in einer Schleife' Denkweise. Man muss hin zu einer 'Ich 
wache gerade auf, finde diese Inforamtion vor, was mache ich damit ehe 
ich wieder schlafen gehe' Denkweise.

von Tobias N. (silberkristall)


Lesenswert?

Ich dachte dann so:
1
unsigned char *buf;
2
3
ISR (TWI_vect)  
4
{
5
  len = read_Ack(); // Erstes Byte ist die Länge.
6
7
  for(i=0;i <len; i++)
8
   {
9
     buf[i] = read_Ack(); // Lese Byte für Byte ein und schreibe in Array
10
   }
11
  len = read_Nack(); // weiss nicht ob das wichtig ist
12
  return buf;
13
}

: Bearbeitet durch User
von Tobias N. (silberkristall)


Lesenswert?

Habe da noch eine Frage.

Wird die ISR mit jedem Byte was empfangen wird aufgerufen oder wird 
diese aufgerufen sobald die Slave Adresse erkannt wurde und arbeitet 
dann bis zum Stop?

von Rudolph (Gast)


Lesenswert?

TWI auf den AVRs ist quasi alles zu Fuss, jedes Byte einzeln.

von Banone (Gast)


Lesenswert?

------

Arduino:

i2c avr <--> avr "mal eben machen" ist schon einfacher mit Arduino:
http://arduino.cc/en/Tutorial/MasterReader

------

AVR:

AVR311 PDF:    http://www.atmel.com/images/doc2565.pdf
AVR311 Software: http://www.atmel.com/Images/AVR311.zip (geht wohl mit 
avr-gcc nicht?!) ---> dann halt diese modifizierte hier:
https://github.com/kelvinlawson/avr311-twi-slave-gcc

AVR TWI Master und Slave Funtionen in C
Beitrag "AVR TWI Master und Slave Funtionen in C"

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Karl Heinz schrieb:
> Nur das man beim
> Client keine Schleife machen wird. Wenn per I2C was reinkommt, dann
> 'benachrichtigt' die Hardware ds Programm mit einem Interrupt. In diesem
> Interrupt entscheidest du dann, was das gerade eingetroffene Byte für
> eine Bedeutung hat und was du damit machst. Ist es das erste Byte in der
> Übertragung, dann speicherst du es dir als Länge der noch zu
> empfangenden Bytes ab, ist es nicht das erste Byte, dann speicherst du
> das Byte als Datenbyte ab und erniedrigst diesen Längenzähler um 1. Wird
> so der Längenzähler zu 0, dann weisst du, dass damit die Nachricht
> komplett übertragen wurde.

Jein. ich mach das immer etwas anders: Ich definiere mir einen 
Empfangspuffer mit einer definierten Größe, sodass die maximal erwartete 
Nachricht noch Platz hat. Danach empfange ich Byte für Byte, kümmere 
mich an der Stelle aber überhaupt nicht darum welche Bedeutung die Bytes 
haben (ob das erste Byte eine Länge oder eine Registernummer oder die 
relative Mondfeuchtigkeit ist, ist an der Stelle irrelevant). Solange 
ich noch Platz im Buffer für das gerade empfangene Byte habe, speichere 
ich das Byte und antworte ich mit ACK, hab ichs nicht mehr Platz, mit 
NACK.

Danach stelle ich den Buffer und die Länge des empfangenen Pakets der 
darüberliegenden Schicht zur Verfügung. Was die damit macht, ist mir 
auch wieder egal.

Tobias N. schrieb:
> ISR (TWI_vect)
> {
>   len = read_Ack(); // Erstes Byte ist die Länge.
>
>   for(i=0;i <len; i++)
>    {
>      buf[i] = read_Ack(); // Lese Byte für Byte ein und schreibe in
> Array
>    }
>   len = read_Nack(); // weiss nicht ob das wichtig ist
>   return buf;
> }

Da denkst du grad ziemlich verkehrt: die TWI-ISR wird für jeden Vorgang 
am TWI aufgerufen (adressierung, byte empfangen usw) da drinnen kannst 
du kein read_Ack() machen.

von Tobias N. (silberkristall)


Lesenswert?

Dann muss ich über das isr alles auswerten? Also auch die adresse, lesen 
oder schreiben, und die einzelnen bytes.

Also:

start signal -> isr

Adresse -> isr

Jedes byte -> isr

Stop signal -> isr

So? Also springt er jedesmal in die isr!?

Danke euch schonmal.

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Tobias N. schrieb:
> So? Also springt er jedesmal in die isr!?

Ja, korrekt. Wobai man am Master noch die Wahl hat, ob man I2C mit oder 
ohne Interrupt realisiert (ohne ist gebräuchlicher), den Slave kann man 
sinnvollerweise nur mit ISR implementieren.

von Tobias N. (silberkristall)


Lesenswert?

Also in etwa so:
1
if (todo > 0)
2
{
3
4
if (todo == 1)
5
{
6
  addr = TWDR;
7
8
  if (addr == lese_addr)
9
  {
10
    todo = 2;
11
    sende ack;
12
  }
13
14
  if (addr == schreibe_addr)
15
  {
16
    todo = 3;
17
    sende ack;
18
  }
19
}
20
21
if (todo == 3)
22
{
23
  len = TWDR;
24
  array_len = 0;
25
  sende ack;
26
  todo = 4;
27
}
28
29
if (todo == 4)
30
  {
31
    data[array_len] = TWDR;
32
    array_len++;
33
    if(array_len == len)
34
      {
35
        sende nack;
36
      }
37
      else
38
      {
39
        sende ack;
40
      }
41
  }
42
43
if stopsignal => todo = 0;
44
45
}
46
else
47
{
48
  if startsignal -> setze todo = 1;
49
}

Nur mal so grob aber so in etwa sollte das ja dann sein, oder?

: Bearbeitet durch User
von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Tobias N. schrieb:
> Nur mal so grob aber so in etwa sollte das ja dann sein, oder?

nein, eigentlich überhaupt nicht :-(

Am AVR (zumindest auf den "großen" wie ATmega) gibt dir die TWI-Hardware 
schon die Rahmenbedingungen für die State Machine vor, gesteuert über 
TWSR (TWI Status Register). Dazu gibts eine wunderbare Beschreibung mit 
wunderbaren Tabellen im Datenblatt. Arbeite das mal durch.

Wenn schon dann sähe es etwa so aus:
1
ISR(TWI_vect)
2
{
3
    // code based on tables 21.4..6 from ATmega328 data sheet
4
5
    // cache some volatile variables
6
    uint8_t index, length;
7
8
    switch (TW_STATUS) {
9
10
    case 0x00:      // Bus error due to an illegal START or STOP condition
11
  TWCR = _BV(TWSTO) | _BV(TWINT);  // return to a well-defined unaddressed slave mode and release SCL and SDA
12
  I2C_Busy = 0;    // transmission aborted
13
  I2C_Error = 1;    // set error flag
14
  break;
15
16
    case 0x60:      // own SLA+W has been received; ACK has been returned
17
    case 0x68:      // own SLA+W has been received; ACK has been returned; Arbitration lost in SLA+R/W as Master
18
    case 0x70:      // general call address has been received; ACK has been returned
19
    case 0x78:      // general call address has been received; ACK has been returned; Arbitration lost in SLA+R/W as Master
20
  I2C_Busy = 1;    // transmission started
21
  I2C_RxIndex = 0;  // reset packet index
22
  TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT) | _BV(TWEA);  // send ACK on next byte
23
  break;
24
25
    case 0x80:      // previously addressed with own SLA+W; data has been received; ACK has been returned
26
    case 0x88:      // previously addressed with own SLA+W; data has been received; NAK has been returned
27
    case 0x90:      // previously addressed with general call; data has been received; ACK has been returned
28
    case 0x98:      // previously addressed with general call; data has been received; NAK has been returned
29
  index = I2C_RxIndex;  // local cache
30
  if (index < I2C_PACKETSIZE) {
31
      I2C_RxBuffer[index++] = TWDR;  // store received data
32
      I2C_RxIndex = index;  // write back cache
33
  }
34
  if (index < I2C_PACKETSIZE) {
35
      TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT) | _BV(TWEA);  // send ACK on next byte
36
  } else {
37
      TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT);  // no space left, send NAK on next byte
38
  }
39
  break;
40
41
    case 0xA8:      // own SLA+R has been received; ACK has been returned
42
    case 0xB0:      // own SLA+R has been received; ACK has been returned; Arbitration lost in SLA+R/W as Master
43
  I2C_Busy = 1;    // transmission started
44
  I2C_TxIndex = 0;  // reset packet index
45
  // fall through
46
47
    case 0xB8:      // data byte in TWDR has been transmitted; ACK has been received
48
  index = I2C_TxIndex;  // local cache
49
  length = I2C_TxLength;  // local cache
50
  if (index < length) {
51
      TWDR = I2C_TxBuffer[index++];  // transmit next byte
52
      I2C_TxIndex = index;  // write back cache
53
  } else {
54
      TWDR = 0;    // we have nothing more...
55
  }
56
  if (index < length) {
57
      TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT) | _BV(TWEA);  // expect ACK
58
  } else {
59
      TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT);  // last byte: expect NAK
60
  }
61
  break;
62
63
    case 0xA0:      // a STOP condition or repeated START condition has been received while still addressed as Slave
64
  I2C_Busy = 0;    // transmission ended
65
  if (I2C_RxIndex > 0) {
66
      TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT);  // disable TWI until data has been processed
67
  } else {
68
      TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT) | _BV(TWEA);  // re-enable TWI
69
  }
70
  break;
71
72
    case 0xC0:      // data byte in TWDR has been transmitted; NAK has been received
73
    case 0xC8:      // last data byte in TWDR has been transmitted (TWEA = 0); ACK has been received
74
  I2C_Busy = 0;    // transmission ended
75
  TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT) | _BV(TWEA);  // re-enable TWI
76
  break;
77
78
    case 0xF8:      // no relevant state information available; TWINT = 0
79
  break;
80
81
    default:      // all other states
82
  I2C_Busy = 0;    // transmission aborted
83
  I2C_Error = 1;    // something strange happened
84
  TWCR = _BV(TWSTO) | _BV(TWINT);  // return to a well-defined unaddressed slave mode and release SCL and SDA
85
  break;
86
    }
87
}

Wie du vielleicht erkennst: I2C ist keine Anfängerkost...

von Tobias N. (silberkristall)


Lesenswert?

Aahh.

In TWCR steht drinne ob Start oder adresse+R oder adresse+W also muss 
ich jedesmal nur das Register auslesen und auswerten :)

Wird ja immer einfacher die ganze Sache g

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Tobias N. schrieb:
> In TWCR steht drinne ob Start oder adresse+R oder adresse+W also muss
> ich jedesmal nur das Register auslesen und auswerten :)

nein.

Tobias N. schrieb:
> Wird ja immer einfacher die ganze Sache

nein.

Bitte - lies dir im Datenblatt das Kapitel zu TWI, und danach die AVR311 
"Using the TWI module as I2C slave". Sonst wird das nix.

nochmal: LIES DAS! Vorher kriegst du von mir keine Antwort mehr.

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.