Forum: Projekte & Code Die genaue Sekunde / RTC


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Peter D. (peda)


Angehängte Dateien:

Lesenswert?

Sehr oft kommt hier die Frage wie man einen Sekundentakt erzeugen kann
oder ob man unbedingt einen Uhrenquarz oder, noch schlimmer, einen
extra RTC-Chip braucht.

Die Antwort lautet: Nein !

Man kann bequem mit dem Timer aus jeder X-beliebigen Quarzfrequenz auf
den Takt genau die Sekunde erzeugen.


Und dann, immer wenn 60 Sekunden um sind, ein Minutenregister
hochzuzählen, nach 60 Minuten ein Stundenregister und nach 24 Stunden
den Tag, das ist ja wohl keine Kunst mehr.


In den Beispiel habe ich versucht, den mathematischen Weg aufzuzeigen.

Außerdem ist dann noch ein Beispielcode in C und in Assembler mit
dabei.

Letztendlich übernehmen beide die nötigen Berechnungen vollautomatisch,
solange man die beschriebenen Randbedingungen einhält.

Man muß also nur seine Quarzfrequenz in den Sourcecode eintragen und
ihn compilieren bzw. assemblieren lassen.



Peter

: Gesperrt durch Moderator
von Tipper (Gast)


Lesenswert?

Hallo Peter,

wie sieht es denn mit der Ganggenauigkeit über 1Jahr aus?

Ist da ein Uhrenquarz nicht genauer und stabiler?


Schönen Gruß   Tipper

von Peter D. (peda)


Lesenswert?

@Tipper

"wie sieht es denn mit der Ganggenauigkeit über 1Jahr aus?"

Ich kann nicht klagen.
Ich habe schon seit mehreren Jahren eine Uhr mit einem billigen
Standard Quarz 11,0592MHz am laufen.

Zu Anfang habe ich sie 3 Monate laufen lassen und mit der Videotext Uhr
verglichen. Dann die ermittelte Frequenz eingetragen und neu compiliert
und geflasht. Der MC ist ein AT89C2051.

Zur Zeit geht sie 14s vor, dieses Jahr habe ich sie noch nicht gestellt
(Sommerzeitumstellung ist mit einprogrammiert).


Es gibt aber auch von Maxim einen neuen Zeitgeber-Chip mit 38kHz, der
mindestens um den Faktor 10 besser ist als ein Standard 38kHz Quarz.

Prinzipiell sind höherfrequente Quarze immer auch genauer.
Früher wurde deshalb für hochgenaue Uhren ein 4,19MHz Quarz verwendet.

Wie man aber an Maxim sieht, kann man durch bessere Technologie diesen
Unterschied kompensieren. Es könnte aber auch sein, daß in dem Chip ein
Temperatursensor mit integriert ist und dann mit einer Korrekturtabelle
die Schwankungen ausgeglichen werden.



Peter

von Tobias Breckle (Gast)


Lesenswert?

junge junge,
so langsam glaub ich du bist echt der mikrocontroller gott!!
und auch noch ne anleitung dazu.
besser gehts ned!! :)

von buz11 (Gast)


Lesenswert?

" junge junge,
so langsam glaub ich du bist echt der mikrocontroller gott!! "

Geht mir auch so .

Ich müsste noch mal auf die Welt kommen , und mit ca. 6 Jahren
anfangen mit MC zu spielen ...

von Marcel Meyer (Gast)


Lesenswert?

Sehr schöne sache, gefällt mir gut, der code ist ebenfalls recht
verständlich aufgebaut und so langsam klappt es auch bei mir mit den
ganzen "timer gelumpe" :-)

Weiter so! Danke!

Mfg
Marcel

von Paul Baumann (Gast)


Lesenswert?

Hallo Leute!
Noch eine Erfahrung hätte ich beizusteuern:Siet dem 10.Oktober habe
ich hier eine Uhr im Gang, die mit At90S2313 und 4MHZ-Quarz arbeitet.
Das Ding habe ich seitdem nicht mehr gestellt und es läuft immer noch
synchron zum Zeitsignal aus Mainflingen.
Man kann generell sagen:Je höher die Quarzfrequenz, desto geringer
die Abweichung.

MfG Paul

von A. Arndt (Gast)


Lesenswert?

Hallo,

ich nehme trotzdem lieber einme RTC mit Gangreserve, da kann mit dem uC
rumspilen und muss nicht ständig die Zeit neu einstellen oder auf
DCF-Dekodierung warten.

Gruss
A. Arndt

von Werner Hoch (Gast)


Lesenswert?

Hi,

Den Code von Peter finde ich ziemlich cool.
Hier sind trotzdem kleine Verbesserungsvorschläge.

Wird im Timer die Option "Clear On Compare Match" verwendet,
so verliert man den Overflow Interrupt.
Oder möchte man nebenher noch eine Zeit mit dem Capture Interrupt
messen, so benötigt man einen durchlaufenden Timer.
Um dies zu erreichen, wird OCR1A nicht fest eingestellt, sondern
bei jedem Aufruf um den gleichen Wert erhöht.

In Peter's Code wurde der Rest auf einmal abgearbeitet. Damit erspart
man sich bei jedem Interrupt einen Vergleich und die Verarbeitungszeit
verkürzt sich in seinem Code. Der Unterschied zwischen kurzem und
langem
Interrupt ist hier der Rest.
Bei den geänderten Codeschnipseln wird der Rest gleichmäßiger
abgearbeitet.
Der Unterschied zwischen kurzem und langem Interrupt beträgt 1 Takt.

mfg
werner

ungetesteter Code für den durchlaufenden Timer:
==================================================

SIGNAL
(SIG_OUTPUT_COMPARE1A) {
/*********************************************************************** 
*/
/*      Insert Key Debouncing Here    */
/*********************************************************************** 
*/

  if( --prescaler == 0 ){
    prescaler = DEBOUNCE;
    second++;      // exact one second over
  }
  if (prescaler <= XTAL % DEBOUNCE) {
    OCR1A += XTAL / DEBOUNCE +1;   /* um 1 Takt längere Periode um
              den Rest abzutragen */
  } else {
    OCR1A += XTAL / DEBOUNCE;   // kurze Periode
  }
}

Um im Sonderfall, daß XTAL%DEBOUNCE null ist, keinen unnötigen
Code zu haben, kann man wieder Präprozessoranweisungen verwenden:

SIGNAL
(SIG_OUTPUT_COMPARE1A) {
/*********************************************************************** 
*/
/*      Insert Key Debouncing Here      */
/*********************************************************************** 
*/

  if( --prescaler == 0 ){
    prescaler = DEBOUNCE;
    second++;      // exact one second over
  }
#if XTAL % DEBOUNCE
  if (prescaler <= XTAL % DEBOUNCE) {
    OCR1A += XTAL / DEBOUNCE +1;   /* um 1 Takt längere Periode um
              den Rest abzutragen */
  } else {
#endif
    OCR1A += XTAL / DEBOUNCE;   /* kurze Periode */
#if XTAL % DEBOUNCE
  }
#endif
}

