Problem mit bitWrite

Hallo,

mit bitWrite kann ich doch in einer Variablen gezielt ein Bit auf 1 oder 0 setzen? Oder?

Irgendwie ändert sich etwas falsch immer im Wechsel bei mir.

Ich taste mich heran in einer DS3231 RTC gezielt die Alarmregister zu setzen.
Bei Register/Adresse 0A und 0D läuft immer was schief.
Ich ändere von 07h bis 0Dh immer Bit 7 und in 0A und 0D zusätzlich Bit 6.

Mit aktuellen Code ändert sich nach jedem Reset der Inhalt von Register 0Ah und 0Dh.

vorher:

Registeradresse:  Byte
07: 10000000
08: 10000000
09: 10000000
0A: 100000
0B: 10000000
0C: 10000000
0D: 100000

nachher:

Registeradresse:  Byte
07: 10000000
08: 10000000
09: 10000000
0A: 1000000
0B: 10000000
0C: 10000000
0D: 1000000

nach Reset

vorher:

Registeradresse:  Byte
07: 10000000
08: 10000000
09: 10000000
0A: 1000000
0B: 10000000
0C: 10000000
0D: 1000000

nachher:

Registeradresse:  Byte
07: 10000000
08: 10000000
09: 10000000
0A: 100000
0B: 10000000
0C: 10000000
0D: 100000

Normalerweise sollte das aber so aussehen mit aktuellen Code, da alle 7. Bits "1" sind und Bit 6 auch eins für 0Ah und 0Dh:

Registeradresse:  Byte
07: 10000000
08: 10000000
09: 10000000
0A: 11000000
0B: 10000000
0C: 10000000
0D: 11000000

Sitze schon 2 Tage vor dem Problem. Arbeite mit seriellen Ausgaben, finde aber nichts was schief läuft.
Ob ich in der set Funktion die Variablen
int addr_07, addr_08, addr_09, addr_0A, addr_0B, addr_0C, addr_0D = 0;
als byte oder int definiere macht keinen Unterschied.

RTC_DS3231_Register_003.ino (12.5 KB)

Hallo,

ich glaube ich kann es auf die Befehlszeile mit Bit 6 eingrenzen.

bitWrite(addr_0A, 6, set_DYDT_1); // setzt das Alarm Day/Date Bit ensprechend
bitWrite(addr_0D, 6, set_DYDT_2); // setzt das Alarm Day/Date Bit ensprechend

Definiere ich die bytes (SET_ALARM_1_DYDT und SET_ALARM_2_DYDT) mit 0 statt 1, funktioniert alles mit den 7. Bits wie es soll. Ist irgendwo ein Fehler in der Variablenübergabe an die Funktion oder in der Funktion? Was mich eben auch wundert ist, warum dann Bit 7 fälschlicherweise mit geändert wird. Hat bitWrite einen Bug?

  byte addr_07, addr_08, addr_09, addr_0A, addr_0B, addr_0C, addr_0D = 0;

Meinst du das setzt alle Bytes auf 0? Das ist nicht der Fall

Wobei die danach eigentlich gleich von den RTC Daten überschrieben werden sollten

Hallo,

ja ich meine das die lokal definierten byte Variablen mit Wert 0 initialisiert werden. Soll man doch lokal so machen.
Auch wenn die dann wieder mit Werten der RTC gefüllt werden.
Gibt es ein Problem mit dem lokal definieren?
Wobei alle anderen Registerinhalte, außer 0Ah und 0Dh, genau das machen was sie sollen.

Doc_Arduino:
ja ich meine das die lokal definierten byte Variablen mit Wert 0 initialisiert werden.

Standardmäßig werden lokale Variablen nicht initialisiert. Weil der Initalisierungs-Code den Funktionsaufruf noch langsamer machen würde. Und so wie du das programmierst wird nur die letzte Variable auf 0 gesetzt!! Die anderen bleiben un-initialisiert. Siehe Trap #15:

Sollte aber wie gesagt hier keine Rolle spielen, da du die Werte gleich von der RTC liest.

Wo noch ein kleiner Fehler ist, beim Rückschreiben der Werte. Du machst da zweimal endTransmission(). Beim zweiten mal fragst du den Rückgabe-Wert ab. Das solltest du nur einmal machen und gleich abfragen. Das muss aber auch nichts mit dem Problem zu tun haben.

Hallo,

war der festen Meinung die Schreibweise mit der mehrfach Definition so schon gesehen zu haben. Scheinbar verguckt.

Also ohne RTC funktioniert bitWrite. Kann oben setzen was ich möchte, immer das tritt ein.

/*
 Arduino Mega 2560 
*/

byte addr_0A = 0;

byte Bit_einzeln = 1;
byte set_DYDT_1  = 1;     // use 0 for DATE and 1 for DAY match   

void setup()  {
  
  Serial.begin(57600);      
  delay(2000);        // Zeit nach Reset zum seriellen Monitor hterm zu connecten

}   // Ende setup

void loop(void) {
   
  Serial.print("7 0D vorher:  "); Serial.println(addr_0A, BIN); 
  bitWrite(addr_0A, 7, Bit_einzeln);
  Serial.print("7 0D nachher: "); Serial.println(addr_0A, BIN); 
  
  Serial.print("6 0A vorher:  "); Serial.println(addr_0A, BIN); 
  bitWrite(addr_0A, 6, set_DYDT_1);          
  Serial.print("6 0A nachher: "); Serial.println(addr_0A, BIN); 
  
  Serial.println();
  delay(2000);
  
  addr_0A = 0;
  
     
}   // Ende loop

Scheint ein Problem mit dem RTC Zugriff zu geben. Ich guck mir das mit dem EndTransmission nochmal genauer an.

Hallo,

wegen dem EndTransmission.

Ich habe doch nur 2 komplette I2C Zugriffe in eine Funktion hintereinander gepackt und modifiziere zwischendurch paar Variablen. Die beiden Zugriffe am Anfang und Ende sind doch für sich immer komplett abgeschlossen. Muß ich laut meiner Meinung nach auch machen, sonst kann ich nicht wieder auf die gewünschte Anfangsadresse des Registers setzen.
Am Anfang beim auslesen, soll die Funktion vorzeitig abgebrochen werden, wenn der Zugriff generell schon scheitern sollte.

Ich kann das aber zum testen umbauen, in dem ich die addr_x Variablen global mache und lesen und zurückschreiben als getrennte Funktion schreibe bzw. die vorhandene read Funktion dafür mit nutze.

Das hier ist Pfusch:

  Wire.write(decToBcd(addr_0D));
  Wire.endTransmission();                 // Byte in Register schreiben
  if (Wire.endTransmission() > 0 )  {  
    error = true;                         // I2C Busfehler
  }

endTransmission() ist die Methode die die Daten überhaupt erst auf den Bus schreibt. Vorher wird nur in den Puffer geschrieben. Beim zweiten mal sollte er theoretisch zwar nichts machen, da dann der Ausgangspuffer leer ist, aber es ist trotzdem falsch.

Entferne da einfach das erste endTransmission(). Dann passt es.

Beim Schreiben der Adresse für den Lese-Zugriff machst du es doch auch korrekt:

  Wire.beginTransmission(i2c_adresse);
  Wire.write(0x07);                       // setzen auf Registernummer bzw. dort wo es losgehen soll
  if (Wire.endTransmission() > 0 )  {     // war Connect fehlerfrei?
     error = true;                        // I2C Busfehler
     return error;                        // Abruch
    }

Hallo,

dann schleppe ich den Fehler schon ewig mit mir rum. Hab's geändert. Leider funktioniert das Ganze immer noch nicht richtig.
Nach jeden Reset wechselt der Inhalt und das auch noch falsch. Bit 7 wird überhaupt nicht auf 1 gesetzt dafür. Dafür wird Bit 5 und 6 immer im Wechsel auf 1 gesetzt nach jeden Reset. Wie seit Anfang an.

Habe zusätzlich wie gesagt die addr_x global gemacht und damit die read und set Funktion sinnvoller.

Registerinhalte wechselt sich immer ab nach jeden Reset.

Alarm 1: 	1111	1
Alarm 2: 	111	1
Registeradresse:  Byte
07: 10000000
08: 10000000
09: 10000000
0A: 100000
0B: 10000000
0C: 10000000
0D: 100000
Registeradresse:  Byte
07: 10000000
08: 10000000
09: 10000000
0A: 1000000
0B: 10000000
0C: 10000000
0D: 1000000

Edit:
Der Witz ist, setze die alle 7. Bits auf 0, bleibt Bit 6 konstant auf 1 und auch Bit 5 bleibt konstant 0.

Alarm 1: 	0	1
Alarm 2: 	0	1
Registeradresse:  Byte
07: 0
08: 0
09: 0
0A: 1000000
0B: 0
0C: 0
0D: 1000000
Registeradresse:  Byte
07: 0
08: 0
09: 0
0A: 1000000
0B: 0
0C: 0
0D: 1000000

Also egal wie ich es derzeit mache, lasse ich nur ein Bit (6 oder 7) auf 1 ändern, egal welches, funktioniert alles. Lasse ich beide ändern haut nichts mehr hin. :roll_eyes:

RTC_DS3231_Register_004.ino (12.8 KB)

Hallo,

wegen der Initialisierung von Variablen muß ich nochmal extra nachhaken und verweise ganz frech auf diesen Link. :slight_smile:
http://forum.arduino.cc/index.php?PHPSESSID=0m1nid3tasqa86i0sth1tn97n5&topic=234379.0

Demnach werden globale und lokale statische Variablen immer automatisch mit 0 initialisiert.
Und lokale nicht statische Variablen müssen mit 0 initialisiert werden, sonst haben sie einen Wert von dem was gerade in dem Speicherbereich steht in dem sie angelegt wurden. Also muß man lokale Variablen mit 0 definieren. So habe ich das seinerzeit verstanden.

Generell ja, aber müssen nicht unbedingt. Es kommt darauf an wie man sie verwendet. Es gibt jede Menge Anwendungen wo man lokale Variablen anlegt und sie danach gleich mit anderem Code beschreibt. Dann kann man sich die Initialisierung theoretisch sparen. Wenn aber die Möglichkeit besteht dass dieser Code nicht ausgeführt wird, aber die Variablen danach trotzdem verwendet werden, muss man sie initialisieren.

Das hier läuft ohne Probleme (Visual C++):

void func()
{
	int var1, var2;

	var1 = 1;
	var2 = 2;

	cout << var1 << endl << var2;
}

Das hier ist dagegen falsch, da die Variablen in einem Codepfad nicht initialisiert werden:

void func(bool run)
{
	int var1, var2;

	if(run)
	{
		var1 = 1;
		var2 = 2;
	}

	cout << var1 << endl << var2;
}

Das compiliert zwar, aber liefert korrekterweise eine Warnung wenn man mit /W4 oder /Wall compiliert:

warning C4701: potentially uninitialized local variable 'var1' used
warning C4701: potentially uninitialized local variable 'var2' used

In der Version ohne die if-Abfrage kommt das nicht, da der Compiler merkt, dass nach der Deklaration auf jeden Fall eine Zuweisung kommt

Abseits von C/C++ ist da auch C# ein schönes Beispiel mit out Parametern:

Wenn man in C# einen Funktions-Parameter mit "ref" definiert ist das wie eine Referenz in C. Also sowas:

void func(int &var)
{ 
    var = 5;
}

Die Änderung ist dann außerhalb sichtbar.

In C# will der Compiler aber unbedingt dass man die übergebene Variable vor dem Funktionsaufruf extra initialisiert. Deshalb gibt es statt dessen out. Funktioniert wie ref aber der Compiler meckert nicht wenn die Variable nicht initialisiert ist.

Hallo,

das verstehe ich jetzt absolut nicht. Die Variablen var1 und var2 werden doch ganz klar in beiden Fällen vor deren Nutzung definiert. Warum soll man danach mit denen nicht vernünftig arbeiten können. Irgendwie bricht jetzt gerade das gesamte Kartenhaus meines Programmierwissens zusammen.

Moment, vielleicht liegt es auch daran, dass er im zweiten Fall, wenn run unwahr ist, keine eindeutige Wertzuweisung hat. Teste mal bitte mit

void func(bool run)
{
int var1 = 0;
int var2 = 0;

if(run)

......

Allerdings sind meine addr_x Variablen mittlerweile global mit 0 initialisiert. Daran kann es eigentlich nicht liegen.

Das geht natürlich. So ist es richtig. Das brauche ich nicht zu testen. Ich habe die Initialisierung auf 0 ja explizit weggelassen. Dann läuft es wenn run true ist, aber geht schief wenn es false ist.

Die Variablen var1 und var2 werden doch ganz klar in beiden Fällen vor deren Nutzung definiert.

Die Variablen werden deklariert. Aber bekommen im zweiten Fall nicht immer einen Wert zugewiesen. Lokale Variablen verwenden denen kein Wert zugewiesen wurde == Programmierfehler.

Aber das ist wie gesagt off topic.
Zu deinem eigentlichen Problem. Ich sehe so vom Ansehen nicht wirklich wo da der Fehler ist. Wenn du sagst, dass es ohne I2C Kommunikation geht und die Kommunikation korrekt funktioniert sollte es eigentlich gehen.

Hallo,

okay. Werde mal weiter probieren, obwohl ich auch nicht mehr weiter weis.
Falls jemanden etwas auffällt, nur raus damit.

Hallo,

so Leute, weiter im Text.

Nachdem sämtliche Test's ohne RTC ohne Fehler liefen, habe ich mich wieder mit der RTC damit beschäftigt.
Habe mir dann vor lauter Frust das "bitWrite" selbst programmiert damit ich wirklich weis was passiert. Funktioniert zwar, brachte aber keine Besserung. Also habe ich angefangen verschiedene Bits im Sketch zu ändern, dabei fiel auf, dass es merkwürdige Effekte gab. Das Problem Überlauf stand im Raum. Und wie ich da so sitze und mir nochmal alles anschaue, fiel der Groschen. Es sollte schon der Wert, also die Wunsch-Bitkombination, im Register ankommen, so wie man es sich ausgedacht hat. Das war leider nicht der Fall. Weil ich mir die RTC write Funktion von jurs zu Grunde gelegt hatte. Damit kann man jedoch nur Zeit und Datum stellen, weil das in den BCD Code und zurück gewandelt wird.

Ich habe also die ganze Zeit unbemerkt meine Bitkombinationen vorm schreiben in BCD gewandelt. Da muß ja in höheren Bit-Wertigkeiten Müll rauskommen. Geändert und funktioniert. :slight_smile: Man war die Fehlersuche schwer. :grin:
Bei direkter Bitmanipulation muß man eben verdammt aufpassen.
Dazu gelernt hab wie man selbst Bits zwangsweise ändert. :slight_smile:
Der Sonntagabend ist gerettet.

Jetzt muß ich den Rest nur wieder in den richtigen Zustand bringen.

RTC_DS3231_Register_006.ino (11.8 KB)

Haha. Jetzt wo du es sagst ist es offensichtlich. Ich hatte das schon gesehen aber gar nicht drüber nachgedacht :slight_smile:

Hallo,

gesehen hat man es immer. Ich ja auch. :wink: Nur richtig wahr genommen hat man es nicht.

Fremden Code zu lesen, zu verstehen und wissen was richtig und was falsch läuft ist immer schwer. Da ziehe ich vor allen die das können, Dich eingeschlossen, meinen Hut. Von solchen Experten gibts ja mehrere im Forum.

Es war ja schon eine Hilfe zu wissen das der gesamte RTC Zugriff an sich sozusagen fehlerfrei war. Bis auf die Kleinigkeiten, die zum Glück keine negative Wirkung hatten.

Am Ende war mir klar gewurden, es muß mit dem lesen oder schreiben mit der RTC zu tun haben. Nur was und wie war die Frage gewesen.

Danke @ all für's drüberschauen.