Multi Master Betrieb mit Due, Uno und Mega über i2c/TWI

Hallo Zusammen,

diese Frage richtet sich hier an die Elektronik Freaks und weniger an die Programmierer denke ich :slight_smile:

also folgendes Thema:

Ich habe ein System gebaut, was aus 4 Arduinos besteht und das ganze soll im Multimaster Betrieb über den i2c Bus kommunizieren.

Ich habe das Problem, dass ich unterschiedliche Daten von einem Arduino anfragen möchte, was sich mit der einfachen Request Methode aus der Wire library leider nicht machen lässt. Es funktioniert bei mir so, dass der Haupt Master einen Request stellt, dieser aber nicht mit der Methode für einen Request, sondern als transmit übertragen wird. Die übertragenen Daten haben zur Folge, dass der Empfänger darauf antwortet (und ebenfalls wieder einen transmit ausführt).

Der Haupt Master weiß immer, wann er so einen transmit mit erwarteter Antwort sendet und schiebt solange nichts anderes mehr über den Bus, bis die erwartete Antwort kommt. Das habe ich Programmcode so verankert und auch mehrfach getestet.

Verbaut sind 2 Megas, 1 Uno und 1 Due. Der Due übernimmt die Funktion des "Master of the Masters" und kontrolliert somit alles, was über den Bus läuft

So... Das Problem:
Der Due arbeitet in der 3,3V Ebene und der Mega bzw. der Uno in der 5V Ebene, also braucht man einen Logil Level Converter, damit die miteinander sprechen können. Ich habe mir einen von MSX bestellt: Klick!

Der erste Test, wo ich einen Mega bzw. einen Uno mit dem Due sprechen lassen habe, hat auch soweit ganz gut funktioniert.

Was aber nicht funktioniert hat, ist die folgende konstallation:

Mit dieser konstellation ist der Bus einfach nicht funktionsfähig gewesen und hat sich ab dem Start aufgehängt.

Desweiteren habe ich folgende weitere Konstellation getestet:

Damit ging es leider auch nicht... Es hat wenigstens zu Anfang funktioniert, aber nach ca. 15 Sekunden hat der Bus ebenfalls aufgehängt und ich musste wieder die Fehlermeldungen lesen, dass auf die Requests keine Antworten gekommen sind.

Völlig frustriert nach mehreren Stunden des probierens habe ich dann einen letzten Test gemacht:

Und da brat mir doch einer einen Storch... es funktioniert einwandfrei und läuft jetzt seit 6 Stunden stabil. keine Abbrüche, keine Hänger, keine verlorenen Daten und nicht ein Request ohne Antwort.

Ich würde vermuten, dass es mit den Pullups zu tun hat. Die Arduinos haben ja Pullups aufgelötet und die Logic Level Converter ebenfalls. Wenn ich also 3 Arduinos parallel in eine Linie hänge sind dann jeweils 3 Pullups parallel plus die Pullups von dem Converter... macht insgesamt 4 Pullups pro Ader, die relativ dicht zusammen parallel hängen. Kann das sein, dass das ein Problem ist?

folgender Code soll angeblich Pullups deaktivieren:

Deklaration im Sketch:

#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif

Einsatz im "setup()" nach "Wire.begin(x)":

  #if defined(__AVR_ATmega168__) || defined(__AVR_ATmega8__) || defined(__AVR_ATmega328P__)
    // deactivate internal pull-ups for twi
    // as per note from atmega8 manual pg167
    cbi(PORTC, 4);
    cbi(PORTC, 5);
  #else
    // deactivate internal pull-ups for twi
    // as per note from atmega128 manual pg204
    cbi(PORTD, 0);
    cbi(PORTD, 1);
  #endif

Quelle: Klick!

Hat bei mir leider nicht funktioniert, ob mit oder ohne den Befehl hat keinen Unterschied gemacht. Auch die Messung auf den SDA und SCL Aders mit Multimeter hat ergeben, dass der Pegel trotzdem auf 5V blieb. Die Pullups werden wohl fest aufgelötet sein und lassen sich nicht deaktivieren...