von Peter D. (peda)


Lesenswert?

Sehr schön.

Es freut mich immmer wieder, wenn jemand meinen Code versteht und
darauf aufbauend sogar noch Verbesserungen oder auch andere Meinungen
dazu hat.


Peter

von Rolf F. (Gast)


Lesenswert?

Ja, weil Quarze um 5 MHz am stabilsten sind wurden früher (um 1930 -
Mitte letztes Jahrhunder) damit die damals geauesten Uhren der Welt
gebaut; Uhrenquarze eignen sich eigentlich nur wegen der geringen
Frequenz und der daraus folgenden niedrigen Stromaufnahme gut für
Uhren.

Wenn man es genau machen will, dann kann man noch (Dreh-)Kondensatoren
(von den Quarz-Kontakten sowie -Gehäuse zu Masse) und einen
Parallel-Widerstand/-Poti anschließen und damit die Frequenz genau
einstellen (mit einem 12-stelligen Frequenzzähler). Genauer geht es
dann nocht mit der Temperatur-Kennlinie der Schaltung, die man mit
Software (u. dem MC-internen Temp.-Sensor) eliminieren kann. Wegen
Schaltsekunden u. A. ist aber sowas wie DCF77 oder NTP meist sinnvoller
als dieses Feintuning.

Mal eine Frage zur Sommer-/Winter-Zeit: Gibt es dafür eine Formel?
Gefunden hab ich ja schon Formeln für Schaltjahre, Tage seit dem 1.1.1,
Wochentag, Kalenderwoche (nach DIN 1355) auch vor 1500 aber noch keine
für Sommer-/Winterzeit.

von Matthias (Gast)


Lesenswert?

Hi

Umstellung von Winterzeit auf Sommerzeit:
Am letzten Sonntagmorgen im März werden die Uhren von 02:00 auf 03:00
Uhr vorgestellt. Man verliert eine Stunde.

Umstellung von Sommerzeit auf Winterzeit:
Am letzten Sonntagmorgen im Oktober werden die Uhren von 03:00 auf
02:00 Uhr zurückgestellt. Man gewinnt eine Stunde.

Matthias

von Peter D. (peda)


Lesenswert?

In meinem Beispielcode für die RTC DS1994 ist sowas in Assembler mit
drin, prüft jeden Sonntag darauf.

Funktioniert aber nicht mehr 2100, da das kein Schaltjahr ist, d.h die
Sonntagserkennung schlägt fehl.

Ich nehme auch ab 2100 gerne sämtliche Beschwerden diesbezüglich
entgegen.


Peter

von Rolf F. (Gast)


Lesenswert?

@Matthias:

Woher hast Du das?
Es muß dafür eine Norm geben, aber welche konnte ich noch nicht
finden.
Naja, wenn die Sommerzeit so plötzlich abgeschafft wird, wie sie
eingeführt wurde, dann hat sich das Problem erledigt.


@Peter:

In "Das C++ Codebook" steht einiges z. B. mit den Tagen seit dem
01.01.0001, das aktuelle Sternzeichen usw..
Für MCs ist aber eher dies hier interessant (i. W. C mit einigen
C++-Resten):

// http://home.nordwest.net/hgm/kalender/kal-61.htm
// bis http://home.nordwest.net/hgm/kalender/kal-66.htm
// http://home.nordwest.net/hgm/kalender/kal-aa.htm

Ich habe das angepasst für einen MC, der mit der Zeit seit 1.1.2000
0:0:0 rechnet. Bis auf Schaltsekunden u. Winter-/Sommerzeit stimmt die
Zeitrechnung damit (auch mit ALLEN zukünftigen Schaltjahren), aber 2068
läuft der 32-Bit-RTC-Zähler über.
Die Wochentage u. Kalenderwochen nach DIN 1355 stimmen mit den
Funktionen auch.
Fehlen eigentlich nur noch die Mondphasen ...

von Matthias (Gast)


Lesenswert?


von Rolf F. (Gast)


Lesenswert?

Aha, danke :-)
Bei meinen Links findest Du eine kompakte u. korrekte Version zur
richtigen Berechnung der Schaltjahre. Mit einem 128-Bit-Sekundenzähler
gibt´s dann nur das Problem der Schaltsekunden und der
Quarz-Ungenauigkeit.

von Stefan (Gast)


Lesenswert?

Kirk an Peter:

Wie siehts aus mit der Lösung unseres Jahr-2100-Problems?


;-)))   Stefan

von Peter D. (peda)


Lesenswert?

@Stefan

"Wie siehts aus mit der Lösung unseres Jahr-2100-Problems?"


Ich stelle sie natürlich am 28.02.2100 hier ins Forum.

Kannst mich ja am 27.02.2100 nochmal daran erinnern.



Peter

von Rolf F. (Gast)


Lesenswert?

Wo soll 2100 ein Problem sein?
Die Schaltjahr-Berechnung ist doch ganz einfach:

 function ist_Schaltjahr(jahr) {
   if (jahr % 4 == 0)
      if (jahr < 1582)
         return 1;
      else if (!(0 == jahr % 100))
              return 1;
           else if (0 == jahr % 400)
                   return 1;
   return 0;
 }

http://www.nwn.de/hgm/kalender/kal-61.htm

Die Abfrage mit dem Jahr 1582 kann man im Microcontroller weglassen.

von Peter D. (peda)


Lesenswert?

"Wo soll 2100 ein Problem sein?"


Ich dachte, daß meine Bemerkungen dahingehend eindeutig sind:

Es macht einfach keinen Sinn, sich schon jetzt damit zu beschäftigen.

Keiner weiß doch, ob der Flash-Speicher überhaupt dazu in der Lage ist,
100 Jahre lang einen Programmcode fehlerfrei zu speichern.


Peter

von Rolf F. (Gast)


Lesenswert?

Ich meine ja nur, dass man die Formel zur Schaljahrberechnung in wenigen
Sekunden korrigieren kann; das sind ja weniger als 10 kurze Zeilen.

von Martin (Gast)


Lesenswert?

Hi, Peter!
Habe ich den folgenden Satz in deiner Beschreibung richtig verstanden?
--------------------------------------------------------------
Deshalb wird jedensmal, wenn der Softwareteiler Null ist und die
Sekunde weitergezählt wird, ein anderer Comparewert geladen. Dieser ist
dann um den Rest größer. Und beim nächsten Timerinterrupt wird dann
wieder der Comparewert geladen, der das Ergebnis der Division war.
--------------------------------------------------------------


Heißt das, dass der Comparewert beim ersten mal 43198 + 64 ist,
um die fehlenden 64 Zyklen in der Sekunde auszugleichen,
und alle anderen Comparewerte in der selben Sekunde sind 43198?

