Forum: Mikrocontroller und Digitale Elektronik Interrupt jede Sekunde (ATTINY26)


von Erik (Gast)


Lesenswert?

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

von crazy horse (Gast)


Lesenswert?

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?

von Erik (Gast)


Lesenswert?

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

von Dennis Strehl (Gast)


Lesenswert?

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

von Jadeclaw D. (jadeclaw)


Lesenswert?

@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.

von Erik (Gast)


Lesenswert?

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

von 3 Newton (Gast)


Lesenswert?

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

von Torsten Landschoff (Gast)


Lesenswert?

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

von Jadeclaw D. (jadeclaw)


Lesenswert?

@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.

von MicroMann (Gast)


Lesenswert?

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.

von Erik (Gast)


Lesenswert?

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

von MicroMann (Gast)


Lesenswert?

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ß

von Torsten Landschoff (Gast)


Lesenswert?

> 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 :)

von Erik (Gast)


Lesenswert?

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

von MicroMann (Gast)


Lesenswert?

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ß

von Erik (Gast)


Lesenswert?

@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

von MicroMann (Gast)


Lesenswert?

Na das ist ja merkwürdig.

Vielleicht setzt er beide ?
Dann würden beide Varianten funktionieren.

Gruß

von MicroMann (Gast)


Lesenswert?

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ß

von Erik (Gast)


Lesenswert?

@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

von horst (Gast)


Lesenswert?

man seid ihr freaks .. sucht euch mal ne freundin

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
Noch kein Account? Hier anmelden.