Tja... soweit so gut, Es funktioniert ja jetzt alles, aber nur, wenn ich einen eigenen Logic Level Converter für jeden der 5V Arduinos einsetze, was mir nicht gefällt. Ich bin froh, dass ich mir gleich 4 Converter bestellt habe und nicht nur einen, sonst wäre ich da wohl nie drauf gekommen.

Wäre Super, wenn mir jemand dazu etwas sagen kann und vielleicht weiß wie sich das alles technisch mit diesem Bus system verhält!

hi,

nachdem SDA- und SCL-pin ja auch als "normale" digitale pins genutzt werden können, ist da wohl nix fix verlötet, sondern die pullups werden gezielt aktiviert. ich würde mal in der wire.cpp danach suchen.

gruß stefan

Also für mich sieht das aus, als wenn das was mit der pinconfig zu tun hat (Auszug aus der Wire.cpp):

static void Wire_Init(void) {
	pmc_enable_periph_clk(WIRE_INTERFACE_ID);
	PIO_Configure(
			g_APinDescription[PIN_WIRE_SDA].pPort,
			g_APinDescription[PIN_WIRE_SDA].ulPinType,
			g_APinDescription[PIN_WIRE_SDA].ulPin,
			g_APinDescription[PIN_WIRE_SDA].ulPinConfiguration);
	PIO_Configure(
			g_APinDescription[PIN_WIRE_SCL].pPort,
			g_APinDescription[PIN_WIRE_SCL].ulPinType,
			g_APinDescription[PIN_WIRE_SCL].ulPin,
			g_APinDescription[PIN_WIRE_SCL].ulPinConfiguration);

	NVIC_DisableIRQ(WIRE_ISR_ID);
	NVIC_ClearPendingIRQ(WIRE_ISR_ID);
	NVIC_SetPriority(WIRE_ISR_ID, 0);
	NVIC_EnableIRQ(WIRE_ISR_ID);
}

Zeilen 359-376

Ich erkenne daraus aber nichts, was auf Pullups schließen lässt.

Gibt's noch mehr Möglichkeiten?

Edit: hier sind auch noch einmal die Zeilen 101-127

void TwoWire::begin(void) {
	if (onBeginCallback)
		onBeginCallback();

	// Disable PDC channel
	twi->TWI_PTCR = UART_PTCR_RXTDIS | UART_PTCR_TXTDIS;

	TWI_ConfigureMaster(twi, twiClock, VARIANT_MCK);
	status = MASTER_IDLE;
}

void TwoWire::begin(uint8_t address) {
	if (onBeginCallback)
		onBeginCallback();

	// Disable PDC channel
	twi->TWI_PTCR = UART_PTCR_RXTDIS | UART_PTCR_TXTDIS;

	TWI_ConfigureSlave(twi, address);
	status = SLAVE_IDLE;
	TWI_EnableIt(twi, TWI_IER_SVACC);
	//| TWI_IER_RXRDY | TWI_IER_TXRDY	| TWI_IER_TXCOMP);
}

void TwoWire::begin(int address) {
	begin((uint8_t) address);
}

und nochmal Edit:

Ich habe folgendes in der twi.c gefinden:

  // activate internal pullups for twi.
  digitalWrite(SDA, 1);
  digitalWrite(SCL, 1);

das sieht doch gut aus! jetzt stellt sich nur die frage, wie ich das im Sketch rückgängig machen kann. reicht da einfach digitalWrite(SDA, 0) und digitalWrite(SCL, 0) nach "Wire.begin()"?

So, ich habe jetzt einmal ein bischen rumprobiert und gemessen, was sich an den Pegelwerten tut. Auch die Widerstände habe ich einmal gemessen. mit folgendem Ergebnis:

Mega 2560

Widerstände Zustand “AUS”:
SDA->GND = 11,5kOhm
SCL->GND = 11,8kOhm
SDA->5V = 10kOhm
SCL->5V = 10kOHM

Widerstände Zustand “EIN” (Wire.begin() gesetzt):
SDA->GND = 0Ohm
SCL->GND = 0Ohm
SDA->5V = 10kOhm
SCL->5V = 10kOHM

Uno