Martin

von Markus Holzapfel (Gast)


Lesenswert?

Hallo Martin,

> Heißt das, dass der Comparewert beim ersten mal 43198 + 64 ist,
> um die fehlenden 64 Zyklen in der Sekunde auszugleichen,
> und alle anderen Comparewerte in der selben Sekunde sind 43198?

Ja. Ganz genau das heißt das.

Gruß,
Markus

von A. Arndt (Gast)


Lesenswert?

Hallo Peter,

bin jetzt auch nochmal auf den DS1994 bzw. DS1904 gestossen, der zählt
ja nur Sekunden hoch und ich müsste dann immer in Minuten,, Stunden
Tage, Monate und Jahre umrechnen, gibt es kein anderes ibutton der
diese Daten wie der Ds1307 ablegt ?

Gruss
A. Arndt

von Peter D. (peda)


Lesenswert?

Ja, ich finde das auch sehr umständlich.

In meinem Tiny12 Beispiel wird einfach beim Einschalten die Uhr
hochgezählt, bis der 32Bit-Wert mit dem im DS1994 gleich ist.
Das kann dann in 100 Jahren schon ein paar Sekunden dauern.


Allerdings ist der DS1994 nicht sehr genau. Bei mir geht er 10min/Jahr
vor. Ich müßte also noch den 32Bit-Wert mit 0,99998049 multiplizieren,
aber dann wird eng im Tiny12 mit dem Programmspeicher.


Peter

von Matthias (Gast)


Lesenswert?

Hi

dann zieh doch einfach alle 52560 Sekunden eine Sekunde ab.

Matthias

von nobody0 (Gast)


Lesenswert?

Naja, wenn da alle x Sekunden korrigiert wird, dann kann das Fehler
verursachen, beispielsweise bei Zeitdifferenz-Messungen.
Beim Multiplizieren ist es ähnlich, denn die Sekunden werden ja nur
ohne Nachkommastellen, also gerundet verwendet; d. h. plötzlich dauert
eine angezeigte Sekunde zwei Sekunden oder null Sekunden; das kann auch
Fehler verursachen.

von Matthias (Gast)


Lesenswert?

Hi

natürlich kann das Probleme machen. Wenn aber nur um eine reine Uhr
geht ist das eigentlich keine große Sache. Man zieht die Sekunde
natürlich nicht um 12:00 Uhr Mittags ab sondern irgendwann mitten in
der Nacht.

Matthias

von tex kerstan (Gast)


Lesenswert?

Hallo Rolf!
Habe ich Deinen Beitrag richtig verstanden, dahingehend, dass Du die
Uhr auf der Basis des Julianischen Datums programmiert hast?
Falls ja und wenn Du es hergeben würdest, würde ich mir das Programm
gerne mal ansehen.
Grüße aus Berlin
tex

von nobody0 (Gast)


Lesenswert?

Hallo tex,
das Programm macht nichts anderes als einen 32-Bit-Zähler hochzuzählen
- jede Sekunde einmal + gelegentlich Minute, Stunde usw. hochzählen.
Code ist u. a. von hier:
 http://home.nordwest.net/hgm/kalender/kal-61.htm
 bis http://home.nordwest.net/hgm/kalender/kal-66.htm
 http://home.nordwest.net/hgm/kalender/kal-aa.htm

Dabei ist mir wichtig, dass Schaltjahre richtig berechnet werden und
leicht berechnet werden kann, ob ein Datum existiert oder nicht, wie z.
B. der 31.2..
Mit ein paar kleinen Funktionen ist das kein Problem.
Zusätzlich habe ich u. A. noch kleine Funktionen beispielsweise zum
Inkrementieren u. Dekrementieren des Datums gemacht.

von tex kerstan (Gast)


Lesenswert?

Hi Nobody0!
Das Geheimnis des Julianischen Datums ist, das es keine falschen Tage
gibt, also keinen 31.2. ... Die ganze Bastelei mit den Tagen, Monaten
ect. ist total überflüssig, wenn man die Information aus dem JT zieht.
Dann kommen alle Gemeinheiten des Kalenders ganz automatisch 31.7 /
31.8. ...
Das Julianische Datum hab ich unter Kontrolle ich bin zu blöd den
Zähler zu programmieren!

von nobody0 (Gast)


Lesenswert?

Das hilft aber nicht, wenn in einem Menü ein User als Geburtsdatum den
31.2.2033 eingeben kann.
Bei 99,9 % aller Menüs oder anderer Eingaben kann dies nicht direkt
abgefangen werden, weil da immer der Tag 1 bis 31 ausgewählt werden
kann und ähnliches für Monat und Jahr gilt.
Dass es das obige Datum nicht gibt und selbst die dem nahekommenden
Daten (Dekrementieren/Inkrementieren bis ein gültiges Datum erreicht
wird) in der Zukunft liegen, sollte mit ein paar kleinen Funktionen
erkannt werden.
Das Datum muß vor der eigentlichen Verarbeitung erstmal einige Checks
bestehen, bevor es akzeptiert werden kann.

von tex kerstan (Gast)


Lesenswert?

OK! Da haben wir total gegensätzliche Anwendungen. Ich brauche eine
genaue Uhrzeit im µC. Da sind abstrakte Usereingaben nur bedingt
relevant. Ich habe halt bisher nur absurde Kalendarien gefunden die in
gigantischen Arrays definiert wurden um die ganzen Absurditäten unseres
Kalenders abzufangen, Sekunden wurden zu Minuten aufaddiert, Minuten zu
Stunden ...
Ich möchte die Uhr über die serielle Zahl des Julianischen Datums
definieren, weil ich so diesen absureden Kram mit Minuten, Sekunden,
Stunden, Tagen, Schaltjahren ... los bin. Die Formeln für das Datum
habe ich aber bei Interrups, Counterpreloads, ... hörts bei mir auf.

von nobody0 (Gast)


Lesenswert?

Das ist doch ganz einfach: Nimm als Zähler die Sekunden seit z. B.
1.1.2000 0:00:00 und rechne ohne Schaltsekunden (d. h. in TAI):

blub = counter;
blub %= 60;
Sekunden = blub;
blub %= 60;
Minuten = blub;
usw.

Für Tage im Monat braucht man ein Array und für Tage im Jahr die
Schaltjahr-Funktion.

In einer Schicht darüber kann man Schaltsekunden berücksichtigen und
eine weitere Schicht darüber Sommer-/Winter-Zeit. Darüber wiederum kann
man weiteres packen, beispielsweise metrische Zeitrechnung für eine
metrische Uhr.

Damit man wenig einstellen muß, nehme ich als Startwert den vom
Zeitpunkt der Compilierung; dafür gibt's ja _DATE__ und __TIME_.

von nobody0 (Gast)


Lesenswert?

Nachtrag: Vor der Minuten-Zeile muss

blub /= 60;

