Как правильно настроить и использовать RTC DS3231 в моем проекте Arduino?

Моя цель - построить будильник (в форме C4 в CSGO). Я не могу правильно настроить или использовать RTC DS3231, он не меняет время/дату и каждый раз выводит одно и то же странное значение (17:59:40,727 -> Mittwoch, 165. 2165 45). :165:85) Я использовал случайную библиотеку для тестирования DS3231 на Arduino Uno. Что я делаю не так?

Вот код *.ino (комментарии на немецком языке):

#include <Wire.h>
#define RTC_I2C_ADDRESS 0x68 // I2C Adresse des RTC  DS3231
    
//Membervariablen
int jahr,monat,tag,stunde,minute,sekunde, wochentag;
int daysInMonth[12]={31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
String daysInWeek[7] = {"Sonntag", "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag"};
String monthInYear[12] = {"Januar","Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember"};
String outputFormat = "%s, %02d.%s %04d %02d:%02d:%02d Uhr";

//Ließt den aktuellen Zeitstempel aus dem RTC Modul.
void rtcReadTime(){
    Wire.beginTransmission(RTC_I2C_ADDRESS); //Aufbau der Verbindung zur Adresse 0x68
    Wire.write(0);
    Wire.endTransmission();
    Wire.requestFrom(RTC_I2C_ADDRESS, 7);
    sekunde    = bcdToDec(Wire.read() & 0x7f);
    minute     = bcdToDec(Wire.read()); 
    stunde     = bcdToDec(Wire.read() & 0x3f); 
    //Der Wochentag wird hier nicht ausgelesen da dieses mit 
    //dem Modul RTC DS3231 nicht über die Wire.h zuverlässig funktioniert.
    /* wochentag  =*/ bcdToDec(Wire.read());
    tag        = bcdToDec(Wire.read());
    monat      = bcdToDec(Wire.read());
    jahr       = bcdToDec(Wire.read())+2000;    
}

//Funktion zum schreiben / setzen der Uhrzeit.
void rtcWriteTime(int jahr, int monat, int tag, int stunde, int minute, int sekunde){
    Wire.beginTransmission(RTC_I2C_ADDRESS);
    Wire.write(0); // Der Wert 0 aktiviert das RTC Modul.
    Wire.write(decToBcd(sekunde));    
    Wire.write(decToBcd(minute));
    Wire.write(decToBcd(stunde));                                  
    Wire.write(decToBcd(0)); // Wochentag unberücksichtigt
    Wire.write(decToBcd(tag));
    Wire.write(decToBcd(monat));
    Wire.write(decToBcd(jahr-2000));  
    Wire.endTransmission();  
}

//Berechnet den Tag der Woche aus dem übergebenen Datumswerten.
byte calcDayOfWeek(int jahr, byte monat, byte tag) {
    static int t[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
    jahr -= monat < 3;
    return ((jahr + jahr/4 - jahr/100 + jahr/400 + t[monat-1] + tag) % 7); 
}

//Convertiert Dezimalzeichen in binäre Zeichen.
byte decToBcd(byte val){
    return ( (val/10*16) + (val%10) );
}

//Convertiert binäre Zeichen in Dezimal Zeichen.
byte bcdToDec(byte val){
    return ( (val/16*10) + (val%16) );
}

//Ließt einen String und liefert einen Integer Wert von einer 
//definierten Stelle (byte num) des Stringwertes.
int getIntFromString (char *stringWithInt, byte num){
    char *tail; 
    while (num>0){
        num--;
        //Bei Kommanseparierten Listen werden die Kommata 
        //übersprungen und geben damit die Unterteilung des Strings an.
        while ((!isdigit (*stringWithInt))&&(*stringWithInt!=0)){
            stringWithInt++;
        }
        tail=stringWithInt;

        //Schleife solange ausführen bis eine Zahl gefunden wurde
        //welche größer 0 ist.
        while ((isdigit(*tail))&&(*tail!=0)){
            tail++;
        }
   
        if (num>0){
              stringWithInt=tail;
        }
    }  
    return(strtol(stringWithInt, &tail, 10));
}  
    
//Setup Methode
void setup(){
    Wire.begin(); //Kommunikation über die Wire.h bibliothek beginnen.       
    Serial.begin(57600);  //Übertragungsgeschwindigkeit 9600 Baud
    Serial.println("Mit dem Befehl kann das Datum und die Uhrzeit gesetzt oder veraendert werden.");
    Serial.println("Beispiel: set 28.08.2013 10:54");
}

//Gibt einen Zeitstempel auf der Seriellen Schnittstelle aus.
void printRTCDateTime(){
    char linebuf[60];
    int dOW = calcDayOfWeek(jahr, monat, tag);
    String wochentagC = daysInWeek[dOW];
    String monatC = monthInYear[monat];

    String result = "";
    result += wochentagC;
    result += ", ";
    result += tag;
    result += ".";
    result += monatC;
    result += " ";  
    result += jahr;  
    result += " "; 
    if(stunde<10) { result += "0"; }
    result += stunde;  
    result += ":";  
    if(minute<10) { result += "0"; }
    result += minute;  
    result += ":"; 
    if(sekunde<10) { result += "0"; }
    result += sekunde;  
    Serial.println(result); 
}
    
//Manuelles setzen der Uhrzeit über den Seriellen Monitor der IDE.
void setRTCTime(){
    char linebuf[30];
    byte counter;
    if (Serial.available()){
        delay(100); // Warte auf das Eintreffen aller Zeichen vom seriellen Monitor
        memset(linebuf,0,sizeof(linebuf)); // Zeilenpuffer löschen
        counter=0; // Zähler auf Null
        while (Serial.available()){
            linebuf[counter]=Serial.read(); // Zeichen in den Zeilenpuffer einfügen
            if (counter<sizeof(linebuf)-1) counter++; // Zeichenzähler erhöhen
        }
        // Wenn in der gelesenen Zeile das Wort 'set' vorkommt dann...
        //(Hier muss man bedenken das die Suche nach 'set' auch nach x Zeichen ein positives Ergebnis liefern wird, zbsp. 123set 09.01.2016 12:00:00)
        if (strstr(linebuf,"set")==linebuf){ 
            tag=getIntFromString (linebuf,1);
            monat=getIntFromString (linebuf,2);
            jahr=getIntFromString (linebuf,3);
            stunde=getIntFromString (linebuf,4);
            minute=getIntFromString (linebuf,5);
            sekunde=getIntFromString (linebuf,6);
        } else {
            Serial.println("Befehl unbekannt.");
            return;
        }

        // Ausgelesene Werte einer groben Plausibilitätsprüfung unterziehen:
        if (!checkDateTime(jahr, monat, tag, stunde, minute, sekunde)){
            Serial.println(linebuf);
            Serial.println("Fehlerhafte Zeitangabe im 'set' Befehl");
            Serial.println("Beispiel: set 28.08.2013 10:54");
            return;
        }
        rtcWriteTime(jahr, monat, tag, stunde, minute, sekunde);
        Serial.println("Zeit und Datum wurden auf neue Werte gesetzt.");
    }
}

//Prüft das eingegebene Datum auf korrektheit.
boolean checkDateTime(int jahr, int monat, int tag, int stunde, int minute, int sekunde){
    boolean result = false;
    if(jahr>2000){
        result = true;  
    } else {
        return false;
    }

    // Schaltjahr prüfen
    if(jahr % 400 == 0 || (jahr % 100 != 0 && jahr % 4 == 0)){
        //Wenn es ein Schaltjahr ist dann den Wert 29 in das Array an der Stelle 1 für den Monat Februar schreiben.
        daysInMonth[1]=29;
    }

    //Monat muss kleiner 13 sein.
    if (monat<13){
        if( tag <= daysInMonth[monat-1] ){
            result = true;
        }
    } else {
        return false;
    }

    //Wert für Stunde muss zwischen 0 und 24 liegen,
    //Wert für Minute und sekunde muss zwischen 0 und 59 liegen
    if(stunde <24 && minute <60 && sekunde <60 && stunde >= 0 && minute >=0 && sekunde >=0){
        result = true;
    } else {
        return false;
    }

    return result;
}

void loop(){ 
    rtcReadTime();
    printRTCDateTime();
    setRTCTime();
    delay(1000);
}

person Fabian Huber    schedule 17.09.2020    source источник
comment
ты не думаешь, что это помогло бы обеспечить ту же самую странную ценность твоей?   -  person Piglet    schedule 18.09.2020


Ответы (1)


Чтобы получить второй вы делаете

 sekunde    = bcdToDec(Wire.read() & 0x7f);

Таким образом, вы просто читаете 7 младших битов первого байта. Это немного сложнее, чем это. Внимательно читайте техпаспорт!

Из даташита:

MSB BIT6 BIT5 BIT4 BIT3 BIT2 BIT1 LSB
 0 |  10 seconds  |      seconds

Вы только что прочитали 7 бит как значение секунд. Это не может работать.

49 секунд будет 0b01001001

Вы читаете 73.

Вы должны рассчитать

0b01001001 & 0x0F -> 9 ваше значение в секундах

и

0b01001001 & 0xF0 -> 4 ваша ценность за 10 секунд

-> 10 * 4 + 9 = 49

У вас такие же или худшие проблемы со всеми остальными значениями. Надеюсь, вы поняли мою точку зрения.

Вы также должны проверить код, который должен установить время. Почему вы записываете целые значения в байты регистра? Здесь также вы не следуете таблице адресов. Вы должны сделать больше, чем просто decToBcd любое значение и запихнуть его в шину. Я не прочитал весь ваш код. Я думаю, вы должны решить эти проблемы в первую очередь.

person Piglet    schedule 18.09.2020