Forum: Compiler & IDEs Problem mit I2C Slave-Software


von Bernd K. (berndklein)


Lesenswert?

Moin Leute,
ich habe folgendes Problem. Derzeit habe ich zwei RN-Control Boards 
basierend auf ATMega32. Für das eine Board habe ich eine I2C-Master 
Software geschrieben, für das zweite die I2C-Slave Software.

Lasse ich beides laufen funktioniert es problemlos. Der Master schiebt 
zwei Bytes zum Slave und der führt dann etwas aus. Soweit so gut.

Eigetnlich soll aber ein ATMega8 als Slave verwendet werden. Hier habe 
ich ein Prototyp-Board gebaut. Das funktioniert auch soweit. Wenn ich 
jetzt die I2C-Slave-Software installiere funktioniert das Ganze nicht 
mehr.

Entweder der Master kann sich nicht mit dem Slave syncen (erst nach zig 
mal Reset - bekommt Fehler das Slaveadresse mit NACK beantwortet wird) 
oder er sendet zwei Bytes und bleibt dann hängen. Der Slave funzt 
ebenfalls nicht, er kriegt irgendwie nicht das Stop-Signal vom Master.
 Es sieht für mich so aus, als ob der Slave NACK sendet.

Wie gesagt mit den RN-Boards (gleiche Software) funktioniert es 
einwandfrei. Nur nicht mit meinem ATMega8-Board.

Abschlußwiderstände sind auf dem RN-Board, das RN-Board und das ATMega8 
board haben auch die gleiche Masse.

Ich bin ratlos - hat jemand eine Idee - was ich noch untersuchen kann?

Danke
Bernd

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Hmm, in den Atmega32 RN-Boards und in dem Atmega8 Board (Eigenbau) kann 
doch nicht die gleiche Software drin sein.

Die Atmega8 Software muss mindestens neu übersetzt sein, wahrscheinlich 
sind auch Konstanten auf den anderen µC angepasst. Hier wäre 
nachzuforschen, ob die Änderungen OK sind und ob sie zum Eigenbauboard 
passen, d.h. man bräuchte einen Quellcode.

Und der Schaltplan des Eigenbauboards wäre bei der Fehlersuche auch 
nützlich. Die Schaltpläne der RN-Boards kann sich der Hilfegeber zur Not 
aus dem Netz suchen, in der Hoffnung die gleiche Revision wie dein 
RN-Board zu finden.

Hat man beide Schaltpläne braucht man zum Schluß auch noch Infos, wie 
die beiden Boards jeweils miteinander verbunden sind.

Wenn man im Quellcode oder im Schaltplan entdeckt, dass das Atmega8 
Board mit externer Taktquelle laufen soll, wäre darüberhinaus auch 
hilfreich zu wissen, wie die AVR Fuses programmiert sind.

von Bernd K. (berndklein)


Angehängte Dateien:

Lesenswert?

Klar die Software ist natürlich für den ATMega8 kompiliert, die 
notwendigen Anpassungen sind auch drin.

Als Anhang schon mal den Schaltplan - nun muss ich aber sagen, der erste 
Prototyp ist OHNE den 4051 - Mir kam es im ersten Step lediglich auf die 
Servos an.

Dann hab ich mal die Hauptdateien angehängt (ok nicht komplett das 
Projekt aber hoffentlich ausreichend für eine Analyse).

Mit den Fuses hast Du mich stutzig gemacht, was könnte denn da nicht 
korrekt sein?

Schaun wir mal ob Ihr mir helfen könnte.

Gruß
Bernd

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Auf die Schnelle...

 Bufferoverflow 