stehen.
Danach hat man die Zeit in Minuten.
Dasselbe muß in die Zeile darunter; danach hat man die Zeit in
Stunden.
So kommt man letztlich zum Jahr.

von Jochen (Gast)


Lesenswert?

Huhu Peter,

hab mal ne Frage:

Du hast jeweil im main-loop folgendes stehen:

ASM:   out  ddrb, second
C:     LED_DIR = second (wobei LED_DIR weiter Oben als DDRB deffiniert
ist)

sollte das nicht jeweils PORTB heißen?

Oder versteh ich da den Code net richtig?

Ansonsten Danke für den Code funktioniert super.

Frohes Fest, Jochen

von Hans (Gast)


Lesenswert?

hat von euch schon mal jemand versucht vom tv-signal eine
referenz-frequenz abzuleiten ??
weil die haben doch ein besseres trift-verhalten als ein normal
quarz...

und wie schauts eigentlich mit den teilen aus handys aus???

sind die stabil oder werden die durch das signal von den basis
stationen stabil gemacht?

und dann ist da noch die frage wie stabil sind die 50hz....fragen über
fragen ;)

73+55 für 2005 de oe6jwf / hans

von Peter D. (peda)


Lesenswert?

@Jochen,

die Ports sind ja nach den Reset auf 0.
Daher kann man eine LED einschalten, indem man das Directionbit auf 1
setzt.


Peter

von Jochen (Gast)


Lesenswert?

Joo, so stimmt das natürlich auch wieder ;-)

Danke für die Antwort

Jochen

von Kurt (Gast)


Lesenswert?

Hallo Hans,

schau mal bei DK6RX

Gruss Kurt

von Alexander Muthmann (Gast)


Lesenswert?

Hallo...

so hab das ganze jetzt mal aufgegriffen, und ein kleines Problem
damit:

ich hab mal den asm code genommen und kompiliert.
Dabei bekam ich in Zeile 71 einen Fehler.
ldi  prescaler, debounce
Das Prog versucht 256 in r18 zu schreiben => geht nich, zu groß die
Zahl => out of Range

kann es also sein, dass das nicht
ldi  prescaler, debounce
sondern
ldi  prescaler, debounce-1
heißen muss???

würd mich freuen wenn mir da mal jemand weiter helfen kann...
schöne grüße
Alex

von Werner Hoch (Gast)


Lesenswert?

Die Zahl 256 ist einfach wieder 0.
Damit wird die Sekunde in 256 Unterschritte (Timerinterrupts)
aufgeteilt.

Die Zahl muß nur so gewählt werden daß
Clock/debounce < 0xFFFF ist
und debounce selber <= 256

Mit 256 (0) hat Peter die Maximale Anzahl an Unterschritten gewählt.
Ich würde eher 250 nehmen, dann vergehen zwischen den Timerinterrupts
4ms.

Wenn du die Fehlermeldung entfernen willst kannst du die konstante
kapseln z.B. LOW_BYTE(debounce).
Ich halte dies aber nicht für empfehlernswert.

BTW: hat der assembler eine Warnung oder eine Fehlermeldung ausgegeben?
Welchen Assembler hast du verwendet?

MfG Werner

von Alexander Muthmann (Gast)


Lesenswert?

Hallo,

ich verwende mom AVRStudio und es war ein Error und keine Warning.
soll ich dann einfach 0 statt 256 nehmen?
also ldi prescaler, 0 ?

schöne Grüße

von Werner Hoch (Gast)


Lesenswert?

Nein, den debounce wird, wie du sicherlich gesehen hast, auch für die
Berechnung anderer Register verwendet.

Wenn du debounce auf 0 setzt sind diese Werte falsch.
Es ist besser du reduzierts debounce ein wenig.

von Alexander Muthmann (Gast)


Lesenswert?

Ne ich meinte die Konstante gleichlassen, nur in den betroffenen Zeilen
halt nicht debounce einzufügen, sondern 0,
also halt wie oben ldi prescaler, 0 statt ldi prescaler, debounce
die anderen Register würden dadurch doch nicht verändert

mfG

von Werner Hoch (Gast)


Lesenswert?

Von der Funktion her kannst du das zwar machen, aber spätestens wenn du
den Wert von debounce beim nächsten mal änderst, wirst du die Null
vergessen und dein Programm funktioniert nicht mehr richtig.

Konstanten sollten im Quelltext alle nur einmal definiert werden.
Konstanten im Programm haben, abgesehen von Spezialfällen, nichts im
Programmquelltext zu suchen.

von Alexander Muthmann (Gast)


Lesenswert?

OK ;)
hab erst vor was mehr als 2 Wochen angefangen, gut zu wissen mit den
Konstanten (leuchtet auch irgendwie ein^^)
schönen Abend noch!

von T. Kirchen (Gast)


Lesenswert?

Hallo zusammen,

ich habe mich mal so durch die Beiträge gehangelt. Bin momentan an
einen Projekt beschäftigt, in dem ein RTC4553 Chip eingesetzt ist.
Da sich das Datenblatt über die Ansteuerung nur spärlich auslässt,
wollte ich mal anfragen, ob jemand schon mal mit dem Chip gearbeitet
hat. Ich kann irgendwie die Zeit nicht vorgeben bzw. auslesen.
Keine Ahnung, ob das an der SPI-Übertragung liegt.
Im Datenblatt wird alles mit A3..A0,D3..D0 angegeben und für die
Übertragung muss man dann die Daten in der Reihenfolge A0..A3,D0..D3
anlegen bzw. auslesen.
Kann da jemand weiterhelfen????

von peter dannegger (Gast)


Lesenswert?

@T. Kirchen

Bitte nicht einen Thread hijacken sondern für neue Fragen einen neuen
Thread aufmachen !


Hier ging es nämlich gerade NICHT um externe RTC-Chips.


Peter

von hanes (Gast)


Lesenswert?

Dar ich dein code im wiki veröffentlichen?

von Peter D. (peda)


Lesenswert?

Ja

Peter

von hanes (Gast)


Lesenswert?


von C. Romas (Gast)


Lesenswert?

Sorry ich bin blutiger Anfänger und habe mir das Assemblerscript mal
egnommen.

Allerdings bekomme ich an den 8 LEDs nichts zu sehen wenn ich es
programmiere. Ich benutze einen ATMega128. Es sind nur die
Kompatibilitätsfuse geändert worden und JTAG ausgeschaltet. Takt ist
demnach noch 1MHz (so stehts auch im AVR Studio)

Im Code habe ich nur an den beiden Zeilen wo auf ddrb geschrieben wird
ddrC genommen.

Habe ich etwas vergessen zu konfigurieren im Code oder liegt das an
meiner HW?

von Werner B. (Gast)


Lesenswert?

Wenn Du DDRB in DDRC änderst, musst Du natürlich auch PORTB in PORTC
(und ein eventuell vorhandenes PINB in ein PINC ändern).