Widerstände Zustand “AUS”:
SDA->GND = <6MOHM
SCL->GND = <6MOHM
SDA->5V = <6MOHM
SCL->5V = <6MOHM

Widerstände Zustand “EIN” (Wire.begin() gesetzt):

SDA->GND = 0Ohm
SCL->GND = 0Ohm
SDA->5V = 37kOhm
SCL->5V = 74kOHM

So… also gibt’s da offenbar deutliche Unterschiede zwischen den beiden. Also weiter prüfen…die Spannungen messen:

Wire.begin() gesetzt:

SDA/SCL auf dem Uno: 5V

SDA/SCL auf dem Mega: 5V

Verwundert nicht weiter… beide haben Pullups und darüber wird die Spannung an SDA/SCL nach oben gezogen.

jetzt wirds interessant… ist Wire.begin() NICHT gesetzt habe ich folgendes gemessen:

SDA/SCL auf dem Uno: 0V

SDA/SCL auf dem Mega: 5V

okay…der Uno aktiviert die Pullups erst, wenn der TWI Bus initialisiert wird, beim Mega sind die dauerhaft aktiv.

Nächster Test: Wire.begin() gesetzt und direkt danach mit digitalWrite(SDA, 0) und digitalWrite(SCL, 0) im setup() geschrieben… mit folgendem Ergebnis der Spannungsmessungen:

SDA/SCL auf dem Uno: 0V

SDA/SCL auf dem Mega: 5V

Fazit: Der Mega 2560 muss fest aufgelötete Pullups haben, da diese selbst im ausgeschalteten Zustand messbar sind und die SDA/SCL Pins im eingeschalteten Zustand immer den 5V Pegel haben, egal was man macht. Auf dem Uno lassen sich die Widerstände mittels digitalWrite(SDA, 0) und digitalWrite(SCL, 0) abschalten. Das macht den Mega für den i2c Einsatz nur bedingt gut geeignet (Flexibilität).

Mal sehen, wie ich das für meine Anlage umsetzen kann…

Fest steht aber eins: der Mega hat feste Pullup Widerstände von jeweils 10kOhm. Auf dem Logik Level Converter von MSX sind ebenfalls 10kOhm Pullups verbaut (mit Lupe die Verschaltung geprüft).

Alsooo…Wenn ich 2 Mega Parallel an einen Logic Level Converter hänge, dann ergibt sich ein Gesamt-Pullup von 10khm/3=3,33kOhm. Der soll PullUp bei den 5V Arduinos ist angegeben mit min. 4,7 kOhm. Mein Widerstand ist dann also zu klein… da hilft wohl wirklich nur aufsplitten.

Ich versuche mal die Konstallation Uno + Mega (beim Uno Pullups aus) an einen und den zweiten Mega an den anderen LLC.

Wenn man sich den Schaltplan ansieht, dann sind da zwei 10k Widerstände frei in der Mitte:

Aus dem Plan geht nicht hervor dass die für I2C sind, aber ich erinnere mich düster mal was in die Richtung gelesen zu haben.

hi.

hab' es nicht für möglich gehalten, aber es ist hier beschrieben:

http://www.varesano.net/blog/fabio/how-disable-internal-arduino-atmega-pullups-sda-and-scl-i2c-bus

gruß stefan

Ah oben rechts im Schaltplan. Die hatte ich übersehen. Da steht es dabei dass die für SDA/SCL sind :slight_smile:

Immerhin hatte ich sie richtig auf dem Board identifiziert. Das SMD Array an der Power LED

tec_freakz:
So, ich habe jetzt einmal ein bischen rumprobiert und gemessen, was sich an den Pegelwerten tut. Auch die Widerstände habe ich einmal gemessen. mit folgendem Ergebnis:

Mega 2560

Widerstände Zustand "AUS":
SDA->GND = 11,5kOhm
SCL->GND = 11,8kOhm
SDA->5V = 10kOhm
SCL->5V = 10kOHM

Widerstände Zustand "EIN" (Wire.begin() gesetzt):
SDA->GND = 0Ohm
SCL->GND = 0Ohm
SDA->5V = 10kOhm
SCL->5V = 10kOHM

