Forum: Mikrocontroller und Digitale Elektronik atmega32 i2c master transimitter/slave receiver problem


von Jürgen S. (jsachs)


Lesenswert?

Hallo,

ich habe ein "Netzwerk" aus mehreren atmega32.
Diese Kommunizieren über i2c. Es wird nur gesendet im Master Transmitter 
Modus und Empfangen im Slave Receiver. Alle atmega sind sowohl Slave und 
Master.
Das Funktioniert soweit prima, jedoch bekomme ich ein Problem wenn ich 
versuche zu senden (Master Transmitter) und etwas Empfange.

Als Test lasse ich einfach beide atmegas Texte hin und her schicken. Was 
klappt bis ich senden will während ein Empfang läuft.

Irgendwie habe ich noch keine Möglichkeit gefunden festzustellen, ob der 
i2c/TWI frei ist.

Ich dachte ich kann "jederzeit" sagen sende ein Startcondition und 
sobald es geht wird dies ausgeführt und ein Interrupt ausgelöst. Das 
geht nicht.

Bin das PDF des atmega nun mehrfach durch.
Habe ich etwas übersehen ?

Der i2c Code ist so alleine nicht Lauffähig, daher bringt das Posten 
hier vermutlich nichts. Ich hoffe es hat trotzdem noch jemand einen 
Tipp.

Ich dachte ein Flag setzen sobald ich etwas Empfange sollte reichen, 
aber scheinbar deckt das nicht alle Sonderfälle ab.

Gruss
Juergen

von Tim (Gast)


Lesenswert?

>Als Test lasse ich einfach beide atmegas Texte hin und her schicken. Was
>klappt bis ich senden will während ein Empfang läuft.

Solange die TWI Hardware arbeitet darfst du nicht daran rumfummeln.
Also laufende Übertragung abschließen und dann Start anfordern.

>Irgendwie habe ich noch keine Möglichkeit gefunden festzustellen, ob der
>i2c/TWI frei ist.

Brauch dich ja auch nicht zu interessieren.

>Der i2c Code ist so alleine nicht Lauffähig, daher bringt das Posten
>hier vermutlich nichts. Ich hoffe es hat trotzdem noch jemand einen
>Tipp.

Poste deinen Code!
Solange keiner sieht was du mit TWI anstellst kann dir keiner helfen.

>Ich dachte ein Flag setzen sobald ich etwas Empfange sollte reichen,
>aber scheinbar deckt das nicht alle Sonderfälle ab.

Von wo bis wo setzt du das Flag?
Code??

von Klaus2m5 (Gast)


Lesenswert?

Alle Master müssen den I2C-Bus auf Startbedingung überwachen und eigene 
Starts unterdrücken, bis wieder ein Stop empfangen wird.

Für die seltenen Fälle, bei denen mehr als ein Master gleichzeitig Start 
signalisiert, muss eine Bus-Arbitration durchgeführt werden. Dabei 
müssen alle Master sofort ihre Übertragung abbrechen, wenn sie selbst 
SDA=1 gesetzt haben, aber tatsächlich SDA=0 ist.

Bei I2C-Teilnehmern, die gleichzeitig Master und Slave sein können, 
gibts dabei noch ein spezielles Problem. Wenn bei einer Bus-Arbitration 
ein Slave adressiert werden sollte, der aber selbst gerade als Master an 
der Bus-Arbitration beteiligt ist, kann auf die Adressierung dieses 
Teilnehmers kein ACK erfolgen.

All diese Probleme, die in einem Single-Master Sytem gar nicht erst 
auftreten würden, müssen von der Software abgefangen werden.

von Jürgen S. (jsachs)


Lesenswert?