von Juel (Gast)


Lesenswert?

Hi, habe mir gerade mein erstes AVR-Testboard aufgebaut, aber leider
bekomme ich die Uhr nicht zum laufen :/
Benutze einen 90S8515 mit 8Mhz quarz.

Habe #define OCR1A OCR1 auf #define OCR1A OCR1A geändert,
und die KEY_INPUT geschichte sowie unten die whileschleife
auskommentiert in der Hoffnung dass die Uhr dann sofort losläuft.
Leider gehts nicht... kann mir jemand weiterhelfen ?

#include <io.h>
#include <interrupt.h>
#include <signal.h>


#ifndef OCR1A
#define OCR1A OCR1A  // 2313 support
#endif

#ifndef WGM12
#define WGM12 CTC1  // 2313 support
#endif

//#ifndef  PINC
//#define  KEY_INPUT  PIND  // 2313
//#else
//#define KEY_INPUT  PINC  // Mega8
//#endif
#define  LED_DIR    DDRB

//#define XTAL    11059201L  // nominal value
#define XTAL    8000000L  // after measuring deviation: 1.5s/d

#define DEBOUNCE  256L    // debounce clock (256Hz = 4msec)

#define uchar unsigned char
#define uint unsigned int

uchar prescaler;
uchar volatile second;      // count seconds


SIGNAL (SIG_OUTPUT_COMPARE1A)
{
/*********************************************************************** 
*/
/*      Insert Key Debouncing Here      */
/*********************************************************************** 
*/

#if XTAL % DEBOUNCE
  OCR1A = XTAL / DEBOUNCE - 1;    // compare DEBOUNCE - 1 times
#endif
  if( --prescaler == 0 ){
    prescaler = (uchar)DEBOUNCE;
    second++;        // exact one second over
#if XTAL % DEBOUNCE      // handle remainder
    OCR1A = XTAL / DEBOUNCE + XTAL % DEBOUNCE - 1; // compare once per
second
#endif
  }
}


int main( void )
{
  LED_DIR = 0xFF;

  //while( KEY_INPUT & 1 );    // start with key 0 pressed

  TCCR1B = 1<<WGM12^1<<CS10;    // divide by 1
          // clear on compare
  OCR1A = XTAL / DEBOUNCE - 1;
  TCNT1 = 0;
  second = 0;
  prescaler = (uchar)DEBOUNCE;

  TIMSK = 1<<OCIE1A;
  sei();

  for(;;){
    if( second == 60 )
      second = 0;
    LED_DIR = second;      // display second (binary)
  }
}

von Werner Hoch (Gast)


Lesenswert?

Hallo Juel,

du hast meine Variante mit der von Peter vermischt ohne die Anpassungen
zu machen:
1. DEBOUNCE < 256 Begründung findest du weiter oben
2. Compare Match Interrupt aktivieren
3. Port bei der ausgabe beschreiben, nicht die Richtungsregister

MfG
Werner

Die Init-Section für einen 2313 sieht bei mir so aus:
--------------
 seg7_init();
  /* seg7_test(); */

  TCCR1B = _BV(CTC1) | _BV(CS10); /* clear on compare match, prescaler
1 */

  OCR1A = UC_CLOCK / SUB_CLOCK - 1;
  TCNT1 = 0;
  prescaler = SUB_CLOCK;

  TIMSK = _BV(OCIE1A);
  sei();
----------------
SUB_CLOCK ist bei dir DEBOUNCE

von Werner Hoch (Gast)


Lesenswert?

TssTs.

Mein Beitrag von vorher ist falsch, bis auf den Punkt 3.
Du mußt den Wert auf den Port ausgeben:
PORTD = second.

von Juel (Gast)


Lesenswert?

Dankeschön, jetzt gehts auch bei mir :)
Echt super die schnelle Antwort!!

von Max (Gast)


Lesenswert?

Hallo Peter,

mit welchem Compiler hast du den Code geschrieben, bzw mit welchem kann
man den Code in einen AVR flashen?
Gruß Max

von Manfred Steiner (Gast)


Lesenswert?

Das ist doch alles voll blöd hier. Da such ich ein Assembler Programm
und alles was ich finde ist kein Assembler!

von Alexander Muthmann (Gast)


Lesenswert?

och du armer ;)
weiß zwar nich was du unter nem Assembler Programm verstehst, ich
versteh das darunter was im ersten post wunderbar zum download steht^^

von Uffz Meier (Gast)


Angehängte Dateien:

Lesenswert?

morschen Leute

ich habe den original Code von ganz oben verwendet und will damit
möglichst genaue Sekunden erreichen. Ausgegeben wird dann Sekunde,
Minuten und Stunden per UART, wenn der Timer Interrupt eintritt (jedoch
am ende des Interrupts um die vorherige Hochzählung nicht zu stören).
Leider scheint es trotzdem zu störungen zu kommen. Nach 1 Stunde habe
ich schon eine Sekunde Abweichung. Kann mir jemand sagen warum, und wie
ich sonst das kontrollierne kann, wenn nicht per UART?

von Werner Hoch (Gast)


Lesenswert?

Dein Oszillator/Quarz ist wahrscheinlich ungenau.

Lass deine Uhr einen Tag laufen, miss den Zeitfehler aus und
korrigiere
XTAL entsprechend auf 3999....Hz

von peter dannegger (Gast)


Lesenswert?

@Uffz,

wenn die UART nicht mit Interrupt und gepuffert ist, dann darf man
sowas lahmarschiges natürlich nicht in einen Interrupt schmeißen.

Alles, was Du in den Interrupt reinpackst, muß bis zum nächsten
Interrupt beendet sein.



Peter

von Uffz Meier (Gast)


Lesenswert?

Ist es so dass die Endlosschleife im main() recht hochfrqeuentiert
unterbrochen wird und in den Interrupt springt? Das sieht nämlich so
aus .
Bei mir ist es wichtig neben dem Counter-Zeug noch Ruhe für andere
Sachen zu haben. Also etwa jede Sekunde in einen Interrupt springen
wäre OK aber scheller würde mein Programmablauf zerreißen.

von peter dannegger (Gast)


Lesenswert?

@Uffz,

genau das ist ja der Sinn eines Interrupts, das Main zu unterbrechen,
ohne daß es gestört wird.
Deshalb sollte man ihn eben so kurz wie möglich halten und keine
arschlahmen Sachen darin machen.
Zerrissen wird dabei nichts.


Peter

von Don Martin (Gast)


Lesenswert?

Hallo Peter,

uchar prescaler;
uchar volatile second;      // count seconds

müsste man prescaler nicht auch als volatile deklarieren?

Martin

von holger (Gast)


Lesenswert?

Angeregt durch einen anderen Thread habe ich gerade mal
die "genaue Sekunde" ausprobiert. Das geht soweit ganz gut.

Die erste Sache die man beachten muss ist natürlich:

