Forum: Mikrocontroller und Digitale Elektronik Uhrzeit - Denkfehler oder Datentyp?


von Thomas (Gast)


Lesenswert?

Hallo, Leute!

Ein kleines Problem plagt mich seit zwei Tagen: Man stelle sich vor, ich 
hätte ein IC, das ich um Mitternacht mit Spannung versorge und ab dann 
jede sekunde ein internes Register von Null an um eins erhöht. Jetzt sei 
es zwei Uhr mittags und ich lese das Register mal aus und habe somit die 
Zahl der vergangenen Sekunden (Das Ding sendet 4 Bytes mit LSByte first 
- muss also die 4 Einzelbytes noch "zusammenkleben".). Mit ein bisschen 
Rumrechnen lässt sich doch jetzt sagen, wie spät es ist. Und das soll 
ein AVR programmiert in GCC tun. Er tut es auch. Allerdings läuft 
irgendwas schief bei manchen Uhrzeiten. Der Fehler müsste so um 17:00 
rum auftreten. Ich hab mal ein Codesnippet angehängt und wäre dankbar, 
wenn Ihr mal drüberschauen könntet...

Die Kommunikation ist übrigens okay, d.h., die empfangenen Variablen 
entsprechen den Registerinhalten in der RTC.
1
uint8_t erstesbyte  = empfangen();   //LS Byte empfangen
2
uint8_t zweitesbyte = empfangen();   //..
3
uint8_t drittesbyte = empfangen();   //..
4
uint8_t viertesbyte = empfangen();   //MS Byte empfangen
5
6
uint32_t sekunden;      // 
7
sekunden = viertesbyte;      //  
8
sekunden = (sekunden<<8)+drittesbyte;  // hier sollen die 4 bytes zu
9
sekunden = (sekunden<<8)+zweitesbyte;  // einer 4-bt-integer zusammen
10
sekunden = (sekunden<<8)+erstesbyte;  // gefasst werden
11
12
uint8_t hh = sekunden/3600;    // hier sollen die vergangenen
13
uint8_t mm = (sekunden-(hh*3600))/60  // sekunden in die aktuelle 
14
uint8_t ss = sekunden-(hh*3600)-(mm*60)  // uhrzeit umgerechnet werden.
15
          // Bezugspunkt sei 00:00:00 uhr

von Jörg X. (Gast)


Lesenswert?

Ungefähr 17:00? Nicht vielleicht 18:12:16? (18*3600+12*60+16 == 65536) 
-- riecht nach einem Überlauf.
Außerdem wird dir der Modulo-Operator (%) sehr weiterhelfen.
Ich würde deine Berechnung ein bisschen umstellen:
1
/* Prototyp */
2
uint8_t empfangen(void);    //Besser einen sprechnederen Namen nehmen
3
                            // sowas wie get_rtc_byte oder so
4
uint32_t sekunden;
5
uint_8 t hh, mm, sec;
6
7
/* WENN empfangen() genau ein Byte liefert: */
8
sekunden = empfangen();                //LS Byte empfangen
9
sekunden |= (uint32_t)empfangen()<<8;  //..
10
sekunden |= (uint32_t)empfangen()<<16; //..
11
sekunden |= (uint32_t)empfangen()<<24; //MS Byte empfangen
12
13
hh = sekunden/3600;
14
sekunden %= 3600 ;      // Rest
15
mm = (sekunden)/60;
16
sec = sekunden % 60;

hth. Jörg

von Thomas (Gast)


Lesenswert?

Ich Depp... Natürlich "|" statt "+"... Okay, das ist besser, 
funktioniert aber immer noch nicht wirklich. Scheint wohl etwas mit der 
Bereichsüberschreitung zu tun zu haben. Muss irgendwie rauskriegen, wie 
ich aus den 4 einzelnen Bytes, eine 4-Byte-var machte, die der AVR 
arithmetisch beackern kann. Wenn ich Deinen Code mal übertrage, bekomme 
ich jensets von 00:04:15 (das sind 255 (!) sekunden) sozusagen einen 
"Neustart" der Uhr. Aber besten Dank schonmal für den oder-hint!

von Andreas K. (a-k)


Lesenswert?

Das geht schon nach 9 Stunden in die Hose, weil
> (hh*3600))
als "int" berechnet wird und damit bei 32767 Schluss ist.

von Karl H. (kbuchegg)


Lesenswert?

Thomas wrote:
> Ich Depp... Natürlich "|" statt "+"...

Das ist in dem Fall völlig Wurscht.

Entscheidend ist, dass bei
  (uint32_t)empfangen()<<8

das uint8_t Ergebnis von empfangen() zuerst mal auf 32 Bit
aufgeblasen wird, bevor es nach links verschoben wird.


> Okay, das ist besser,
> funktioniert aber immer noch nicht wirklich.#

Zeig noch mal deinen jetzigen Code

von Roland Praml (Gast)


Lesenswert?

Ein kleiner Tipp: Sowas mach ich immer im Simulator in dem ich mir ein 
entsprechendes Code-snippet zusammenbastle.

Gruß
Roland

von Roland Praml (Gast)


Lesenswert?

man könnte hier auch gut mit einer union-struct arbeiten:
1
union
2
{
3
  uint8_t b[4];  // 4  Bytes
4
  uint32_t i;     // belegen den sellben Speicher wie i
5
} u;
6
7
unsignet cnt;
8
9
for (cnt = 0; cnt < 4; cnt++) u.b[cnt] = empfangen(); // 4x empfangen
10
11
hh = u.i / 3600;
12
...
(untested)

