Zeitfunktion

Gast #4656935
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
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
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
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
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
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
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
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
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 #4658171
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.
Gast #4658380
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.
Gast #4658690
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?
Gast #4659267
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...

Antwort schreiben

Eine eigene Frage? Dieser Thread ist über 10 Jahre alt. Stell sie in einem eigenen Thread – dort lesen mehr Leute mit, und sie wird öfter und schneller beantwortet.

Neuen Thread starten

Eine Lösung, Ergänzung oder Rückmeldung zu diesem Thread? Dann antworte hier.

Formatierung
  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] – Link zu einem Artikel
  • Verweis auf einen anderen Beitrag: Rechtsklick auf dessen Beitrags-ID (#8078628), „Adresse kopieren“, in den Text einfügen

Mehr zur Formatierung

Längeren Sourcecode als Anhang, nicht in den Text. JPG nur für Fotos und Scans; Zeichnungen und Screenshots als PNG oder GIF. Siehe Bildformate.

Schon registriert? Anmelden

Wird nicht angezeigt. Wir schicken dir einen Link; sobald du damit bestätigst, erscheint dein Beitrag.

Mit dem Abschicken legst du einen kostenlosen Account an und erkennst die Nutzungsbedingungen und die Datenschutzerklärung an.