>  OCR1A = XTAL / DEBOUNCE - 1;    // compare DEBOUNCE - 1 times

OCR1A darf nicht größer 65535 werden.

Bei den neuen AVR mit 20MHz kommt aber folgendes Problem
dazu. DEBOUNCE ist mit 256 zu klein ! Es muss mindestens 306 sein.
XTAL / 65536 = 305,7 .

prescaler muss dann ein uint sein. So klappt die "genaue Sekunde"
wieder ;)

von manow (Gast)


Lesenswert?

Ich hab den Code gestern ausprobiert und mich gewundert wieso bei mir 
die Sekunde ziemlich schnell lief? Den Fehler habe ich dann auch 
gefunden. prescaler wird mit 256 geladen, das ist doch zu groß, oder?¿? 
Nach uint geändert und schon läuft die Sekunde.

von Peter D. (peda)


Lesenswert?

manow wrote:
> Ich hab den Code gestern ausprobiert und mich gewundert wieso bei mir
> die Sekunde ziemlich schnell lief? Den Fehler habe ich dann auch
> gefunden. prescaler wird mit 256 geladen, das ist doch zu groß, oder?¿?
> Nach uint geändert und schon läuft die Sekunde.

Ne, das paßt schon.
256 wird zu 0 und nach 256 mal ist es wieder 0.
Man kann also mit einem Byte /256 teilen.

Der Fehler ist woanders, kannst ja mal Deinen Code als Anhang senden.


Peter

von manow (Gast)


Lesenswert?

Hmm, seltsamer weise kann ich den Fehler nicht mehr rekonstruieren. 
Jetzt funkt. es auch so, im Gegensatz zu heute halb drei in der Früh. 
Vielleicht war ich etwas langsam...

von Artur Funk (Gast)


Lesenswert?

@  peter dannegger: Hast du schon daran gedacht, dass man manchmal auf 
die RTC gar nicht verzichten kann (deutlichkeitshalber). Und wenn ich an 
die IRs denke, die jede Sekunde ausgelöst werden, dann gebe ich lieber 
2€ für eine RTC aus, als mein Programm jede Sekunde zu nerven. Ich 
verwende für die Zeitmessung DS1337 in Verbindung mit einem Soft 
I2C-Master Interface, bin sehr zufrieden. Klar in manchen Fällen reicht 
auch wenn man mit dem  µC selbst die Sekunden erzeugt, aber bei einem 
Atmega 128 der randvoll programmiert ist und mehreren Sachen erledigen 
soll, sollte man auf die nicht zwingenden ISR's verzichten (Achtung 
meine Meinung).

Gruß

Artur

von Peter D. (peda)


Lesenswert?

Artur Funk wrote:
> @  peter dannegger: Hast du schon daran gedacht, dass man manchmal auf
> die RTC gar nicht verzichten kann (deutlichkeitshalber).

Ja, für extrem sparsamsten Batteriebetrieb.


> Und wenn ich an
> die IRs denke, die jede Sekunde ausgelöst werden, dann gebe ich lieber
> 2€ für eine RTC aus, als mein Programm jede Sekunde zu nerven.

Bedenke, auf nem MC läuft kein Windows.

MCs können Interrupts in 0,nix abarbeiten.
Selbst ein Interrupt jede 1ms kostet unter 1% CPU-Last.
Die CPU ist also überhaupt nicht genervt, sie hat nur ein ganz kleines 
bischen weniger lange Weile.

Wenn man mal den ganzen Kommunikationscode für die externe RTC 
berücksichtigt, spart ne interne RTC sogar Code und Entwicklungszeit.


Peter

von Jo H. (haveaniceday)


Angehängte Dateien:

Lesenswert?

Hallo zusammen,
der Thread ist ja schon realtiv alt, aber hoffe mal das mit trozdem noch 
einer weiterhelfen kann.
Ich wollte das Program gerne benutzen, allerdings bekomme ich es nicht 
ans laufen. Habe bis jetzt nur die MHz zahl des Quarz (16MHz) angepasst 
und die signal.h rausgenommen, da mir WinAVR sagt sie sei zu alt.
Ich benutze einen ATmega32, würde mich freuen wenn ihr mir weiterhelfen 
könntet, ich muss dazusagen, ich kenne mich noch nicht so gut mit dem 
Programmieren vom µC aus.
Danke schonmal, hier der Coe:

#include <avr/io.h>
#include <avr/interrupt.h>
//#include <avr/signal.h> //zu alt


#ifndef OCR1A
#define OCR1A OCR1  // 2313 support
#endif

#ifndef WGM12
#define WGM12 CTC1  // 2313 support
#endif

#ifndef  PINC
#define  KEY_INPUT  PIND  // 2313
#else
#define KEY_INPUT  PINC  // Mega8
#endif
#define  LED_DIR    DDRB

//#define XTAL    16000000L  // nominal value
#define XTAL    16000000L  // after measuring deviation: 1.5s/d

#define DEBOUNCE  256L    // debounce clock (256Hz = 4msec)

#define uchar unsigned char
#define uint unsigned int

uchar prescaler;
uchar volatile second;      // count seconds



SIGNAL (SIG_OUTPUT_COMPARE1A)
{
/*********************************************************************** 
*/
/*      Insert Key Debouncing Here      */
/*********************************************************************** 
*/

#if XTAL % DEBOUNCE
  OCR1A = XTAL / DEBOUNCE - 1;    // compare DEBOUNCE - 1 times
#endif
  if( --prescaler == 0 ){
    prescaler = (uchar)DEBOUNCE;
    second++;        // exact one second over
#if XTAL % DEBOUNCE      // handle remainder
    OCR1A = XTAL / DEBOUNCE + XTAL % DEBOUNCE - 1; // compare once per 
second
#endif
  }
}


int main( void )
{
  LED_DIR = 0xFF;
  DDRD  |= (1 << PD6);
  while ( KEY_INPUT & 1 );    // start with key 0 pressed

  TCCR1B = 1<<WGM12^1<<CS10;    // divide by 1
          // clear on compare
  OCR1A = XTAL / DEBOUNCE - 1;
  TCNT1 = 0;
  second = 0;
  prescaler = (uchar)DEBOUNCE;

  TIMSK = 1<<OCIE1A;
  sei();

  if(second >= 30)
    PORTD |= (1 << PD6);


  for(;;){
    if( second == 60 )
      second = 0;
    LED_DIR = second;      // display second (binary)
  }
}

von Elektrikser (Gast)


Lesenswert?

SIGNAL (SIG_OUTPUT_COMPARE1A)

muss ersetzt werden durch

ISR (TIMER1_COMPA_vect)

Gruß Elektrikser

von Elektrikser (Gast)


Lesenswert?

oder du lässt die signal.h drin und bindest die compat/deprecated.h 
zusätzlich ein...

von Jo H. (haveaniceday)


Lesenswert?

