Forum: Mikrocontroller und Digitale Elektronik HOWTO: Dynamische I2C Adressen (per USI)


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Andreas J. (ajbln)


Angehängte Dateien:

Bewertung
1 lesenswert
nicht lesenswert
Hier eine kleine Anleitung, wie man dynamisch I2C Adressen vergeben 
kann.

Was heißt eigentlich dynamisch?
Bei I2C haben die Slaves normalerweise feste Adressen. Oder bei den 
meisten ICs eben Jumper, um diese festzulegen.
Nun möchte man aber manchmal vielleicht mehrere Mikrocontroller mit 
einem Bus vernetzen und sie mit einem Master kommunizieren lassen.
Und das, ohne a) extra Adress-Jumper auf der Platine vorzusehen (um 
mögliche Fehlbenutzungen zu vermeiden und/oder Platz zu sparen) oder b) 
mehrere Softwareversionen auszuliefern, die sich nur anhand der 
I2C-Adresse unterscheiden.

Ziel ist es also, eine Software zu schreiben, die es ermöglicht, dass 
sich die Slaves mit Hilfe eines Masters eine im Bus eindeutige 
I2C-Adresse aushandeln.

In meinem Beispiel ist es übrigens eine skalierbare 
Aquariumsbeleuchtung: ein Mastercontroller (ATmega) steuert bis zu 8 
LED-Controller (ATtiny) an.

Da ich I2C als Busprotokoll vorgesehen habe, lässt sich dafür natürlich 
die im I2C-Protokoll definierte Kollisionserkennung (arbitration) dafür 
verwenden.
Definiert ist diese eigentlich nur für den Multimaster-Betrieb, lässt 
sich aber natürlich genauso auch für den Singlemaster/Multislave-Betrieb 
anwenden.
Die Kollisionserkennung funktioniert so, dass mehrer Sender, die über 
die selbe Adresse angesprochen wurden, natürlich gleichzeitig ihre Daten 
senden. Aber nach dem Senden ihres Bits auf der SDA-Leitung prüfen, ob 
das Bit auch tatsächlich so auf dem Bus angekommen ist.
Sendet also ein Slave ein High und ein anderer zeitgleich ein Low, 
erkennt (durch die stärkere Wirkung von Low) der Slave mit dem High, 
dass ein Low anliegt und beendet die Übertragung umgehend.

Nun brauchen wir aber noch ein paar Zutaten für die dynamische 
Adressvergabe:
1) Eine für alle Slaves einheitliche I2C-Adresse zum Aushandeln der 
dedizierten Slave-Adresse mit dem Master.
2) Eine eindeutige „ID“, die jeder Slave an den Master schickt, um von 
ihm eine neue Adresse zu erhalten.

Der Algorithmus funktioniert pseudomäßig so:
GCA = General Call Address, also die Slave-Adresse, auf der alle Slaves 
horchen
GET ID = Registerwert für „gebt mir eure ID“
SET Address = Registerwert für „der Slave mit der ID x bekommt die 
Adresse y“

Master: [GCA (w)], [GET ID], [I2C-Restart], [GCA (r)]
Die (noch nicht durchnummerierten) Slaves antworten mit: [ID], wobei nur 
einer am Ende übrig bleibt (der mit den meisten Low’s am Anfang).
Der Master sendet dann: [I2C-Restart], [GAC (w)][SET 
Address][ID][Address][Stop]

Alle Slaves lesen mit und der Slave mit der passenden ID übernimmt seine 
neue eindeutige Adresse. Der Master kontrolliert dies, in dem er den 
Slave mit dieser Adresse anpingt.
Der Master muss das dann natürlich solange wiederholen, bis er alle 
Slaves gefunden hat, also kein Slave mehr eine ID sendet.

Als „ID“ bietet sich eine Zufallszahl an, eine „echte“ natürlich. 
Idealerweise über das Rauschen eines ADC-Pins. Man kann natürlich auch 
irgendeine eingebrannte ID von einem Chip nehmen (wie bei einem DS18B20 
z.B.). Für eine etwas bessere zufällige Bitverteilung sollte man jedes 
Zufallsbit für seine Zufallszahl aus dem niederwertigsten Bit einer 
einzelnen ADC-Berechnung nehmen. Sprich für 32 Bit (wie in meinem Falle) 
benötige man 32 ADC-Berechnungen.
Jetzt kommen wir zu einer Hürde, die ich zumindest bei den ATtiny’s 
erstmal überwinden musste:
ATtiny’s haben als I2C-Hardware-Support das sogenannte USI. Und 
„eigentlich“ sogar eine Kollisionserkennung im USISR Register: Das Bit 
USIDC wird demnach high, wenn die Hardware feststellt, dass SDA nicht so 
ist wie gesetzt (also - s.o. – dass ein High gesendet, aber ein Low 
detektiert wurde, der umgekehrte Fall existiert ja nicht).
Nun sendet man per USI aber normalerweise ein ganzes Byte. Und hier 
liegt das Problem!
USIDC funktioniert nur für ein Bit, und zwar nur für das, was gerade 
gesendet wurde.
Sendet man ein Byte, würde die Erkennung nur auf dem niederwertigsten 
Bit angewendet werden.

Das kann man algorithmisch übrigens auch über den Master lösen, ich 
wähle aber den Weg über die Kollisionserkennung.

Die Lösung des Problems ist: die ID bitweise und nicht byteweise zu 
übertragen, was den USI-Teil geringfügig komplizierter macht. Aber sehr 
gut funktioniert. Nimmt man den Beispielcode für USI Slave (App Note 
AVR312) muss dafür die Statemachine erweitert werden und eben das Byte 
bitweise übertragen werden.
Sorry, dass ich hier keinen Beispielcode mit angebe, sondern nur den Weg 
beschreibe.
Das liegt daran, dass meine Implementierung sauberes C++ 11 ist und fast 
nichts mehr mit dem Original zu tun hat.

Ich habe noch ein Bild von dem Versuchsaufbau angehängt: Links sind zwei 
LED-Controller-Boards, auf dem Breadboard ist der Master. Über 
Blinkcodes teilen die Slaves ihre ID mit und der Master, wieviele Slaves 
er gefunden hat. Welcher Slave die Adresse "1" und welcher "2" hat kann 
sich nach jedem Reset ändern, erkannt werden aber immer beide (Master 
blinkt immer "2").

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.