Du mißt BLÖDSINN. Du kannst nicht mit einem Ohmmeter an einer eingeschaltenen Schaltung etwas sinnvolles messen.
Das Ohmmeter gibt einen Konstantstrom aus und mißt die Spannung an den Anschlüssen. Wenn jetzt am zu messenden Bauteil eine Spannung anliegt dann mißt Du diese. Außerdem auch ohne Spannungsversorgung mißt Du Blödsinn, weil der kleine Strom des Ohmmeters meist die Halbleiter nicht polarisieren kann.

Du merkst selbst daß Du blödsinn mißt, weil wenn Du das Ohmmeter umpolst andere Werte herauskommen.

PS.

hab' es nicht für möglich gehalten, aber es ist hier beschrieben:

Gedenken wir kurz Fabio.

Grüße Uwe

hi,

mit 28 ein herzversagen, das ist hart...

gruß stefan

Hier einmal ein Zitat aus den kommentaren der Seite:

Wow ! after 2 days i found the problem, the Mega2560 board have 2 hardware resistors pulled to +5v , therefore is impossible to disable it by software, you can see in the schematic here http://arduino.cc/en/uploads/Main/arduino-mega2560_R3-schematic.pdf in the upper right.

On the board th

In der Tat sind auf dem Mega feste Pullups, wie auch auf dem Schematischen Bild zu sehen.

Ich sehe ein, dass es Bödsinn ist die Widerstände in eingeschalteten Zustand zu messen. Ist echt Quatsch... aber man konnte ja zumindest an den Spannungen gut sehen, was da los ist. :wink:

Ich bin mit dem deaktivieren der Pullups auf dem Uno übrigens nicht wirklich weiter gekommen.

Was allerdngs interessant ist...eine zweite konstallation funktioniert auch:


Der dritte Logic Level Converter ist in der 5V Ebene leer (SDA und SCL sind nicht dran, nur 5V und GND).

Sobald ich den dritten abnehme, kommen wieder die Fehler. Und was noch interessanter ist: von dem leeren LLC scheint nur die positive 5V Spannung der entscheidene Faktor zu sein, denn sobald die fehlt, gehts auch nicht mehr. Das einzige, was der dann macht ist doch das hochziehen der Spannung, wenn die Bus Ader wieder in den idle geht oder sehe ich das falsch?

Wie kann man sich das denn erklären?

Du solltest vielleicht etwas mehr über die Bus-Leitungen und Pullups wissen. Ein I2C Bus sollte gerade (1 Strang) durchlaufen, keine Stern-Topologie. Dann sollten an beiden Enden Pullups sitzen, um die Leitungen abzuschließen. Das gilt für alle Bussysteme mit Open-Collector Ausgängen. Ich nehme dafür gerne ein Flachbandkabel, an das die Buchsen für die Teilnehmer zwischendrin nach Bedarf angequetscht werden können. Die Pullups können ruhig auf einem Board am Ende des Bus sitzen, dann muß man keine weiteren hinzufügen.

Pullups zwischendrin sind bis zu einer gewissen Größe akzeptabel, können den Bus ggf. langsamer machen (d.h. nur mit 100kHz betreiben). Kritisch wird es, wenn ein Teilnehmer nicht mehr in der Lage ist, die Signale auf Low-Level herunterzuziehen.

Zum Ausmessen der installierten Pullups alles zusammenstöpseln, aber nicht einschalten. Dann lassen sich die Widerstände zwischen Signal und Vcc messen. Mit dem Ohmmeter in beiden Richtungen (Polaritäten) messen, und den größeren Wert nehmen. Eventuell per Software eingeschaltete Pullups sollten sich auch per Software abschalten lassen, als keine Rolle spielen.

Die fest eingebauten Pullups sind natürlich Mist, ich würde die zumindest am Level Converter rauslöten oder fräsen. Dann kannst Du die beiden Megas an die Enden des 5V Bus hängen, Uno und Converter dazwischen. Der Due hängt dann hinter dem Converter am 3.3V Bus.

