Freitag, 9. Januar 2015

Die DS1307 und ihre Tücken

 Man glaubt ja immer, mit dem Arduino und den ganzen verfügbaren Beispielen ist das Leben einfach. Ist es meistens auch. Aber eben nicht immer.

 Schauen wir gemütlich die Sekundenanzeige einer Uhr an, die von einer DS1307 und einem Arduino gesteuert wird:



 Eigentlich langweilig, aber da! Bei 1:03 und bei 2:28! Da werden plötzlich 85 Sekunden angezeigt. Umgangssprachlich: WTF?

 Der überall zu findende Code für das Auslesen der DS1307 lautet:

Wire.requestFrom(_address, 7);

_seconds = bcdToDec(Wire.read() & 0x7f);
_minutes = bcdToDec(Wire.read());
_hours = bcdToDec(Wire.read() & 0x3f);
_dayOfWeek = bcdToDec(Wire.read());
_date = bcdToDec(Wire.read());
_month = bcdToDec(Wire.read());
_year = bcdToDec(Wire.read());

 Also: fordere von der DS1307 sieben Bytes an und verarbeite sie dann. Aber das ist eben nicht ganz richtig. Denn wer sagt denn, daß wir auch sieben Bytes bekommen haben?

 Ich habe daraufhin den Code modifiziert, so daß er 99 Sekunden zurück gibt, wenn keine sieben Bytes zurück kommen. Denn das ist der überall mißachtete Rückgabewert der Funktion "Wire.requestFrom()".

 Und siehe da:



 Bei 1:39 werden 99 Sekunden angezeigt! Es werden eben manchmal 7 Bytes angefordert, aber keine 7 Bytes zurück gegeben.

 Die Lösung lautete damit für mich: fordere 7 Bytes an, und wenn Du 7 bekommen hast, verarbeite sie. Aber eben nur dann. Bei mir:

do {
  // Reset the register pointer
  Wire.beginTransmission(_address);
  Wire.write((uint8_t)0x00);
  result = Wire.endTransmission(false); 
  
  count = Wire.requestFrom(_address, 7);
  
  if(count == 7) {
    // Success
    _seconds = bcdToDec(Wire.read() & 0x7f);
    _minutes = bcdToDec(Wire.read());
    _hours = bcdToDec(Wire.read() & 0x3f);
    _dayOfWeek = bcdToDec(Wire.read());
    _date = bcdToDec(Wire.read());
    _month = bcdToDec(Wire.read());
    _year = bcdToDec(Wire.read());
  }
  else {
    // Fail
    for (int i=0; i<count; i++) {
      Wire.read();
    }
    retries++;
  }

  result = Wire.endTransmission(true);
while ((count != 7) && (retries < 8));

Nach 8 Versuchen wird das Ganze abgebrochen. Jetzt zählen die Sekunden sauber und wie sie sollen...


Keine Kommentare:

Kommentar veröffentlichen