Forum: Mikrocontroller und Digitale Elektronik Probleme mit I2C


von Michael L. (nightflyer88)


Angehängte Dateien:

Lesenswert?

Hallo Zusammen

Ich habe mehrere Attiny85 die als I2C Slave arbeiten. Jeder Slave 
erfasst Daten, wie Temperatur, Strom, Spannung, Drehzahl. Die Messwerte 
werden dann von einem Master ausgelesen.

Ich habe schon mehrere solche Sensoren gebaut, und bis jetzt haben alle 
zuverlässig funktioniert.

Jetzt habe ich einen Drehzahlsensor gebaut, und nun habe ich 
irgendwelche Störungen auf dem I2C Bus. Seltsamerweise funktionierst, 
wenn der Drehzahlsensor alleine am Bus hängt, sobald ein weitere Sensor, 
wie zB Temperatur, dazukommt, funktioniert der Drehzahlsensor nicht mehr 
zuverlässig.

Habe schon den ATtiny85 ersetzt, ohne erfolg...

Kann mir da jemand weiter helfen ??

von I2c (Gast)


Lesenswert?

Eventuell ist das kein Hardwareproblem.
SourceCode ??

von wosnet (Gast)


Lesenswert?

ICs haben keine Blockkondensatoren an den Versorgungspins.
I2C-Leitungen haben keine Pullup-Widerstände.

von icke (Gast)


Lesenswert?

An die Versorgungsleitung vom Mikrocontroller könnte vielleicht jeweils 
ein Kondensator gegen Masse.
Wie sehen die Pull-Ups aus? Wo sind die angebracht?
Wer ist der Master? Wodran erkennst du die Störungen (Oszi?, oder 
nur"keine Verbindung")?
Code vom Master und vom Slave?

von Michael L. (nightflyer88)


Angehängte Dateien:

Lesenswert?

Die Pullup Widerstände befinden sind beim Master.

Das Problem bemerke ich daran, das die Messwerte fehlerhaft ankommen.

Ich habe zu Testzwecken die ganze Messroutine auskommentiert, und den 
Drehzahlmesswert auf 5 gesetzt. Der Master sollte jetzt immer 5 vom 
Slave lesen, manchmal kommt aber 0.

Das Problem habe ich mit zwei anderen Slaves nicht. Sollte also nicht am 
Master liegen.

Habe schon eine funktionierende SW von einem "Temperatursensor" Slave 
geladen, das Problem besteht weiterhin.

Seltsam ist jedoch, das das Problem nur besteht, wenn mehrere Slaves am 
Bus hängen.

Nein - ich habe keine Doppelbelegung der Slaveadressen...

von Frank M. (frank_m35)


Lesenswert?

Einfacher Versuch:
Spiel mal mit den Pull-Up Widerständen:
http://dsscircuits.com/articles/effects-of-varying-i2c-pull-up-resistors.html

von Michael L. (nightflyer88)


Lesenswert?

So ich habe nach einer Pause wieder weiter gearbeitet...

Ich habe das Problem weiter analysiert. Wenn nur ein Slave am Bus hängt, 
funktionierts. Kommt ein zweiter dazu, fangen die Probleme an. Der 
Fehler tritt nicht nur mit dem Drehzahlsensor auf, sonder auch mit den 
anderen Sensoren.

Ich vermute, dass der fehler daran liegt, dass wenn ein Slave die 
falsche adresse empfängt, er den Bus nicht korrekt frei gibt.


Hier ein Kurzfassung des Codes (ist in jedem Slave der gleiche):

Enable Interrupts                    'Interrupts einschalten
Enable Usi_start
On Usi_start I2c_slave               'Bei Startbit gehe zu i2c_slave

Do                                   'Hauptprogramm
Loop

I2c_slave:
  Usisr = &B11000000
  Bitwait Usisr.6 , Set              'Warten bis Slaveadresse empfangen
  If Usidr = Slave_adress Then       'richtige Slave Adresse
    ...Senden oder Empfangen...
  Else                               'falsche Slave Adresse
    Usisr = &B01000000               '<---- ist hier das Problem ??
  End If
Return


Bin mir nicht sicher ob das handling mit der falschen Slave Adresse 
richtig ist. Komme mit dem Datenblatt nicht ganz zurecht...

von Michi (Gast)


Lesenswert?

Michael L. schrieb:
> Ich vermute, dass der fehler daran liegt, dass wenn ein Slave die
> falsche adresse empfängt, er den Bus nicht korrekt frei gibt.
Negative Logic?
Ein Slave sollte sich bei einer "falschen Adresse" überhaupt nicht 
angesprochen fühlen.

von Michael L. (nightflyer88)


Lesenswert?

Genau das meine ich auch !

