Forum: Mikrocontroller und Digitale Elektronik Probleme mit I2C-Kommunikation zwischen STM32 und AVR-Slaves


von mh (Gast)


Angehängte Dateien:

Lesenswert?

Hallo,
ich versuche vier AVRs (Slaves) mit einem STM32 (Master) über I2C 
kommunizieren zu lassen.
Die Kommunikation läuft immer eine Zeit lang (etwa 10 sec), bis einer 
der Slaves die SDA-Leitung permanent auf Low zieht (auf dem einen Bild 
ist die letzte Aktion auf dem Bus dargestellt).
Was mir auch aufgefallen ist, dass manchmal ein größerer Abstand 
zwischen Slave-Adresse+W und Datenbyte ist, woran liegt das? (siehe 
Bild)
Um das Ganze wieder laufen zu lassen, bringt es nur was den Slave zu 
finden und zu resetten der gegen Low zieht, den STM32 zu resetten bringt 
nix.
Zwischen STM32 und AVRs ist ein PCA9515, auf der 3,3V-Seite mit 
4,7k-Pullups und auf der 5V-Seite 3,6k-Pullups.
Hab das ganze bereits mit 10kHz, 100kHz und 400kHz Busfrequenz probiert, 
immer dasselbe Ergebnis.

Den Quell-Code für den STM32 findet man in dem Thread hier:

Beitrag "STM32: I2C-Error-Handling IRQ wird nicht aufgerufen"

Der Quell-Code von den AVR-Slaves ist hier:
1
#include <avr/interrupt.h>
2
#include <avr/io.h>
3
#include <stdint.h>
4
#include "TWI_Slave.h"
5
6
#define ACK_ENABLE    TWCR = (1<<TWEN)|(1<<TWIE)|(1<<TWINT)|(1<<TWEA)|(0<<TWSTA)|(0<<TWSTO)|(0<<TWWC);
7
#define ACK_DISABLE    TWCR = (1<<TWEN)|(1<<TWIE)|(1<<TWINT)|(0<<TWEA)|(0<<TWSTA)|(0<<TWSTO)|(0<<TWWC);
8
#define TWI_RESET    TWCR = (1<<TWEN)|(1<<TWIE)|(1<<TWINT)|(1<<TWEA)|(0<<TWSTA)|(0<<TWSTO)|(0<<TWWC);
9
10
// Initialisiert den Slave mit einer TWI-Adresse und macht ihn bereit
11
// zum Empfang von Daten
12
void TWI_SLA_Init(void)
13
{
14
  TWAR = (TWI_SLA_ADR | (1<<TWGCE));      // Set own TWI slave address ( +1 for General Call).
15
  TWDR = 0xFF;                // Default content = SDA released.
16
  TWI_RESET;
17
}
18
19
20
ISR(TWI_vect)
21
{
22
  static uint8_t datacounter = 0;
23
  
24
  switch (TWSR)
25
  {
26
// --- TWI Slave Transmitter status codes ----------------------------------
27
28
    case TWI_STX_ADR_ACK:    // Own SLA+R has been received; ACK has been returned
29
      datacounter = 0;
30
31
    case TWI_STX_DATA_ACK:    // Data byte in TWDR has been transmitted; ACK has been received
32
    
33
      switch (datacounter)
34
      {
35
        case 0:
36
          TWDR = error;
37
          datacounter = 1;
38
          break;
39
        case 1:
40
          TWDR = strom;
41
          datacounter = 2;
42
          break;
43
        case 2:
44
          TWDR = spannung;
45
          datacounter = 3;
46
          break;
47
        case 3:
48
          // Bei Motorstart soll 255 ausgegeben werden, ansonsten commutate_time
49
          TWDR = (motor_start ? 255 : commutate_time);
50
          // Bei Drehzahl-Berechnung mit Prescaler = 256 rechnen! (wegen shift)
51
          datacounter = 0;
52
          break;
53
        default:
54
          TWDR = 0;
55
          datacounter = 0;
56
      }
57
      
58
      ACK_ENABLE;
59
      break;
60
61
// ---  TWI Slave Receiver status codes ------------------------------------
62
63
    case TWI_SRX_ADR_ACK:    // Own SLA+W has been received ACK has been returned
64
      
65
      ACK_ENABLE;
66
      break;
67
68
    case TWI_SRX_ADR_DATA_ACK:  // Previously addressed with own SLA+W;
69
                  // data has been received; ACK has been returned
70
      twi_timeout = 0;
71
      soll_pwm = TWDR;
72
      ACK_ENABLE;
73
      break;
74
75
76
    case TWI_SRX_ADR_DATA_NACK:
77
    case TWI_STX_DATA_NACK:
78
    case TWI_STX_DATA_ACK_LAST_BYTE:
79
    case TWI_SRX_STOP_RESTART:
80
    default:
81
      ACK_DISABLE;
82
  }
83
}