#define I2C_RECEIVE_DATA_BUFFER_SIZE  0x10
uint8_t localbuffer[] = "1234567890";
  for (int x = 0; x < I2C_RECEIVE_DATA_BUFFER_SIZE; x++) {
    localbuffer[x] = 0;

Abhilfe:
uint8_t localbuffer[I2C_RECEIVE_DATA_BUFFER_SIZE];

Beim Beschreiben des
    I2cReceiveData[I2cReceiveDataIndex++] = TWDR;
    I2cReceiveDataLength++;
sollte man auch prüfen, ob Bufferoverflow auftritt. Wo setzt du 
I2cReceiveDataLength auf 0 zurück, brauchst du die Variable überhaupt?

 cli und sei und TWCR in ISR 

Würde ich nicht machen. Du beschwörst Probleme mit Interrupts im 
Interrupt herauf.

 Fuses 

Nicht primär relevant, du arbeitest mit internem Takt. Wie schnell läuft 
dein I2C Bus in etwa? Nicht dass da ein 16 MHz Atmega32 einen 1 MHz 
Atmega8 füttert.

 Schaltplan 

AREF an AVcc, d.h. externen +5V , würde ich nicht machen. Grund wird 
öfters im Forum diskutiert.

Ist das das RN-Board:
http://www.shop.robotikhardware.de/shop/catalog/product_info.php?products_id=10

von Bernd K. (berndklein)


Lesenswert?

Konnte mich erst heute wieder melden - bin viel unterwegs und schaffe es 
dann häufig nicht eher.

Schaltplan:
Yeap, ich nutze das RN-Control V. 1.4.1

CLI/SEI im ISR
hab ich mal ausgebaut, brachte aber leider auch keine Änderung

Bufferoverflow:
Das hätte in der Tat zu einem Bufferoverflow führen können. Die Variable 
wird jetzt resetet wenn der Slave angesprochen wird.
1
ISR(TWI_vect)
2
{
3
  static byte i2c_state;
4
  byte twi_status;
5
  //cli();
6
  // Disable Global Interrupt
7
  //uart_puts("ISR ");
8
  // Get TWI Status Register, mask the prescaler bits (TWPS1,TWPS0)
9
  twi_status = TWSR & 0xF8;
10
11
  switch (twi_status) {
12
  case TW_SR_SLA_ACK: // 0x60: SLA+W received, ACK returned
13
    i2c_state = 0; // Start I2C State for Register Address required
14
    I2cReceiveDataLength = I2cReceiveDataIndex = I2cSendDataIndex = 0;
15
    TWCR |= (1 << TWINT); // Clear TWINT Flag
16
    i2c_state = I2C_SLAVE_RX;
17
    uart_puts("-1");
18
    break;
19
20
  case TW_SR_DATA_ACK: // 0x80: data received, ACK returned
21
    I2cReceiveData[I2cReceiveDataIndex++] = TWDR;
22
    I2cReceiveDataLength++;
23
    TWCR |= (1 << TWINT); // Clear TWINT Flag
24
    I2cState = I2C_SLAVE_RX;
25
    uart_puts("-2");
26
    break;
27
28
  case TW_SR_STOP: // 0xA0: stop or repeated start condition received while selected
29
    TWCR |= (1 << TWINT); // Clear TWINT Flag
30
    uart_puts("-3");
31
32
    // if stop condition received, call receive procedure
33
    i2cSlaveReceive(I2cReceiveDataIndex, I2cReceiveData);
34
35
    I2cState = I2C_IDLE;
36
    break;
37
38
  case TW_ST_SLA_ACK: // 0xA8: SLA+R received, ACK returned
39
  case TW_ST_DATA_ACK: // 0xB8: data transmitted, ACK received
40
    TWCR |= (1 << TWINT); // Clear TWINT Flag
41
    I2cState = I2C_SLAVE_TX;
42
    uart_puts("-4");
43
    break;
44
45
  case TW_ST_DATA_NACK: // 0xC0: data transmitted, NACK received
46
  case TW_ST_LAST_DATA: // 0xC8: last data byte transmitted, ACK received
47
  case TW_BUS_ERROR: // 0x00: illegal start or stop condition
48
  default:
49
    I2cReceiveDataLength = I2cReceiveDataIndex = I2cSendDataIndex = 0;
50
    TWCR |= (1 << TWINT); // Clear TWINT Flag
51
    I2cState = I2C_ERROR_NODEV;
52
    uart_puts("-E");
53
  }
54
55
  // Enable Global Interrupt
56
  //sei();
57
  //uart_puts("S#:");
58
  //uart_putc(i2c_state);
59
}

Nach wie vor passiert folgendes:
Der Master sendet die Adresse des Slaves. Slave zeigt auf UART "-1" (ok)
Master sendet 1. Byte - Slave zeigt auf UART "-2" (ok)
Master sendet 2. Byte - Slave zeigt auf UART "-2" (ok)
Master sollte STOP sende - Beim Slave passiert irgend wie nix - alles 
steht.

INFO1:
Der ATMega8 läuft mit internen 8Mhz Takt

INFO2:
Sobald ich den Code für das RN-Control kompiliere und drauf spiele - 
funktioniert es einwandfrei

?????? - sehr eigenartig -
Kann es daran liegen, dass ich beim ATMega8 eine interne Taktquelle 
(8Mhz) verwende?

Gruß
Bernd

von Stefan B. (stefan) Benutzerseite


Lesenswert?

> CLI/SEI im ISR
> hab ich mal ausgebaut, brachte aber leider auch keine Änderung

Offen ist noch: Warum löschst du manuell das TWINT Flag?

Wenn der Slave länger braucht als der Master und sich ein TWI Interrupt 
aufstaut, wird dieser dadurch weggeworfen!

Und du hast auf deinem 8 MHz Slave an deinem 16 MHz Master Probleme, 
dass ein Interrupt mit der STOP Bedingung nicht kommt... richtig?

> Die Variable
> wird jetzt resetet wenn der Slave angesprochen wird.

Offen ist noch: Wie groß ist localbuffer?

von Bernd K. (berndklein)


Lesenswert?

> Offen ist noch: Warum löschst du manuell das TWINT Flag?

Tja, das hab ich in allen Beispielen so gelesen - ohne nachzudenken 
übernommen ;-)

