Forum: Mikrocontroller und Digitale Elektronik Probleme bei I2C Kommunikation


von Lisa (Gast)


Lesenswert?

Hallo an alle =)

ich habe bereits eine I2C-Kommunikation programmiert, allerdings mit 
Polling. Mein Controller (AT MEGA 164 PA) ist dabei der Master. Nach der 
Initialisierung sendet er die Start Condition. Dann wird gewartet bis 
das TWINT-Flag gesetzt ist und die Slave Adresse wird gesendet. Beim 
Polling erhalte ich dabei auch ein ACK (Status Code 0x18) Dann kann ich 
meine Daten senden und die Kommunikation wieder beenden.

Nun möchte ich das Ganze Interrupt gesteuert machen. Statt zu warten, 
bis TWINT gesetzt wird, springt das Programm in die ISR. Das 
funktioniert ja auch noch beim Senden der Start Condition. Dann sende 
ich die Slave Adresse mit dem Senden-Befehl (SLA+W). Dies entspricht der 
Adresse beim Polling. Danach springt das Programm auch wieder in den 
Interrupt, allerdings mit dem Status-Code 0x20, also einem NACK.

An was liegt das nun? Es ist doch eigentlich der gleiche Code wie beim 
Polling nur mit Interrupt?
1
//Header einbinden
2
#include <avr/io.h>
3
#include <util/delay.h>
4
#include <stdint.h>
5
#include <avr/interrupt.h>
6
7
8
//Prototypen der Funktionen
9
void initialisiere_I2C (void);
10
void sendeDaten_Master(unsigned char data);
11
unsigned char empfangeDaten_Master(void);
12
void sleep_ms (uint16_t ms);
13
14
//Variable deklarieren
15
volatile int master = 1;
16
volatile int transmit = 1;
17
18
volatile unsigned char data = 0xFF;
19
char test;
20
21
int SLA_W = 0x90;  //0b10010000 -> SLA = 0x48 -> nach links shiften und 0/1 anhaengen
22
int SLA_R = 0x91;  //0b10010001
23
24
//TWI Defines: TWI an Port C angeschlossen
25
#define TWI_DDR    DDRC
26
#define TWI_PORT    PORTC
27
#define SCL      PC0
28
#define SDA      PC1
29
30
//TWI Defines: TWI an Port B angeschlossen
31
#define Temp_DDR    DDRB
32
#define Temp_PORT    PORTB
33
#define EnTemp    PB0
34
35
36
int main(void)
37
{
38
  //Ports als Ausgang setzen
39
  TWI_DDR |= (1 << SDA);
40
  TWI_DDR |= (1 << SCL);
41
  
42
  //Ports als Ausgang setzen
43
  Temp_DDR |= (1 << EnTemp);
44
    
45
   //Ports auf high setzen
46
   Temp_PORT &= ~(1<<EnTemp);
47
48
  initialisiere_I2C();
49
  
50
  //Interrupts aktivieren
51
  sei();
52
  
53
  if (master == 1)
54
  {
55
    if (transmit == 1)
56
    {
57
      sendeDaten_Master(0x00);
58
    }
59
    else
60
    {
61
      empfangeDaten_Master();
62
    }
63
  }
64
  
65
    while(1)
66
    {
67
    ; //leere Endlosschleife    
68
    }
69
}
70
void initialisiere_I2C (void)
71
{
72
  //Bit Rate 100,000kHz
73
  TWBR=0x20;
74
  //analog comparator initialisieren
75
  ACSR=0x80;
76
  //Schnittstelle aktivieren
77
  TWCR= (1<<TWIE)|(1<<TWEN);
78
  //Prescaler auf 1 --> TWSR = 0x0
79
  TWSR &= ~(1<<TWPS0);
80
  TWSR &= ~(1<<TWPS1);
81
  //SCL Frequenz = 250000
82
}
83
84
85
void sendeDaten_Master(unsigned char data)
86
{
87
  //Start Condition senden
88
  TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN)|(1<<TWIE);  
89
}
90
unsigned char empfangeDaten_Master(void)
91
{
92
  //Start Condition senden
93
  TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN)|(1<<TWIE);
94
  return data;