Schonmal Vielen Dank für Tipps und Hinweise. Falls weitere Informationen 
benötigt werden, einfach Bescheid sagen.

von aSma>> (Gast)


Lesenswert?

Servus,
mh schrieb:
> Zwischen STM32 und AVRs ist ein PCA9515, auf der 3,3V-Seite mit
> 4,7k-Pullups und auf der 5V-Seite 3,6k-Pullups.
> Hab das ganze bereits mit 10kHz, 100kHz und 400kHz Busfrequenz probiert,
> immer dasselbe Ergebnis.

Die beiden stm32 pins sind 5 Volt tolerant. Man braucht nur einmal einen 
Pullup anzuschließen. Bei 400khz und 5 Volt kannst du so 1,8K einmal an 
SDA und SCL anschließen! Aber du hast ja ein Scope und dadurch kannst du 
die Widerstände perfekt anpassen.
1
i2c_BusyFlag = 0;
2
bldc_communication_enable = 0;
3
bldc_communication_BusyFlag = 0;

sind diese Var volatile?

Sonst fange an zu debuggen und gucke das Programm stehen bleibt.

mfg

von Dr. Sommer (Gast)


Lesenswert?

Ohne dein Problem jetzt im Detail analysiert zu haben, ich hatte mal in 
einer ähnlichen Konstellation (STM32 I2C Master, AVR Slave) das Problem 
dass der STM32 zu schnell gesendet hat, und der AVR nicht mit gekommen 
ist und dann die Kommunikation blockiert hat. Versuch mal Pausen 
zwischen Adresse senden und Bytes übertragen, und zwischen die Bytes, 
einzubauen, damit der AVR Zeit hat zu reagieren.

von mh (Gast)


Lesenswert?

Danke für die Antworten.
Oben im Code muss bei default:
1
default:
2
      ACK_ENABLE;
3
  }
stehen, sonst tut sich überhaupt nix ;)

aSma>> schrieb:
> Die beiden stm32 pins sind 5 Volt tolerant. Man braucht nur einmal einen
> Pullup anzuschließen. Bei 400khz und 5 Volt kannst du so 1,8K einmal an
> SDA und SCL anschließen! Aber du hast ja ein Scope und dadurch kannst du
> die Widerstände perfekt anpassen.

Ok, wusste ich nicht.

> i2c_BusyFlag = 0;
> bldc_communication_enable = 0;
> bldc_communication_BusyFlag = 0;
>
> sind diese Var volatile?

Ja, sind alle volatile.

> Sonst fange an zu debuggen und gucke das Programm stehen bleibt.

Ich vermute mal dass es am STM32 liegt, da alles mit nem AVR als Master 
funktionierte.

Dr. Sommer schrieb:
> Ohne dein Problem jetzt im Detail analysiert zu haben, ich hatte mal in
> einer ähnlichen Konstellation (STM32 I2C Master, AVR Slave) das Problem
> dass der STM32 zu schnell gesendet hat, und der AVR nicht mit gekommen
> ist und dann die Kommunikation blockiert hat. Versuch mal Pausen
> zwischen Adresse senden und Bytes übertragen, und zwischen die Bytes,
> einzubauen, damit der AVR Zeit hat zu reagieren.

Kommen daher die Pausen? Kann der Slave da Pausen erzwingen? (siehe 
gepostetes Bild)

