Forum: Compiler & IDEs Trotz uint32_t Überlauf bei Zahlen > 65535?


von Daniel H. (Firma: keine) (commander)


Lesenswert?

Hallo,

für die Uni sollen wir ein kleines Mikrocontroller-Programm schreiben, 
welche eine Uhr darstellt.
Nach einiger Überlegung habe ich mich dazu entschlossen, es quasi wie 
die Unix-Zeit zu gestalten, also einen Counter, der pro Sekunde um 1 
erhöht wird, und aus welchem ich dann, wenn nötig, durch "mod 3600" und 
"mod 60"-Operationen die aktuelle Zeit ermittle.

Um dies zu können muss die Zählervariable also Zahlen bis mindestens 
86399 darstellen können.
Da dies mit uint16_t nicht möglich ist verwende ich also auf uint32_t, 
womit sich ja sogar zahlen >4,2 Mrd speichern lassen.

Nun habe ich aber das Problem, dass ein Überlauf stattfindet, sobald die 
Zeit größer als 18:12:15 wird, was nun genau 65535 entspricht. Danach 
springt die Uhr auf 00:00:00 um.

Als Plattform kommt ein Butterfly-Modul mit dem Atmega169 zum Einsatz.

Die Defines:
1
#include <stdio.h>
2
#include <stdint.h>
3
#include <avr/interrupt.h>
4
#include "butterfly.h"

Hier die Deklaration:
1
uint32_t g_time=0;

Und der Bereich in den der Zähler erhöht wird:
1
if(timer_get() != c_time)
2
{
3
  if(g_time == 86399)
4
  {
5
    g_time=0;
6
  }
7
  else
8
  {
9
    g_time++;
10
  } 
11
  g_hour=g_time/3600;
12
  g_minute=(g_time-(g_hour*3600))/60;
13
  g_second=(g_time-(g_hour*3600)-(g_minute*60));

Ich bin nun langsam mit meinem Latein am Ende und vor allem habe ich 
keine Lust, eine kompliziertere Lösung zu basteln (es sei denn, es ist 
unbedingt nötig).

Dankeschön im voraus.

Gruß,
Daniel

von pumpkin (Gast)


Lesenswert?

Ohne über deine Rechnung weiter zu sinnieren kommt mir spontan folgende 
Idee zum Fehler: Kann es sein, dass die Berechnung anstatt im 
32Bit-Format im 16Bit-Format durchgeführt wird? Wie sind g_hour & co. 
definiert?

pumpkin

von Daniel H. (Firma: keine) (commander)


Lesenswert?

Hi,

g_hour, g_minute und g_second sind als uint8_t definiert.

Gruß,
Daniel

von pumpkin (Gast)


Lesenswert?

Probiere mal folgendes aus:
1
g_hour= (u_int8_t) (g_time / 3600UL);

pumpkin

von pumpkin (Gast)


Lesenswert?

Typo:
1
g_hour= (uint8_t) (g_time / 3600UL);

von Daniel H. (Firma: keine) (commander)


Lesenswert?

Hi,

bringt leider nicht den gewünschten Erfolg.

23:59:00 wird immer noch als 05:46:45 angezeigt.

23*3600+59*60 - 5*3600 - 46*60 - 45 = 65535 = uint16_t

Ich werd hier noch blöde :(

von Daniel H. (Firma: keine) (commander)


Lesenswert?

Hi,

ich habs. Hätte mein Hirn vor meinem letzten Post nochmal anschmeißen 
sollen. Das Problem lag in der Funktion, mit der ich die aktuelle Zeit 
einstelle. Nachdem ich nun dort
1
else if(state==11)
2
{
3
  g_time=(3600*s_hour+60*s_minute);
4
  state=6;
5
}

dass hier eingetragen habe gehts.
1
else if(state==11)
2
{
3
  g_time=(uint32_t) (3600UL*s_hour+60UL*s_minute);
4
  state=6;
5
}

Wofür steht eigentlich dieses "UL"?

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Das macht aus einer numerischen Konstante, die sonst als int aufgefasst 
wird, ein unsigned long, also hier uint32_t.

von Florian (Gast)


Lesenswert?

>Nach einiger Überlegung habe ich mich dazu entschlossen, es quasi wie
>die Unix-Zeit zu gestalten, also einen Counter, der pro Sekunde um 1
>erhöht wird, und aus welchem ich dann, wenn nötig, durch "mod 3600" und
>"mod 60"-Operationen die aktuelle Zeit ermittle.
1
  g_hour=g_time/3600;
2
  g_minute=(g_time-(g_hour*3600))/60;
3
  g_second=(g_time-(g_hour*3600)-(g_minute*60));

Verstehen wir dasselbe unter der Modulo-Funktion?

Wie wäre es mit
1
 g_minute = ( g_time / 60 ) % 60;
2
 g_second = ( g_time % 60 );

oder mit
1
 g_second = ( g_time % 60 );
2
 g_tmp = g_time / 60;
3
 g_minute = ( g_tmp % 60 );
4
 g_tmp /= 60;
5
 g_hour = g_tmp;

In meinen Augen sollte man versuchen, die Anzahl der Multiplikationen 
und der Divisionen gering zu halten, da diese relativ viel Zeit in 
Anspruch nehmen. Im Grunde genommen handelt es sich bei der 
'Modulo-Funktion' auch um eine Division, allerdings kann diese manchmal 
vom Compiler bereits sehr stark optimiert werden. Leider ist 60 kein 
sehr schöner Wert dafür und leider ist für diese Art von Berechnung ein 
Minimum an Divisionen notwendig, aber das ist eine generelle Stilfrage 
bzw. Abwägung zwischen Lesbarkeit und Geschwindigkeit.

von Daniel H. (Firma: keine) (commander)


Lesenswert?

Hi,

ja, hast recht, ich hab mich da verschrieben, ich meinte die 
stinknormale Division :)

Was die Anzahl Divisionen angeht, ich sehe nur zwei Möglichkeiten mit 
vertretbarem Aufwand eine Uhr zu realisieren.
Entweder ich mache es, wie schon beschrieben, über einen Sekundenzähler, 
aus dem ich mir dann mit den bereits vorgestellten Operationen die 
"echte" Uhrzeit ermittle, oder aber ich bastel mir eine Uhr, bei der 
ebenfalls der Sekundenzähler erhöht wird, und sobald er 60 erreicht wird 
ein Minutenzähler erhöht wird usw..
Problem ist hier, dass man ziemlich viele verschiede Fälle unterscheiden 
muss, damit die Uhr korrekt funktioniert (ist Stunde 23, Minuten 59, 
Sekunden 59 + Kombinationen aus all dem).
Die erste Möglichkeit erscheint für mich als Mikrocontroller-Neuling 
eigentlich die leichtere. Zumal es sich um ein Projekt für die Uni 
handelt, bei dem weniger die Performance im Vordergrund steht sondern es 
einfach darum geht, dass man sich mit der Materie auseinandersetzt und 
mit den Datenblättern usw. des Mikrocontrollers klar kommt.

Die Uhr ist jetzt auch letztenendes das Tüpfelchen auf dem i. In 
vorherigen Aufgaben haben wir die Bibliothek "butterfly.c" entwickelt, 
diese enthält diverse Funktionen für den Joystick, das LCD-Display, den 
Piezo-Lautsprecher und eben auch einen Timer, der pro Sekunde eine 
Variable hochzählt.

Allerdings werde ich das Ganze (sprich die Uhr und alles was noch 
gefordert ist) heute nochmal von vorne beginnen, da ich mich inzwischen 
ein bischen verrannt habe und den Fehler gemacht habe, einfach drauf los 
zu programmieren. Es funktioniert zwar im moment alles, aber es fehlen 
noch einige Funktionen, die nach ihrer Implementierung zu gut wie alle 
anderen Funktionen stören (zumindest was die Display-Ausgabe, und somit 
das Herzstück des ganzen Projekts angeht).

Dankeschön euch alle für die Hilfe :)