Das Flag was ich weiter oben erwähnt habe, ist schon wieder drausen. Es 
hat ja nicht funktioniert :-(

Ich kann den Code auf die schnelle nur hier "inline" posten.
Ach ja, das Makro "_BVC()" ist "((0<<x))" und für mich einfacher mal ein 
Flag zu löschen und dazu zu nehmen.

Wie gesagt der Code ist nur auf MT und SR ausgelegt.
1
/*
2
 * 
3
 * 
4
 * 
5
 */
6
7
#include "i2c.h"
8
#include <util/atomic.h>
9
#include <avr/interrupt.h>
10
#include <util/twi.h> 
11
#include "avr_helper.h"
12
#include "uart.h"
13
14
//#define IC2_DEBUG
15
16
enum 
17
{
18
  i2c_unknown = 0,
19
  i2c_Idle,
20
  i2c_send_start,
21
  i2c_send_addr,
22
  i2c_send_data,
23
  i2c_send_stop
24
};
25
26
struct _sI2cStatus
27
{
28
  uint8_t status;
29
  uint8_t txBuffer[20];
30
  uint8_t txPos;
31
  uint8_t toSendPos;
32
  uint8_t isMaster:1;
33
};
34
35
volatile struct _sI2cStatus i2cStatus;
36
37
#define getTWIstatus (TWSR & TW_STATUS_MASK)
38
39
void setI2CAddr(uint8_t addr)
40
{
41
  TWAR = (addr & 0x7F)<<1;
42
}
43
44
void initI2C(uint8_t isMaster, uint16_t baud, uint8_t myAddr)
45
{
46
  if (isMaster)
47
    i2cStatus.isMaster = 1;
48
  else
49
    i2cStatus.isMaster = 0;
50
  
51
  i2cStatus.status = i2c_Idle;
52
  
53
  uint8_t scl = (F_CPU/baud -16) / 2;  
54
  
55
  TWBR = scl;
56
  setI2CAddr(myAddr);
57
  
58
  PORTC = _BV(PC0)|_BV(PC1);  // Pullup an
59
  
60
  
61
  //last enable interrupt
62
  TWCR = _BVC(TWINT)|_BV(TWEA)|_BVC(TWSTA)|_BVC(TWSTO)|_BV(TWEN)|_BV(TWIE);  
63
  //TWCR |= _BV(TWIE) | _BV(TWEN);
64
  
65
}
66
67
void sendI2CStart(void)
68
{
69
#ifdef I2C_DEBUG
70
  char buffer[10];
71
  uint8_t twcr;
72
  uartPut('t');
73
  twcr = TWCR;
74
  uartPuts(uint8ToAh(buffer, twcr));
75
  uartPut(',');
76
  uartPuts(uint8ToAh(buffer, getTWIstatus));
77
  uartPut(':');
78
#endif
79
  ATOMIC_BLOCK ( ATOMIC_RESTORESTATE )
80
  {
81
    TWCR = _BV(TWINT)|_BV(TWEA)|_BV(TWSTA)|_BVC(TWSTO)|_BV(TWEN)|_BV(TWIE);  
82
    i2cStatus.status = i2c_send_start;
83
  }
84
#ifdef I2C_DEBUG
85
  //twcr = TWCR;
86
  uartPuts(uint8ToAh(buffer, twcr));
87
  uartPut(',');
88
  uartPuts(uint8ToAh(buffer, getTWIstatus));
89
  uartPut('\r');
90
#endif
91
}
92
93
void sendI2CStop(void)
94
{
95
  ATOMIC_BLOCK ( ATOMIC_RESTORESTATE )
96
  {
97
#ifdef I2C_DEBUG
98
    uartPut('e');
99
#endif
100
    TWCR = _BV(TWINT)|_BV(TWEA)|_BVC(TWSTA)|_BV(TWSTO)|_BV(TWEN)|_BV(TWIE);  
101
    i2cStatus.status = i2c_send_stop;
102
  }
103
}
104
105
void sendI2C(uint8_t addr, uint8_t *data, uint8_t len)
106
{
107
  // wait to get last packet send
108
  // currently we do not support multiple commands in buffer
109
  while (i2cStatus.txPos > 0)
110
  {
111
//#ifdef I2C_DEBUG
112
    uartPut('-');
113
//#endif
114
    nop();
115
  }
116
  /*
117
  while(!(getTWIstatus & _BV(TWINT)))
118
  {
119
      uartPut('.');
120
  }
121
  */
122
  ATOMIC_BLOCK ( ATOMIC_RESTORESTATE )
123
  {
124
    if (len < sizeof(i2cStatus.txBuffer))
125
    {
126
#ifdef I2C_DEBUG
127
      uartPut('+');
128
#endif
129
      i2cStatus.txBuffer[0] = (addr<<1)|TW_WRITE; // 0 = WRITE, 0x01 = READ
130
      
131
      uint8_t i;
132
      for(i=0;i<len;i++)
133
      {
134
  i2cStatus.txBuffer[i+1] = data[i];
135
      }
136
      
137
      //memcpy(&(i2cStatus.txBuffer)+1, data, len);
138
      //uartPut('2');
139
      
140
      i2cStatus.txPos = len+1; //add addressbyte
141
      i2cStatus.toSendPos = 0;
142
      sendI2CStart();
143
    }
144
  }
145
}
146
147
ISR(TWI_vect)
148
{
149
#ifdef I2C_DEBUG
150
  char buffer[10];
151
  uartPut('i');
152
#endif
153
  switch(getTWIstatus)
154
  {
155
    case TW_START:  // START send
156
    {
157
#ifdef I2C_DEBUG
158
      uartPuts("TWI_TW_START,");
159
      uartPuts(uint8ToAh(buffer, getTWIstatus));
160
      uartPut('\r');
161
#endif
162
      TWDR = i2cStatus.txBuffer[0];
163
      i2cStatus.toSendPos++;
164
      i2cStatus.status = i2c_send_addr;
165
      TWCR = _BV(TWINT)|_BV(TWEA)|_BVC(TWSTA)|_BVC(TWSTO)|_BV(TWEN)|_BV(TWIE);  
166
      break;
167
    }
168
    case TW_MT_SLA_ACK :
169
    case TW_MT_DATA_ACK:
170
    {
171
#ifdef I2C_DEBUG
172
      uartPuts("TW_MT_SLA/DATA_ACK,");
173
      uartPuts(uint8ToAh(buffer, getTWIstatus));
174
      uartPut('\r');
175
#endif      
176
      if (i2cStatus.toSendPos < i2cStatus.txPos)
177
      {
178
  TWDR = i2cStatus.txBuffer[i2cStatus.toSendPos];
179
  i2cStatus.toSendPos++;
180
  i2cStatus.status = i2c_send_data;
181
  TWCR = _BV(TWINT)|_BV(TWEA)|_BVC(TWSTA)|_BVC(TWSTO)|_BV(TWEN)|_BV(TWIE);  
182
      }
183
      else
184
      {
185
  i2cStatus.txPos = 0;
186
  sendI2CStop();
187
      }
188
      break;
189
    }
190
    case TW_MT_DATA_NACK:
191
    {
192
#ifdef I2C_DEBUG
193
      uartPuts("TW_MT_DATA_NACK,");
194
      uartPuts(uint8ToAh(buffer, getTWIstatus));
195
      uartPut('\r');
196
#endif
197
      i2cStatus.txPos = 0;
198
      sendI2CStop();
199
      break;
200
    }
201
    case TW_MT_ARB_LOST:
202
    {
203
#ifdef I2C_DEBUG
204
      uartPuts("TW_MT_ARB_LOST,");
205
      uartPuts(uint8ToAh(buffer, getTWIstatus));
206
      uartPut('\r');
207
#endif
208
      //TODO we need to support this
209
      // ups we lost bus control
210
      break;
211
    }
212
    case TW_MT_SLA_NACK:  // addr not used, abbort
213
    {
214
#ifdef I2C_DEBUG
215
      uartPuts("TWI_MT_SLA_NACK,");
216
      uartPuts(uint8ToAh(buffer, getTWIstatus));
217
      uartPut('\r');
218
#endif
219
      i2cStatus.txPos = 0;
220
      //TWCR = _BV(TWINT)|_BVC(TWSTA)|_BVC(TWSTO)|_BV(TWEN)|_BV(TWIE);  
221
      sendI2CStop();
222
      break;
223
    }
224
    case TW_SR_SLA_ACK :
225
    {
226
#ifdef I2C_DEBUG
227
      uartPuts("TW_SR_SLA_ACK,");
228
      uartPuts(uint8ToAh(buffer, getTWIstatus));
229
      uartPut('\r');
230
#endif
231
      char c = TWDR;
232
      uartPut(c);
233
      uartPut('\r');
234
      TWCR = _BV(TWINT)|_BV(TWEA)|_BVC(TWSTA)|_BVC(TWSTO)|_BV(TWEN)|_BV(TWIE);
235
      break;
236
    }
237
    case TW_SR_DATA_ACK:
238
    {
239
#ifdef I2C_DEBUG
240
      uartPuts("TW_SR_DATA_ACK,");
241
      uartPuts(uint8ToAh(buffer, getTWIstatus));
242
      uartPut('\r');
243
#endif
244
      char c = TWDR;
245
      uartPut(c);
246
      TWCR = _BV(TWINT)|_BV(TWEA)|_BVC(TWSTA)|_BVC(TWSTO)|_BV(TWEN)|_BV(TWIE);
247
      break;
248
    }
249
    case TW_SR_STOP:
250
    {
251
#ifdef I2C_DEBUG
252
      uartPuts("TW_SR_STOP,");
253
      uartPuts(uint8ToAh(buffer, getTWIstatus));
254
      uartPut('\r');
255
#endif
256
      char c = TWDR;
257
      uartPut('\r');
258
      TWCR = _BV(TWINT)|_BV(TWEA)|_BVC(TWSTA)|_BVC(TWSTO)|_BV(TWEN)|_BV(TWIE);
259
      i2cStatus.status = i2c_Idle;
260
      break;
261
    }
262
    default:
263
    {
264
#ifdef I2C_DEBUG
265
      uartPuts("TWI int default,");
266
      uartPuts(uint8ToAh(buffer, getTWIstatus));
267
      uartPut('\r');
268
#endif
269
      i2cStatus.txPos = 0;
270
      //TWCR = _BV(TWINT)|_BVC(TWSTA)|_BVC(TWSTO)|_BV(TWEN)|_BV(TWIE);  
271
      sendI2CStop();
272
      break;
273
    }
274
    
275
  }
276
  
277
  
278
  // last clear interrupt so we free TWI
279
  //TWCR |= _BV(TWINT);
280
}

von Tim (Gast)


Lesenswert?

Hier ein parr Sachen die mir aufgefallen sind:
Wofür i2cStatus.status? Wird nur geschrieben.
sendI2C prüft anhand von i2cStatus.txPos ob TWI arbeitslos ist,
kann so aber NIE erkennen ob das TWI gerade im SR Modus ist.

Wenn du also gerade Daten empfängst und sendI2C aufrufst geht das
garantiert schief.

Vorschlag:
Definiere für i2cStatus.status ein i2c_rvc_data und setzt das in
TW_SR_SLA_ACK.
sendI2C darf nur noch senden wenn i2cStatus.status == i2c_Idle.

Beachte auch das es diverse Möglichkeiten gibt den Master Status zu 
verlieren: 0x38, 0x68, 0x78.
Du fängst aber im Moment nur 0x38 ab.

von Jürgen S. (jsachs)


Lesenswert?

Hallo,

i2cStatus.txPos dient dazu wie voll der Sendespeicher ist. Im Moment 
Unterstütze ich nur 1 Packet in der Queue. Daher schicke ich den avr in 
eine while bis das letzte Packet verschickt wurde. Will ich mal 
ausbauen, aber Schritt für Schritt :-)

