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
@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
" 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 ...
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
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
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
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
}
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
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.
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
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
@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 ...
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.
@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
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.
"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
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
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
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
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
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.
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
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
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.htmhttp://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.
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!
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.
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.
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_.
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.
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
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
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
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
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
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.
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 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.
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????
@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
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?
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
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^^
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?
Dein Oszillator/Quarz ist wahrscheinlich ungenau.
Lass deine Uhr einen Tag laufen, miss den Zeitfehler aus und
korrigiere
XTAL entsprechend auf 3999....Hz
@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
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.
@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
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 ;)
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.
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
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...
@ 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
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
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)
}
}
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
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
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?
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.
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...
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
AlsodieganzeRechnungnochmal:
2
3
OCR1A=11059008/256-1=43198,Rest64.
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
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
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...
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?
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.
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
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.
> 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.
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?