Wenn der Slave eine "falsche Adresse" empfängt, sollte er die SCL 
Leitung wieder loslassen, und auf einen neue Start-kondition warten.

Aber macht er das ? Mit dem Befehl "Usisr = &B01000000" ?


If Usidr = Slave_adress Then       'richtige Slave Adresse
    ...Senden oder Empfangen...
Else                               'falsche Slave Adresse
    Usisr = &B01000000               '<---- ist hier das Problem ??
End If


Im Datenblatt des ATtiny85 auf Seite 116 ist die Beschreibung zum I2C 
Bus. Aber irgendwie sehe ich nicht wie ich das genau machen muss...

von Michael L. (michaelx)


Lesenswert?

Hi Namensvetter,

kenne den Tiny zwar nicht so genau, aber da ist mir bissel was 
merkwürdig.

Michael L. schrieb:
> So ich habe nach einer Pause wieder weiter gearbeitet...
>
> Ich habe das Problem weiter analysiert. Wenn nur ein Slave am Bus hängt,
> funktionierts. Kommt ein zweiter dazu, fangen die Probleme an. Der
> Fehler tritt nicht nur mit dem Drehzahlsensor auf, sonder auch mit den
> anderen Sensoren.

Was läuft bei dir als Master? Evtl. generiert er keine Stop Condition. 
WIMRE hatte ich das mal bei einem Software-I2C-Master. Da ging dann auch 
nur 1 Slave.

> Ich vermute, dass der fehler daran liegt, dass wenn ein Slave die
> falsche adresse empfängt, er den Bus nicht korrekt frei gibt.
>
>
> Hier ein Kurzfassung des Codes (ist in jedem Slave der gleiche):
>
> Enable Interrupts                    'Interrupts einschalten
> Enable Usi_start
> On Usi_start I2c_slave               'Bei Startbit gehe zu i2c_slave
>
> Do                                   'Hauptprogramm
> Loop
>
> I2c_slave:
>   Usisr = &B11000000
>   Bitwait Usisr.6 , Set              'Warten bis Slaveadresse empfangen
>   If Usidr = Slave_adress Then       'richtige Slave Adresse

Wenn du senden und empfangen willst, solltest du bedenken, dass die 
Slave-Adresse selbst nur 7 Bit lang ist, das 8. Bit ist R/W.

>     ...Senden oder Empfangen...
>   Else                               'falsche Slave Adresse
>     Usisr = &B01000000               '<---- ist hier das Problem ??

Hier hab ich heftig gegrübelt! Wenn ich die Doku richtig verstanden 
habe, bewirkt das Setzen des "Counter Overflow Interrupt Flag" auf 1 das 
Rücksetzen des Flags auf 0. - Naja, von Atmel etwas abenteuerlich 
desingt, um es mal vorsichtig auszudrücken.

Aber müsstest du nicht generell am Ende des Unterprogramms auch das 
"Start Condition Interrupt Flag" zurücksetzen? Also
1
Usisr = &B11000000
 vor dem return.

>   End If
> Return
>
>
> Bin mir nicht sicher ob das handling mit der falschen Slave Adresse
> richtig ist. Komme mit dem Datenblatt nicht ganz zurecht...

Ja, das USI ist schon gruselig, da gibt es bessere Lösungen.

Aber Vielleicht helfen dir ja meine Anmerkungen weiter.

Grüße.

von Michael L. (nightflyer88)


Angehängte Dateien:

Lesenswert?

Probiere nun seit Tagen ohne Erfolg...

Also im Master sieht das Ganze so aus:

  I2cstart
  I2cwbyte Adres                                            'sende Slave 
Adresse
  I2cwbyte &HC1                                             'Befehl 
Anzahl Messwerte
  I2cstart
  Incr Adres
  I2cwbyte Adres                                            'sende Slave 
Adresse +1 f¸r Lesen
  I2crbyte H_adr , Ack                                      'lese 
Antwort
  I2cstop

Am Bus hängt ebenfalls ein BMP085 und ein Eeprom 24C1024, mit denen hat 
bis jetzt immer alles funktioniert, sollte also nicht am Master liegen.

Zu meinem Versuchsaufbau:
zwei Slave am Bus, der erste mit Adresse H28, der zweite H30.

Programm Ablauf:
1. Der Master scant alle Adressen von H28 bis H65, d.h. er schreibt auf 
die erste Adresse (H28) den Befehl HB1, gibt der Slave die Antwort HAA, 
hängt an der Adresse ein Slave. Das ganze geht so weiter bis alle 
Adresse geprüft sind.

2. Der Master kennt nun alle belegten Adressen. Bei jedem Slave fragt er 
nun mit dem Befehl HC1 nach, wieviel Messwerte der Slave besitzt.

