Forum: Mikrocontroller und Digitale Elektronik Problem mit I2C bei atmega328p (arduino uno)


von Philipp H. (philipp_ho)


Angehängte Dateien:

Lesenswert?

Hallo zusammen,

ich versuche gerade eine I2C Verbindung zwischen 2 atmega328p (arduino 
unos) aufzubauen. Es soll zunächst nur vom Master die Übertragung 
initiiert und ein paar bytes übertragen werden, beim Slave soll nach 
Erkennen der eigenen Adresse die entsprechende Interrupt Routine 
ausgeführt werden. Fehlercodes werden noch nicht wirklich 
berücksichtigt, es geht erstmal nur um die Grundfunktion. Der Slave 
springt tatsächlich auch in die IR, meldet aber für das Statusregister 
TWSR 0x00, also "Bus error due to an illegal
START or STOP condition". Der Master kommt bis zum Aussenden der 
Adresse, aber nicht weiter. Auch der Logic Analyzer zeigt seltsames.

Hier die Funktionen zur Initialisierung, Senden und Interrupt Routine:
1
void I2cInit(uint8_t sysClock, uint16_t bitRate)
2
{
3
  //Set Slave Adress
4
  TWAR = ((I2C_ADDRESS<<1) & 0xFE);
5
  //Set bit rate
6
  TWBR = (((sysClock*1000)/(2*bitRate))-8);
7
  //Enable ACK (active bus connection)
8
  TWCR |= (1<<TWEA);
9
  //Enable TWI
10
  TWCR |= (1<<TWEN);
11
  //Enable TWI Interrupt
12
  TWCR |= (1<<TWIE);
13
}
14
15
bool i2cTxMaster (uint8_t *firstelementofData,uint8_t lengthOfData,uint8_t address)
16
{
17
        //Result variable 
18
  bool result = true;
19
  //Loop Variable
20
  uint8_t i=0;
21
        //Disable TWI Interrupt
22
  TWCR &= !(1<<TWIE);
23
  //Transmit START condition
24
  TWCR |= (1<<TWINT)|(1<<TWSTA)|(1<<TWEN);
25
  //Wait for TWINT flag
26
  while(!(TWCR & (1<<TWINT)));
27
  //Terminate if status != 0x08
28
  if ((TWSR & 0xF8) != 0x08)
29
  {
30
    result = false;
31
    return result;
32
  }
33
  //Clear START bit
34
  TWCR &= !(1<<TWSTA);
35
  //Load SLA+W to data register
36
  TWDR = ((address<<1) & 0xFE);
37
  //Transmit Address Byte
38
  TWCR |= (1<<TWINT)|(1<<TWEN);
39
  //Wait for TWINT flag
40
  while(!(TWCR & (1<<TWINT)));
41
  //Terminate if status != 0x18
42
  if ((TWSR & 0xF8) != 0x18)
43
  {
44
    result = false;
45
    return result;
46
  }
47
  //Transmit data bytes
48
  for (i=0; i<lengthOfData; i++)
49
  {
50
    //Load current byte to data register
51
    TWDR = *(firstelementofData+i);
52
    //Send data byte
53
    TWCR |= (1<<TWINT)|(1<<TWEN);
54
    //Wait for TWINT flag
55
    while(!(TWCR & (1<<TWINT)));
56
    //Terminate if status != 0x28
57
    if ((TWSR & 0xF8) != 0x28)
58
    {
59
      result = false;
60
      return result;
61
    }
62
  }
63
  //Transmit STOP Condition
64
  TWCR |= (1<<TWINT)|(1<<TWSTO)|(1<<TWEN);
65
  //Enable TWI Interrupt
66
  TWCR |= (1<<TWIE);
67
  return result;
68
}
69
70
ISR(TWI_vect)
71
{
72
  //Disable TWI interrupt
73
  TWCR &= !(1<<TWIE); 
74
  if (((TWSR & 0xF8) == 0x60) || ((TWSR & 0xF8) == 0x70))
75
  {
76
    payloadPendingUpLength=0;
77
    //Clear TWINT flag
78
    TWCR |= (1<<TWINT)|(1<<TWEN);
79
    //Wait for TWINT flag
80
    while(!(TWCR & (1<<TWINT)));
81
    while ((TWSR & 0xF8) != 0xA0)
82
    {
83
      payloadPendingUpContent[payloadPendingUpLength] = TWDR;
84
      payloadPendingUpLength++;
85
      //Clear TWINT flag
86
      TWCR |= (1<<TWINT)|(1<<TWEN);
87
      //Wait for TWINT flag
88
      while(!(TWCR & (1<<TWINT)));
89
    }
90
  }
91
  payloadPendingUp = true;
92
  //Enable TWI interrupt
93
  TWCR |= (1<<TWIE);
94
}