Danke erstmal für deine Antwort, aber leider läuft es immernoch nich 
nicht. Am Ausgang PD6 (der angehen soll wenn der sekunden takt zwischen 
0 und 30s liegt) und das tut er leider noch nicht, vondaher denke ich 
das er noch nicht Zählt...

LG Haveaniceday

von Elektrikser (Gast)


Lesenswert?

Bekommst du noch irgendwelche Warnungen/Fehler beim Compilieren?
Sind die Fuses richtig gesetzt?

von Peter D. (peda)


Lesenswert?

Jo Hannes wrote:

> Am Ausgang PD6 (der angehen soll wenn der sekunden takt zwischen
> 0 und 30s liegt) und das tut er leider noch nicht
1
  second = 0;
2
... 
3
  if(second >= 30)
4
    PORTD |= (1 << PD6);

Das wird er mit diesem Programm auch nie tun.

Warscheinlich wird der Compiler den Test komplett wegoptmieren,
0 kann ja nie >= 30 sein.


Peter

von Elektrikser (Gast)


Lesenswert?

Autsch, noch nicht wach. Hätte ich sehen müssen...

von Jo H. (haveaniceday)


Lesenswert?

Super, danke, werde es gleich  mal ändern und ausprobieren.
Aber hätte noch eine Frage. Undzwar was ich nich so ganz an dem Program 
verstehe sind folgende Zeilen:
1
#ifndef OCR1A
2
#define OCR1A OCR1  // 2313 support
3
#endif
4
5
#ifndef WGM12
6
#define WGM12 CTC1  // 2313 support
7
#endif
8
9
#ifndef  PINC
10
#define  KEY_INPUT  PIND  // 2313
11
#else
12
#define KEY_INPUT  PINC  // Mega8
13
#endif
14
#define  LED_DIR    DDRB

würde das Program nicht auch ohne diese Zeilen funktionieren? Soweit ich 
weiß sind es doch nur Definitonen, die aber weiter garnicht im Program 
verwendet werden...Oder?!
Ist evtl. ne doofe Frage, aber es lässt sich für mich nicht genau 
erschließen wofür diese Zeilen sind. Bin halt wie gesagt noch Anfänger.

Danke, LG Haveaniceday

von Jo H. (haveaniceday)


Lesenswert?

Habe das Program jetzt folgender maßen geändert:
1
  for(;;){ 
2
    if( second == 60 )
3
      second = 0;
4
    LED_DIR = second;      // display second (binary)
5
  
6
  if(second >= 30)
7
    PORTD |= (1 << PD6);
8
  }
aber es läuft immernoch nicht. So müsste es doch richtig sein oder?!?

von Daniel S. (sany)


Lesenswert?

Also,

Dieser Code funktioniert bei mir auf Anhieb mit einem Atmega16 bei 14.3 
Mhz.

Wie könnte Ich denn nun die Uhrzeit auf einem LCD Display ausgeben?

von Jo H. (haveaniceday)


Lesenswert?

Welche Code funktioniert bei dir? Meiner oder der Orginale?

von Daniel S. (sany)


Lesenswert?

Also dein Code funktioniert bei mir...

Frage mich gerade nur wie ich das ganze als LCD Anzeige krieg?

von Jo H. (haveaniceday)


Lesenswert?

Ok Danke, dann wird wohl ihrgendwas an meiner Hardware defekt sein. Hat 
einer ne idea was es sein könnte?
Habe mal den Quarz gemessen und die Amplitue ist am einen Pin größer als 
an dem anderen, könnte das der Grund sein?

von Ralf W. (Gast)


Angehängte Dateien:

Lesenswert?

Hallo,

ich habe auch grade den original Code getestet. Bei mir läuft
er ohne Probleme. Jedoch verwende ich die Starttaste nicht.

@ Jo Hannes
Ich glaube der Fehler liegt in diesem Code:
1
#ifndef  PINC
2
#define  KEY_INPUT  PIND  // 2313
3
#else
4
#define KEY_INPUT  PINC  // Mega8
5
#endif
6
#define  LED_DIR    DDRB
Die Uhr startet wenn du die entsprechende Taste drückst.
Wenn deine Taste am Port D hängt wird die Uhr mit dem
obigen Code nicht starten, denn der ATMega32 hat einen Port C.

der Code müsste korrekt lauten:
1
#ifndef  PINC
2
#define  KEY_INPUT  PINC  // 2313
3
#else
4
#define KEY_INPUT  PIND  // Mega8
5
#endif
6
#define  LED_DIR    DDRB
oder ganz kurz:
1
#define KEY_INPUT  PIND
@ Daniel Steiner
Ich hänge meine Version mal an (ATMega8 und HD44780 LCD Display).
Zur Ausgabe benutze ich die LCD library von Peter Fleury.

von Jo H. (haveaniceday)


Lesenswert?

Habe es so probiert, funzt aber leider trozdem nicht. Habe auf meinem 
Board 3 Taster, auf PD1-3...
Wie hast du das den ohne die Taster Betätigung umgesetzt? Das fände ich 
nämlich noch besser...

von Jo H. (haveaniceday)


Lesenswert?

läuft :)
Danke für eure Hilfe.
 Habs folgender maßen geändert:
1
#define KEY_INPUT  PIND2  // Mega8

Mich würde trozdem noch interresieren, wie du deinen auto-start 
realisiert hast.

von Jo H. (haveaniceday)


Lesenswert?

Brauche nochmal eure Hilfe. Mein Quarz läuft anscheinen sehr ungenau, 
evtl. zu ungenau?!?
Habe die Uhr jetzt 10 Stunden laufen lassen und die Zeit ist 6-7 
Sekunden zu früh. Das heißt, rein rechnerisch wären es nach 24Stunden 
ca.34sek.
Wollte jetzt nach der Formel von Peter meine Quarz Frequenz ändern, aber 
ich bekomme nicht dasselbe Ergebnis raus...
Und zwar komme ich bei dieser Zeile nicht weiter:
1
Also die ganze Rechnung nochmal: 
2
3
OCR1A = 11059008 / 256 - 1 = 43198, Rest 64.
Ich bekomme immer 43198,25 Raus, ka wieso da Rest 64 rauskommen soll...

Muss ich die "exakte" Quarzfrequenz überhaupt ausrechnen, oder berechnet 
der Compiler  das selber, wenn ich nur die "Abweichung" in sek. 
eintrage? Wenn ja wo muss dieser wert eingetragen werden?

Danke schon mal

ciao Hannes

von Ralf W. (Gast)


Lesenswert?

Hallo,

freut mich, das die Uhr jetzt läuft.

>Mich würde trozdem noch interresieren, wie du deinen auto-start
>realisiert hast.

Einfach diese Zeile in main weglassen.
 while( KEY_INPUT & 1 );    // start with key 0 pressed

>Ich bekomme immer 43198,25 Raus, ka wieso da Rest 64 rauskommen soll...