Du solltest vielleicht etwas mehr über die Bus-Leitungen und Pullups wissen. Ein I2C Bus sollte gerade (1 Strang) durchlaufen, keine Stern-Topologie. Dann sollten an beiden Enden Pullups sitzen, um die Leitungen abzuschließen. Das gilt für alle Bussysteme mit Open-Collector Ausgängen. Ich nehme dafür gerne ein Flachbandkabel, an das die Buchsen für die Teilnehmer zwischendrin nach Bedarf angequetscht werden können. Die Pullups können ruhig auf einem Board am Ende des Bus sitzen, dann muß man keine weiteren hinzufügen.

In den Topologie Schemen, die ich so gefunden habe (example1, example2, example3) war leider nichts von einem Abschluss an beiden Enden zu sehen, der Baum-Aufbau aber schon.

Ich habe, um einen anderen möglichen Ansatz zur Lösung zu finden, mal einen Test gemacht zwecks 5V Pullupspannung am i2c Bus des Due (das Thema, ob es geht und ob das in Hinblick auf 3,3V maximale I/O Spannung vom Due auch sicher ist, gibts auch schon im Forum “Networking, Protocols, and Devices”, bisher aber leider ohne Antwort).

Der Test läuft über die SDA1/SCL1 Pins (sind ohne Onboard Pullups) mit folgenden Sketches (Exmaples “Master writer” und “Slave Receiver” hab ich mal leicht dafür modifiziert:

//Master Writer

#include <Wire.h>

void setup()
{
  Serial.begin(9600);
  Wire.begin(); // join i2c bus (address optional for master)
  Serial.println("Setup scceeded.");
}

byte x = 0;

void loop()
{
  Wire.beginTransmission(4); // transmit to device #4
  Wire.write("x is ");        // sends five bytes
  Wire.write(x);              // sends one byte
  Wire.endTransmission();    // stop transmitting
  Serial.print("writing i2c: ");
  Serial.print("x is ");
  Serial.println(x);
  x++;
  
  delay(500);
}
//Slave Receiver

#include <Wire.h>

unsigned long receiveindex = 0;


void setup()
{
  Serial.begin(9600);
  
  Wire1.begin(4);                // join i2c bus with address #4
  Wire1.onReceive(receiveEvent); // register event
  Serial.begin(9600);           // start serial for output
  //Serial.write("Setup abgeschlossen");
}

void loop()
{
  delay(100);
}

// function that executes whenever data is received from master
// this function is registered as an event, see setup()
void receiveEvent(int howMany)
{
  while(1 < Wire1.available()) // loop through all but the last
  {
    char c = Wire1.read(); // receive byte as a character
    Serial.print(c);         // print the character
  }
  int x = Wire1.read();    // receive byte as an integer
  Serial.print(x);         // print the integer
  Serial.print(" recieveEvenet: ");
  Serial.println(recieveindex, DEC);
  recieveindex++;
}

Ich bin inzwischen nach ca. 2-3 Stunden bei über 38.000 erfolgreichen Übertragungen:

x is 192 recieveEvenet: 38830
x is 193 recieveEvenet: 38831
x is 194 recieveEvenet: 38832
x is 195 recieveEvenet: 38833
x is 196 recieveEvenet: 38834

Ich würde nach diesem Test also sagen, dass der Due mit dem “pulluplosen” TWI Kanal (Wire1) auf mit 5V Pullupspannung zurechtkommt. Trotzdem wäre an dieser Stelle nocheinmal die Frage an die Fachleute gerichtet, ob das wirklich gut und sicher funktional ist?!

Um dann mal auf die Variante 3V3 und 5V getrennt zurückzukommen:

DrDiettrich:
Die fest eingebauten Pullups sind natürlich Mist, ich würde die zumindest am Level Converter rauslöten oder fräsen. Dann kannst Du die beiden Megas an die Enden des 5V Bus hängen, Uno und Converter dazwischen. Der Due hängt dann hinter dem Converter am 3.3V Bus.

Ich würde auf diese fertigen Converter eventuell komplett verzichten. Wie siehts aus, wenn ich einfach einen eigenen Logic Level Converter baue, der dann nur aus TO-92 MOSFETS besteht?

Ich bin ach ein bischen recherche auf diesen Thread hier aus dem Forum gestoßen, und da steht drin, dass der 2N7000 als Logic Level Converter MOSFET geeignet ist und wohl auch bei jemanden den regelmäßigen Einsatz findet. Den würde ich mir dann mal schnappen und daraus einen LLC ohne Onboard Mosfets bauen.

Die Pullups an beiden Bus-Enden sorgen für geordnete elektrische Verhältnisse (Wellenwiderstand…). Ein offenes Ende kann zu Reflektionen führen, d.h. ein Impuls läuft auf der Leitung nochmal zurück und ändert das ganze Timing. Für Highspeed oder lange Leitungen sollte man ein Buskabel mit dem passenden Wellenwiderstand verwenden, was nicht ganz einfach ist. Kabel für <300 Ohm sind handelsüblich, aber solche Pullups wären viel zu klein.

Ein einfacher (unidirektionaler) Level-Konverter funktioniert nicht am I2C Bus, weil dort die Teilnehmer auf beiden Bussen die Leitung gleichzeitig auf Low ziehen können und sogar müssen, damit die Übertragung funktioniert (Handshake).

Ohne Konverter gibt’s Probleme mit der eingebauten Hysterese der digitalen Pins, die brauchen 0,7*Vcc um High zu erkennen, d.h. 3.5V bei Vcc=5V. Andererseits vertragen diese Pins nur Vcc+0,7V, d.h. der Due mit Vcc=3,3V ist ab 4V Pegel in Gefahr. Eventuell funktioniert eine direkte Mischung, wenn man nur externe Pullups verwendet, und die an 4V hängt. Am einfachsten den einfachen Pullup zu einem Spannungsteiler ergänzen, mit z.B. 10k nach 5V und 39k nach Gnd. Ich hab’s noch nicht ausprobiert, sollte aber klappen.

hi,

ich verwende 2N7000 mit 2 10k widerständen als LLC. geht üroblemlos.

aber damit hast Du ja wieder pullups drin.

gruß stefan

Die Pullups kann man ja weglassen, wenn der Konverter nicht an einem Ende des jeweiligen Bus hängt. Auf der 3.3V Seite darf der Pullup auch niedriger sein.

hi,
wie sieht der converter dann aus? 5V am 5V-signal, dasselbe bei 3,3V?

gruß stefan

Sorry für die späte Antwort. Ich war die Woche im Prüfungsstress und habe nicht mehr die Zeit gefunden, mich da weiter drum zu kümmern.

Ich hab mich da jetzt nochmal rangemacht.

Alsoo... ich habe jetzt mal mit dem 2N7000 einen LLC aufgebaut und das ganze auch mal als Schaltplan gezeichnet, wie das jetzt etwa aussieht:

Ja... der Converter hat da jetzt also keine eigenen Pullups mehr und den Uno hab ich mal rausgenommen (der hängt dazwischen, pullups lassen sich da per code deaktivieren, was ich auch gemacht habe, daher vernachlässigt).

Sprich der Aufbau sieht jetzt so aus, dass ich in der Tat einen Baumstruktur-Aufbau in der 5V Linie habe.

Auf der 3V3 Seite habe ich seitens des LLCs jetzt mal keine Pullups drin, weil der Due 1,5k fest drauf hat (nicht deaktivierbar), was ja schon recht wenig ist und der ist auch nicht weit von dem LLC entfernt (~15cm).

Also bisher läufts stabil, mal schauen, wie sich das so in den kommenden Tagen macht :slight_smile:

Ich denke mal, dass das Hauptproblem der sink current war. Der lag insgesamt mit den anderen Pullups bei ca. 4-6mA, was viel zu viel ist. Die Grenze ist bei den meisten i2c Controllern mit 3mA festgelegt. Die 10k pullups des converters haben mir also den sink current unnötig hoch gejagt, was ja ein Problem ist, denn der Strom muss immer von dem Teilnehmer über die SCA/SCL Pins abgeleitet werden, der gerade spricht. Wenn da dann mal einer bei ist, den das überlastet, hängt sich das ganze system deswegen auf. jetzt liegt der Gesamtstrom etwa bei 3-4mA, was wohl noch einen reibungslosen Betrieb möglich macht.