Wie im Datenblatt vorgegeben sind SDA und SCL über Pull-up Widerstände 
mit Vcc verbunden (4k7). Die SCL Frequenz wird mit 50k initialisiert.

Ist da ein dummer Fehler im Code oder könnte es eher an der Hardware 
liegen?

von Spess53 (Gast)


Lesenswert?

Hi

>Die SCL Frequenz wird mit 50k initialisiert.

Was soll das heißen?

MfG Spess

von Philipp H. (philipp_ho)


Angehängte Dateien:

Lesenswert?

Im Prinzip die bitrate, also 50kbit, wollte mich nur an die Formulierung 
im Datenblatt halten. Zum testen soll es erstmal eine niedrige bitrate 
sein.

von Karl M. (Gast)


Lesenswert?

Hallo, zum Glück wird die Frequenz in Hertz angegeben und was deine 
Kilobit angeht passt das überhaupt nicht zusammen!

von Wolfgang (Gast)


Lesenswert?

Philipp H. schrieb:
> Auch der Logic Analyzer zeigt seltsames
Welche Abtastrate verwendest du?

> Logic_2.png

Aus deinem Bild kann man nichts erkennen, weil die Auflösung für 50kHz 
Clock absolut unzureichend ist. Hänge die logicdata-Datei mit an.

von Oliver S. (oliverso)


Lesenswert?

Alle grünen Punkte im logic-Diagramm sind Startbedingungen. Da würde ich 
als Slave auch die Arbeit verweigern.

Oliver

von Oliver S. (oliverso)


Lesenswert?

Philipp H. schrieb:
> while(!(TWCR & (1<<TWINT)));
> while ((TWSR & 0xF8) != 0xA0)
>...

Das ist in der TWI ISR allerdings völliger Murks.
Da fehlt es doch noch etwas am grundsätzlichen Verständnis, was 
Interrupt ist, und was er macht.

Die grundlegende Ablauf bei TWI Master mit Interrupt ist:

Start senden

ISR: Adresse Write senden, reti
ISR: wenn ACK, Datenbyte senden, wenn Nack, Stop senden, reti
...
ISR: Stop senden, reti

Oliver

: Bearbeitet durch User
von Philipp H. (philipp_ho)


Lesenswert?

Oliver S. schrieb:
> Die grundlegende Ablauf bei TWI Master mit Interrupt ist:
>
> Start senden
>
> ISR: Adresse Write senden, reti
> ISR: wenn ACK, Datenbyte senden, wenn Nack, Stop senden, reti
> ...
> ISR: Stop senden, reti
>
> Oliver

Die ISR ist nur für den Slave aktiviert, der Master soll nur mit der 
entsprechenden Funktion senden. Aber klar, ich sehe was du meinst, in 
der ISR sollte nur der Status Code bei einem Interrupt ausgelesen und 
entsprechend ein Schritt ausgeführt werden, das ändere ich. Aber dann 
leuchten mir immer noch nicht die wiederholten STARTs ein. Der Master 
sendet zuletzt Adress write und wartet dann auf TWINT, der Slave gibt 
den Status Code für fehlerhaftes START/STOP aus und springt aus der ISR, 
danach passiert bei beiden nichts mehr.

von Philipp H. (philipp_ho)


Angehängte Dateien:

Lesenswert?

Wolfgang schrieb:
> Hänge die logicdata-Datei mit an.

Habe nochmal eine erstellt.

von Oliver S. (oliverso)


Lesenswert?

Philipp H. schrieb:
> Die ISR ist nur für den Slave aktiviert,

Dann zeig halt mal deinen ganzen Code.

Oliver

von Stefan F. (Gast)


Lesenswert?

Philipp H. schrieb:
>> Hänge die logicdata-Datei mit an.
> Habe nochmal eine erstellt.

Da kann man nichts erkennen, außer lauter Nadelimpulse.

von Philipp H. (philipp_ho)


Angehängte Dateien:

Lesenswert?

Oliver S. schrieb:
> Philipp H. schrieb:
>> Die ISR ist nur für den Slave aktiviert,
>
> Dann zeig halt mal deinen ganzen Code.
>
> Oliver

Das wäre die while Schleife der main zum testen der Verbindung. Das 
obere für den Master, unten für den Slave. Der Rest des gesamten Codes 
hat noch nichts mit dem I2C Bus zu tun, aber ich hänge mal die 
Projektdatei an.
1
 
2
    for (i=0;i<5;i++)
3
    {
4
      Atmega328pStopWatchStart(1000);
5
      while(!Atmega328pStopWatchCheck());
6
    }
7
    test[0] = 0x01;
8
    test[1] = 0x02;
9
    test[2] = 0x03;
10
    UartPrintLine("I2C init",0,0);
11
    UartPrintNewLine;
12
    
13
    i2cTxMaster(&test[0], 3, 0x01);
14
    
15
    /*
16
    sei();
17
    while(1)
18
    {
19
      if (payloadPendingUp)
20
      {
21
        UartPrintLine("I2C receive: ", &payloadPendingUpContent[0], payloadPendingUpLength);
22
        UartPrintNewLine();
23
        payloadPendingUp = false;
24
      }
25
    }
26
    */

von Stefan F. (Gast)


Lesenswert?

Philipp, du musst schon aussagekräftige Bilder vom Signal zeigen und den 
ganzen Quelltext, sonst kann dir nach wie vor niemand helfen.

Am besten reduzierst du den Code auf eine minimale Menge von Zeilen, die 
das Problem noch haben, so dass derirrelevante Teil möglichst entfällt. 
Aber es muss compilier- und ausführbar sein, damit wir es nachvollziehen 
können.

Ein kleiner Tipp: "&test[0]" kannst du durch "test" ersetzen. Das ist 
das Selbe und besser lesbar.

von Philipp H. (philipp_ho)


Lesenswert?

Stefan ⛄ F. schrieb:
> Philipp, du musst schon aussagekräftige Bilder vom Signal zeigen und den
> ganzen Quelltext, sonst kann dir nach wie vor niemand helfen.

Bei der .logicdata kann man doch beliebig ranzoomen? Das ist das was ich 
bekomme. Und mehr Quelltext als das komplette Projekt habe ich nicht.

Stefan ⛄ F. schrieb:
> Am besten reduzierst du den Code auf eine minimale Menge von Zeilen, die
> das Problem noch haben, so dass derirrelevante Teil möglichst entfällt.

Da ich die Ursache des Problems nicht kenne wüsste ich nicht wie, ich 
habe ja schon die Funktionen die für das Problem relevant sind gepostet.

Stefan ⛄ F. schrieb:
> Ein kleiner Tipp: "&test[0]" kannst du durch "test" ersetzen. Das ist
> das Selbe und besser lesbar.

Sicher? Die Funktion erwartet einen Pointer.

Tut mir leid wenn ich mich etwas doof anstelle, bin ja dankbar dass ihr 
euch die Zeit nehmt mir zu helfen. Aber hier verstehe ich nicht ganz was 
ich noch zusätzlich an Infos liefern kann.

von Einer K. (Gast)


Lesenswert?

Philipp H. schrieb:
> Sicher? Die Funktion erwartet einen Pointer.

Sicher!
Der Name eines Arrays zerfällt zu einem Pointer.

Tipp:
Ein Fachbuch erwerben.

von Oliver S. (oliverso)


Lesenswert?

Philipp H. schrieb:
> Bei der .logicdata kann man doch beliebig ranzoomen? Das ist das was ich
> bekomme.

Das hat halt mit I2C nix zu tun, was man da sieht.

Ich habe den Sourcecode jetzt mal kurz überflogen.

Mit welcher Frequnez läuft dein Arduino Uno?

Oliver

von Philipp H. (philipp_ho)


Lesenswert?

Oliver S. schrieb:
> Das hat halt mit I2C nix zu tun, was man da sieht.

Genau deshalb werde ich ja auch nicht schlau daraus.

Oliver S. schrieb:
> Mit welcher Frequnez läuft dein Arduino Uno?

16MHz, die Fuse bits stehen auf external clock und es sollte kein 
prescaler aktiv sein.

von Oliver S. (oliverso)


Lesenswert?

Igendwie fehlt mir da ein define für F_CPU, aber egal.  Die 
Initialisierung der USART solltest du dir dazu nochmal anschauen, und 
alle Warnungen einschalten schadet auch nicht.

Das bzw. ein Hauptproblem am TWI ist dein rumgefummel an den TWCR-Bits.
Schief geht das schonmal in i2cTxMaster in der Zeile hier:
1
TWCR &= !(1<<TWIE);

Die macht nicht das, was du wolltest, sondern disabled dir den TWI, die 
Leitungen gehen high, und danach macht der nicht mehr das, was du dir 
vorstellst.

Anstelle von
1
TWCR |= (1<<TWINT)|(1<<TWSTA)|(1<<TWEN);

schreib einfach:
1
TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN);

Da ist alles gesetzt, was soll, und alles andere nicht. Das gilt für 
alle Zugriffe auf TWCR.

Weiter hab ich mir das nicht angeschaut, den Rest kriegst du auch selber 
hin.

Oliver

: Bearbeitet durch User
von Philipp H. (Gast)


Lesenswert?

Stimmt, vielen Dank für den Input und die Hilfe! Werde das morgen mal 
ändern und weiter probieren.

von Philipp H. (philipp_ho)


Angehängte Dateien:

Lesenswert?

Habe die Anmerkungen umgesetzt und jetzt sieht das ganze schon etwas 
anders aus. Aber dennoch sendet der Master jetzt ununterbrochen STARTS, 
TWINT wird nicht gesetzt, der Slave gibt nach interrupt noch immer 0x00 
als status code.
1
void I2cInit(uint8_t sysClock, uint16_t bitRate)
2
{
3
  //Set Slave Adress
4
  TWAR = ((I2C_ADDRESS<<1) & 0xFE);
5
  //Set bit rate
6
  TWBR = (((sysClock*1000)/(2*bitRate))-8);
7
}
8
9
bool i2cTxMaster (uint8_t *firstelementofData,uint8_t lengthOfData,uint8_t address)
10
{
11
  //Result variable 
12
  bool result = true;
13
  //Loop Variable
14
  uint8_t i=0;
15
  //Transmit START condition
16
  TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN);
17
  //Wait for TWINT flag
18
  while(!(TWCR & (1<<TWINT)));
19
  //Terminate if status != 0x08
20
  if ((TWSR & 0xF8) != 0x08)
21
  {
22
    result = false;
23
    return result;
24
  }
25
  //Load SLA+W to data register
26
  TWDR = ((address<<1) & 0xFE);
27
  //Transmit Address Byte
28
  TWCR = (1<<TWINT)|(1<<TWEN);
29
  //Wait for TWINT flag
30
  while(!(TWCR & (1<<TWINT)));
31
  //Terminate if status != 0x18
32
  if ((TWSR & 0xF8) != 0x18)
33
  {
34
    result = false;
35
    return result;
36
  }
37
  //Transmit data bytes
38
  for (i=0; i<lengthOfData; i++)
39
  {
40
    //Load current byte to data register
41
    TWDR = *(firstelementofData+i);
42
    //Send data byte
43
    TWCR = (1<<TWINT)|(1<<TWEN);
44
    //Wait for TWINT flag
45
    while(!(TWCR & (1<<TWINT)));
46
    //Terminate if status != 0x28
47
    if ((TWSR & 0xF8) != 0x28)
48
    {
49
      result = false;
50
      return result;
51
    }
52
  }
53
  //Transmit STOP Condition
54
  TWCR = (1<<TWINT)|(1<<TWSTO)|(1<<TWEN);
55
  return result;
56
}
57
58
ISR(TWI_vect)
59
{
60
  //Own address of general call received
61
  if (((TWSR & 0xF8) == 0x60) || ((TWSR & 0xF8) == 0x70))
62
  {
63
    payloadPendingUpLength=0;
64
    //Clear TWINT flag
65
    TWCR = (1<<TWINT)|(1<<TWEN);
66
  }
67
  //Byte received
68
  else if ((TWSR & 0xF8) == 0x80 || (TWSR & 0xF8) == 0x90)
69
  {
70
    payloadPendingUpContent[payloadPendingUpLength] = TWDR;
71
    payloadPendingUpLength++;
72
    //Clear TWINT flag
73
    TWCR = (1<<TWINT)|(1<<TWEN);
74
  }
75
  //STOP received
76
  else if ((TWSR & 0xF8) == 0xA0)
77
  {
78
    payloadPendingUp = true;
79
    //Clear TWINT flag
80
    TWCR = (1<<TWINT)|(1<<TWEN);
81
  }
82
}
83
84
//Main loop Master:
85
for (i=0;i<5;i++)
86
    {
87
      Atmega328pStopWatchStart(1000);
88
      while(!Atmega328pStopWatchCheck());
89
    }
90
    test[0] = 0x01;
91
    test[1] = 0x02;
92
    test[2] = 0x03;
93
    i2cTxMaster(&test[0], 3, 0x01);
94
95
//Main loop Slave:
96
//Enable ACK (active bus connection),TWI and TWI interrupt
97
    TWCR = (1<<TWEA)|(1<<TWEN)|(1<<TWIE);
98
    sei();
99
    while(1)
100
    {
101
      if (payloadPendingUp)
102
      {
103
        UartPrintLine("I2C receive: ", &payloadPendingUpContent[0], payloadPendingUpLength);
104
        UartPrintNewLine();
105
        payloadPendingUp = false;
106
      }
107
    }

Warum könnte der Master hier schon bei START stecken bleiben?

von Oliver S. (oliverso)


Lesenswert?

1
I2C init 
2
3
IC2 send START  0x24 
4
5
TWSR:  0x08 
6
7
I2C START ok 
8
9
Transmit address+w  0x02 
10
11
Address acknowledged 
12
13
Data transmission successful

Sagt dein TWI-Master-Code gegen einen dummy-Slave. Der Master-Code 
funktioniert also soweit.

Da wirst du wohl entweder in den nicht-TWI-relevanten Codestellen (die 
ich für den test auskommentiert habe)  oder im Slave oder in der 
Hardware ein Problem haben.

Oliver

von Philipp H. (philipp_ho)


Lesenswert?

Vielen Dank, es funktioniert jetzt! GND war nicht richtig verbunden und 
beim Slave musste man zusätzlich noch TWEA TWIE auf 1 halten, das hatte 
ich durch den wechsel von |= zu = zwischendurch nicht mehr drin.

Eine Sache ist allerdings noch seltsam. Nach erfolgreicher Übertragung 
steht payloadPendingUp auf true, in der while(1) schleife der main 
sollte also "I2C receive: ... " ausgegeben und payloadPendingUp wieder 
auf false gesetzt werden, die if(payloadPendingUp) schleife wird aber 
nicht ausgeführt. Wenn ich aber z.b UartPrintLine("Main Loop",0,0) vor 
die if schreibe geht es dann. Eine Idee warum das so ist?

Auf jeden Fall danke für eure Zeit und Hilfe!

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.