Zeitfunktion

Gast #4656935
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.
Gast #4656939
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?
#4657033
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
Gast #4657082
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
}
Gast #4657227
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.
Gast #4657255
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, ....
Gast #4658006
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.
Persönliche Seite #4658059
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.
Gast #4658132
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.
Gast #4658380
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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren