Arduino Due I2C funktioniert nicht

Hallo,

bei meiner kleinen Schaltung habe ich mehrere Teilnehmer über den I2C Bus und einen Pegelwander (von 3,3 auf 5V) verbunden und versuche die Teilnehmer anzusprechen. Leider funktioniert dies mit dem Arduino Due nicht (Software 1.5.6R2).

Da ich auch über andere Arduino verfüge, habe ich den gleichen Test (ohne Pegelwandler auf 5V-Technik) mit einem Duemilanove Board gemacht. Alles funktioniert einwandfrei (Software 1.0.5) - Alle Teilnehmer werden gefunden und sind ansprechbar.

Dann habe ich angefangen mit einem Scope mir die Signalpegel anzusehen. Bei beiden Boards sind die SCL/SDA Pegel ca 3.8V hoch und sehen auch ähnlich aus. Auch das umschalten beim Due auf SDA1/SCL1 brachte keine Veränderung. Definitiv ist die Kommunikation über I2C in dieser Software nicht möglich. Zum Dauertest habe ich den : MultiSpeedI2CScanner von Rob Tillaart verwendet. Es werden vom Due keine Teilnehmer gefunden. Eine Abfrage wie zb. E/A-Ports (PCF8574AP) ist so zur Zeit nicht möglich.

Auch der Wechsel auf einen Mega kommt nicht in Frage, da ich für die weitere Verarbeitung die Leitungsfähigkeit und den Speicher des Due benötige.

Weiss jemand eine Lösung oder kann jemand sich die Libs TWI und Wire ansehen? Meiner Meinung nach muss dort der Fehler sein. Im Forum habe ich bisher noch keine richtige Antwort gefunden und mir scheint, dass dieses Problem schon länger besteht.

Vielen Dank und viele Grüsse
Detlef

Dann habe ich angefangen mit einem Scope mir die Signalpegel anzusehen. Bei beiden Boards sind die SCL/SDA Pegel ca 3.8V hoch und sehen auch ähnlich aus.

Wie sehen die denn aus? Kannst du ein Bild posten?

3.8V ist eigentlich zu hoch für den Due, dort sollten höchstens 3.3V anliegen. Wie sieht Deine Schaltung denn aus? Hast Du Pull-ups nach 5V? Bei einem 3.3V-Bus sollten die nach 3V3 gehen und nicht nach 5V.

Hallo Pylon,

die 3,8V sind ok, da alle Busteilnehmer mit 5V arbeiten, aber nicht alle mit 3,3V können. Für den Due habe ich einen Pegelwandler dazwischen geschaltet. Der setzt die Signale von 3,3V nach 5V und zurück nach 3,3V für den Due um. (Pegelwandler - Watterott electronic).
Die Schaltung mit dem Pegelwandler habe ich ebenfalls mit dem Duemilanova getestet - alle ok.
hier das Scope auf der Arduino Due Seite- Bild 1
und hier das Scor auf der 5V-Seite der Schaltung hinter dem Pegelwandler - Bild2

Viele Grüsse
Detlef

Der von Dir verwendete Pegelwandler taugt für I2C nicht, da er nicht bidirektional arbeitet. Ich würde Dir einen Wandler wie diesen empfehlen: https://www.sparkfun.com/products/12009

Hallo Pylon,

hab leider den falschen Link eingesetzt. Hier nun der richtige Level shifter - Watterott electronic.
Wie ich schon gesagt hatte, habe ich den Test mit Levelshifter und Arduino Duemilanove gemacht, bei dem alles funktionierte.
Einen anderen versuch den ich gemacht hatte war alles auf 3,3 V setzen und dann direkt am Due anschließen. Hat auch nicht funktioniert.

Interessanterweise ist das Signal nach den Scope-Bildern zu urteilen auf der Due-Seite noch intakt, während das SDA-Signal auf hinter dem Wandler verschwunden ist. Wenn der Wandler funktionieren würde, dann müsste das Signal auf beiden Seiten identisch sein, einfach mit unterschiedlichem Pegel. Du bist Dir absolut sicher, dass Du einen Wandler wie im zweiten Link besitzt?
Kannst Du ein Verdrahtungsschema zeichnen und posten?

Wie ich schon gesagt hatte, habe ich den Test mit Levelshifter und Arduino Duemilanove gemacht, bei dem alles funktionierte.

Und wie hattest Du den angschlossen? Auf beiden Seiten (LV,HV) mit 5V?

Einen anderen versuch den ich gemacht hatte war alles auf 3,3 V setzen und dann direkt am Due anschließen. Hat auch nicht funktioniert.

Kannst Du auch von diesem Versuch ein Verdrahtungsschema zeichnen?

Hallo Pylon,

klar kann ich eine Schema aufzeichnen. Ich glaube nur nicht dass es am Schema oder an der Beschaltung liegt, da der Duemilanova an der gleichen Schaltung einwandfrei funktioniert. Den Pegelwandler habe ich dann LV und HV mit jeweils 5V versorgt. Alles andere war gleich. Die simpelste Schaltung war nur ein PCF85874AP am I2C nichts weiter. der läuft mit 3,3 V aber auch der Due erkennt diesen nicht.
Bei den Scopes muss ich nochmal sehen, ob die Aufzeichnung(Trigger) ok war. ich habe im Moment längere Leitungen dazwischen, so dass sich die Leitungskapazitäten hierbei auswirken.
Werd den Versuch nochmal überprüfen.

Ein anderes Testprogramm hatte ich auch noch laufen. Hierbei wurde der DS1307 ausgelesen, jedoch nicht mit der Wire-Bibliothek. Der Uhrenbaustein benötigt 5V und die hatte ich über den Levelshifter in der beschriebenen Art angeschlossen. Der Uhrenbaustein konnte einwandfrei mit dem Due gelesen und geschrieben werden.

Daher meine Vermutung, dass die Bibliothek Wire und TWI nicht für den Due funktionieren.

Hier die Schaltung, wie der einfache Test mit Pegelwandler aufgebaut war.
Siehe Bild unten:

Detlef

Schaltung I2C Due.tiff (300 KB)

In der Schaltung wäre der Chip nicht versorgt, aber ich nehme an, das ist ein Versehen.

Du hast dort aber wieder den unidirektionalen Pegelwandler benutzt. Ich frage nochmals: Du bist sicher, dass Du den bidirektionalen Pegelwandler hast? Da steht also nichts von TX/RX oder dergleichen?

Wenn die Scope-Grafiken die Wirklichkeit darstellen, dann funktioniert der Due wie gewünscht. Ein falscher Pegelwandler würde genau zu dem Ergebnis führen, das in den Bildern ersichtlich wird. Deshalb frage ich etwas penetrant nach.
Wäre schon auf Due-Seite kein Signal vorhanden, würde ich auch die Bibliothek in Betracht ziehen, aber so kann ich mir das nicht vorstellen.

Hi,
jetzt die Schaltung angeschlossen an den Duemilanova

zuerst das Bild - Signal vor dem Levelshifter am Duemilanovaanschluss
dann das Bild - Signale hinter dem Levelshifter in der Schaltung
zuletzt - Logiganlyzer Duemilanova in der Schaltung

Hi,
jetzt die Schaltung angeschlossen an den Duemilanova

zuerst das Bild - Signal vor dem Levelshifter am Due-Anschluss
dann das Bild - Signale hinter dem Levelshifter in der Schaltung
zuletzt - Logiganlyzer Due in der Schaltung

Wie die Schaltungen im Vergleich zeigen sieht eigentlich alles gut aus. Auch die Mitschnitte des Logiganalyzer zeigen dass alles richtig sein soll. Trotzdem funktioniert irgendetwas beim Due anders als beim Duemilanove.

Habe diesmal auch zum kompilieren die Version 1.5.6-R2 für beide Arduinos verwendet.

Aufgrund Deiner Nachfrage habe ich nochmals den Pegelwandler verglichen. Die Nummer in der Rechnung ist das stimmt mit dem 4-Kanal Pegelwandler überein. Es ist also das richtige Gerät. (Art.Nr.: 20110451)

Kannst Du den Test-Sketch noch posten? Interessant wäre, wie diese Bilder aussehen, wenn immer das gleiche Byte oder die gleiche Bytefolge (kurz) über die Leitung geht.

Wenn Du Dir Deinen Post anschaust, siehst Du, wieso immer Code-Tags um den Code gehören!

hier der Testsketch:

//
//    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
//

#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 };
states state = STOP;

uint32_t startScan;
uint32_t stopScan;

// TWI clock frequence
static const uint32_t TWI_CLOCK = 100000;

void setup() 
{
  Serial.begin(115200);
  Wire.begin();
  displayHelp();
}


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;
  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;
  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("\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++)
    {
      Serial.print(F("\t"));
      Serial.print(speed[s]);
    }
    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);
      //delay(1);
      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."));
  }
}

Da beim Arduino Due das Register TWBR nicht vorhanden ist, ist der Befehl auskommentiert. Der Bustakt ist dann wie im Standard 100kHz.

So, hab heute Morgen noch einen Test mit meinem Logikanalyzer im I2C-Interpreter-Mode gemacht. Ich habe hierfür das o.g. Testscript verwendet und nur einen PC8574 im I2C-Bus eingebaut. Dieser Chip funktioniert mit 5 und 3,3V, so dass kein Levelshifter erforderlich ist. Meine Vermutung ist ja die Library des Due.

Der Duemilanova erkennt den Chip mit der Adresse h38. In der CSV ab Zeile 1575.
Beim Due geht es bei Zeile 1226 los. der Chip wird jedoch nicht erkannt.

Die Aufzeichnungen zeigen unterschiedliche Reaktionen. Leider kann ich nicht deuten was dies in der Konsequenz erforderlich macht.
Das Ergebnis ist in den beiden CSV-Dateien zu sehen.

Detlef

duemilanove mit h38.csv (36.8 KB)

due mit h38.csv (41.9 KB)

Dein Testscript ist für einen 8-bit AVR Arduino geschrieben und funktioniert so auf dem Due nicht. Leider hat Arduino beim Wechsel auf den Due die Bedeutung des Rückgabewerts der Wire.endTransmission()-Methode geändert. Bei den "alten" Arduinos heisst ein Rückgabewert 0 Erfolg, alles andere bedeutet Fehler. Beim Due ist der Rückgabewert die Anzahl gesendeter Bytes.
Auch ist in der TWI-Bibliothek keine Möglichkeit vorgesehen, das ACK/NAK zu überprüfen. Somit ist ein I2C-Scanner, wie Du ihn verwendest, mit dieser Plattform und dieser Library nicht möglich.

Du kannst die Implementation abändern:

uint8_t TwoWire::endTransmission(uint8_t sendStop) {
	// transmit buffer (blocking)
	TWI_StartWrite(twi, txAddress, 0, 0, txBuffer[0]);
	if (! TWI_WaitByteSent(twi, XMIT_TIMEOUT)) return 0;
	int sent = 1;
	while (sent < txBufferLength) {
		TWI_WriteByte(twi, txBuffer[sent++]);
		if (! TWI_WaitByteSent(twi, XMIT_TIMEOUT)) return 0;
	}
	TWI_Stop( twi);
	if (! TWI_WaitTransferComplete(twi, XMIT_TIMEOUT)) return 0;

	// empty buffer
	txBufferLength = 0;

	status = MASTER_IDLE;
	return sent;
}

Dann kriegst Du einen Null zurück, wenn ein NAK empfangen wurde. Deinen Scanner musst Du sowieso überarbeiten, so macht er keinen Sinn, denn er testet eigentlich verschiedene Geschwindigkeiten durch, aber durch Deine Auskommentieren macht er das gar nicht mehr.

Hiermit sollte es auf dem Due funktionieren (ungetestet, nur Quick-Hack):

//
//    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
//

#include <Wire.h>
#include <Arduino.h>


// 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 };
states state = STOP;

uint32_t startScan;
uint32_t stopScan;

void setup() 
{
  Serial.begin(115200);
  Wire.begin();
  displayHelp();
}


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;
  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;
  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("\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;

  // TEST
  for (uint8_t address = 0; address < 128; address++)
  {
    bool printLine = printAll;
    bool found;

      Wire.beginTransmission (address);
      found = (Wire.endTransmission () != 0);
      //delay(1);
      fnd |= found[s];
      // give device 5 millis
      if (fnd && delayFlag) delay(RESTORE_LATENCY);
    }

    if (found) count++;
    printLine |= found;

    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"));
      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."));
  }
}

Hallo Pylon,

vielen Dank für die Unterstützung und die Geduld. Nun muss ich ne ganze Menge Software umschreiben. Die Teilnehmer sind auf die Art, die Du beschrieben hast erreichbar.
Viele Grüsse

Detlef