von mh (Gast)


Lesenswert?

Ich finds seltsam, dass der Slave (siehe Bild der letzten Aktion auf dem 
Bus) einfach die SDA-Leitung auf Low zieht, obwohl der ja noch nix zu 
sagen hat. Hier versucht der Master ein neues Telegram zu schicken, 
verschickt ein Start-Signal und beim ersten Datenbit geht irgendwas beim 
Slave schief.

von Klaus (Gast)


Lesenswert?

mh schrieb:
> Hier versucht der Master ein neues Telegram zu schicken,
> verschickt ein Start-Signal und beim ersten Datenbit geht irgendwas beim
> Slave schief.

Das passiert gerne, wenn ein Read nicht mit einem NAK beim letzten Byte 
abgeschlossen wird. Der Slave will unbedingt noch etwas loswerden. Da 
hilft dann nur, per Bitbanging auf SCL den Rest des Bytes auf dem Slave 
"herauszutakten". Wenn man dabei SDA offen lässt, ergibt sich ein NAK 
und der Spuk ist vorbei.

MfG Klaus

von mh (Gast)


Lesenswert?

Klaus schrieb:
> mh schrieb:
>> Hier versucht der Master ein neues Telegram zu schicken,
>> verschickt ein Start-Signal und beim ersten Datenbit geht irgendwas beim
>> Slave schief.
>
> Das passiert gerne, wenn ein Read nicht mit einem NAK beim letzten Byte
> abgeschlossen wird. Der Slave will unbedingt noch etwas loswerden. Da
> hilft dann nur, per Bitbanging auf SCL den Rest des Bytes auf dem Slave
> "herauszutakten". Wenn man dabei SDA offen lässt, ergibt sich ein NAK
> und der Spuk ist vorbei.
>
> MfG Klaus

Ok, gut zu wissen.
Der Fehler passiert hier im reinen Sendebetrieb, also Master sendet zum 
Slave.
Falls SDA permanent auf Low ist, reagiert das I2C-Modul vom STM32 auch 
nicht mehr.

Dr. Sommer schrieb:
> Ohne dein Problem jetzt im Detail analysiert zu haben, ich hatte mal in
> einer ähnlichen Konstellation (STM32 I2C Master, AVR Slave) das Problem
> dass der STM32 zu schnell gesendet hat, und der AVR nicht mit gekommen
> ist und dann die Kommunikation blockiert hat. Versuch mal Pausen
> zwischen Adresse senden und Bytes übertragen, und zwischen die Bytes,
> einzubauen, damit der AVR Zeit hat zu reagieren.

Hab das mit der Pause mal ausprobiert, leider kein Erfolg...

von m.n. (Gast)


Lesenswert?

Bei einem STM32F4 hat mich das IIC-Interface einmal so genervt, daß ich 
die betreffenden Pins per Software hab wackeln lassen, wie vor 
Jahrzehnten beim 8051 (o.ä.).
Dann lief der Kram fehlerfrei ;-)

von mh (Gast)


Lesenswert?

m.n. schrieb:
> Bei einem STM32F4 hat mich das IIC-Interface einmal so genervt,
> daß ich
> die betreffenden Pins per Software hab wackeln lassen, wie vor
> Jahrzehnten beim 8051 (o.ä.).
> Dann lief der Kram fehlerfrei ;-)

Den Gedanken hatte ich auch schon, bin aber wohl noch nicht so weit ;-)

Stimmt das Programm des I2C-Slaves eigentlich so wie ich ihn 
programmiert hab (also wie ich ACK enable)? Hab das ganze mal zu einem 
Minimalprogramm zusammengekürzt:
1
#define ACK_ENABLE    TWCR = (1<<TWEN)|(1<<TWIE)|(1<<TWINT)|(1<<TWEA)|(0<<TWSTA)|(0<<TWSTO)|(0<<TWWC);
2
#define ACK_DISABLE    TWCR = (1<<TWEN)|(1<<TWIE)|(1<<TWINT)|(0<<TWEA)|(0<<TWSTA)|(0<<TWSTO)|(0<<TWWC);
3
4
// Initialisiert den Slave mit einer TWI-Adresse und macht ihn bereit
5
// zum Empfang von Daten
6
void TWI_SLA_Init(void)
7
{
8
  TWAR = (TWI_SLA_ADR | (1<<TWGCE));      // Set own TWI slave address ( +1 for General Call).
9
  TWDR = 0xFF;                // Default content = SDA released.
10
  ACK_ENABLE;
11
}
12
13
14
ISR(TWI_vect)
15
{
16
  static uint8_t datacounter = 0;
17
  
18
  switch (TWSR)
19
  {
20
21
// ---  TWI Slave Receiver status codes ------------------------------------
22
23
    case TWI_SRX_ADR_ACK:    // Own SLA+W has been received ACK has been returned
24
      
25
      ACK_ENABLE;
26
      break;
27
28
    case TWI_SRX_ADR_DATA_ACK:  // Previously addressed with own SLA+W;
29
                  // data has been received; ACK has been returned
30
      twi_timeout = 0;
31
      soll_pwm = TWDR;
32
      ACK_ENABLE;
33
      break;
34
35
    default:
36
      ACK_ENABLE;
37
  }
38
}

von mh (Gast)


Lesenswert?

Achja, vielleicht noch was ich überhaupt erreichen will.
Ich möchte Sla-Adr+W und 1 Datenbyte zum Slave senden.

von aSma>> (Gast)


Lesenswert?

Servus,
man könnte noch folgendes ausprobieren:
1
void I2C_StretchClockCmd(I2C_TypeDef* I2Cx, FunctionalState NewState);

Achtung, desto mehr Bauteile vorhanden sind, desto größer die 
Wahrscheinlichkeit einen Fehler zu erzeugen.

Am besten erstmal eine minimal Schaltung mit 1 slave und 1 master und 
zwei Pullups bei 100khz.

Nicht vergessen sollte man, dass die Slaves alle eine andere Slave_Add 
haben müssen!

mfg

von m.n. (Gast)


Lesenswert?

mh schrieb:
>> Bei einem STM32F4 hat mich das IIC-Interface einmal so genervt,
>> daß ich
>> die betreffenden Pins per Software hab wackeln lassen, wie vor
>> Jahrzehnten beim 8051 (o.ä.).
>> Dann lief der Kram fehlerfrei ;-)
>
> Den Gedanken hatte ich auch schon, bin aber wohl noch nicht so weit ;-)

Mit Deiner Warterei machst Du den gleichen Fehler wie ich ;-)

von mh (Gast)


Angehängte Dateien:

Lesenswert?

m.n. schrieb:
> mh schrieb:
>>> Bei einem STM32F4 hat mich das IIC-Interface einmal so genervt,
>>> daß ich
>>> die betreffenden Pins per Software hab wackeln lassen, wie vor
>>> Jahrzehnten beim 8051 (o.ä.).
>>> Dann lief der Kram fehlerfrei ;-)
>>
>> Den Gedanken hatte ich auch schon, bin aber wohl noch nicht so weit ;-)
>
> Mit Deiner Warterei machst Du den gleichen Fehler wie ich ;-)

Ich bezweifel inzwischen dass es am STM32 liegt.
Die AVR-Slaves steuern BLDC-Motoren an. Falls die Motoren stillstehen 
funktioniert die Kommunikation ohne Probleme.
Falls die Motoren laufen (es laufen somit andere IRQs im Hintergrund 
mit), kommt es zu Verzögerungen in der Kommunikation von Seiten der 
AVRs.
Hab hier nochmal ein neues Bild angehängt. Hier sieht man den Ausfall 
der Kommunikation nach einem vollständigen Telegramm (der passende Slave 
hat vorher sogar mit ACK quittiert). Alle! Slaves ziehen dann SDA gegen 
Low (SDA geht nur wieder auf High, falls alle Slaves resettet wurden).

aSma>> schrieb:
> Servus,
> man könnte noch folgendes ausprobieren:void
> I2C_StretchClockCmd(I2C_TypeDef* I2Cx, FunctionalState NewState);

Hab ich ohne Erfolg getestet.

