Guten Abend, bin noch nicht so erfahren mit dem Umgang mit Mikrocontrollern, zerbreche mir schon seit längerem den Kopf wie ich denn eine Uhr Software machen könnte, damit ich Datum und Uhrzeit mitzählen lassen kann, hab mir schon die Timer im Datenblatt des Atmega8L angeschaut und weis nicht wie ich das realisieren könnte, hat mir jemand nen tipp oder beispiel code? gruß Walter
Du musst den Timer so konfigurieren, dass er jede Sekunde einen Impuls erzeugt und zählst bei dem Impuls einfach die Sekundne um eins hoch. Nicht schwer!
ja nur was muss ich da für einen Vorteiler benutzen damit ich auf eine Sekunde komme? mein Atmega8 läuft mit 3,684 Mhz
Eine Sekunde wirst du ev. nicht hinkriegen mittels Takt+Prescaler. Macht aber nichts. Gehst halt dann auf 1/10 oder 1/100 Sekunde oder überhaupt auf einen völlig anderen Bruchteil einer Sekunde, sagen wir mal x. Für deine ersten Schritte ist es am einfachsten den Overflow Interrupt dafür zu benutzen. Später wirst du lernen wie man das besser machen kann. Also: Du hast deinen Timer so aufgesetzt, dass er x mal in der Sekunde einen Overflow auslöst. Also brauchst du eine Variable, die mitzählt, wie oft der Overflow ausgelöst wurde: Bei jedem Overflow zählst du sie um 1 hoch. Hat diese Variable den Wert x erreicht, ist eine Sekunde vergangen. Also erhöhst du eine andere Variable, die die Sekunden in deinem Programm darstellt und setzt den Overflow-Zähler wieder auf 0. Hat die Sekunden-Variable den Wert 60 erreicht, setzt du die Sekunden wieder auf 0 und erhöhst dafür die Minuten. Selbiges in grün: Nach 60 Minuten ist eine Stunde vergangen und nach 24 Stunden ist ein 1 Tag vergangen. Ab da wird es dann marginal schwieriger, da die Weiterschaltung der Monate in Abhängigkeit vom Tag vom Monat selbst abhängt. Hat also der Tagzähler 30 erreicht und ist gerade zb. April, so ist der nächste Tag der 1. und der Monat geht um 1 hoch (=Mai). usw. Konzentrier dich zunächst auf die Stunden:Minuten:Sekunden Oder noch einfacher: Vergiss Stunden und Minuten fürs erste und zähl einfach nur die Sekunden hoch. In der Phase kannst du dich voll auf den Timer konzentrieren ohne dich mit Gedanken über Stunden und Tage und Monate zu verzetteln.
Also dein 16 Bit Timer zählt bis 65536, dann läuft er über. Bei 3,684 MHz und nem Vorteiler von 64 zält der Timer in einer Sekunde auf 57562,5. Du musst nun deinen Timer auf 7973,5 vorladen, dann läuft der Timer genau einmal pro Sekunde über. Alle Angaben sind ohne Gewähr ;) Rechne lieber nochmal nach, der Rechenweg dürfte ja soweit klar sein oder?
> ja nur was muss ich da für einen Vorteiler benutzen damit ich auf eine > Sekunde komme? mein Atmega8 läuft mit 3,684 Mhz Da wirst du halt ein bischen Rechnen und Probieren müssen :-) Ein 8-Bit Zähler generiert nach 256 Zählvorgängen einen Overflow Interrupt. Wenn dein Quarz also 3684000 Schwingungen in der Sekunde macht und du einen Vorteiler von 1 benutzen würdest, dann würde der 8-Bit Zähler in einer Sekunde 3684000 / 256 Überläufe produzieren. Das sind 14390.625 Überläufe pro Sekunde.
Timer1 Prescaler /8, OCR1A auf 4605, dann haste einen Output-Compare-Interrupt (im CTC-Mode) alle 100dstel Sekunden. Variablen zählen im INT. Besser wäre es, einen 'geraden' Quarz zu wählen (8 oder 16 MHz), da teilt sich's leichter!
> hm kannst den Rechenweg kurz erklären?
Ist doch simpel:
Der Vorteiler sagt dir wieviele Takte vergehen müssen, damit
der Zähler um 1 hochzählt. Klar?
Also: Wenn dein Quarz 3864000 Takte in der Sekunde generiert,
wie weit zählt der dann in 1 Sekunde mit einem Vorteiler von 64?
Offensichtlich bis 3864000 / 64 -> 57562.5
Ziel ist es wieder sich an den Overflow zu klemmen. Wann tritt
der ein? Na wenn der Zähler überläuft. Und da es sich um einen 16
Bit Zähler handelt, kommt der Überlauf bei 65536.
Also: Bei welchem Wert musst du den Timer loslaufen lassen, damit
nach genau 57562.5 Zählvorgängen der Wert von 65536 erreicht wird?
Der Startwert muss offensichtlich 65536 - 57562.5, also 7973.5
sein. Denn wenn der Timer dann 57562.5 mal getickt hat, ist der
gewünschte Wert von 65536 erreicht und er generiert einen Overflow.
Was für dich heist: 1 Sekunde ist vergangen.
Sorry, meinte OCR1A auf 4604 (immer eins weniger als der Teilerfaktor, da von 0 aus gezählt wird)!
@karl heinz: ist ja theoretisch richtig, aber wie lässt du den Timer einen halben Takt zählen? Der Teilerfaktor muss immer ohne Nachkommaanteil passen!
Alles ist zwar ok was hier gesagt wird aber wenn du Dattenblatt genau studiert hättest dann wärest du wahrscheinlich REAL TIME CKLOCK gesehn. The clock source for Timer/Counter2 is named clkT2S. clkT2S is by default connected to the main system I/O clock clkI/O. By setting the AS2 bit in ASSR, Timer/Counter2 is asynchronously clocked from the TOSC1 pin. This enables use of Timer/Counter2 as a Real Time Counter (RTC). When AS2 is set, pins TOSC1 and TOSC2 are disconnected from Port B. A crystal can then be connected between the TOSC1 and TOSC2 pins to serve as an independent clock source for Timer/Counter2. The Oscillator is optimized for use with a 32.768 kHz crystal. Applying an external clock source to TOSC1 is not recommended. For Timer/Counter2, the possible prescaled selections are: clkT2S/8, clkT2S/32, clkT2S/64, clkT2S/128, clkT2S/256, and clkT2S/1024. Additionally, clkT2S as well as 0 (stop) may be selected. Ausserdem egal wie erfahren du bist du muss das Datenblatt lesen. Es giebt keine fertige Lösungen.
Das mit dem externen Uhrenquarz ist wegen ungenauer Quarze mit Vorsicht zu geniessen! Der produziert natürlich am Timer 2 jede Sekunde einen Überlauf, aber wie gesagt, hatte damit schon erhebliche Ungenauigkeiten drin!
ohje klappt noch nicht so wirklich, ist das so richtig: int sek = 0; TIMSK |= (1 << 0); // Timer/Counter0 Interrupt Aktivieren TCCR0 |= (1 << 2);; // Prescaler auf 256 Einstellen if (bit_is_set(TIFR,0) ) { // Prüfen ob Ein Überlauf Vorliegt sek++; PrintInt(sek); uart_puts("\r\n"); }
ah doch funktioniert, ups hatte das ganze in einer schleife somit wurde bei jedem durchlauf der timer neu initialisiert und es kam immer 1 raus ;)
so ich bekomme nun schon einen sekunden takt hin, jedoch habe ich vermutlich einen Fehler im Programm, denn die erste Sec zahl die er mir ausgibt fängt immer bei rund 600 an, normal müsste doch bei 0 anfangen? // Timer Initialisieren if (bit_is_clear(TIMSK,0)) { // Timer/Counter0 Interrupt TIMSK |= (1 << 0); } if (bit_is_clear(TCCR0,2)) { // Prescaler auf 8 Einstellen TCCR0 |= (1 << 2); } int count, sec; if (bit_is_set(TIFR,0) ) { // Prüfen ob Ein Überlauf Vorliegt count++; } if (count == 14400) { sec++; PrintInt(sec); uart_puts("\r\n"); } // Uhr Ende
Man Sonic was redes du denn da, Quarz so viel fehler wo hast du denn Quarz gekauft ich glaube du hast in selber gebastelt. Wenn du den externen Quarz als ungenau bezwéichnst was sagst du dann zu dem internen Quarz vergleich mal die Datenblätter befor du hier ein unsinn verbereitst. Mensch.
Ich denke mal hier geht es in erster Linie nicht darum eine exakte Uhr zu bauen. Hier geht es in erster Linie darum programmieren zu lernen bzw. den Umgang mit dem Timer zu üben. > denn die erste Sec zahl die er mir > ausgibt fängt immer bei rund 600 an Verfolg mal nach, was den der Startwert der Variable Sec ist. Es bringt nichts Programmteile zu posten, die sich, ausser der Erhöhung von Sec, nicht mit Sec befassen.
> @karl heinz: > ist ja theoretisch richtig, aber wie lässt du den Timer einen halben > Takt zählen? Der Teilerfaktor muss immer ohne Nachkommaanteil passen! Indem ich ihn abwechseln einmal um 1 zu hoch und das nächste mal um 1 zu tief zählen lasse. Gemittelt hat er dann einen halben Takt gezählt :-) Im übrigen ist der ganze Weg auch theoretisch nicht richtig, da da einige Takte unter den Tisch fallen. Kein Mensch würde das bei einer richtigen Uhr mit einem Overflow lösen sondern würde auf den CTC ausweichen. Aber darum gehts im Moment noch nicht (zumindest mir nicht). Walter muss jetzt erst mal in die Gänge kommen.
Karl heinz Buchegger wrote: > Ich denke mal hier geht es in erster Linie nicht darum eine > exakte Uhr zu bauen. Hier geht es in erster Linie darum > programmieren zu lernen bzw. den Umgang mit dem Timer zu > üben. > >> denn die erste Sec zahl die er mir >> ausgibt fängt immer bei rund 600 an > > Verfolg mal nach, was den der Startwert der Variable Sec ist. > Es bringt nichts Programmteile zu posten, die sich, > ausser der Erhöhung von Sec, nicht mit Sec befassen. Sorry. Hab ich übersehen. Da haben wirs ja: if (bit_is_clear(TCCR0,2)) { // Prescaler auf 8 Einstellen TCCR0 |= (1 << 2); } int count, sec; Wenn eine Variable zur Welt kommt, hat sie irgendeinen Wert, der muss noch nicht mal gültig sein (zb. im Falle von double). Dein sec enthält also irgendwas zufälliges. Wenn du willst, dass das mit 0 loslegst, musst du sec schon initialisieren: int cout, sec = 0;
Wenn alle ihr uhrenprogramm hier reinstellen, dann werd ichs auch mal machen! G Ist in bascom, rechnet wochentage, schaltjahre etc, berechnung erfolgt im hauptprogramm, wen ein status-bit gesetzt wurde. bis jetzt noch nicht in reeller hardware getestet, allerdings.
zum berechnen der timer benutze ich das programm RNAVR (einfach mal googlen).da gibst du einfach taktfrequenz und interrupt frequenz an, und du bekommst die werte angezeigt. genau das richtige für den faulen, grins
@ GISMO: diese Erkenntnisse habe ich aus der Praxis! Ein mehrfach geteilter (MHz-Bereich)-Quarz ist entsprechend genauer und temperaturstabiler, wegen der Masse des Quarzes als ein kHz-Quarz, der nicht geteilt wird. Probier doch einfach mal ein paar aus, nicht nur drauf verlassen was im Datenblatt steht!
Was soll ich Probieren, nichts ist Perfekt. Ausserdem ich benutze ein Real Time Counter seit 2000, wenn du ganz genauers nehmen willst dann nehm doch eine Atomuhr, geht auch ebenfalls falsch. Vielleicht solte man auch ein Atomuhr Temperatur kompensation verpassen.
ist denn der aufwand ewig mit quarzen rumzuspielen gerechtfertigt? also im zeitalter von dcf kann man doch einmal am tag, oder pro woche einfach abgleichen, oder?
> Wenn du den externen Quarz als ungenau bezwéichnst Externe Quarze sind natürlich genauso ungenau. Auch ein Uhrenquarz ist nicht absolut genau. Deswegen verbaut man unter anderem ja auch Kondensatoren um den Quarz in seiner Frequenz noch ziehen zu können. Jeder Uhrmacher gleicht den Quarz einer Uhr auf die Sollfrequenz ab. > was sagst du dann zu dem internen Quarz Welcher interner Quarz? In einem AVR ist kein 'interner Quarz' eingebaut. Das sind ganz normale R-C Schwingkreise.
Ich hab's, wie Sebastian angesprochen hat, mit DCF77 gemacht, die RTC übernimmt bei Ausfall oder fehlerhaftem Signal.
Sebastian Heyn wrote: > zum berechnen der timer benutze ich das programm RNAVR (einfach mal > googlen).da gibst du einfach taktfrequenz und interrupt frequenz an, und > du bekommst die werte angezeigt. genau das richtige für den faulen, > grins Dann mußt Du aber noch die Werte ins Programm eintippen, wäre mir viel zuviel Arbeit. Ich bin noch fauler, ich trag einfach die Formel ein und laß das Rechnen den Präprozessor machen. Bei einem anderen Quarz, schreib ich dann nur die neue Quarzfrequenz hin, laß es neu compilieren und gut is. Peter
so jetzt zählt er mir die Überläufe, jedoch viel zu langsam, nach ein paar minuten habe ich 1000 überläufe das kann doch nicht sein, was mach ich denn blos Falsch? // Timer Initialisieren if (bit_is_clear(TIMSK,0)) { // Timer/Counter0 Interrupt TIMSK |= (1 << 0); } if (bit_is_clear(TCCR0,2)) { // Prescaler auf 8 Einstellen TCCR0 |= (1 << 2); } if (bit_is_set(TIFR,0) ) { // Prüfen ob Ein Überlauf Vorliegt sek++; } uart_puts("Count:"); PrintInt(count); uart_puts("\r\n"); uart_puts("Sec:"); PrintInt(sec); uart_puts("\r\n"); // Uhr Ende
Die Abfragen ob das Bit gelöscht ist um es dann zu setzen kannst du dir sparen. Es ist wesentlich einfacher das Bit immer zu setzen, egal ob es schon gesetzt ist oder nicht. Nun, du aktivierst den Timer-Interrupt hast aber keinen Interrupt Handler. Den Timer Interrupt zu benutzen ist eine gute Idee. Was hingegen schlecht ist, ist die Abfrage ob das Overflow Bit in TIFR gesetzt wird. Ein Interrupt Handler würde ganz einfach aufgerufen werden und gut ists. Das könnte so aussehen:
1 | #include <avr/io.h> |
2 | #include <avr/interrupt.h> |
3 | |
4 | volatile int Sek; |
5 | |
6 | ISR( TIMER0_OVF_vect ) |
7 | {
|
8 | Sek++; |
9 | }
|
10 | |
11 | int main( void ) |
12 | {
|
13 | Sek = 0; |
14 | |
15 | TIMSK = ( 1 << TOIE0 ); // Timer Overflow Interrupts zulassen |
16 | TCCR0 = ( 1 << CS01 ); // Vorteiler von 8 |
17 | |
18 | sei(); // und alle Interrupts scharf stellen, ab jetzt kommen sie |
19 | |
20 | while( 1 ) { |
21 | uart_puts( "Sec:" ); |
22 | PrintInt( Sek ); |
23 | uart_puts( "\r\n" ); |
24 | }
|
25 | }
|
Das ergibt jetzt keine Sekunde, aber es soll ja für dich auch noch was übrig bleiben.
ah ok danke ;) jetzt geht es bekomm nun endlich die richtige sekunde hochgezählt, vielen dank nochmal für die geduld ;)
ähhm i würde keine timer nehmen. denn wenn du eine sekunde mit dem timer auszählst und dann das auf nen lcd oder sonstigem anzeigen willst, wirst du jede sekunde verfälschen um den faktor der lcd ansteuerung. schreib deinen code für die uhr und dann leg warte schleifen ein damit du eine sekunde kriegst. viel genauer und super leicht zu berechnen. gruss
aso solltest es natürlich in asm schreiben ansonsten iss alles zufall mit dem timings.
fubu1000 wrote: > i würde keine timer nehmen. denn wenn du eine sekunde mit dem timer > auszählst und dann das auf nen lcd oder sonstigem anzeigen willst, wirst > du jede sekunde verfälschen um den faktor der lcd ansteuerung. Nö ! Das ist ja gerade der Witz an nem Timer, daß er in Hardware läuft. Irgendwelche Softwareausführunszeiten kümmern ihn in keinster Weise. Ein Timer ist immer auf den Zyklus genau. > schreib deinen code für die uhr und dann leg warte schleifen ein damit > du eine sekunde kriegst. Gratulation ! Das ist die absolut richtige Methode, um total ungenau zu werden. Warteschleifen sind Software und damit völlig Programm- und Compilierungsabhängig. Warum willst Du nen Anfänger verarschen ? Was hat er Dir getan ? Peter
Also ich wollte mir auch eine Software Uhr basteln. Welchen Takt sollte ich denn jetzt dafür verwenden? Den internen oder ist doch besser meinen 4MHZ Oszillator über XTAL1? Es gibt ja auch noch die Möglichkeit einen Oszillator zwischen XTAL1 und XTAL2 zu schalten? Also, welches ist auf Dauer gesehen die Beste Lösung? Das Ganze möchte ich unter BASCOM zum Laufen kriegen. Ich verwende hierfür doch einen Timerinterrupt um mir ein Sekunden Signal zu schaffen oder? Es gibt dort ja auch die vorgefertigte WAIT 1-Anweisung. Ist die Genau? DANKE schon einmal für die ANTWORTEN.
die wait 1 anweisung ist total nutzlos, weil dein programm, also die zeit berechnen ja auch x zeit benötigt. deshalb wirst du pro sekunde immer ungenauer. ich hatte oben mal ein bascom beispiel gepostet für eine uhr. (rtc.bas)
> Also ich wollte mir auch eine Software Uhr basteln. > Welchen Takt sollte ich denn jetzt dafür verwenden? Der interne ist sehr ungenau und diversen Schwankungen unterworfen (Versorgungsspannung, Temperatur). Nimm einen normalen Quarz (sonic meint, spezielle 32kHz Uhrenquarze sind zu ungenau) zw. XTAL1 und XTAL2. Meine Uhr lief anfangs mit 7,3728MHz und auf 24 Stunden hatte ich eine Sekunde Abweichung von der normalen Zeit.
Hi nochmal, wenn du Wert auf eine genaue Uhr legst, ist es das Beste einen 'geraden' Quarz zu verwenden, z.B. 4, 8 oder 16 MHz. Dieser lässt sich per Output-Compare-Mode (mit CTC-Mode) genau auf 1 Sekunde runterteilen. Nun hast du einen Interrupt, der genau jede Sekunde ausgelöst wird, unabhängig vom restlichen Programm. Immer dran denken: beim µC wird ab 0 gezählt, also ist der Wert für z.B. OCRA1 1999 wenn er bei Zählerstand 2000 auslösen soll! Wenn die Baudrate genau sein soll ist natürlich z.B. ein 7,3728MHz - Quarz besser, die Uhr dafür schwieriger zu realisieren.
> Wenn die Baudrate genau sein soll ist natürlich z.B. ein 7,3728MHz - > Quarz besser, die Uhr dafür schwieriger zu realisieren. Warum? Prescaler des Timers z. B. auf 8 gesetzt, Timer-Overflow-Interrupt aktivieren und in der ISR ein (unisgned int) bei jedem Interrupt hochzählen. Wenn dieser Wert 3600 ist, ist genau eine Sekunde rum. Oder vertue ich ich da gerade?
Du liegst mit den 3600 richtig, das wären 3600Hz, also 277.77(irgendwas) µs. Zeit ist 1/Frequenz, unbedingt Berücksichtigen! Du kannst sehr wohl auf 3600 zählen, Ist aber wie schon gesagt, Programmäßig schwieriger zu realisieren. Bei einem geraden Quarz kannst du die Uhr bequem in der Interrupt-Routine unterbringen und hast 1s Zeit für den Rest des Programmes, wobei bei deiner Version der 'normale' Programmablauf alle 277...µs unterbrochen wird.
Quarzungenauigkeiten kann man nichr 'rausrechnen, nur messen und entsprechen kompensieren. Jedenfalls gilt: je höher die Quarzfrequenz und je größer der Teiler, desto kleiner der Fehler.
> ähhm > i würde keine timer nehmen. denn wenn du eine sekunde mit dem timer > auszählst und dann das auf nen lcd oder sonstigem anzeigen willst, wirst > du jede sekunde verfälschen um den faktor der lcd ansteuerung. Weist du. Wenn du dich darüber mokiert hättest, dass das Ganze am Overflow Interrupt aufsetzt, anstatt am CTC, hätte ich das ja noch verstanden. Ich hab ihm den Overflow eingeredet, damit er mal anfängt sich mit Timern zu beschäftigen. Der Overflow Interrupt ist besser als nichts, aber der CTC ist das Mittel mit dem man das machen würde. > schreib deinen code für die uhr und dann leg warte schleifen ein damit > du eine sekunde kriegst. > viel genauer und super leicht zu berechnen. Das allerdings geht genau in die falsche Richtung. Mit Warteschleifen kriegst du keine halbwegs genaue, langzeitstabile Uhr hin. Selbst die Uhr mit Overflow-Interrupt ist deinen Warteschleifen haushoch überlegen.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.