Forum: Mikrocontroller und Digitale Elektronik Zeitfunktion


von Gaat12 (Gast)


Lesenswert?

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.

von Rolf M. (rmagnus)


Lesenswert?

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.

von Klaus 2. (klaus2m5)


Lesenswert?

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!

von Gaat12 (Gast)


Lesenswert?

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.

von Hosenmatz (Gast)


Lesenswert?

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?

von Klaus 2. (klaus2m5)


Lesenswert?

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
von Hosenmatz (Gast)


Lesenswert?

@ Klaus 2m5
Schön. Der TO hat nun einen Anhaltspunkt, worauf er achten muss.

von m.n. (Gast)


Lesenswert?

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
}

von Gaat12 (Gast)


Lesenswert?

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.

von m.n. (Gast)


Lesenswert?

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

von Gaat12 (Gast)


Lesenswert?

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.

von Einer K. (Gast)


Lesenswert?

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)

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

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.

von Gaat12 (Gast)


Lesenswert?

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.

von Einer K. (Gast)


Lesenswert?

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.

von m.n. (Gast)


Lesenswert?

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.

von m.n. (Gast)


Lesenswert?

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.

von Gaat12 (Gast)


Lesenswert?

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?

von Einer K. (Gast)


Lesenswert?

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