So und hier bei Punkt 2 fangen die Probleme an. Beim ersten Slave (H28), 
wird mit dem Befehl HC1 anzahl Messwerte abgefragt, der Slave gibt den 
Wert "2" zurück, soweit gut.
Beim zweiten Slave (H30), mit dem gleichen Befehl (HC1), kommt der Wert 
"255" zurück, sollte aber "1" antworten.

Das Komische --> ändere ich bei Slave(H28) anzahl Messwerte von "2" auf 
"1", funktioniert auch Slave(H30)

von Michael L. (michaelx)


Lesenswert?

Michael L. schrieb:
> Probiere nun seit Tagen ohne Erfolg...
>
> Also im Master sieht das Ganze so aus:

Ich habe mal versucht, das wieder lesbar zu machen ...

>   I2cstart
>   I2cwbyte Adres                   'sende Slave Adresse
>   I2cwbyte &HC1                    'Befehl Anzahl Messwerte
>   I2cstart
>   Incr Adres
>   I2cwbyte Adres                   'sende Slave Adresse +1 f¸r Lesen
>   I2crbyte H_adr , Ack             'lese Antwort
>   I2cstop

Richtig so?

> Am Bus hängt ebenfalls ein BMP085 und ein Eeprom 24C1024, mit denen hat
> bis jetzt immer alles funktioniert, sollte also nicht am Master liegen.

Wenn mehrere Devices gleichzeitig drann waren und auch mal mehr als 1 
Byte pro Devices und mehrere Devices nacheinander erfolgreich gelesen 
wurden, dann sollte es passen.

> Zu meinem Versuchsaufbau:
> zwei Slave am Bus, der erste mit Adresse H28, der zweite H30.
>
> Programm Ablauf:
> ...

Ich bin jetzt nicht so mit dem Basic-Dialekt vertraut, dass ich jetzt 
alles richtig durchdringe, aber ich denke der folgende Code definiert 
den Aufruf der ISR:
1
On Usi_start I2c_slave

Das würde allerdings bedeuten, dass du alles in der ISR abhandelst. 
Möglicherweise liegt das Problem darin, und dein Testergebnis ist nur 
Zufall. Denn normalerweise ollte eine ISR so *kurz* wie *möglich* 
gehalten sein. D.h. du solltest möglichst nur die Adresse prüfen, und 
bei Übereinstimmung nur ACK signalisieren, und den Rest im Hauptprogramm 
machen.

Auch solltest du das clock stretching nicht vergessen, das nimmt dir das 
USI nicht ab, soweit ich das sehe.

Eine Frage ist auch noch offen:
Was hast du als Master, und ist das Hardware-I2C oder Software?

Grüße.

von Michael L. (nightflyer88)


Lesenswert?

Ja da hast du recht, die ganze SW in der ISR ist nicht optimal. Ich 
versuche nun das ganze so zu machen wie es von ATMEL gedacht wäre. Mit 
START interrupt und OVF Interrupt.

Habe nun ein ganz anderes Problem: springe ich von der OVF ISR zurück 
und beim nächsten OVF wieder hinein, übernimmt BASCOM das byte 
"I2c_mode" nicht, habe also immer den wert 0 !?!

Nun ein ganz einfaches Beispiel:
START, 2 Byte zum Slave übertragen, STOP

Master:  ATtiny45, SW I2C

Bascom-Code:

i2cstart
i2cwbyte &h30       'Slave Adresse
i2cwbyte &hB1       'Daten byte
i2cstop




Slave: ATtiny85, HW USI

Bascom-Code:

Enable Interrupts             'Interrupts einschalten
Enable Usi_start
On Usi_start I2c_start        'Bei Startbit gehe zu I2c_start
On Usi_ovf I2c_read_write     'Bei Daten¸berlauf gehe zu I2c_read_write

Do                            'Hauptprogramm
Loop

End

I2c_start:
  I2c_mode = 0
  Acknowledge = 0
  Enable Usi_ovf
  Usisr = Bits(7 , 6)          'Reset Startbit, Reset Counteroverflow
  Usidr = 0
Return


I2c_read_write:
  If Acknowledge = 0 Then
    Select Case I2c_mode
      Case 0                       'Adress Mode
        Select Case Usidr
          Case Slave_adress
            I2c_mode = 1
            Acknowledge = 1
            Usisr = 14             'ACK
            Usidr = 0
            Usisr = Bits(6)
          Case Master_read
            I2c_mode = 2
          Case Else
            'Usisr = Bits(6)
            'Disable Usi_ovf
        End Select
      Case 1                       'Master write Mode
        Printbin #1 , Usidr
      Case 2                       'Master read Mode
    End Select
  Else
    Usisr = 0
    Usidr = Bits(7)
    Usisr = Bits(6)
    Acknowledge = 0
  End If
Return



