Guten Morgen. Ich würde mir gerne eine Zeitfunktion für meinen Atmega erstellen. Mein Ansatz ist, dass ich mit Hilfe von overflow Interrupts jede milli sekunde einen Counter hochzähle. Ist das eine gängige Methode oder ist das zu ineffizient? Viele Grüße.
Gaat12 schrieb: > Mein Ansatz ist, dass ich mit Hilfe von overflow Interrupts jede milli > sekunde einen Counter hochzähle. Ist das eine gängige Methode oder ist > das zu ineffizient? Das ist eine gängige Methode. Der Ansatz ist genau der richtige.
Wenn Du eine eingermaßen genaue Millisekunde brauchst, musst Du wohl eher den CTC-Mode nutzen. Der relevante Interrupt ist dann der Compare Match, nicht der Overflow!
Rolf M. schrieb: > Das ist eine gängige Methode. Der Ansatz ist genau der richtige. Ok, sehr gut. Danke für die Antwort. Klaus 2. schrieb: > Wenn Du eine eingermaßen genaue Millisekunde brauchst, musst Du > wohl > eher den CTC-Mode nutzen. Der relevante Interrupt ist dann der Compare > Match, nicht der Overflow! Ja, ich hätte es schon gerne genau. Dann werde ich mir den CTC Modus mal anschauen.
Klaus 2. schrieb: > Wenn Du eine eingermaßen genaue Millisekunde brauchst, musst Du wohl > eher den CTC-Mode nutzen. Der relevante Interrupt ist dann der Compare > Match, nicht der Overflow! Magst Du das mal begründen? Warum oder unter welchen Voraussetzungen ist der CTC-Mode besser als der Overflow?
Mit CTC kann man den Divisor der Taktquelle (nach Prescaler) bestimmen. Beim Overflow im Normal-Mode ist er immer 256. Für gerade Takraten bräuchte man aber 250 (1,024ms statt 1 ms) und auch nur wenn es einen passenden Prescaler gibt. Nach dem Prescaler 250 kHz / 256 = 976,5625 Hz = 1,024 ms Es gibt aber nur Prescaler /8, (32 beim T2,) /64. Das passt dann nur für 2, (8 T2!) und 16 MHz Wenn es wirklich genau sein soll empfehle ich https://www.mikrocontroller.net/articles/AVR_-_Die_genaue_Sekunde_/_RTC
:
Bearbeitet durch User
Lass den Timer einfach durchlaufen und erzeuge per OCRx die gewünschten Intervalle per Interrupt. Beispiel für Timer1 mit 1 ms Intervall: #define MS_TEILER (F_CPU/1000) volatile uint32_t zeit; void init_timer1(void) { OCR1B = MS_TEILER; // erster Aufruf 1 ms nach init TIMSK1 |= BIT(OCIE1B); // ISR freigeben COMP1B TCCR1B = BIT(CS10); // Timer1 starten } ISR(TIMER1_COMPB_vect) // wird jede ms aufgerufen { OCR1B += MS_TEILER; // nachladen zeit++; // ms zählen }
Ich habe es jetzt mit dem CTC Modus umgesetzt und es funktioniert gut. Klaus 2. schrieb: > Wenn es wirklich genau sein soll empfehle ich > https://www.mikrocontroller.net/articles/AVR_-_Die... Eine minimale Abweichung ist nicht weiter schlimm. m.n. schrieb: > Lass den Timer einfach durchlaufen und erzeuge per OCRx die > gewünschten > Intervalle per Interrupt. Beispiel für Timer1 mit 1 ms Intervall: > > #define MS_TEILER (F_CPU/1000) > volatile uint32_t zeit; > > void init_timer1(void) > { > OCR1B = MS_TEILER; // erster Aufruf 1 ms nach init Den Schritt, den du im OCR1B Register ausführst verstehe ich nicht.
Gaat12 schrieb: >> OCR1B = MS_TEILER; // erster Aufruf 1 ms nach init > > Den Schritt, den du im OCR1B Register ausführst verstehe ich nicht. Nach einem Reset ist TCNT1 = 0. Setzt man OCR1B auf den betreffenden ms-Wert (bei 16 MHz wären es 16000) und startet den Timer1 ohne Vorteiler, wird der erste Interrupt nach 1 ms ausgelöst. In der ISR wird mit OCR1B += MS_TEILER der Vergleichswert von 16000 auf 32000 erhöht, was zur Folge hat, daß der nächste Interrupt wieder nach 1 ms ausglöst wird. Weiter geht es dann mit 48000, 64000 und - aufgepaßt - mit 80000-65536 = 14464, da beim 16 Bit Timer der Überlauf des Timers selbst und der des OCR1B-Registers nicht gewertet werden. Einfach ignorieren. Danach 30464, 46464, ....
m.n. schrieb: > Weiter geht es dann mit 48000, 64000 und - aufgepaßt - mit 80000-65536 = > 14464, da beim 16 Bit Timer der Überlauf des Timers selbst und der des > OCR1B-Registers nicht gewertet werden. Einfach ignorieren. > > Danach 30464, 46464, .... Das sehe ich aber nicht so. Werde es aber später mal ausprobieren.
Gaat12 schrieb: > Das sehe ich aber nicht so. Ich aber schon... Und damit steht es mindestens 2 gegen 1 (vielleicht ist es ja eins dieser Probleme, welche man demokratisch lösen kann)
Wenn der Tiner im CTC-Mode configuriert ist brauch man nicht merh daran zumzufutscheln. Beispiel für Tiner2 eines ATmega168:
1 | #include <avr/io.h> |
2 | |
3 | // Taktrate: 1MHz ist Werkseinstellung
|
4 | #define F_CPU 1000000
|
5 | |
6 | // IRQs pro Sekunde
|
7 | #define IRQS 1000
|
8 | |
9 | // Prescaler für Timer2.
|
10 | // Timer2 ist nur ein 8-Bit Zähler, d.h. OCR2A kann nur Werte
|
11 | // 0..255 aufnehmen, washalb der Takt-Vorteiler gebraucht wird.
|
12 | #define PRESCALE 8
|
13 | |
14 | static void timer2_init (void) |
15 | {
|
16 | // Mode #2 (CTC) und PRESCALE = 8
|
17 | TCCR2A = 1 << WGM21; |
18 | TCCR2B = 1 << CS21; |
19 | |
20 | // OutputCompare für gewünschte Timer2 Frequenz
|
21 | OCR2A = (uint32_t) F_CPU / PRESCALE / IRQS -1; |
22 | |
23 | // OutputCompare-Interrupt A für Timer 2
|
24 | TIMSK2 = 1 << OCIE2A; |
25 | }
|
26 | |
27 | |
28 | ISR (TIMER2_COMPA_vect) |
29 | {
|
30 | // ISR wird 1000x pro Sekunde aufgerufen.
|
31 | }
|
Und fertig. Timer 2 ist nur ein 8-Bit Zähler, d.h. der Wert für OCR2A muss zwischen 0 und 255 liegen, und die Division F_CPU / PRESCALE / IRQS muss ohne Rest aufgehen. Wenn man mag, kann man das auch noch extra abtesten:
1 | // Testet Plausibilität der Einstellungen für
|
2 | // F_CPU, PRESCALE und IRQS
|
3 | |
4 | #if F_CPU / PRESCALE / IRQS -1 < 1 \
|
5 | || F_CPU / PRESCALE / IRQS -1 > 0xff
|
6 | #error Timer2 schafft das nicht!
|
7 | #error Nimm andere Werte fuer F_CPU, PRESCALE, IRQS.
|
8 | #endif
|
9 | |
10 | #if (F_CPU % (PRESCALE * IRQS)) != 0
|
11 | #warning Timer2 arbeitet ungenau
|
12 | #endif
|
13 | |
14 | #if (IRQS % 100) != 0
|
15 | # warning Mit diesem Wert fuer IRQS sind 10ms nich exakt realisierbar.
|
16 | # warning Nimm ein Vielfaches von 100.
|
17 | #endif
|
Zum Entprellen von Tasten eignet sich z.B. ein Raster von 10ms, das man dann per Zähler in der ISR realisieren kann:
1 | // Diese ISR wird 1000x pro Sekunde aufgerufen.
|
2 | |
3 | ISR (TIMER2_COMPA_vect) |
4 | {
|
5 | static uint8_t irqs_10ms; |
6 | |
7 | wdt_reset(); |
8 | |
9 | ////////////////////////////////////////////////////////////
|
10 | // 10 ms-Takt für Jobs: countdown-Zähler, Tasten-Entprellung, DCF, ...
|
11 | irqs_10ms = 1 + irqs_10ms; |
12 | |
13 | // Sind 10 Millisekunden voll?
|
14 | // IRQS wird oben definiert, es gibt die Anzahl der IRQs pro Sekunde an.
|
15 | // Um 10ms zu erhalten, teilen wir also durch 100
|
16 | if (irqs_10ms >= IRQS / 100) |
17 | irqs_10ms = 0; |
18 | |
19 | // Führe nicht alle Jobs gleichzeitig aus, damit eine ISR nicht
|
20 | // zu lange dauert. Stattdessen staffeln wir die Aufrufe: es gibt
|
21 | // nur einen je 10ms, aber immer einen anderen (oder garkeinen).
|
22 | |
23 | if (0 == irqs_10ms) job_countdown(); |
24 | // else if (1 == irqs_10ms) mach_was_alle_10ms();
|
25 | // else if (2 == irqs_10ms) mach_nochwas_alle_10ms();
|
26 | }
|
Je nach Anwendung kommt man natürlich auch ohne einen soft-Zähler in der ISR aus, so hat man bei einem 16-Bit Zähler wie Timer1 weniger Einschränkungen was die Werte angeht.
U. F. schrieb: > Gaat12 schrieb: >> Das sehe ich aber nicht so. > Ich aber schon... > Und damit steht es mindestens 2 gegen 1 > > (vielleicht ist es ja eins dieser Probleme, welche man demokratisch > lösen kann) Ich hab den Code von m.n. jetzt mal mit meinen verglichen. Und ich erhalte unterschiedliche Zeiten. Vielleicht habe ich auch irgendwo einen Fehler.... Aber dann verstehe ich noch immer nicht, was nach dem erreichen des Wertes 64.000 passiert.
Gaat12 schrieb: > Aber dann verstehe ich noch immer nicht, was nach dem erreichen des > Wertes 64.000 passiert. Ein Überlauf, bei der nächsten Addition. Ist nichts schlimmes, muss man nur wissen. Und hier tuts das ganz prächtig.
Johann L. schrieb: > Wenn der Tiner im CTC-Mode configuriert ist brauch man nicht merh daran > zumzufutscheln. Und alle weiteren Funktionen des Timers sind auch wegzumzugefutschelt ;-) Der TO möchte einen ms-Zähler inkrementieren, da ist das Nachladen eines Compare-Registers doch nebenbei schnell erledigt. Weitere Compare-Register können immer noch für andere Funktionen genutzt werden und werden durch den CTC-Modus nicht belegt oder die Funktionen nicht gestört.
Ich zeige noch einmal Beispiele. http://mino-elektronik.de/Generator/takte_impulse.htm#bsp3a Hier wird Timer1 verwendet, um unabhängig voneinander 2 x Schrittmotore anzusteueren. http://mino-elektronik.de/Generator/takte_impulse.htm#bsp5 Ein 8-Bit Timer (Timer1 beim ATtiny25) liefert 50 ns genaues Timing auch für lange Zeiten.
U. F. schrieb: > Ein Überlauf, bei der nächsten Addition. > Ist nichts schlimmes, muss man nur wissen. > Und hier tuts das ganz prächtig. Das ein Überlauf auftritt ist mir klar. Aber dann passt doch der spätere Wert nicht mehr oder doch?
Doch, der passt! Der Additionsüberlauf ist ja exakt der gleiche, wie der Zählerüberlauf. Gleich, identisch, synchron... wie auch immer... Die neutralisieren sich...
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.