von Andreas K. (a-k)


Lesenswert?

Wenn eine Modulo-Operation vom Compiler optimiert werden kann, dann kann 
auch die entsprechende Division optimiert werden (allerdings ist eine so 
optimierte Modulo-Operation auf einem 8-Bit-Prozessor wie AVR etwas 
billiger).

Zur Standardlib gehören jedoch auch die Funktionen div() und ldiv(), die 
beide Operationen ein einem Aufwasch durchführen. Was sie in diesem Fall 
sinnvoll macht, sofern die Laufzeit relevant ist.

von Stefan K. (_sk_)


Lesenswert?

>Problem ist hier, dass man ziemlich viele verschiede Fälle unterscheiden
>muss, damit die Uhr korrekt funktioniert (ist Stunde 23, Minuten 59,
>Sekunden 59 + Kombinationen aus all dem).

Machst Du Dir da nicht einen Knoten im Kopf?
Der Unix-Zeit Ansatz hat sicher manche Vorteile, einfacher zu 
programmieren (und später für den mc WESENTLICH einfacher zu berechnen) 
ist die ganz normale Zeitdarstellung mit getrennten asec, min und std 
Variablen:
1
  uint8_t sec;
2
  uint8_t min;
3
  uint8_t std;
4
5
  sec++;
6
  if (sec > 59){
7
    sec = 0;
8
    min++;
9
    if (min > 59){
10
      min = 0;
11
      std++;
12
      if (std > 23){
13
        std = 0;
14
      }
15
    }
16
  }
Viele Grüße, Stefan

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.