Bei der START-kondition springt der Slave zu "I2c_start:" und schaltet 
das Overflow Interrupt  ein. Bei einen Counter Overflow, springt der 
Slave zu "I2c_read_write:", prüft die empfangene Adresse und setzt das 
"I2c_mode" byte auf 1.

Bei einem erneuten OVF interrupt ist das "I2c_mode" byte wieder 0 ! 
Bascom übernimmt den wert irgendwie nicht !?!

Die Adresse wird vom Slave richtig empfangen, und das "I2c_mode" byte 
wird auch wirklich auf 1 gesetzt, an dem liegt es nicht.

von Michael L. (michaelx)


Lesenswert?

Hi,

ich bin jetzt nicht der BASCOM-Experte, habe es mehr mit ASM und C. Aber 
wenn der Wert der Variablen scheinbar nicht gespeichert wird, könnte es 
daran liegen, dass diese nur lokal (und damit üblicherweise flüchtig) 
ist. Du musst sie also global definieren.

Grüße.

von Michael L. (nightflyer88)


Lesenswert?

ja genau das wird es sein. Die Variable wird wahrscheinlich im SRAM 
durch etwas anderes überschrieben.

Aber wie mache ich das genau ?

Ich habe mal mit $hwstack = 32, $swstack = 10, $framesize = 40 probiert, 
ändert aber am resultat nichts.

Die Variable kann ich an einer bestimmten Adresse im SRAM ablegen, geht 
aber auch nicht.
DIM i2c_mode as byte at &h100 overlay

In Bascom kann man doch irgendwo schauen, an welcher adresse im SRAM 
welche Variable abgelegt wird ?

von Michael L. (michaelx)


Lesenswert?

Da kann ich dir leider nicht helfen.

Mir fällt da nur noch ein:

1. Variable auf jeden Fall global deklarieren
2. Hab noch gesehen, das BASCOM beim ISR-Aufruf Variablen sichert und am 
Ende wiederhstellt. Das kann man auch steuern, aber musst du dir selbst 
mal richtig anschauen.

Viel Erfolg noch.

von Jobst M. (jobstens-de)


Lesenswert?

Wie sieht denn die Verdrahtung des I²C-Busses aus? Ist es noch ein Bus?

Zwei Slaves die selbe Adresse?

Ist das richtig, dass der Master nicht die Hardware TWI benutzt?



Gruß

Jobst

von Michael L. (nightflyer88)


Angehängte Dateien:

Lesenswert?

So die Sache mit den Variablen funktioniert. Auch mehrere bytes 
empfangen funktioniert ebenfalls. Nun habe ich Probleme beim Senden.

Der Code im Master:

I2cstart
I2cwbyte &H31                             'sende Slave Adresse
I2crbyte A , Ack                          'lese Antwort
I2cstop

Printbin #1 , A


Der Code vom Slave oben im Anhang.
Zur Funktionsweise des Slaves:

1. Startkondition empfangen, Adress_mode, setzte i2c_mode = 0
2. Slave Adresse empfangen -> Master_read mode, setzte i2c_mode = 12
3. ACK an Master, setzte i2c_mode = 2, Byte zum senden ins Datenregister 
(USIDR = 52)  legen.

bis dahin funktionierts, nun kommt aber beim Master nichts an !? Kommt 
immer 0 an.

Bin mir nicht sicher ob ich das mit dem INPUT und OUTPUT richtig mache

von Michael L. (nightflyer88)


Lesenswert?

Ich weis einfach nicht  wie ich das machen muss, die Daten werden 
einfach nicht zum Master übertragen. Beim Master kommt jetzt immer &HFF 
an.

Der SDA Pin sollte doch einfach auf OUTPUT geschaltet werden und so 
sollten die Daten zum Master übertragen werden ?!?

von Jobst M. (jobstens-de)


Lesenswert?

Michael L. schrieb:
> Ich weis einfach nicht  wie ich das machen muss

Vielleicht solltest Du einfach mal die Fragen beantworten, die Dir 
gestellt werden ...

von Michael L. (nightflyer88)


Lesenswert?

> Wie sieht denn die Verdrahtung des I²C-Busses aus? Ist es noch ein Bus?
SDA und SCL Leitung mit je einem 4.7k Pullup

> Zwei Slaves die selbe Adresse?
Unmöglich, da nur ein Slave dranhängt

> Ist das richtig, dass der Master nicht die Hardware TWI benutzt?
Nein, ist SW mit Bascom

Mein Versuchsaufbau:
Master: Attiny45, SW I2C mit Bascom
Slave: Attiny85, HW I2C mit Bascom

sonst hängt da nichts mehr am Bus


Wie gesagt an der HW und am Master liegts nicht, es geht jetzt nur darum 
wie ich das USI vom Slave programmieren muss, damit er die Daten an den 
Master sendet.

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.