Gruß
Roland

von Thomas (Gast)


Lesenswert?

Okiedo... Hier noch mal der aktuelle Code, der bei 255 sekunden "schlapp 
macht". Danach sagt die Uhr wieder 0:00:00 und fängt von vorne an.
1
uint8_t ow_rtc_1st=~(_1w_receivebyte());
2
uint8_t ow_rtc_2nd=~(_1w_receivebyte());
3
uint8_t ow_rtc_3rd=~(_1w_receivebyte());
4
uint8_t ow_rtc_4th=~(_1w_receivebyte());
5
6
7
  uint32_t seconds=ow_rtc_4th;      //MSB
8
  seconds=(seconds>>8)|ow_rtc_3rd;
9
  seconds=(seconds>>8)|ow_rtc_2nd;
10
  seconds=(seconds>>8)|ow_rtc_1st;    //LSB
11
  
12
  
13
  uint8_t hh = seconds/3600;
14
  seconds%=3600 ;      // Rest
15
  uint8_t mm = seconds/60;
16
  uint8_t ss = seconds%60;

von Andreas K. (a-k)


Lesenswert?

Thomas wrote:
>   seconds=(seconds>>8)|ow_rtc_3rd;

Der Unterschied zwischen Links und Rechts ist dir klar?

von Walter (Gast)


Lesenswert?

du solltest lieber nach links schieben

von Thomas (Gast)


Lesenswert?

Eigentlich schieb ich auch nach links, hab nur in meiner Verzweiflung 
das mal mit rechts durchgetestet und vergessen, es wieder zu ändern. In 
Echtigkeit schieb ich nach links.
1
uint8_t ow_rtc_1st=~(_1w_receivebyte());
2
  uint8_t ow_rtc_2nd=~(_1w_receivebyte());
3
  uint8_t ow_rtc_3rd=~(_1w_receivebyte());
4
  uint8_t ow_rtc_4th=~(_1w_receivebyte());
5
  _1w_detectpresence();
6
7
  uint32_t seconds=ow_rtc_4th;      //MSB
8
  seconds=(seconds<<8)|ow_rtc_3rd;
9
  seconds=(seconds<<8)|ow_rtc_2nd;
10
  seconds=(seconds<<8)|ow_rtc_1st;    //LSB
11
  
12
  
13
  uint8_t hh = seconds/3600;
14
  seconds%=3600 ;      // Rest
15
  uint8_t mm = seconds/60;
16
  uint8_t ss = seconds%60;

von Helmi (Gast)


Lesenswert?

Kann es sein das du hier alle bits invertierts (~) ?

uint8_t ow_rtc_1st=~(_1w_receivebyte());
  uint8_t ow_rtc_2nd=~(_1w_receivebyte());
  uint8_t ow_rtc_3rd=~(_1w_receivebyte());
  uint8_t ow_rtc_4th=~(_1w_receivebyte());

versuch es doch mal so

  uint8_t ow_rtc_1st=(_1w_receivebyte());
  uint8_t ow_rtc_2nd=(_1w_receivebyte());
  uint8_t ow_rtc_3rd=(_1w_receivebyte());
  uint8_t ow_rtc_4th=(_1w_receivebyte());


Gruss Helmi

von Thomas (Gast)


Lesenswert?

Ja, ich invertiere alle Bits, aber das ist Absicht. Wie gesagt, an der 
Kommunikation hängts nicht, sondern eher an der "Zusammenführung" oder 
Berechnung...

von Walter (Gast)


Lesenswert?

die Berechnung ist meiner Meinung nach richtig, also

entweder es kommen andere Daten als du erwartest,
oder die Ausgabe der Ergebnisse ist falsch!

Wie ist denn das genaue Verhalten der Zeitanzeige?

von yalu (Gast)


Lesenswert?

Ist das Bei-255-Sekunden-Schlapp-Mach-Problem immer noch da, oder lag
das nur an der verkehrten Schieberichtung? In meinen Augen scheint die
Umrechnung der Sekunden in hh, mm und ss korrekt zu sein.

Ein möglicher Fehler liegt aber im Einlesen der 4 Bytes: Hast du
sichergestellt, dass sich die Werte während des Einlesens nicht
ändern? Bspw. dadurch, dass du die 4 Bytes auf Hardwareseite latchst?
Sonst kann (und wird) es hin und wieder passieren, dass der
Sekundenwechsel genau während des Einlesens der Bytes stattfindet.
Die gelesenen Werte sind dann falsch.

Kontrollieren kannst du dies, indem du die 4 Bytes ein zweites Mal
einliest. Haben sie den gleichen Wert wie beim ersten Mal, sind die
Werte ok. Wenn nicht, hast du einen Sekundensprung erwischt. Die Werte
müssen dann korrigiert oder - und das ist die sicherere Methode - ein
drittes Mal eingelesen werden.

von helmi (Gast)


Lesenswert?

Dann gibt die Daten doch einmal einzeln aus also ow_rtc_1st .. 
ow_rtc_4st und dann die zusammenfassung seconds

wenn in z.B. in ow_rtc_1st = 0x12
                ow_rtc_2st = 0x34
                ow_rtc_3st = 0x56
                ow_rtc_4st = 0x78

dann muss in Seconds = 0x78563412 sein

wenn das dann gleich ist müsste die zusammenfassung ok sein

Gruss helmi

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.