In einem anderen Beispiel für einen Slave habe ich folgendes gelesen und 
auch mal ausprobiert
1
#define TWCR_ACK TWCR   = (1 << TWEN) | (1 << TWIE) | (1 << TWINT) | (1 << TWEA) | (0 << TWSTA) | (0 << TWSTO) | (0 << TWWC);

Hilft aber auch nix :-(

>Und du hast auf deinem 8 MHz Slave an deinem 16 MHz Master Probleme,
>dass ein Interrupt mit der STOP Bedingung nicht kommt... richtig?

Yeap, so sieht es aus

>Offen ist noch: Wie groß ist localbuffer?
10Bytes

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Bernd Klein schrieb:

>>Offen ist noch: Wie groß ist localbuffer?
> 10Bytes

Also weiterhin zu klein.

von Stefan B. (stefan) Benutzerseite


Lesenswert?

> Tja, das hab ich in allen Beispielen so gelesen - ohne nachzudenken
> übernommen ;-)

Dann teile die Beispiele in diejenigen auf die

TWI per Software-Polling ohne TWI-Interrupt machen. Von denen solltest 
du nicht abschauen.

und diejenigen die

TWI per Hardware mit TWI-Interrupt machen. Das sind die für dich 
interessanten. Wenn du da eins findest, welches manuell in der ISR TWINT 
löscht, bitte verlinken :-)

von Bernd K. (berndklein)


Lesenswert?

> Offen ist noch: Wie groß ist localbuffer?
> 10Bytes

> Also weiterhin zu klein.

?? Warum das denn? Ich übertrage doch nur zwei Bytes und dann fängt der 
Buffer beim nächsten START wieder bei 0 an.

>Wenn du da eins findest, welches manuell in der ISR TWINT
>löscht, bitte verlinken :-)

http://www.rn-wissen.de/index.php/TWI_Slave_mit_avr-gcc#Slave_sourcecode

Bei SLAVE-Sourcecode

Abgesehen davon, erklärt das Ganze aber immer noch nicht, warum es auf 
dem RN-Control-Board als SLAVE funktioniert und auf dem ATMega8 nicht 
oder?

von Bernd K. (berndklein)


Lesenswert?

Ach übrigens, hab mich vertan. Der Buffer ist 0x10Bytes groß also 
16Bytes

von Bernd K. (berndklein)


Lesenswert?

Nochmals ein Nachtrag.
Ich habe jetzt mal beim MASTER (RN-Control) etwas geändert. Er sendet 
jetzt 6 Datenbytes - alles kommt auch beim Slave an - fehlerfrei - 
sobald der Master aber jetzt das stop-signal senden möchte ist schluss 
mit lustig.

Nicht geht mehr :-(

Ich versteh es einfach nicht.

Gruß
Bernd

von ... (Gast)


Lesenswert?

Bernd Klein schrieb:
> Der ATMega8 läuft mit internen 8Mhz Takt
...
> Kann es daran liegen, dass ich beim ATMega8 eine interne Taktquelle
> (8Mhz) verwende?

Irgendwas passt da nicht zusammen.
Der interne Takt eines Mega8 ist 1MHz.

von Bernd K. (berndklein)


Lesenswert?

????
die interene kalibrierte Oszillator kann auf 8Mhz gesetzt werden
1
 Auszug ATMEL-Doku: The calibrated internal RC Oscillator provides a fixed 1.0, 2.0, 4.0, or 8.0 MHz clock

Aber mir ist mittlerweile noch was aufgefallen.
Ich habe am MASTER mal vor dem STOP und NACH dem STOP eine UART-Ausgabe 
gemacht - siehe da - ich bekomme beides angezeigt. Soll heißen, STOP 
wurde gesendet aber der Slave kann wohl nicht damit umgehen - warum auch 
immer.

Naja, vielleicht kriege ich es ja mit einem von Euch noch hin :-)

von ... (Gast)


Lesenswert?

