Hallo,
ich habe es jetzt geschafft meinen Wecker zum Laufen zu bringen, ohne
ihn nach jedem Auslösen neu programmieren zu müssen
(http://www.youtube.com/watch?v=2AaPri_VT-c&feature=plcp).
Allerdings ist ein kleines Manko geblieben:
Wenn der Wecker auslöst, soll natürlich ein Hupton ertönen. Durch
ausprobieren habe ich herausgefunden, das die Lautstärke akzeptabel ist,
wenn ich
1
PORTB^=0x01;
2
waitMs(1);
3
PORTB^=0x01;
4
waitMs(1);
5
PORTB^=0x01;
6
waitMs(1);
7
PORTB^=0x01;
8
waitMs(1);
ungefähr 195 Mal wiederhole. Spätestens an diesem Punkt kann man
feststellen, welche Ausmaße folglich das „Testprogramm“ hatte =)
Wenn ich aber nur einmal den Befehl setze, hört man nur ein sekündliches
Knacken.
Die sekündliche Abfrage kommt denke ich vom Zähler der Uhr.
Nun meine Frage: kann man die Wiederholung eines Befehls zu einem Befehl
zusammenfassen?
(1) Du kannst es über einen Timer laufen lassen, der gestartet und nach
einer entsprechenden Zeit wieder gestoppt wird.
(2) Oder du schaltest für eine bestimmte Zeit einen PWM ein und
schaltest ihn dann wieder aus. Wenn du keinen HardwarePWM hast, den du
entsprechend umbiegen kannst, dann kannst du es über Software mit einem
Timer lösen, wie bei 1 schon erwähnt
Der Vorteil von Timer/HW-PWM liegt darin, dass es Interruptgesteuert ist
und deswegen die main(), in der es anscheinend steht (Ohne kompletten
Quelltext kann man sowas natürlich nicht sagen...), nicht zum Stillstand
bringt.
delay und wait ist zwar manchmal ganz schön, wie aber schon oft an
anderer Stelle gesagt nicht DIE Lösung, sondern meistens eher der
Fehler...
P. M. schrieb:> Sag doch ein paar Worte über den verwendeten Code
so ein blödsinn! code und schaltungen beschreibt man nicht, sondern man
zeigt sie her!
Also,
um auf möglichst alle Antworten zu reagieren:
ich hbe noch nicht viel Erfahrung im Programmieren. Habe aber relativ
gute Elektronik-Kenntnisse und wenn man mir etwas langsam und geduldig
erklärt, raff ichs.
Hier der relevante Teil des Quellcodes:
Dazwischen die Funktion zum Weckerstellen,
die Weckerauslösung mit dem bereits erwähnten Hupton:
1
if((h==hr)&&(m==min))
2
{
3
PORTB^=0x01;
4
waitMs(1);
5
...
6
}
,und die Funktion für die LCD-Darstellung
Ich betreibe die Weckeruhr mit dem ATmega8.
Wie funktioniert das denn mit dem PWM-Ausgang?
Werden dort die Impulsdauern über Variablen festgelegt?
> char str[2];
hier passen 2 zeichen rein.
Du schreibst aber
sprintf(str,"%02d",min);
3 zeichen rein, 2mal eine ziffer und dann eine 0byte für das ende. Damit
überschreibst du dir den speicher, vermutlich geht irgendetwas aus
diesem grund nicht.
Peter II schrieb:> 3 zeichen rein, 2mal eine ziffer und dann eine 0byte für das ende. Damit> überschreibst du dir den speicher
Heißt das dann, dass ich das durch
1
charstr[3]
ersetzen sollte?
Peter II schrieb:> vermutlich geht irgendetwas aus> diesem grund nicht.
Ja gut, aber das ist ja nur ein Ausdruck zum Darstellen auf dem LCD, das
hat doch dann keinen Einfluss auf andere Routinen, oder?
Für Deine Anwendung ist die while() Schleife sicher prima geeignet. PWM
ist eine Alternative, die technisch eleganter erscheinen mag, allerdings
auch anspruchsvoller zu verstehen und zu programmieren ist.
PWM ist eine Hardwarefunktion, im Datenblatt beschrieben. Dabei wird ein
Timer verwendet, um den Ton zu erzeugen. Du sagst dem Timer, welche
Frequenz er erzeugen soll. Die Anzahl der Schwingungen zählst Du in
einer Interrupt-Routine, so dass Du den Timer nach einer gewissen Anzahl
von Schwingungen stoppen kannst.
Aber dazu müsstest Du dich mit PWM und Timer und Interrupts
beschäftigen.
Steffen Keller schrieb:> Ja gut, aber das ist ja nur ein Ausdruck zum Darstellen auf dem LCD, das> hat doch dann keinen Einfluss auf andere Routinen, oder?
doch weil du damit wild im speicher rumschreibst, damit kann alles
mögliche passieren.
> Heißt das dann, dass ich das durch> char str [3]> ersetzen sollte?
ja
@ Stefan:
Soweit habe ich das jetzt mit dem PWM verstanden:
Es wird eine Variable, sagen wir "i" definiert.
Nun zählt ein Timer i aufwärts. An einem bestimmten Zählerstand von i
wird dann ein Interupt ausgelöst. Die Zählzeit bis dahin ist abhängig
vom Quarz und vom maximalen Zählerstand.
In der Interupt-Routine wird dann ein Ausgang auf 1 oder 0 gesetzt.
*Frage von mir: kann da mit einem Flankenwechsel PORTB^=0x05 oder so
gearbeitet werden?
Nach der Routine wird wieder ins Hauptprogramm gesprungen und der Zähler
zählt weiter.
Steffen Keller schrieb:> Also,> um auf möglichst alle Antworten zu reagieren:> ich hbe noch nicht viel Erfahrung im Programmieren.
Merkt man.
Wo auch immer du den Code geklaut hast, der hatte auch nicht viel
Ahnung.
AN deinem Wecker wirst du nicht viel Freude haben, weil der ziemlich
schnell falsch gehen wird.
Erstes Hauptproblem:
Um eine einigermassen genaue Uhr zu bekommen, kommst du am CTC Modus des
Timers nicht vorbei.
Timer Overflow mit Vorladen des Timers ist Unsinn, ausserdem kriegst du
nicht die notwendige Langzeit-Genauigkeit. Bei Uhren, die monatelang
laufen sollen, spielt es eine Rolle, dass der Uhr-Zählmechanismus
möglichst auf den Takt genau angestossen wird. Andernfalls summieren
sich die Abweichungen ganz schnell auf. D.h. vom Prinzip her muss der
Timer ganz alleine und ohne äussere Einflussnahme auf das Zählregister
den Takt machen. Es gibt keine Garantie, dass die Anzahl der Takte vom
Auftreten des Overflows bis zum tatsächlichen Neusetzen des
Zählregisters im Overflow-Interrupt immer gleich ist. Je nachdem,
welcher Befehl in der Hauptschleife gerade in Arbeit ist, dauert das
eine unterschiedliche Anzahl Takte. Spielt bei kurzen Programmen keine
Rolle, wenn ein Programm aber über Monate hinweg läuft, dann summiert
sich diese Abweichung - die Uhr geht falsch.
Der CTC Modus vermeidet dieses Problem, weil hier der Timer ganz
alleine, ohne Unterstützung durch einen Interrupt eine genau bestimmte
Anzahl an Takten abzählt. Ob dann die Zeit selbst ein paar Takte später
hochgezählt wird oder nicht, spielt keine große Rolle. Wichtig ist, dass
das 'Pendel' (das ist der Timer im CTC Modus) exakt regelmässig
schwingt. Wenn die 'Zeiger' mal ein bischen mehr, mal ein bischen
weniger lang diesem Schwingen hinterherhinken, ist dahingehend ziemlich
egal. Dauert es mal ein weniger länger, dann kriegt der 'Zeiger' den
nächsten Vorrückpuls eben ein bischen früher. Die Genauigkeit des
'Pendels' ist bei einer Uhr der entscheidende Punkt, nicht die der
Zeiger.
Das zweite ist, dass dein Quarz eben nicht exakt mit der aufgedruckten
Frequenz schwingt. Auch wenn da 8Mhz drauf steht, sind es eben nicht
exakt 8000000 Schwingungen in der Sekunden sondern das können auch 100
weniger oder 100 mehr sein (oder irgendein Wert dazwischen). Wieder: bei
kurz laufenden Programmen spielt das keine Rolle. Läuft aber ein
Programm ein paar Monate dann summiert sich dieser Fehler. Und das geht
ganz schnell! Es ist nicht ungewöhnlich, dass eine Uhr im CTC-Modus, die
mit exakt der am Quarz aufgedruckten Frequenz durchgerechnet wurde, am
nächsten Tag schon ein paar Sekunden falsch geht. Am Ende des Monats
sind es dann schon Minuten und in einem halben Jahr weckt dich dein
Wecker dann nicht mehr wie eingestellt um 6, sondern um halb 7
(Realzeit).
Dann:
Das Erhöhen und weiterschalten der Uhrzeit nimmst du mit in den
Timer-Interrupt hinein. Und zwar vollständig!
Nicht so wie jetzt, dass die die Weiterschaltung der Zeit in der
Hauptschleife passiert. Deine Uhr muss auch dann weiterlaufen, wenn die
Hauptschleife mit etwas völlig anderem beschäftigt ist, wie zb der
Behandlung der Benutzereingabe, der sich gerade die Alarmzeit ansieht.
Wenn dein Benutzer sich die Alarmzeit 3 Minuten lang ansieht und
einstellt, dann darf deswegen die eigentliche Uhr nicht plötzlich
nachgehen, nur weil der Benutzer so lange gebraucht hat! Also: Die
komplette Abhandlung der Uhr gehört in den Interrupt. Das ist nicht viel
Code und verlängert die ISR Abarbeitung nicht wesentlich - aber: deine
Uhr 'verliert' dadurch die Zeit nicht mehr.
Karl Heinz Buchegger schrieb:> Wo auch immer du den Code geklaut hast
Ok, den Teil mit dem Timer und der Uhr hatte ich aus einem Buch. Und für
Anfänger ist es meiner Meinung nach wichtig, Gehilfen zu haben. Und
dafür werden solche Bücher ja geschrieben. Ich würde also nicht von
"klauen" reden :/ Der Wecker war aber ein eigener Versuch, der sich nun
weiterentwickelt (sonst wäre ich ja nicht hier).
Karl Heinz Buchegger schrieb:> Auch wenn da 8Mhz drauf steht
Die Toleranz ist klar. Hängt ja auch von äußeren Umständen ab. Sind die
Abweichungen bei kleineren Frequenzen genauso hoch (ich habe 3,686Mhz
drin)?
Zu deiner Aussage, den Zählmechanismus in die ISR reinzustecken:
Sieht das dann so aus:
Steffen Keller schrieb:> Karl Heinz Buchegger schrieb:>> Wo auch immer du den Code geklaut hast>> Ok, den Teil mit dem Timer und der Uhr hatte ich aus einem Buch. Und für> Anfänger ist es meiner Meinung nach wichtig, Gehilfen zu haben. Und> dafür werden solche Bücher ja geschrieben. Ich würde also nicht von> "klauen" reden :/ Der Wecker war aber ein eigener Versuch, der sich nun> weiterentwickelt (sonst wäre ich ja nicht hier).
Oops.
Bücher sind gut und wichtig. Kein Einwand.
Allerdings gibt es mir zu denken, wenn der Autor des Buches so einen
Quatsch macht.
> Zu deiner Aussage, den Zählmechanismus in die ISR reinzustecken:>> Sieht das dann so aus:>
1
>ISR(TIMER1_OVF_vect)
2
>{
3
>TCNT1=51136;tc++;
4
>sec=tc;
5
>
6
>if(sec==60)
7
>
8
>{tc=0;sec=0;min++;
9
>
10
>if(min==60){min=0;hr++;if(hr==24)hr=0;}
11
>}
12
>
> ?
Genau.
Aber arbeite ein wenig an deiner äusseren Form! Interessanterweise haben
nämnlich diejenigen mit den scheuslichsten und unübersichtlichsten
Programmen immer die tollsten und blödesten Fehler. Bei dem Code gehts
noch, weil er nicht sehr umfangreich ist. Aber das wird sich im Laufe
der Zeit ändern
(die Variable tc brauchst du nicht mehr.
1
ISR(TIMER1_OVF_vect)
2
{
3
TCNT1=51136;
4
5
sec++;
6
if(sec==60)
7
{
8
sec=0;
9
min++;
10
11
if(min==60)
12
{
13
min=0;
14
hr++;
15
16
if(hr==24)
17
hr=0;
18
}
19
}
20
}
Pro Zeile ein Befehl. Achte darauf, dass { und zugehörige }
untereinanderstehen (es gibt da auch noch andere Formatiersysteme). Bei
jeder { kommen der nächste Befehl in eine neue Zeile. Befehle, die in
einem { } Block stehen, werden konsistent um 2 (das ist mein System, es
gibt da auch andere Einrücktiefen) Leerzeichen eingerückt.
So sieht man auf einen Blick, welche Befehle zusammengehören und wie die
Schachtelung der Programmblöcke ist. Welche Programmteile hängen wovon
ab? Diese Frage muss einfach mit einem Blick klärbar sein.
Wie gesagt: Bei deinem kleinen Programm mag dir das noch als unwichtig
erscheinen. Wenn deine Programme aber mal größer werden, sind solche
formalen Richtlinien essentiell um möglichst viele Fehler schon im
Vorfeld auszuschliessen.
Karl Heinz Buchegger schrieb:> Aber arbeite ein wenig an deiner äusseren Form!
Alles klar =) Darauf werde ich in Zukunft achten.
Dann verändere ich gleich mal den QC :D
Wenn du den Code so umändern würdest, dass die ISR alle 1/1000 Sekunde
aufgerufen wird (du brauchst dann klarerweise eine Variable die bis 1000
zählt um die Sekunden weiterzuschalten), dann könntest du deinen Summer
auch in der ISR jeweils umschalten.
Vorteil: Du brauchst dich in der Hauptschleife nicht mehr darum kümmern
und du hast kein _delay_ms in der Hauptschleife.
Denn das, einen _delay_ms, den willst du nach Möglichkeit vermeiden,
wenn du kannst. _delay_ms ist selten die Lösung für ein Problem. Aber
_delay_ms wird oft selbst zu einem Problem.
1
#include<avr/io.h>
2
3
#define SUMMER_DDR DDRB
4
#define SUMMER_PORT PORTB
5
#define SUMMER_PIN PB0
6
7
volatileuint8_tsoundOn;
8
volatileuint8_thr=22,min=13,sec=0;
9
10
uint8_talarmHr=22;
11
uint8_talarmMin=14;
12
13
charstr[10];
14
15
ISR(TIMER1_OVF_vect)
16
{
17
staticuint8_tmilliSec;
18
19
TCNT1=.....;// musst du, zusammen mit dem Vorteiler bei der
20
// Timereinstellung, für 1/1000 Sekunde neu berechnen
21
22
//
23
// Um den Summer kümmern, wenn er eingeschaltet ist
24
//
25
if(soundOn)
26
SUMMER_PORT^=_BV(SUMMER_PIN);
27
28
//
29
// Die eigentliche Uhrzeit weiterschalten
30
//
31
milliSec++;
32
if(milliSec==1000)
33
{
34
milliSec=0;
35
sec++;
36
37
if(sec==60)
38
{
39
sec=0;
40
min++;
41
42
if(min==60)
43
{
44
min=0;
45
hr++;
46
47
if(hr==24)
48
hr=0;
49
}
50
}
51
}
52
}
53
54
intmain(void)
55
{
56
SUMMER_DDR|=_BV(SUMMER_PIN);
57
58
TIMSK|=_BV(TOIE1);
59
TCCR1B|=(1<<CS12);
60
TCNT1=0x0000;
61
sei();
62
63
lcdInit();
64
lcdClear();
65
66
lcdGoto(1,4);
67
lcdWrite("Uhr");
68
69
while(1)
70
{
71
sprintf(str,"%02d:%02d:%02d",hr,min,sec);
72
lcdGoto(1,9);
73
lcdWrite(str);
74
75
if(hr==alarmHr&&min==alarmMin)
76
soundOn=1;
77
else
78
soundOn=0;
79
}
80
}
Wenn der Summer tröten soll, setzt du in der Hauptschleife einfach die
Variable 'soundOn' auf 1 und der Tröterich gibt Laut. Soll er wieder
ruhig sein ... Variable auf 0 setzen und der Spuk ist vorbei.
(Um den CTC Modus kümmern wir uns später)
Steffen Keller schrieb:> Jetzt läuft aber die Uhr nicht mehr...
Du hast keine Hauptschleife mehr :-)
Scroll ein wenig hoch, den Post vor deinem.
Ich hab dir da mal was zusammengestellt.
Den Timer müsstest du neu auf 1/1000 Sekunde ausrechnen, aber abgesehen
davon, ist der Code vollständig
Nein!
Davon, dass die Ausgabe aufs LCD ebenfalls in die ISR soll, war nicht
die Rede. Die Weiterschaltung der Zeit kommt in die ISR, die Ausgabe
bleibt in der Hauptschleife!
Wie nennt sich das hier eigentlich? Online-Programming? Wir entwicklen
ein Programm on-the-fly im Forum? Früher hat man mal selbst
programmiert, wenns nicht ging hat man selbst debugt. Und solange
probiert bis es ging. Heute schmeißt man jeden einzelnen Schritt ins
Forum? Ist doch irgendwie abartig.
gruß cyblord
cyblord ---- schrieb:> Wie nennt sich das hier eigentlich? Online-Programming? Wir entwicklen> ein Programm on-the-fly im Forum? Früher hat man mal selbst> programmiert, wenns nicht ging hat man selbst debugt. Und solange> probiert bis es ging. Heute schmeißt man jeden einzelnen Schritt ins> Forum? Ist doch irgendwie abartig.
Irgendwann muss man dem Nachwuchs auch die richtigen Techniken zeigen,
wie man Programmier-Probleme angeht.
Da gabs schon schlimmeres. Er hatte ja prinzipiell schon was, man muss
ihn jetzt in die richtige Richtung lenken. Und sein Buch scheint da
nicht so der Bringer zu sein.
Dietrich L. schrieb:> Karl Heinz Buchegger schrieb:>> static uint8_t milliSec;>> Sollte das nicht uint16_t heißen? Er soll ja bis 1000 zählen :-)
Oops. Danke für den Catch.
Das hatte ich beim Online-Programmieren übersehen!
Danke.
Das ist nur ein Vorschlag!
Wie wärs wenn du einfach noch mal gescheit C auf dem Computer lernst und
dich dann weiter mit dem µC beschäftigst. Und vor allem die GCC-AVR
Tutorials durcharbeitest, da lernst du dann z.B., dass sowas wie die LCD
Ansteuerung in einer ISR nichts zu suchen hat.
Weil man einfach sieht, dass dir noch ein bisschen was fehlt. Allerdings
scheinst du C schon mal recht gut begriffen zu haben, aber C ist nun mal
nicht Fehlertolerant und haut einem sofort auf die Finger! Das sind
schon solche kleinen Dinge wie, dass ein String immer mit '\0' endet!
Wie gesagt, das ist nur ein Vorschlag, aber du wirst sehen, dass das
Programmieren ansonsten sehr frustrierend sein wird.
Beim Overflow-Ausrechnen habe ich noch einige Hindernisse:
Den Wert fürs Register, wenn ich Millisekunden will, berechne ich dann
so:
(2^16 - (Quarzfrequenz / Prescaler)) / 1000
Und da muss dann ein ganzzahliger Wert rauskommen, oder?
Ich bekomme aber entweder Zahlen mit drei Dezimalstellen oder einen
negativen Wert mit drei Dezimalstellen.
Hi
>Den Wert fürs Register, wenn ich Millisekunden will, berechne ich dann>so:
Nimm die Formel aus dem Datenblatt
Timer->Modes of Operation->CTC
und stelle sie um. Deine ist falsch.
MfG Spess
Die Formel aus dem Datenblatt müsste demnach diese sein:
f(OCR1A) = f(clk_I/O) / 2*N*(1+OCR1A)
N ist der Prescaler
f(clk_I/O) ist die Quarzfrequenz
f(OCR1A) ist die Frequenz der Overflows
Hab ich das so richtig verstanden?
Sorry, musst kurzfristig mal weg.
Meiner Meinung nach ist es am Vernünftigsten, wenn man versteht, wie
diese Formel zu Stande kommt als einfach nur Werte in irgendeine Formel
einzusetzen.
Hier
FAQ: Timer
findet sich ein Ansatz, mit dem man versteht was man wie rechnet. Timer
sind eigentlich nicht schwer und auch die Bestimmung der Zahlenwerte ist
nicht wirklich schwer, wenn man sich einmal überlegt hat, was da
eigentlich passiert.
(Und im Endeffekt landet man natürlich wieder bei den Formel. Nur mit
einem Unterschied: plötzlich versteht man, warum die Formel genauso
aussieht wie sie aussieht, und wenn man mal die Formel nicht parat hat,
dann leitet man sich die Zahlenwerte einfach so auf die Schnelle her)
Karl Heinz Buchegger schrieb:> //> // Um den Summer kümmern, wenn er eingeschaltet ist> //> if( soundOn )> SUMMER_PORT ^= _BV( SUMMER_PIN );> [...]> Wenn der Summer tröten soll, setzt du in der Hauptschleife einfach die> Variable 'soundOn' auf 1 und der Tröterich gibt Laut. Soll er wieder> ruhig sein ... Variable auf 0 setzen und der Spuk ist vorbei.
Je nach Art und Anschaltung des Tröterichs bietet es sich evtl. an, den
Pin im Aus-Zustand definiert auf Low zu schalten, statt ihn auf dem
letzten Zustand zu belassen.
Steffen Keller schrieb:> Ich bekomme aber entweder Zahlen mit drei Dezimalstellen oder einen> negativen Wert mit drei Dezimalstellen.
Hinweis:
Die 1000 stammen ja daraus, dass du den Summerport alle 1ms umschalten
willst. Das muss aber nicht unbedingt so sein.
Wenn sich die Zahlen mit 1002 oder 998 besser ausgehen, dann kannst du
natürlich auch die nehmen. Für die Uhr ist es egal. Ob da mit 1000 oder
mit 998 für 1 volle Sekunde verglichen wird, spielt keine Rolle.
Und ob die Tröte jetzt genau 500Hz von sich gibt, oder 449Hz oder 551Hz
oder 505Hz, wird praktisch gesehen auch keine Rolle spielen.
Schlussfolgerung: Es gibt keinen Grund, warum du an den 1000 kleben
musst. Rechne die Werte für 1000 durch, schneide die Kommastellen ab und
rechne daraus dann zurück, welchen Sekundenteilungsfaktor (tolles Wort
:-); das was ursprünglich die 1000 waren) du dadurch erhältst. Und der,
bei dem du am nächsten an den 1000 bist, den nimmst du (sofern dein Wert
zum Vorsetzen des Timers noch ins 16 Bit Register passt und nicht
negativ ist, selbstverständlich). Noch beim '1000-er' Vergleich für die
Sekundenweiterschaltung den errechneten Wert eintragen, dann läuft auch
die Uhr wieder richtig.
Karl Heinz Buchegger schrieb:> Da gabs schon schlimmeres. Er hatte ja prinzipiell schon was, man muss> ihn jetzt in die richtige Richtung lenken. Und sein Buch scheint da> nicht so der Bringer zu sein.
Das Buch heißt "AVR-Mikrocontroller in C programmieren" von Dr. Günter
Spanner.
Das Buch habe ich auch und ratet mal was ich gerade versuche zu
programmieren- richtig eine Uhr ;-) genauer eine Fussballuhr (siehe
Bild)
Als Betreuer einer Fussballmanschaft bin ich für die Zeitnahme
zuständig. Wenn jetzt z.B. in der 13.Min 2.Halbzeit ein Tor fällt muss
ich 45-min plus 13-min rechnen und wenn ein Spieler fragt "walTTer wie
lange noch?" muss ich 45 minus 13 rechnen, was meine geistigen
Fähigkeiten als fast 60 jährigen auf eine harte Probe stellt.
Deswegen meine Fussballuhr:
Tastendruck: 1.Halbzeit 00-45 min / Restspielzeit
Tastendruck: 2.Halbzeit 45-90 min / Restspielzeit
Nach 95 min legt sich die Uhr schlafen und wird durch einen Tastendruck
wieder aufgeweckt. (nach einer Woche beim nächsten Spiel)
walTTer
Mal ein klein wenig Off-Topic:
Christopher C. schrieb:> Und vor allem die GCC-AVR> Tutorials durcharbeitest
Solche Ratschläge lese ich öfter. Das ist schonmal etwas konstruktiver
als "lern erstmal C" oder "nimm nochmal dein C-Buch zur Hand", es ist
gut gemeint, und richtig und wichtig, aber... kann man sowas nicht mal
(bitte :) direkt verlinken?
Ich kann C schon lange, und darum kenne ich auch keine gescheiten
Tutorials mehr (hab mal mit CTUTOR.DOC gelernt, falls das noch wer
kennt. Damals, als .DOC noch nix mit MS Word zu tun hatte). Es gibt ja
doch viel Ramsch, was sich Tutorial schimpft. Selber-Googlen führt da
auch mal zu dem Driss, und wie soll der Anfänger Spreu von Weizen
trennen? Gibt es eine Wiki-Seite mit solchen Ressourcen? Die wäre ja
banal einfach zu verlinken.
Wenn öfter mal was gutes direkt verlinkt wird, hilft es dem Fragesteller
beim Lernen, dem Forum beim weniger-Anfängerfragen-beantworten-müssen
und auch so jemandem wie mir beim Tipps Weitergeben.
Steffen Keller schrieb:> Die Toleranz ist klar. Hängt ja auch von äußeren Umständen ab. Sind die> Abweichungen bei kleineren Frequenzen genauso hoch (ich habe 3,686Mhz> drin)?
Beim Quarz als Zeitbasis für eine Uhr gibt es drei Problemstellen.
1. Die Nennfrequenz des Quarzes ist mit einem einfachen Teiler nicht
exakt auf einen brauchbaren Basistakt (z.B. 1 ms) herunterteilbar.
2. Der Quarz weicht von der Nennfrequenz ab
3. Der Quarz ändert seine Frequenz mit der Temperatur/Alterung
Zu 1. kann man sich evtl. einen Quarz mit passender Frequenz
heraussuchen. Das nützt aber wegen (2) alleine nichts, um auf eine
Genauigkeit´zu kommen, wie sie für eine Uhr erforderlich ist. Da hilft
nur Abgleichen in Form von Hardware (Trimmkondensator), was man in Uhren
ohne µC macht, sofern die passenden Meßtechnik verfügbar ist, oder per
Software durch Taktausblendung.
Beitrag "Die genaue Sekunde / RTC"
Gegen 3. hilft nur Voralterung und eine flache Temperaturkennlinie
und/oder geringe Temperaturschwankungen. Temperatureffekte kann man
durch passenden Quarzschnitt klein halten, so dass der Quarz im
Wendepunkt bzw. Max/Min der f(T)-Kennlinie arbeitet (-> Datenblatt).