Hallo, ich verfolge jetzt schon mehrfach die tollen Antworten von Wissenden in diesem Forum. Leider bin ich neu und mit Controllern nur ansatzweise vertraut, weshalb ich jetzt auch mal Hilfe brauche. Ich nenne den tollen ATTINY26 mein Eigen. Jetzt soll ich für einen Freund ein Programm schreiben, welches eine interne Uhr braucht. Ich habe versucht einen Timer-Interrupt jede Sekunde auslösen zu lassen. Leider klappt das irgendwie gar nicht und aus dem Datenblatt werde ich nicht ganz schlau. Kann mir vielleicht jemand nen C-Code-Schnipsel posten der so genau wie möglich jede Sekunde einen Interrupt erzeugt? Ich verwende den internen 1MHz Oszillator. Wäre echt sehr dankbar. Grüße, Erik
Vergiss es, das wird nicht mal ne Schätzuhr. Absolut unbrauchbar für eine Uhr. Solls ein Timer werden (z.B. für Platinenbelichtung) geht das noch. Bei einer richtigen Uhr addieren sich die Fehler immer weiter. 1% Abweichung macht ne Viertelstunde pro Tag... Was genau willst du machen?
Ok, sorry habe mich etwas unverständlich ausgedrückt. Es soll keine Uhr in dem Sinne werden. Es soll nur eine bestimmte Zeit gewartet werden und dann eine Aktion ausgeführt werden. Die Zeiten sollen später über Taster in Sekunden und Minuten programmierbar sein. Eine Art Eieruhr also :-) Ich brauche also diese Sekunde nur so genau wie intern irgend möglich. Schonmal Danke! Gruß, Erik
Falls du einen externen Quarz benutzen könntest wäre es einfacher. Dann könntest du einfach den Timer mit CK/128 laufen lassen und bei jedem Überlauf einen Interrupt erzeugen. Falls der µC unbedingt mit 1MHz laufen muss, kannst du den Teiler auf 64 setzen und den Counter so einstellen, dass er nur bis 125 zählt, dann hättest du jede 125tel Sekunde einen Interrupt, was du dann per Software weiter teilen müsstest. "so genau wie intern irgend möglich." Der interne Oszillator soll von der Qualität her bei den neueren Modellen echt mies sein, vor allem auch bei der Mega x8er-Reihe. Wie das beim AtTiny26 ist weiß ich nicht, aber als wirklich genau wird man das wohl auch nicht bezeichnen können. Kannst dir ja mal anschauen was im Datenblatt darüber steht. Ca. Seite 160 glaub ich. MfG
@Erik: Wenn es ok ist, dass das Teil ca. 1 sekunde pro Minute danebengeht, dann reicht der interne Oszillator. Da der ATTiny26 noch den guten Oszillator hat, mal mit dem OSCCAL-Register beschäftigen, evtl. kann die benötigte Kurzzeitkonstanz auch ohne Quarz erreicht werden. Wenn das Teil am Stromnetz hängt, kannst du auch die Netzfrequenz als Interruptquelle nehmen, das ist über den Tag bereits recht genau, auf längere Zeit sogar richtig gut. Ansonsten: Quarz. Am besten mit einem Vielfachen von 2 als Frequenz. (z.B. 3.2768MHz) @Dennis: Das ist korrekt, ab ATTiny13/2313 oder auch ATMega48/88/168 hat der interne Oszillator einen derartigen Jitter drauf, dass das bei serieller Übertragung schon Probleme bereitet. Ältere AVRs sind da richtig rocksolid dagegen. Siehe: http://elm-chan.org/docs/avr/jitter.html Text muss man nicht verstehen, die Bilder rechts sind deutlich genug. Gruss Jadeclaw.
Hallo, also ich denke, dass eine Sekunde Abweichung pro Min. noch OK wäre. Was und wie müsste ich da einstellen? Stromnetz? Klingt auch Klasse nur habe ich keine Ahnung wie ich die 50Hz an nen Eingang des µC kriegen soll. Müsste dann ja auch noch mit Schmitt-Trigger arbeiten oder so um schöne Rechtecke zu kriegen, oder? Das mit dem Quarz wäre klar am Besten, allerdings ist sehr wenig Platz auf der Platine! Gruß, Erik
Schau mal bei reichelt.de nach SMD Quarz. Die haben einige, wenn auch nicht die schönen unrunden für Zeitanwendungen. Du kannst Dir auch überlegen einen Real Time Clock Chip zu verwenden. Aber Quarz ist viel einfacher. Hier zeigt einer wie es geht: http://www.dumdididum.de/blackstrom/avr/avr_uhr/index.shtml (ka ob das Ding genau ist) Aber mach nur keinen Unfug, irgendwie könnte man ja auf die Idee kommen das hier ein Zünder gebaut werden soll... 3N
Zur Einstellung des Timers sollte etwa folgender Assemblercode gehen: init_timer: ldi r16,(1<<CS02)|(1<<CS00) ; Vorteiler: 1024 Takte out TCCR0,r16 ; Timer Counter Control Register ldi r16,(1<<TOIE0) ; Timer Overflow Interrupt Enable out TIMSK,r16 Das sorgt dafür, dass der Timer alle 1024 Takte gezählt wird. Dabei wird der Inhalt von TCNT0 erhöht. Wenn er von 255 erhöht wird, kommt es zu einem Überlauf. Der Zähler fängt wieder von 0 an und es wird der Timer Overflow Interrupt 0 ausgelöst. D.h., mit dieser Einstellung bekommst Du ca. 4 Interrupts pro Sekunde. Die entsprechende Routine muss in der Interrupttabelle eingetragen werden. Gewöhnlich hast Du die am Programmanfang stehen, in etwa so (Datenblatt Seite 57): .org 0 rjmp reset .org 6 rjmp timer0_overflow In der Routine kannst Du dann entsprechend reagieren und z.B. einen Zähler verändern etc. HTH Torsten
@Erik: RE: OSCCAL: Du programmierst den Timer so, als wäre der interne Oszillator genau auf Sollwert (=1MHz). Dann lässt du die Schaltung laufen und notierst die Abweichung. Läuft das Teil zu schnell, den Inhalt von OSCCAL erniedrigen, läuft es zu langsam, OSCCAL erhöhen. Jeder Controller wird im Werk ausgemessen und ein entsprechender Korrekturwert für 1MHz defaultmässig eingetragen. Nur - Lagerung, Temperatur und vom Werkstest abweichende Versorgungsspannung machen es nötig, da mal nachzugleichen. Der selbstermittelte Korrekturwert muss übrigens nach jeden Reset neu eingetragen werden. Siehe Seite 28 unten im Datenblatt. Gruss Jadeclaw.
Genauer wirds noch, wenn man statt des Timer overflow den Compare Match Interrupt des Couters1 nimmt. Dort kann man sich eine viertel Sekunde einstellen, und wenn der Compare Match auftritt, stellt man den Zähler auf Null zurück. 1 MHz interne Frequenz, Compare Match bei 244, Prescaler 1024. So wird die viertel Sekunde etwa 1024*244/1MHz = 0,2499 lang. Der Fehler liegt dann bei 0,06 % (Fehler des internen Oszillators nicht mitgerechnet). Also etwa 2 Sekunden pro Stunde.
So Leutle. Schonmal herzlichen Dank für die vielen Antworten. Ich bin jetzt schon mal einen Schritt weiter. @3 Newton: Naja, ein Zünder nicht gerade. Eher ne Lichtsteuerung für ein Aquarium. Klar könnte man das Aquarium auch sprengen ;-) @Torsten: Ich bin froh, dass mein Wissen über Assembler noch gereicht hat um deinen Code mit Abwandlung in C (s.unten) zu übertragen. @Jadeclaw: Danke für den Tip. Habe das im Datenblatt auch schon gesehen. Für die ersten Gehversuche werde ich es mal auf dem Default belassen. Wenn alles geht, gleiche ich an. So, jetzt mal zu meinen bisherigen Ergebnissen in C dank euch: 1 TCCR1A |= (1<<PWM1A); 2 TCCR1B |= (1<<CS13)|(1<<CS12)|(1<<CS10); 3 OCR1C = 244; 4 OCR1A = 0; 5 TIMSK |= (1<<OCIE1A); Zur Erläuterung: Mit (1) schalte ich den Timer in den PWM-Modus damit er beim Wert 244 (Zeile 3) wieder auf 0 springt und von vorne losläuft. (2) ergibt einen Vorteiler von CK/4096. (4) sollte der Wert sein, bei dem der Interrupt ausgelöst wird. (5) schaltet den Interrupt frei. In der Simulation sieht es soweit gut aus und meine Interruptroutine wird angesprungen. Nur weiß ich nicht genau, ob mein Vorgehen richtig ist. Zu meiner Rechnung: 1MHz/4096=244,1406250000Hz 1/244,1406250000Hz=0,0040960000 Sekunden Das bedeutet, der Timer wird alle 0,0040960000 Sekunden um 1 erhöht. Bei 244 wären das dann 0,0040960000 Sekunden * 244 = 0,9994240000 Sekunden Der Timer springt ab 244 auf 0, Interrupt wird ausgelöst und dass dann bei etwa 1 Sekunde, und das Spiel beginnt von vorne. Ist das soweit OK? Gruß, Erik
Naja net ganz. zu 1. Brauchste nicht, da du keine Ausgangspins mit PWM fütterst. 2. TCCR1B = (1<<CTC1)| + das was du schon da stehen hast 3. genau 4. Jap 5. So ists Denke so funzt es. Cool. Der Mega8 hat nur einen Prescaler bis 1024. Ist ja echt edel der Tiny ;-) Gruß
> Cool. Der Mega8 hat nur einen Prescaler bis 1024. Ist ja echt edel
der
Tiny ;-)
In der Tat, das habe ich garnicht gesehen. Der Timer0 hat nur einen
10-bit Prescaler. Man lernt nie aus :)
Super! Mein neuer Code: TCCR1B |= (1<<CTC1)|(1<<CS13)|(1<<CS12)|(1<<CS10); OCR1C = 244; OCR1A = 0; TIMSK |= (1<<OCIE1A); Jetzt scheints tatsächlich zu gehen. Habe CTC1 übersehen. Ohne springt er ja nicht auf 0 bei 244. Allerdings hat er das ohne CTC1 mit TCCR1A |= (1<<PWM1A); gemacht, weshalb ich dachte, es müsste mit rein. Also vielen Dank MicroMann für die kleine Korrektur. Dann kanns ja jetzt mit dem Projekt richtig los gehen. Vielen Dank euch allen! Gruß, Erik
Kacke : Hab doch noch Fehlerchen entdeckt : Wenn du den Zähler mit CTC1 zurücksetzt, tritt ein Overflow-Interrupt auf, kein Compare-Match. Also : alles wie gehabt, nur Overflow-Interrupt abfragen 5. TIMSK |= (1<<TOV1); (siehe Datasheet S 73 Abschnitt 'Timer Counter1 Output CompareRegister C OCR1C') Und nicht vergessen : Global Interrupts einschalten (SEI) Gruß
@MicroMann: Komisch, habe es in der Simulation versucht. Er macht den Compare-Match bei 0 wunderbar. Meine Interruptroutine für den Compare-Match wird angesprungen und abgearbeitet. Gruß, Erik
Na das ist ja merkwürdig. Vielleicht setzt er beide ? Dann würden beide Varianten funktionieren. Gruß
Aha, jetzt habe ichs. Er setzt nicht beide. Aber du prüfts den Compare Match mit OCR1A. D.h. Das Erreichen des Wertes in OCR1C löst einen Overflow-Interrupt aus. Der Wert in OCR1A = 0 löst dann den Compare Match aus. Effektiv ist es völlig Wurscht wie du es jetzt machst, da es noch nicht einmal eine Zeile Code spart (OCR1A=0 kannst du weglassen, weil default). Wenn du auf Timer overflow prüfst, rührst du halt die anderen OCR1 nicht an. Ist wohl nur ne Stil-Frage. Gruß
@MicroMann: Ja, du hattest recht! Es geht mit beiden Versionen. Ich benutze jetzt die mit Overflow, da es mir irgendwie logischer erscheint. Ist aber eher Geschmackssache. Nochmals Danke für die schnelle Hilfe. Werde mal sehen, wie das Endprodukt wird. Gruß,Erik
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.