www.mikrocontroller.net

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


Autor: Daniel H. (Firma: keine) (commander)
Datum:

Bewertung
0 lesenswert
nicht 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:
#include <stdio.h>
#include <stdint.h>
#include <avr/interrupt.h>
#include "butterfly.h"

Hier die Deklaration:
uint32_t g_time=0;

Und der Bereich in den der Zähler erhöht wird:
if(timer_get() != c_time)
{
  if(g_time == 86399)
  {
    g_time=0;
  }
  else
  {
    g_time++;
  } 
  g_hour=g_time/3600;
  g_minute=(g_time-(g_hour*3600))/60;
  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

Autor: pumpkin (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Daniel H. (Firma: keine) (commander)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi,

g_hour, g_minute und g_second sind als uint8_t definiert.

Gruß,
Daniel

Autor: pumpkin (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Probiere mal folgendes aus:
g_hour= (u_int8_t) (g_time / 3600UL);

pumpkin

Autor: pumpkin (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Typo:
g_hour= (uint8_t) (g_time / 3600UL);

Autor: Daniel H. (Firma: keine) (commander)
Datum:

Bewertung
0 lesenswert
nicht 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 :(

Autor: Daniel H. (Firma: keine) (commander)
Datum:

Bewertung
0 lesenswert
nicht 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
else if(state==11)
{
  g_time=(3600*s_hour+60*s_minute);
  state=6;
}

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

Wofür steht eigentlich dieses "UL"?

Autor: Rufus Τ. Firefly (rufus) (Moderator) Benutzerseite
Datum:

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

Autor: Florian (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.
  g_hour=g_time/3600;
  g_minute=(g_time-(g_hour*3600))/60;
  g_second=(g_time-(g_hour*3600)-(g_minute*60));

Verstehen wir dasselbe unter der Modulo-Funktion?

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

oder mit
 g_second = ( g_time % 60 );
 g_tmp = g_time / 60;
 g_minute = ( g_tmp % 60 );
 g_tmp /= 60;
 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.

Autor: Daniel H. (Firma: keine) (commander)
Datum:

Bewertung
0 lesenswert
nicht 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 :)

Autor: Andreas K. (a-k)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Stefan Kleinwort (_sk_)
Datum:

Bewertung
0 lesenswert
nicht 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:
  uint8_t sec;
  uint8_t min;
  uint8_t std;

  sec++;
  if (sec > 59){
    sec = 0;
    min++;
    if (min > 59){
      min = 0;
      std++;
      if (std > 23){
        std = 0;
      }
    }
  }
Viele Grüße, Stefan

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.