95
}
96
97
98
ISR(TWI_vect)
99
{
100
  if (master == 1)
101
  {
102
    if (transmit == 1)
103
    {
104
      switch (TWSR)
105
      {
106
        //Status pruefen -> START gesendet
107
        case 0x08:
108
          TWDR = SLA_W;
109
          //TWINT und TWEN setzen, Start und Stop loeschen
110
          TWCR = (1<<TWINT) | (1<<TWEN)|(1<<TWIE);
111
        break;
112
        
113
        //Status pruefen -> ACK (SLA+W) erhalten
114
        case 0x18:
115
          //Daten senden
116
          TWDR = data;
117
          TWCR = (1<<TWINT) | (1<<TWEN)| (1<<TWIE);
118
        break;
119
        
120
        //Status pruefen -> NACK (SLA+W) erhalten
121
        case 0x20:
122
          // ERROR(); -> Fehlermeldung
123
          test = 0x0;
124
        break;
125
        
126
        //Status pruefen -> ACK fuer Daten erhalten
127
        case 0x28:
128
          //Uebertragung beenden
129
          TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWSTO)| (1<<TWIE);
130
        break;
131
        
132
        //Status pruefen -> NACK (Daten) erhalten
133
        case 0x30:
134
          // ERROR(); -> Fehlermeldung
135
        break;
136
      }
137
    } else if (transmit == 0)
138
    {
139
      switch (TWSR)
140
      {
141
        //Status pruefen -> START gesendet
142
        case 0x08:
143
          TWDR = SLA_R;
144
          TWCR = (1<<TWINT) | (1<<TWEN)| (1<<TWIE);
145
        break;
146
        
147
        //Status pruefen -> ACK (SLA+R) erhalten
148
        case 0x40:
149
          //Daten empfangen
150
          data = TWDR;
151
          TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWEA) | (1<<TWIE);
152
        break;
153
        
154
        //Status pruefen -> NACK (SLA+R) erhalten
155
        case 0x48:
156
          // ERROR(); -> Fehlermeldung
157
        break;
158
        
159
        //Status pruefen -> ACK fuer Daten erhalten
160
        case 0x50:
161
          //Uebertragung beenden
162
          TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWSTO);
163
        break;
164
        
165
        //Status pruefen -> NACK (Daten) erhalten
166
        case 0x58:
167
          // ERROR(); -> Fehlermeldung
168
        break;
169
      }
170
    } else
171
    {
172
      // ERROR(); -> Fehlermeldung
173
    }
174
  } 
175
}

von Stephan B. (matrixstorm)


Lesenswert?

Ggf. bitte zukuenftig MAKROS aus

http://www.nongnu.org/avr-libc/user-manual/group__util__twi.html

verwenden. Das macht den Code les- und wartbarer.

Mindestens empfangeDaten_Master() duerfte einfach (unblockiert) 
durchlaufen und falsche daten zurueckliefern.

von Lisa (Gast)


Lesenswert?

oh, danke für den Hinweis, das hab ich wohl noch vom Polling drin

habs nun geändert =)

Leider bekomme ich auch beim Senden ein NACK zurück wenn ich die Slave 
Adresse sende.

von Lisa (Gast)


Angehängte Dateien:

Lesenswert?

Ich hab jetzt mal den Code mit den Makros im Anhang eingefügt.

Leider erhalte ich immer noch ein NACK

von Karlheinz (Gast)


Lesenswert?

Hallo,

ich hab deinen Code jetzt nur mal überflogen

die switch-Anweisung

> switch (TWSR)

sollte zumindest
> switch (TWSR & 0xf8)

lauten, denn da sind noch Bits die dir das Ergebnis verhauen können

vielleicht gehts dann

von Lisa (Gast)


Lesenswert?

Schon mal vorab ein großes Dankeschön, denn ich weiß echt nicht mehr 
weiter...

Eine Frage jedoch: Warum switch (TWSR & 0xF8)
Der Prescaler ist ja auf 1, also sind die ersten beiden Bits auf 0, 
daher wird ja wirklich nur der Status abgefragt. Das war zumindest meine 
Logik, ich bin aber noch nicht so lange am embedded programmieren....

von Karl H. (kbuchegg)


Lesenswert?

Lisa schrieb:

> Eine Frage jedoch: Warum switch (TWSR & 0xF8)
> Der Prescaler ist ja auf 1, also sind die ersten beiden Bits auf 0,
> daher wird ja wirklich nur der Status abgefragt.

In deinem konkreten Fall: ja.
Aber gesetz den Fall du änderst das mal, dann geht die Fehlersuche von 
vorne los.
In deinem Fall dürfte das jetzt nicht das Problem sein, aber im 
generellen ist es kein guter Stil, wenn man so programmiert, dass man 
bei Abfragen Annahmen über Bits trifft, die überhaupt nicht 
'dazugehören'.
Das ist einfach eine Form von defensivem Programmieren, dass man so 
programmiert, dass nicht gleich alles auseinanderfällt, wenn man an 
anderer Stelle was ändert.

von Lisa (Gast)


Lesenswert?

Ahhh, ok, danke für die Erklärung. Jetzt habe ich das verstanden.

Zu dem Problem mit dem Programm:

wenn ich einen Breakpoint beim Funktionsaufruf sendeDaten_Master(0x00); 
hab und dann in der ISR in case TW_MT_SLA_ACK dann funktionsiert das 
Programm. Auch beim Breakpoint beim Funktionsaufruf und bei case 
TW_MT_DATA_ACK funktioniert es. DIe Kommunikation klappt also.

ABER: wenn ich nun den Breakpoint beim Funktionsaufruf 
(sendeDaten_Master(0x00)) entferne und das Programm bis in die ISR zu 
case TW_MT_SLA_ACK oder case TW_MT_DATA_ACK laufen lasse, bekomme ich 
ein NACK.

Der Code ist allerdings der gleiche. Außer den Breakpoints wurde nicht 
verändert. Warum funktioniert es mit weniger Breakpoints plötzlich nicht 
mehr? Ich bin echt verwirrt....

Ich hoffe ihr habt die Beschreibung soweit verstanden

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.