Bernd Klein schrieb:
> ????
> die interene kalibrierte Oszillator kann auf 8Mhz gesetzt werden

Mist, da hab ich mich im Datenblatt vertan :(
Du hast natürlich Recht.

von Visitor (Gast)


Lesenswert?

Moin,

Ich hatte schon mal ein ähnliches Problem mit einem Tiny45 als I2C 
Slave.
Bei mir half am Ende eine andere Version von WinAVR. Ich weiß nicht 
mehr, welche Version nicht ging, aber die 20070525 hat funktioniert.

Vielleicht hilft Dir das weiter, Gruß

von Bernd K. (berndklein)


Lesenswert?

Mühsam ernährt sich das Eichhörnchen. Ich bin schon wieder einen Schritt 
weiter :-)

Habe unter folgendem Link: http://www.schatenseite.de/i2c-ledmatrix.html

Eine ATMega8 Version gefunden - auf mein Prototyp-Board gespielt und 
-siehe da es funktioniert.

Mhm, jetzt weiß ich zwar das in meinem Code (Slave) irgendwo der Fehler 
steckt, aber leider nicht wo :-(

Ok, die Suche geht weiter...

von Bernd K. (berndklein)


Lesenswert?

ICH HAB ES GEFUNDEN !

folgende Codezeile führte dazu, dass die Software nicht richtig läuft:
1
i2cSlaveReceive(I2cReceiveDataIndex, I2cReceiveData);

die im Interrupt-Handler beim STOP aufgerufen wurde
1
  case TW_SR_STOP: // 0xA0: stop or repeated start condition received while selected
2
    //TWCR |= (1 << TWINT); // Clear TWINT Flag
3
    // if stop condition received, call receive procedure
4
    uart_puts("-2 ");
5
  // ----> i2cSlaveReceive(I2cReceiveDataIndex, I2cReceiveData);
6
7
    I2cState = I2C_IDLE;
8
    uart_puts(" | ");
9
    TWCR_RESET    ;
10
    break;

dahinter verbirgt sich eine CallBack-Funktion
1
void i2cSetSlaveReceiveHandler(void(*i2cSlaveRx_func)(byte receiveDataLength,
2
        byte* recieveData)) {
3
    i2cSlaveReceive = i2cSlaveRx_func;
4
}

die aus der main.c wie folgt initialisiert wurde
1
i2cSetSlaveReceiveHandler(i2cSlaveReceiveService);

in i2cSlaveReceiveService sollte dann die eigentliche Bearbeitung meiner 
Daten erfolgen.
Aber irgendwas habe ich falsch gemacht - und jetzt bin ich müde und kann 
micht auch nicht mehr konzentrieren - vielleicht morgen.

Fakt ist: der Fehler ist gefunden !

Erstmal an Alle DANKE
Gruß
Bernd

von Tobi (Gast)


Lesenswert?

Noch'n Tip (ohne den Code gesehen zu haben):

Gerne wird der "Error" Status beim Slave übersehen. Das führt dann zum 
hängen vom ganzen I2C Bus (SDA und SCL beine auf Low).

von Andreas V. (tico)


Lesenswert?

Stefan B. schrieb:
> TWI per Hardware mit TWI-Interrupt machen. Das sind die für dich
> interessanten. Wenn du da eins findest, welches manuell in der ISR TWINT
> löscht, bitte verlinken :-)

Verstehe ich nicht. Wieso sollte man TWINT nicht in der ISR löschen? 
Wenn man z.B. das Senden eines Buffers komplett von der ISR machen 
lässt, muss man TWINT sogar in der ISR löschen, denn

> The TWINT Flag must be cleared by software by writing a logic one to
> it. Note that this flag is not automatically cleared by hardware when
> executing the interrupt routine.

So steht es jedenfalls im Datenblatt vom ATmega 164P/324P/644P und ich 
nehme an, dass es bei den anderen AVRs genauso ist.

Gruss
Andreas

von Stefan B. (stefan) Benutzerseite


Lesenswert?

@ Andreas

Mein Fehler. Sorry, wenn ich Verwirrung gestiftet habe. Bei üblichen 
ISRs lässt man i.d.R. das Interruptflag in Ruhe.

TWI-Interrupts sind anders als übliche Interrupt-ISRs zu betrachten. Bei 
TWI wird in der ISR ggf. eine Antwort (z.B. ACK) an den Auslöser des 
Interrupts gesendet (TWCR = ...). Und dabei ist das Setzen des TWINT 
Flags Bestandteil des Antwortmechanismus.

Im Zweifelsfall immer ans Datenblatt oder Atmel Beispielcode halten.

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.