> Achtung, desto mehr Bauteile vorhanden sind, desto größer die
> Wahrscheinlichkeit einen Fehler zu erzeugen.
>
> Am besten erstmal eine minimal Schaltung mit 1 slave und 1 master und
> zwei Pullups bei 100khz.
>
> Nicht vergessen sollte man, dass die Slaves alle eine andere Slave_Add
> haben müssen!
>
> mfg

Alle haben unterschiedliche Adressen (Kommunikation geht ohne 
Motorbetrieb).

von mh (Gast)


Lesenswert?

Man erhält auch genau dasselbe Verhalten, falls nur ein Slave am Bus 
hängt.
Wenn man dann diesen einen resettet geht alles wieder für eine Weile.

von mh (Gast)


Lesenswert?

Ok, es liegt definitiv am STM32 ;-)
Hab jetzt einen AVR als Master und der kann alles ansteuern, ohne dass 
etwas jemals ausfällt.
Ich versteh aber dann nicht was da genau schief läuft.
Warum verleitet der STM32 die Slaves dazu sich aufzuhängen, bzw. SDA auf 
Low zu ziehen??

von mh (Gast)


Angehängte Dateien:

Lesenswert?

Hab hier noch ein paar Bilder.
Die Lücken entstehen offensichtlich aus den Bearbeitungszeiten im Slave.
"Lücke1" lässt sich zumindest damit erklären und damit kommt scheinbar 
der STM32 auch zurecht.
Der STM32 scheint "Lücke2" nicht zu vertragen und hängt dann.
Der AVR als Master wartet einfach ab.
Weiß jemand wie man das lösen kann??

von ... (Gast)


Lesenswert?

Aus meiner Erfahrung kann ich nur sagen Finger weg von I2C in Umgebungen 
mit elektrischen Störungen. I2C zusammen mit Motoren o.ä. ist eine 
einzige Katastrophe. Wenn du noch die Möglichkeit hast etwas zu ändern 
dann nimm lieber was anderes.

von Pieter (Gast)


Lesenswert?

sorry, das ist ulk.

Motor+I2C wird bei uns in einem Blutbehandlungsgerät seit Jahren 
eingesetzt.
Läuft einfach.

Habe hier STM32F4 (Master) gegen 8051(Slave) mit 100KHz I2C laufen.
Wenn der 8051 den Motor "bearbeitet" wird SCL auf Low gezogen und der 
Master wartet dann schon mal 100us bis es weitergeht.

VG
Pieter

von mh (Gast)


Lesenswert?

Pieter schrieb:
> Habe hier STM32F4 (Master) gegen 8051(Slave) mit 100KHz I2C laufen.
> Wenn der 8051 den Motor "bearbeitet" wird SCL auf Low gezogen und der
> Master wartet dann schon mal 100us bis es weitergeht.

Wie hast du den I2C vom STM32 initialisiert?
Meine Routinen sind hier zu finden:
Beitrag "STM32: I2C-Error-Handling IRQ wird nicht aufgerufen"

Ich sag mal nach der passenden Adressierung+W macht die Lücke oder das 
Warten auf den Slave ja Sinn.
Aber die Lücke im Bild "Luecke2" tritt ja vor der eigentlichen 
Adressierung auf. Ich schätz mal dass der STM32 damit auch nicht 
zurechtkommt, der AVR aber schon (warum auch immer).

von Pieter (Gast)


Lesenswert?

STM32F4 ferkel ich in Pascal, 8051 in ASM.
Mit I2C Clock Stretch kann ein Slave den Master jeder Zeit anhalten. 
Eine Zeitüberwachung ist notwendig.

von aSma>> (Gast)


Lesenswert?

Servus,
dein SDA Leitung hat voll die Störungspeaks. Du könntest es mal mit 
einen Wellenwiderstand versuchen.

Weiterhin muss bei einer Störung der Master ein Reset machen! Ich kennen 
jetzt nicht genau, ob der f4 einen festen Timeout hat.
1
void I2C1_ER_IRQHandler(void)

Wie sieht es aus wird dieser Handler aktiviert? Kannst mal eine LED 
togglen.