Die noch nicht abgefangenen Sonderfälle sind in Arbeit. Ich will mich da 
erst mal rein arbeiten.

Mit dem Vorschlag für den i2cStatus.status. So hat ich das auch 
umgesetzt, nur in einer extra Variablen. Nur kam es trotzdem noch vor. 
Das ich ein Start senden will und ein Empfang anfängt.
Aber ich versuche das gleich nochmal, vielleicht habe ich mich gestern 
auch vertan.

Ich werde berichten :-)

Gruss
Juergen

von Jsachs (Gast)


Lesenswert?

Ich habe gestern das och modifiziert und es scheint doch zu gehen.
Ich lasse zwei master sich gegenseitig Meldungen schicken, die dann auf 
die Serielle ausgegeben werden. Und das ganze mir richtig speed :-)
Nun habe ich noch folgendes Problem:
Wird der "Bus Master" während der Übertragung resetet, bleibt der Bus 
natürlich belegt und ist bis zu einem Reset beider Master nicht nutzbar.
Der "Salve Receiver" denkt der Bus ist noch belegt. Der MT der neu 
gestartet wurde fängt aber auch keine Übertragung an. Ich nehme an das 
passiert nur wärend der Übertragung eines bytes.
Starte ich beide CPUs neu läuft alles wieder prima.

