So, ich habe mal ein bisschen mit dem Kabel und Kapazitäten gespielt.
Die 100m Kabel haben zwischen zwei Adern mit offenen Enden rund 13nF.
Damit geht am Arduino gar nix. Wenn ich auch nur die Ader für die Taktleitung am Master anschließe, hängt er sich auf, selbst mit 780 Hz.
Ich habe die PullUps auf bis zu 330 Ohm reduziert, bringt aber nix.
Dann habe ich mit diversen verfügbaren Kapazitäten herumgespielt. Wenn ich 7nF zwischen SCL und SDA hänge und 330 Ohm PullUps verwende, dann funktioniert der Bus. sogar mit 400 kHz.
Das Kabel will ich jetzt nicht zerschneiden und zerknüddeln. Das brauche ich noch.
Der Test hat aber gezeigt, dass zumindest bei 5V die Limits überschritten sind. Zwar war die längste Strecke, die ich überbrücken wollte "nur" ca. 30m lang, was vielleicht funktionieren würde, aber später sollte noch der eine oder andere Arduino hinzu kommen, und dann klemmt's vielleicht.
Das Verhalten des Multiscan-Sketches kann ich mir noch nicht erklären. Vielleicht kann mir dabei jemand unter die Arme greifen.
Wenn ich SDA und SCL unbeschaltet lasse, dann läuft ein Scan komplett durch und meldet für jede Adresse und Taktrate, dass er keine Gegenstelle gefunden hat.
Wenn ich dann nur eine Ader an SCL des Masters klemme, dann bleibt das Sketch direkt nach der Anzeige des Headers stehen. Klemme ich die Ader wieder ab, läuft der Scann weiter, oder das Programm bleibt komplett stehen. Dann muss ich den Arduino resetten.
Was passiert da? Warum läuft das Programm nicht weiter? Die Datenleitung sollte relativ unbeeiflusst sein.
Ich habe den Multiscan-Sketch erweitert, um noch langsamere Geschwindigkeiten mit Vorteilern testen zu können.
Das ist nix Wildes, aber vielleicht hilft es ja jemandem, der womöglich noch weniger Ahnung von C hat als ich.
Man kann mit den Kommandos 1, 4 und 6 die Vorteiler 1, 4 und 64 einstellen. Die verwendete Frequenz wird im Header entsprechend angezeigt.
//
// FILE: MultiSpeedI2CScanner.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.1.03
// PURPOSE: I2C scanner @different speeds
// DATE: 2013-11-05
// URL:
//
// Released to the public domain
//
// added prescale control (24.7.2014)
// Attention! Novice!
//
#include <Wire.h>
#include <Arduino.h>
// scans devices from 50 to 800KHz I2C speeds.
// lower than 50 is not possible
// DS3231 RTC works on 800 KHz. TWBR = 2; (?)
long speed[] = {
50, 100, 200, 250, 400, 500, 800 };
const int speeds = sizeof(speed)/sizeof(speed[0]);
// DELAY BETWEEN TESTS
#define RESTORE_LATENCY 5 // for delay between tests of found devices.
bool delayFlag = false;
// MINIMIZE OUTPUT
bool printAll = true;
bool header = true;
// STATE MACHINE
enum states {
STOP, ONCE, CONT, HELP, PRESCALE1, PRESCALE4, PRESCALE64 };
states state = STOP;
uint32_t startScan;
uint32_t stopScan;
byte Prescaler;
float HeaderSpeed;
void setup()
{
Serial.begin(57600);
Wire.begin();
displayHelp();
Prescaler = 1;
}
void loop()
{
switch (getCommand())
{
case 's':
state = ONCE;
break;
case 'c':
state = CONT;
break;
case 'd':
delayFlag = !delayFlag;
Serial.print(F("<delay="));
Serial.println(delayFlag?F("5>"):F("0>"));
break;
case 'e':
// eeprom test TODO
break;
case 'h':
header = !header;
Serial.print(F("<header="));
Serial.println(header?F("yes>"):F("no>"));
break;
case '?':
state = HELP;
break;
case 'p':
printAll = !printAll;
Serial.print(F("<print="));
Serial.println(printAll?F("all>"):F("found>"));
break;
case 'q':
state = HELP;
break;
case '1':
state = PRESCALE1;
break;
case '4':
state = PRESCALE4;
break;
case '6':
state = PRESCALE64;
break;
default:
break;
}
switch(state)
{
case ONCE:
I2Cscan();
state = HELP;
break;
case CONT:
I2Cscan();
delay(1000);
break;
case HELP:
displayHelp();
state = STOP;
break;
case STOP:
break;
case PRESCALE1:
Prescaler = 1;
bitClear(TWSR,TWPS0);
bitClear(TWSR,TWPS1);
break;
case PRESCALE4:
Prescaler = 4;
bitSet (TWSR,TWPS0);
bitClear(TWSR,TWPS1);
break;
case PRESCALE64:
Prescaler = 64;
bitSet (TWSR, TWPS0);
bitSet (TWSR, TWPS1);
// TWSR |= _BV (TWPS0);
// TWSR |= _BV (TWPS1);
break;
default: // ignore all non commands
break;
}
}
char getCommand()
{
char c = '\0';
if (Serial.available())
{
c = Serial.read();
}
return c;
}
void displayHelp()
{
Serial.println(F("\nArduino I2C Scanner - 0.1.03\n"));
Serial.println(F("\ts = single scan"));
Serial.println(F("\tc = continuous scan - 1 second delay"));
Serial.println(F("\tq = quit continuous scan"));
Serial.println(F("\t1 = Prescaler = 1 (default)"));
Serial.println(F("\t4 = Prescaler = 4"));
Serial.println(F("\t6 = Prescaler = 64"));
Serial.println(F("\td = toggle latency delay between successful tests."));
Serial.println(F("\tp = toggle printAll - printFound."));
Serial.println(F("\th = toggle header - noHeader."));
Serial.println(F("\t? = help - this page"));
Serial.println();
}
void I2Cscan()
{
startScan = millis();
uint8_t count = 0;
if (header)
{
Serial.print(F("TIME\tDEC\tHEX\t"));
for (uint8_t s = 0; s < speeds; s++)
{
HeaderSpeed = speed[s];
HeaderSpeed = HeaderSpeed/Prescaler;
Serial.print(F("\t"));
Serial.print(HeaderSpeed);
}
Serial.println(F("\t[KHz]"));
for (uint8_t s = 0; s < speeds + 5; s++)
{
Serial.print(F("--------"));
}
Serial.println();
}
// TEST
for (uint8_t address = 0; address < 128; address++)
{
bool printLine = printAll;
bool found[speeds];
bool fnd = false;
for (uint8_t s = 0; s < speeds ; s++)
{
TWBR = (F_CPU/(speed[s]*1000) - 16)/2;
Wire.beginTransmission (address);
found[s] = (Wire.endTransmission () == 0);
fnd |= found[s];
// give device 5 millis
if (fnd && delayFlag) delay(RESTORE_LATENCY);
}
if (fnd) count++;
printLine |= fnd;
if (printLine)
{
Serial.print(millis());
Serial.print(F("\t"));
Serial.print(address, DEC);
Serial.print(F("\t0x"));
Serial.print(address, HEX);
Serial.print(F("\t"));
for (uint8_t s = 0; s < speeds ; s++)
{
Serial.print(F("\t"));
Serial.print(found[s]? F("V"):F("."));
}
Serial.println();
}
}
stopScan = millis();
if (header)
{
Serial.println();
Serial.print(count);
Serial.print(F(" devices found in "));
Serial.print(stopScan - startScan);
Serial.println(F(" milliseconds."));
}
}
EDIT:
Ich habe das jetzt noch mal mit einer externen Spannungsversorgung (vorher über USB) und 220 Ohm Pullups getestet.
Jetzt läuft es auch mit den 100m Kabel, wenn ich nur zwei Adern für SDA und SCL verwende. Bis zu einem Takt 3,9 kHz wird ein Slave erkannt. Darüber nicht mehr.
Klemme ich zusätzlich eine Ader für GND an, läuft der Scann zwar noch durch, aber es wird kein Slave mehr erkannt. Das liegt dann wohl an der weiteren Kapazitätserhöhung.
Das ist aber schon mal ein Fortschritt. Ein BusExtender könnte die Sache vielleicht doch noch zum Laufen bringen. Beim P82B715 sprechen die zwar "nur" davon, dass die Kapazität 10x höher, also 4nF sein darf, aber die gehen dabei von 100 kHz aus- Ich wäre damit zumindest schon mal in der richtigen Größenordnung.
EDIT2:
Ich habe jetzt noch ca. 15m Steuerleitung YSLY 3x1 getestet. Das hat zwischen zwei Adern 3,3 nF und funktioniert bis 125 kHz. Lege ich die dritte Ader auf GND, geht es nur noch bis 100kHz.
Parallele Adern auf GND erhöhen die Kapazität drastisch und senken die Ü-Rate.
Ich werde es mit höheren Spannungen und Treiberbausteinen testen, auch wenn die nicht gerade preiswert sind.
Es sieht aber so aus, als wäre es für eine hohe Reichweite unumgänglich, eine separate, geschirmte Leitung für den Bus zu benutzen.
EDIT3:
Jetzt mit 100m CAT6 SSTP getestet. Ich habe es so verdrahtet, wie es in den AppNotes der Datenblätter zu den Treiberbausteinen vorgeschlagen wird. Ein verdrilltes Paar für SCL und GND, ein Paar für SDA und Vcc. 220Ohm Pullup.
Damit funktioniert der Scan bis 250 kHz und der Slave wird erkannt. Bei 400 kHz hängt sich der Aufbau auf und ich muss den Slave resetten.
Das sind doch schon mal ganz brauchbare Werte, die mich optimistisch stimmen. Mit höheren Spannungen geht bestimmt noch mehr, was aber für meine Zwecke nicht notwendig ist.