Als alternative kann man ein Timeout in deine while(busy) Schleife 
reinprügeln.

Sonst EMV Vorschriften beachten.

mfg

von mh (Gast)


Lesenswert?

aSma>> schrieb:
> Servus,
> dein SDA Leitung hat voll die Störungspeaks. Du könntest es mal mit
> einen Wellenwiderstand versuchen.

Die Peaks entstehen vermutlich, da zu dem Zeitpunkt der Master die 
Leitung loslässt, der Slave die aber wieder auf Low zieht (= ACK).

> Weiterhin muss bei einer Störung der Master ein Reset machen! Ich kennen
> jetzt nicht genau, ob der f4 einen festen Timeout hat.void
> I2C1_ER_IRQHandler(void)
>
> Wie sieht es aus wird dieser Handler aktiviert? Kannst mal eine LED
> togglen.

Die wird leider nicht aufgerufen. Laut Reference_Manual ist der 
Fehlerfall wenn eine Leitung von einem Slave auf Low gezogen wird nicht 
im Error-Handler mit drin.
Reset beim STM32 bringt leider nix, da ja der Slave weiterhin auf Low 
zieht. Gibt es eine Routine o.ä., wo einfach 9 Takte bei SCL ausgegeben 
wird? Das könnte die Situation beheben.
Ein anderer Ansatz wäre das Auftreten von den Lücken vor den 
Adressdaten, am Slave zu beheben. Aber wie?

von mh (Gast)


Lesenswert?

Aktiviert eigentlich:
1
void I2C_StretchClockCmd(I2C_TypeDef* I2Cx, FunctionalState NewState);
das Clock-Stretching nur im Slave-Mode oder auch im Master-Mode?
Wenn es für den Master-Mode auch geht, was macht es da genau?
Aus der Doku werd ich nicht so recht schlau...

von mh (Gast)


Lesenswert?

Noch eine andere Frage...
Ist Clock-Stretching vor Addr+W vom Slave aus laut Standard erlaubt?

von mh (Gast)


Lesenswert?

Keine eine Idee??
Wenn es mit nem AVR als Master geht muss es doch irgendwie (mit 
einfachen Mitteln) auch mit einem STM32 als Master gehen...

von aSma>> (Gast)


Lesenswert?

Servus,
mach mal beim avr ein timeout rein. Aber das ist nur als 
Vorsichtsmaßnahme im Fehlerfall anzusehen und soll nicht als Dauerlösung 
angesehen werden!

Clockstrech sollte natürlich der Slave drauf haben.

Sonst versuche es mit den Wellenwiderstand oder Optokoppler.

von m.n. (Gast)


Lesenswert?

aSma>> schrieb:
> mach mal beim avr ein timeout rein. Aber das ist nur als
> Vorsichtsmaßnahme im Fehlerfall anzusehen und soll nicht als Dauerlösung
> angesehen werden!
>
> Clockstrech sollte natürlich der Slave drauf haben.
>
> Sonst versuche es mit den Wellenwiderstand oder Optokoppler.

Das ist doch alles Stochern mit der Stecknadel im Heuhaufen aber keine 
Lösung des Problems.

mh schrieb:
> Keine eine Idee??

Doch, s.o.
Da muß man auch keinen Wellenwiderstand kaufen ;-)

von mh (Gast)


Lesenswert?

Servus,
danke für die Antwort!
Hab was interessantes rausgefunden.
Falls ich beim AVR noch zusätzlich Bus-Error in den case behandle und 
dort dann das Stop-Bit setz, dann gibt er den Bus wieder frei.
Dann genügt es nur den STM32 zu resetten und alles läuft wieder für ne 
Zeit.
Beim STM32 muss ich dann im Event-IRQ das STOPF-Flag abfragen, da er 
dann da reinspringt, falls der Fehler auftaucht.

von mh (Gast)


Lesenswert?

Also es funktioniert jetzt scheinbar.
Ich initialisiere das I2C-Modul bei der STOPF-Abfrage im EV-IRQ neu. 
Dann funktioniert alles (anscheinend).
Aber kommt mir trotzdem ein wenig gefrickelt vor...

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.