Wie kann man so etwas sinnvoll handhaben ?
Wenn für eine gewisse Zeit nichts passiert, einfach mal ein Stop senden 
???

Für Tipps wäre ich dankbar.

Gruss
Juergen

von Tim (Gast)


Lesenswert?

Freut mich das es jetzt klappt.

Ich habe hier einen timeout Zähler in jedem µC laufen.
Der wird bei jedem TWI-IRQ zurückgesetzt.
Wenn sich für eine gewisse Zeit nichts auf dem Bus tut
gibt es einen Init des TWI und der Bus ist wieder frei.

Und noch so als Tipp: Bau eine CRC ein.
Wenn du dann nach der CRC noch ein Dummy Byte sendest kann
der Slave durch ACK/NACK dem Master mitteilen ob die CRC gepasst hat.

von Jürgen S. (jsachs)


Lesenswert?

Hallo,

wenn ich einen Fehler feststelle. Sagen wir der Bus ist für eine gewisse 
Zeit still und der Verdacht da ist was faul.

Nun möchte ich das TWI in einen definierten Zustand bringen. Was muss 
ich dann machen ?

Ist folgendes ausreichend ?
1
/*
2
 * get us in a known state
3
 */
4
void reinitI2C(void)
5
{
6
  i2cStatus.status = i2c_Idle;
7
  TWCR |= _BV(TWSTO);
8
  TWCR &= ~_BV(TWEA);
9
  TWCR |= _BV(TWEA);
10
}

Nachdem TWEA ja auch die Hardware freigibt, bin ich mir da unsicher.
Ich möchte aus einem Fehler, egal ob Master oder Slave in einen 
definierten Zustand (Idle) wechseln.

Natürlich nur bei Fehler die Fatal sind, also zu Busausfällen führen, 
weil ein Slave hängt oder nicht mehr reagiert oder der Master während 
dem Transfer resetet/abgeschaltet wurde.

Gruss
Juergen

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.