RTC (MCP79410), die Zweite...

Hallo Leute,

ich habe eine Frage zum Verständnis des folgenden Ausdrucks:

WriteRTCByte(6,0x11);    //YEAR=11

Was bedeutet das "0x11"? (Dass die 11 das Jahr ist, habe ich inzwischen nach intensiven Bemühungen auch selbst herausgefunden - das müsst Ihr mir nicht erklären. Mir geht's um diese "0x__"-Form, die ich nicht verstehe.)
Ich möchte das aus einem String auslesen und ins richtige Datenformat dort einfügen.

Hier ein Beispiel für einen solchen String:
nmeaString = "$GPRMC,201336.000,A,5436.2017,N,01510.1622,E,0.45,343.59,290316,,,A*6F"

Daraus habe ich die Zeitdaten extrahiert.
Nun ist mein Problem, die richtige Übergabe der Werte an die RTC.

(Danke nochmal an Doc_Arduino für die Hilfe beim letzten RTC-Problem.)

Hallo SuesseKleineMaus :wink:

Das 0x wird bei Hexadezimal angegeben. Eigentlich müsste dann 0x11 = 16^1 + 16^0 = 16+1 = 17 sein, oder?

Дружба, товарищ!

Danke für die Info!
Bedeutet das, dass man stattdessen einfach auch eine dezimale 17 senden könnte?
Etwa so:

WriteRTCByte(6,17);    //YEAR=11

Das Interessante ist, wenn man den Wert aus der RTC ausliest und gleich direkt in einen String packt, bleibt es bei der 11.

Fuktioniert nicht. Hab's gerade getestet.

Was funktioniert, ist Folgendes:

  byte yy = 0x16;
  WriteRTCByte(6,yy);    //YEAR=16

Ich weiß nur nicht, wie man eine Dezimalzahl in die Form 0x?? konvertiert.

byte a = 0b10110 // 22 in Binär        Basis 2
byte a = 026     // 22 in Oktal        Basis 8
byte a = 22      // 22 in Dezimal      Basis 10
byte a = 0x16    // 22 in Hexadezimal  Basis 16

Schau mal hier nach, vielleicht hilft das etwas weiter:
http://www.arndt-bruenner.de/mathe/scripts/Zahlensysteme.htm

Um besonders zart besaiteten Usern keinen Grund zu geben, ihre Hose wechseln zu müssen (wie in der Vergangenheit geschehen), habe ich mich zu dieser Namensänderung entschlossen.
Liebe, überaus harmlose Grüße, die SüßeKleineMaus, aka Kriegerdaemon…

Danke für Deine Rücksichtnahme!!!

Die Sache verhält sich etwas anders, da Zehner und Einer getrennt gezählt werden. Datenblatt “TABLE 5-1: DETAILED RTCC REGISTER MAP” gibt Aufschluß:
06h RTCYEAR YRTEN3 YRTEN2 YRTEN1 YRTEN0 YRONE3 YRONE2 YRONE1 YRONE0

Daher bedeutet 0x11 = 0b00010001 tatsächlich das Jahr elf!

Stichwort BCD-Codierung.

Aha, dann ist das ja ähnlich wie meine eigene serielle Kommunikation mit dem UNO: Ich sende einen ASCII-String mit Zeit-Daten, aus dem dann der ASCII-Wert für die Ziffern '0' bis '9' umgerechnet werden soll?! Das ist ja noch gaaaaaaaaanz was anders und man muss hier die ASCII-Tabelle zu Rate ziehen...

Mit ASCII hat das nichts gemein. Hier geht es darum, daß das rechte Halbbyte die Einer von 0 bis 9 zählt, während das linke Halbbyte die Zehner von Null bis Neun zählt. Das Jahr 29 also 0b00101001, das Jahr 30 mit 0b00110000.

ASCII bedeutet '0' = 48 = 0x30 oder zweites Beispiel "30" = 13104 = 0x3330 (zwei Byte! Die dezimale Zahl hat mein Windows Rechner berechnet.)

Oh weia... Aber das steht doch alles dann in den betreffenden Datenblättern, was wie wo übertragen wird? Oder liege ich da auch falsch??

Hallo Kriegerdaemon, :slight_smile:

Leute, hier gibts nicht zu formatieren. Solange nicht nach oder von ASCII wandeln möchte. Nur dann muß man +/-48 rechnen.

WriteRTCByte(6,15);      // nicht mit RTC verwenden, nur Bsp.
WriteRTCByte(6,0x0F);    // nicht mit RTC verwenden, nur Bsp.

beide schreiben die dezimale Zahl 15 bzw. die hexadezimale Zahl F in das Register. Genauso wie 100kW gleich 136PS sind. Gleiche Leistung nur anders dargestellt.

Das einzigste was du bei der RTC beachten mußt ist, das die Werte die man hier ausliest oder schreibt im BCD Format sein müssen. Also nichts mit Dezimal oder Hex. Das muß man umrechnen. Dazu gabs im anderen Thread von jurs die Umrechnungsfunktionen.

Edit: eine fehlte “drüben”, nochmal komplett, von jurs

byte decToBcd(byte val) // Hilfsfunktion zum Lesen/Schreiben der RTC
// Convert decimal number to binary coded decimal
// Hilfsfunktion für die Echtzeituhr
{
  return ( (val/10*16) + (val%10) );
}

byte bcdToDec(byte val)  // Hilfsfunktion zum Lesen/Schreiben der RTC
// Convert binary coded decimal to decimal number
// Hilfsfunktion für die Echtzeituhr
{
  return ( (val/16*10) + (val%16) );
}

RudiDL5:
Oh weia... Aber das steht doch alles dann in den betreffenden Datenblättern, was wie wo übertragen wird? Oder liege ich da auch falsch??

Vollkommen richtig. Im Vorgängerthema hatte ich ja extra geschrieben, so einen Baustein MCP79410 noch nie gesehen zu haben. Meine Erfahrung mit DS3231 gepaart mit dem Lesen des Datenblatts veranlaßt mich, hier was zu schreiben. Wenn der TO bereit ist, sich darauf einzulassen, probiert er meinen Vorschlag aus und berichtet über Erfolg oder Mißerfolg. Gefährlich ist es ja nicht und daraus lerne ich dann auch was.

Gefährlich ist es ja nicht und daraus lerne ich dann auch was.

daumenhoch Ich selbst habe auch rein gar nix gegen Lernen und/oder gute Infos. Die letzten Wochen hier haben mich große Schritte weiter in die Arduino-Welt gebracht und ich bin gespannt, was noch so für Überraschungen auf mich warten :wink:

Vielen Dank, Doc! (und natürlich auch allen Anderen)
Die Konvertierungsfunktion scheint zu funktionieren. :slight_smile: :slight_smile: :slight_smile:
Ich mach das jetzt mal fertig und poste dann mal den ganzen Sketch. (eine RTC mittels GPS-Sensor einstellen)

Was das Hexadezimal-System so schön macht ist dass jede Ziffer 4 Bit darstellt:

0x11 = 0001 0001
0x99 = 1001 1001
0xA0 = 1010 0000 (A = 10)
0x0F = 0000 1111 (F = 15)

Man betrachtet als einfach beide Halb-Bytes (Nibble). So sieht man sofort welche Bits gesetzt sind ohne irgendwas rechnen zu müssen.

BCD (binary coded decimal) ist dann ein Sonderfall. Hier gehen nur 0-99 in 8 Bit. Jedes Nibble geht nur von 0 bis 9 statt von 0 bis F. Nach 09 geh es nicht mit A0 weiter, sondern mit 10. Bei kleinen Zahlen ist das daher auch schön lesbar.
Kann auch für Interfaces interessant sein. Für 7-Segment Anzeigen gibt es z.B. BCD-Dekoder, die vier Bit in eine Ziffer umsetzen.

Und die Umrechnung Dezimal -> BCD und umgekehrt ist im Code trivial.

Naja, ich find das schon ziemlich komisch, dass ein Halbbit nicht bis F geht. Aber, Du hast Recht, dafür ist es tatsächlich leicht verständlich.
Hier wie versprochen der Sketch für die Einstellung einer RTC nach der GPS-Zeit:

/*  Info: This scetch works on Arduino Nano V3.0
 *  Pin connections:
 *  Arduino Pin "5V"  -> RTC MCP79410 Pin "Vcc" and -> GPS-Breakout Pin "VIN"
 *  Arduino PIN "GND" -> RTC MCP79410 Pin "GND" and -> GPS-Breakout Pin "GND"
 *  Arduino PIN "D2"  -> GPS-Breakout Pin "RX"
 *  Arduino PIN "D3"  -> GPS-Breakout Pin "TX"
 *  Arduino PIN "A4"  -> RTC MCP79410 Pin "SDA"
 *  Arduino PIN "A5"  -> RTC MCP79410 Pin "SCL"
 *  Auf dem Adruino Nano V3.0 gibt es zwei "GND"-Pins - man kann jeden nehmen.

    *** Special thanks to Doc_Arduino, agmue & Serenifly @ forum.arduino.cc ***/

// *** GPS section ***
#include <Adafruit_GPS.h>
#include <SoftwareSerial.h>
SoftwareSerial mySerial(3,2);
#define PMTK_SET_NMEA_UPDATE_1HZ  "$PMTK220,1000*1F"
#define PMTK_SET_NMEA_OUTPUT_RMC "$PMTK314,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*29"
char zeichen;
String nmeaString;
String gpsTimeStamp = "1";
unsigned int nmeaStringLaenge;
int ssInt;
int mmInt;
int hhInt;
int ddInt;
int mMInt;
int yyInt;
int ssWait;

// *** RTC section ***
#include <Wire.h>
String rtcTimeStamp = "2";

void setup() {
// *** GPS section ***
  while (!Serial); // Prüfen, ob diese Zeile gelöscht werden kann.
  Serial.begin(9600);
  mySerial.begin(9600);
  delay(2000);
  mySerial.println(PMTK_SET_NMEA_OUTPUT_RMC);
  mySerial.println(PMTK_SET_NMEA_UPDATE_1HZ);

// *** RTC section ***
  Wire.begin();
  delay(2000);
}

void loop() {
// *** GPS section ***
nmeaString = getNMEAString();
nmeaStringLaenge = nmeaString.length();
// *** Zeit & Datum aus String extrahieren ***
    if (nmeaString.substring(1,6) == "GPRMC") {
      ssInt = nmeaString.substring(11,13).toInt();
      mmInt = nmeaString.substring(9,11).toInt();
      hhInt = nmeaString.substring(7,9).toInt();
      ddInt = nmeaString.substring(nmeaStringLaenge - 15,nmeaStringLaenge - 13).toInt();
      mMInt = nmeaString.substring(nmeaStringLaenge - 13,nmeaStringLaenge - 11).toInt();
      yyInt = nmeaString.substring(nmeaStringLaenge - 11,nmeaStringLaenge - 9).toInt();
// Plausibilitätsschranke
      if (
        yyInt > 15 && // after 2015
        yyInt < 20 && // before 2020
        mMInt >  0 && // higher 0
        mMInt < 13 && // beyond 13 months/year
        ddInt >  0 && // higher 0
        ddInt < 32 && // beyond 32 days/month
        hhInt < 24 && // beyond 24 hours/day
        mmInt < 60 && // beyond 60 minutes/hour
        ssInt < 60    // beyond 60 seconds/minute
        ) {
        ssWait = 60-ssInt;
        Serial.print("Plausibilitaetsschranke ueberwunden. \n Wartezeit: ");
        Serial.print(ssWait);
        Serial.println(" Sekunden.");
        delay(ssWait * 1000);
        mmInt = mmInt +1;
        
// *** RTC section ***
        WriteRTCByte(0,0);                  //stop RTC
        WriteRTCByte(1,decToBCD(mmInt));    //minute
        WriteRTCByte(2,decToBCD(hhInt));    //hour
//      WriteRTCByte(3,0x06);               //day of the week = 1(Monday) & VBAT = 1 (Wochentag ermitteln?!)
        WriteRTCByte(4,decToBCD(ddInt));    //day of the month
        WriteRTCByte(5,decToBCD(mMInt));    //month
        WriteRTCByte(6,decToBCD(yyInt));    //year
        WriteRTCByte(0,0x80);               //start RTC, second = 00
      } else {
        Serial.print("Plausibilitaetstest nicht bestanden: ");
        Serial.println(nmeaString);
      }
    }
  nmeaString = getNMEAString();
  nmeaStringLaenge = nmeaString.length();
// *** Zeit & Datum aus String extrahieren ***
    if (nmeaString.substring(1,6) == "GPRMC") {
      gpsTimeStamp = "20";
      gpsTimeStamp = gpsTimeStamp
                   + nmeaString.substring(nmeaStringLaenge - 11,nmeaStringLaenge - 9) + "."
                   + nmeaString.substring(nmeaStringLaenge - 13,nmeaStringLaenge - 11) + "."
                   + nmeaString.substring(nmeaStringLaenge - 15,nmeaStringLaenge - 13) + " "
                   + nmeaString.substring(7,9) + ":"
                   + nmeaString.substring(9,11) + ":"
                   + nmeaString.substring(11,13);
      rtcTimeStamp = "20";
      rtcTimeStamp = rtcTimeStamp
                   + ReadRTCData(6,8) + "."
                   + ReadRTCData(5,5) + "."
                   + ReadRTCData(4,6) + " "
                   + ReadRTCData(2,6) + ":"
                   + ReadRTCData(1,7) + ":"
                   + ReadRTCData(0,7);
//  Serial.println(nmeaString);
      Serial.println("RTC time: " + rtcTimeStamp + "\nGPS time: " + gpsTimeStamp);
      if (rtcTimeStamp == gpsTimeStamp) {
        Serial.println("RTC adjustment sucessfully finished.\nSystem halted for 60 sec.\nPlease switch off!");
        Serial.println(nmeaString);
      delay(60000);
    }
  }
}

// *** function section ***

// function to write a byte to RTC register
void WriteRTCByte(const unsigned char adress, const unsigned char data) {
  Wire.beginTransmission(0x6f);
  Wire.write(adress);
  Wire.write(data);
  Wire.endTransmission();
}

// function to read a byte from RTC register
unsigned char ReadRTCByte(const unsigned char adr) {
  unsigned char data;
  Wire.beginTransmission(0x6f);
  Wire.write(adr);
  Wire.endTransmission();
  Wire.requestFrom(0x6f,1);
  while (Wire.available()) data=Wire.read();
  return data;
}

// function to read data from RTC register
String ReadRTCData(const unsigned char adr, const unsigned char validbits) {
  unsigned char hexaDezimalZahlAlsString = ReadRTCByte(adr) & 0xff >> (8 - validbits);
  String dezimalZahlAlsString = String(hexaDezimalZahlAlsString,HEX);
  if (hexaDezimalZahlAlsString < 10)
    dezimalZahlAlsString = "0" + dezimalZahlAlsString;
  return dezimalZahlAlsString;
}

// function to convert binary coded decimal to an integer
byte bcdToDec(byte bcdValue) {
  return ((bcdValue/16*10) + (bcdValue%16));
}

// function to convert an integer to binary coded decimal
byte decToBCD(byte decValue) {
  return ((decValue/10*16) + (decValue%10));
}

// function to read a NMEA-String from GPS device
// return is the NMEA string or a String contains "error"
String getNMEAString() {
  String _NMEAString = "$";
  char _Zeichen;
  byte _CheckSumCalculated = 0;
  boolean _TransferSwitch = true;
  boolean _CheckSumCalculatorSwitch = false;
  boolean _StartRecord = false;
  unsigned int _NMEAStringLaenge;
  while (_TransferSwitch) {
    if (mySerial.available()) {
      _Zeichen = mySerial.read();
      if (_Zeichen == '

Ein kleiner bug ist immernoch drin: Das Programm rechnet aus, bis die nächste Minute voll ist und fährt dann erst mit der Einstellung der Zeit fort. Das musste ich so machen, weil die Sekunden nicht einstellbar sind. Wenn die Wartezeit länger als etwa 30 s beträgt, dann bleibt das Programm hängen. Hab nicht rausgekriegt, warum. Mit einem Reset zur rechten Zeit geht’s aber.) {
        _StartRecord = ! _StartRecord;
        _CheckSumCalculatorSwitch = true;
        if (! _StartRecord) {
          _TransferSwitch = false;
        }
      } else {
        if (_StartRecord) {
          _NMEAString = _NMEAString + _Zeichen;
        }
        if (_Zeichen == ‘*’) {
          _CheckSumCalculatorSwitch = false;
        }
        if (_CheckSumCalculatorSwitch) {
          _CheckSumCalculated = _CheckSumCalculated ^ _Zeichen; // calculate XOR checksum
        }
      }
    }
  }
// NMEA-String qualifizieren und bei positiver Bewertung übergeben
  _NMEAStringLaenge = _NMEAString.length();
  if (_NMEAString.substring(_NMEAStringLaenge - 4,_NMEAStringLaenge - 2).toInt() == String(_CheckSumCalculated,HEX).toInt()) {
    return _NMEAString;
  } else {
    return “error”;
  }
}


Ein kleiner bug ist immernoch drin: Das Programm rechnet aus, bis die nächste Minute voll ist und fährt dann erst mit der Einstellung der Zeit fort. Das musste ich so machen, weil die Sekunden nicht einstellbar sind. Wenn die Wartezeit länger als etwa 30 s beträgt, dann bleibt das Programm hängen. Hab nicht rausgekriegt, warum. Mit einem Reset zur rechten Zeit geht's aber.

BCD hat seine echten stärken in reinen Logik-Systemen ohne Mikrocontroller. Vor allem wenn man Anzeigen hat. Da macht es das Design sehr viel einfacher.

Aber auch auf Mikrocontrollern wurde BCD lange implementiert. So gibt es z.B. auf vielen Prozessoren ein Flag für einen Übertrag von einem Halb-Byte ins nächste. Es gibt nämlich Algorithmen um direkt in BCD zu rechnen. Manche Prozessoren haben auch Befehle die dabei helfen (z.B. um das Ergebnis einer Addition zu korrigieren). Das ist aber nicht mehr trivial. In Assembler finde ich es da oft einfacher wenn man die Eingabe von BCD in Binär übersetzt, dann normal rechnet und für die Ausgabe wieder zurück in BCD konvertiert. Jedenfalls wenn man genug Speicher hat. Auf sehr, sehr kleinen Prozessoren kann es sparsamer sein alles in BCD zu machen. Das ist aber mehr von historischem Interesse.

Das Rechnen in BCD hat den Vorteil, dass Merkwürdigkeiten beim Umrechnen von Binär-Float und Dezimal nicht auftreten. Deshalb war/ist das in Prozessoren integriert. Viele Taschenrechner arbeiten z.B. in BCD nicht nur wegen der einfacheren Anzeige sondern auch weil keine Rundungsfehler auftreten. Komplexe Funktionen wie Sinus und Logarithmus können leichter mit sehr hoher Präzision berechnet werden.

Hallo wir benötigen Ihre Hilfe.
Wir müssen einen Segelwindenservo (RS-10) mit dem Arduiono und einer externen Stromquelle programmieren , doch trotz vieler Recherche bekommen wir ihn nicht ans laufen.
Kann uns jemand helfen und den Sketch aufschreiben?
Wir würden uns freuen.

@RuLArduino: Eröffne einen eigenen Thread. Für eine Hilfe sind auch schon vorhandene (fehlerhafte) Sketche und ein Schaltplan äußerst sinnvoll.