Der Rest von 64 stammt aus einer Modulo Division. Diese gibt den
den Rest aus der Division zweier -ganzer Zahlen- zurück.

11059008 = (43198 + 1) * 256 + 64.

>oder berechnet der Compiler  das selber

Nein das macht er nicht. Man müsste ihm erst die Formel hier beibringen.

((24  60  60  256  43200) - (1,5  256  43200)) / (24  60  60) = 
11059008 Hz.


>10h 6 - 7 sek schneller
Ähm ich glaub das sind nur ca. 16s in 24h. Du musst bei der
obigen Formel aufpassen, das du (15,6  256  43200?) addierst.
Deine Uhr geht ja schneller.

gruß ralf

von Jo H. (haveaniceday)


Lesenswert?

Ja stimmt, weiß auch nicht wie ich auf die 34s gekommen bin^^
Aber ich glaube das diese Zahl auch nicht stimmt, denn jetzt nach ca. 1 
stunde hat sich das Zeitfenster nochmals um ca. 4-5s verschoben...
Könnte es sein, das mein Quarz eine weg hat, weil es so "ungenau" läuft? 
Weil es ja schon Welten zwischen den 1,5s von Peter und meiner Differenz 
sind? Weiß leider nicht genau wo die Toleranzen bei so einem Quarz 
liegen.

Kann auch nicht genau sagen, in welche richtig sich die Uhr verschiebt, 
weil nur eine LED alle 30s an und aus geht...

von Sven L. (friemler)


Lesenswert?

Mal 'ne andere Frage: wie genau ist eigentlich die Netzfrequenz?

Es wäre doch sehr einfach da einen Abgleich zu schaffen.

von Andreas K. (a-k)


Lesenswert?


von Link (Gast)


Lesenswert?


von Sven L. (friemler)


Lesenswert?

Danke für die Links.

Also ist es für Uhren im längerfristigem Betrieb kein Problem sich 
darauf zu verlassen? Die Kraftwerke würden sich bei Unsynchronisation 
(also dann, wenn die Last zuviel wird) eh vom Netz trennen, oder?

von Michael K. (Gast)


Lesenswert?

Lässt sich das Ganze auch mit einem 8-bit-Timer realisieren? In meiner 
Applikation wird der 16-bit-Timer bereits verwendet und die Dauer bis zu 
einem Überlauf soll veränderbar sein. Mein Mega168 läuft mit 12MHz.

von Falk B. (falk)


Lesenswert?


von Rick M. (rick00)


Lesenswert?

Hallo!

Arbeite auch gerade den Artikel durch und habe noch eine Frage:

Warum steht am Anfang der ISR die Präprozessor-Anweisung:


#if XTAL % DEBOUNCE                     // bei rest
  OCR1A = XTAL / DEBOUNCE - 1;          // compare DEBOUNCE - 1 times
#endif


Die Zuweisung des Compare-Wertes erfolgt doch im Haupt-Programm, bzw. 
bei mir in der Initialisierungs-Phase des Timers. Dort wird doch bereits 
gerechnet.


Die Präprozessor-Anweisung in der if-Anweisung ist mir klar, diese 
korrigiert die Abweichung der Quarzes. (Toleranz)


Gruß Rick

von Rick M. (rick00)


Lesenswert?

Sorry, hab erst jetzt gelesen, daß hier keine Fragen gestellt werden 
sollen.
Kann den Beitrag leider nicht mehr löschen.

von eProfi (Gast)


Lesenswert?

Ich habe heute den Artikel zum ersten Mal gelesen.
Natürlich kam mir sofort folgende Idee:
Warum zwei Zähler verwenden (einen für die Ticks und einen für den 
Fehler)?
Gebraucht wird ein Zähler "vergangene Zeit" mit einer Einheit von z.B. 
1/2^32 Sekunden. Und auf diesen wird bei jedem Timerirq ein Wert 
addiert, wie bei einer DDS - fertig. Dieser Wert ergibt sich aus der 
realen Frequenz der Ticks und kann beliebig groß sein, genauso wie er 
sich im Betrieb ändern kann wie z.B. durch TempKompensation oder 
Heruntertakten für Energiespar-Modus.


Beispiel:
Timer-Frequenz sei 123,45 Hz (8,10044552ms)
Der zu addierende Wert ist dann 2^32/123,45=34791148,610, also 34791149

Das oberste Bit dieses Zählers toggelt dann langfristig exakt 2 mal pro 
Sekunde, woraus man die Sekunden ableiten kann,z.B:

if(Byte3(maintimer)&0x80) {Byte3(maintimer)&=0x7f;seconds++;}

Anderes Beispiel: man verwendet einen 40, 48- oder 64Bit-Zähler für eine 
Stunde, einen Tag oder ein Jahr, das würde sehr zu meiner Idee passen, 
dass ein Tag nicht 24 (Alt)-Stunden, sondern 16 Neu-Stunden hat.
www.mikrocontroller.net/topic/56014
www.mikrocontroller.net/topic/95935
www.mikrocontroller.net/topic/125583

Beim Programmieren von µC, die binär arbeiten, ist es oft sinnvoll, die 
dezimale Denkweise abzulegen und selbst binär / hex zu denken.

von eProfi (Gast)


Lesenswert?

> if(Byte3(maintimer)&0x80) {Byte3(maintimer)&=0x7f;seconds++;}

stimmt nicht ganz, da so die Sekunden mit 2 Hz hochgezählt werden.
Als Ausgleich also den halben Wert addieren, kostete ein Bit 
Genauigkeit.

Oder: man muss die "fallende Flanke" des Bit31 erkennen, um auf 1 Hz zu 
kommen:
  if((byte3old & 0x80)&&(~byte3(maintimer) & 0x80)){sec++;}
  byte3old = byte3(maintimer);


In Asm geht das dank Carry-Bit wesentlich eleganter, wobei man (hier 
Turbo-)C und Asm mischen kann:

TimerIRQ:
  maintimer += timeradd; //bei Überlauf ist eine Sekunde vergangen
  asm{
    mov ax,sek  //Carry wird nicht verändert
    adc ax,0    //Übertrag, wenn maintimer übergelaufen war
    mov sek,ax  //Wert in Variable sek zurückspeichern
    }

Beim AVR dürfte das ähnlich funktionieren.

Noch einfacher geht es nicht.

von Micha (Gast)


Lesenswert?

Ich würde gerne den im Artikel beschriebenen Code (nicht mit Uhrenquarz) 
benutzen und noch über den Idle Modus hinaus Energie einsparen, z.B. 
durch herunterteilen des Systemtaktes. Allerdings müsste das dynamisch 
möglich sein, bspw. bei einem Stromausfall. Seht ihr eine Möglichkeit 
das zu realisieren, ohne dass der Zeitfehler ins unermessliche steigt?

Dieser Beitrag ist gesperrt und kann nicht beantwortet werden.