Timer2 Frage

Hallo,
ich schlage mich rein experimentel mal mit den Timern rum.

Ich wollte über Timer 2 alle 1/10tel Sekunde einen Interrupt habe.
Aber alles möglich was mir die diversen Rechnungen mit Prescalern und Co so rauswarfen
lieferten nicht das richtige Ergebnis.
Also erst mal einfacher : Jede Millisekunde ein Int.
Ausgerechnet und auch ein Beispielprogramm gefunden das die selben Werte benutzte.
Daraus entstand Folgendes :

#include <LiquidCrystal_I2C.h>
#include <Wire.h>
#include <Arduino.h>
#include <avr/io.h>
#include <avr/interrupt.h>

#define I2C_ADDRESS 0x27
LiquidCrystal_I2C lcd(I2C_ADDRESS, 2, 1, 0, 4, 5, 6, 7,3,POSITIVE); // Neuer Adapter
//LiquidCrystal_I2C lcd(I2C_ADDRESS, 4, 5, 6, 0, 1, 2, 3, 7, NEGATIVE);

volatile uint16_t intCnt = 0;

void setup()
{
  lcd.begin(16,2);
  lcd.print("TIMERTESTER V1");
  pinMode(7,OUTPUT); 
  digitalWrite(7,LOW);
  cli();
  

  TCCR2A &= ~((1<<WGM21) | (1<<WGM20)); // Prescaler auf 128
  TCCR2B &= ~(1<<WGM22);
  
  TIMSK2 &= ~(1<<OCIE2A);
  
  // Overflow Interrupt erlauben

  TIMSK2|=(1<<TOIE2);
  TCNT2 = 131;
  sei();
}

volatile uint32_t secs2 = 0;

ISR (TIMER2_OVF_vect)
{
 
  intCnt++;
  
  TCNT2 = 131;
  
  //digitalWrite(7,!digitalRead(7));
  // Interrupt Aktion alle
  //(1000000/8)/256 Hz = 488,28125 Hz
  //bzw.
  //1/488,28125 s = 2,048 ms  
}


uint32_t oldTime1 = 0;
uint32_t secs1    = 0;
uint8_t Buffer[16];
void loop()
{
  if ((millis() - oldTime1) >= 1000)
  {
    oldTime1 = millis();
    lcd.setCursor(0,1);
    secs1++;
    sprintf((char *) Buffer,"T1:%04ld",secs1);
    lcd.print((char *) Buffer);
  }
  if (intCnt >= 1000) 
  {
    secs2++;
    intCnt = 0;
    lcd.setCursor(8,1);
    sprintf((char *) Buffer,"T2:%04ld",secs2);
    lcd.print((char *) Buffer);
  }
}

Aber das Ergebnis ist das der Zähler in dem Print bei T2 doppelt so schnell läuft wie
der den ich über "millis" update.

Wo ist mein Fehler.
Ich gehe schwer davon aus da mein Standard UNO mit 16Mhz läuft.

Ulli

1.) Du musst die Timer Register erst mal auf 0 setzen. In der Arduino Software sind die teilweise durch PWM vorbesetzt. Jedensfalls was Modus und Precaler betrifft

2.) Danach setzt du die betreffenden Bits mit einem Oder. Du löscht ein paar Bits, was unsinnig ist.

Ich glaube ich habe es erst mal für dieses Beispiel.
Einen Prescaler von 128 gibt es beim 328 und Timer2 gar nicht :slight_smile:
Was ich da eingestellt habe ist ein Prescaler von 64 und wenn ich dann
wie der Calculator das auch sagt 6 vorlade dann stimmt's.

Die Zähler laufen zwar langsam auseinander, aber ich nehme an das liegt daran
das millis nicht unbeding 100% genau ist und mein Uno ja auch mit Resonator
und nicht mit Quartz arbeitet.

Ulli

Jetzt habe ich mal den Prescaler auf 64 und den Preload auf 231.
Das soll 10 KHz Interruptfrequenz ergeben und das ist auch so.
Jetzt laufen sogar beide Counter supersyncron. (Warum das besser als bei 1Khz ist
sehe ich auch nicht da bei beiden Werten 0% Fehler im Timer vorliegen - rechnerisch).

Nochmal der aktuelle Code :

#include <LiquidCrystal_I2C.h>
#include <Wire.h>
#include <Arduino.h>
#include <avr/io.h>
#include <avr/interrupt.h>

#define I2C_ADDRESS 0x27
LiquidCrystal_I2C lcd(I2C_ADDRESS, 2, 1, 0, 4, 5, 6, 7,3,POSITIVE); // Neuer Adapter
//LiquidCrystal_I2C lcd(I2C_ADDRESS, 4, 5, 6, 0, 1, 2, 3, 7, NEGATIVE);

volatile uint16_t intCnt = 0;

void setup()
{
  lcd.begin(16,2);
  lcd.print("TIMERTESTER V1");
  pinMode(7,OUTPUT); 
  digitalWrite(7,LOW);
  cli();
  

  TCCR2A = (1 << CS22) | (1 << CS21);
  
 // TCCR2A &= ~((1<<WGM21) | (1<<WGM20)); // Prescaler auf 128
 // TCCR2B &= ~(1<<WGM22);
  
  TIMSK2 &= ~(1<<OCIE2A);
  
  // Overflow Interrupt erlauben

  TIMSK2|=(1<<TOIE2);
  TCNT2 = 231;
  sei();
}

volatile uint32_t secs2 = 0;
         uint32_t oldSecs2 = 0;

ISR (TIMER2_OVF_vect)
{
 
  intCnt++;
  
  TCNT2 = 231;
  
  //digitalWrite(7,!digitalRead(7));
  // Interrupt Aktion alle
  //(1000000/8)/256 Hz = 488,28125 Hz
  //bzw.
  //1/488,28125 s = 2,048 ms  
}


uint32_t oldTime1 = 0;
uint32_t secs1    = 0;
uint8_t Buffer[16];
void loop()
{
  if ((millis() - oldTime1) >= 1000)
  {
    oldTime1 = millis();
    lcd.setCursor(0,1);
    secs1++;
    sprintf((char *) Buffer,"T1:%04ld",secs1);
    lcd.print((char *) Buffer);
  }
  if (secs2 != oldSecs2)
  {
    oldSecs2 = secs2;
    lcd.setCursor(8,1);
    sprintf((char *) Buffer,"T2:%04ld",secs2);
    lcd.print((char *) Buffer);
  }
  if (intCnt >= 10000) 
  {
    secs2++;
    intCnt = 0;
      lcd.setCursor(8,1);
    sprintf((char *) Buffer,"T2:%04ld",secs2);
    lcd.print((char *) Buffer);
  }
}

Wenn ich mir so die Bedeutungen der Register angucke stellt sich mir die Frage :
Kann ich eigentlich, je nach Modus, auch die PWMs des Timers weiter nutzen ?

Gibt es eigentlich ein schönes Tutorial (habe schon einige gefunden) die auch die
Gemeinheiten der Arduino Libs berücksichtigt und wo die Fallen liegen wenn man sie
direkt programmiert ?

Ulli

Auch Irre. Das Beispiel oben funktioniert aber das Register für den Prescaler
war falsch :slight_smile: . Es muss statt TCCR2A TCCR2B sein.

Hatte mich gewundert das bei Änderungen nix passierte.

Erstaunlich finde ich ja folgendes :
Ich wollte eine Interruptzeit von 100µs erreichen.

Das klappt laut Rechner mit 0% Fehler mit 3 Prescalern :

8 bei einem Offset oder besser Preload von 56
32 mit Preload 206
und
65 mit Preload 231

Variante 2 und 3 laufen (länger als 20 Minuten habe ich nicht kontrolliert) sycron mit
dem der millis-Variante.
Option 1, das ist die mit den häufigsten Interrupts driftet nach 3 Minuten schon um 4 Sekunden
weg. Schneller als die millis-Variante.
Kann mir das einer erklären oder hat mein Prescalcalculator einen Fehler ?

Ulli

Hallo,

millis wird auch "nur" durch Timer Interrupts gezählt. Sollte also snycron sein. Wenn nicht, dann stören sich die Interrupts gegenseitig zu lange, so meine Vermutung. Anders kann ich es mir im Moment nicht erklären. Was du noch nicht beachtet hast ist, alle Timer Register vorher zu reseten, sprich zu Nullen. Du hast sonst keine Kontrolle darüber welcher Modus usw. wirklich derzeit eingestellt sind. Gründe hat Serenifly schon geschrieben. Dein Timer 2 soll wohl im Normal Mode laufen. Ob er das wirklich macht bezweifel ich.
Bei deinen Varianten blicke ich eh nicht durch, weil nicht klar dokumentiert. Wenn Du PWM nutzen möchtest, dann musst du auch einen PWM Mode einstellen. Zwei Modi parallel geht nicht.
Und um deinen milli Counter sauber auszulesen, musste noch atomic verwenden. Sonst wird es in dem Moment Fehlerhaft wo du es nicht vermutest.
Nochwas. Wie kommst du auf einen "PreLoad" 231? Nimm bitte nur die Formeln aus dem Datenblatt. Nichts anderes. Die sind bei jedem Timer Modi etwas verschieden. Ich vermute aber das hast du nachträglich angeglichen, weil der Modi nicht stimmt.

Hallo Ulli,
die Ungenauigkeiten bei denen Varianten entstehen, weil Du den Timerwert ( TCNT2 ) in der Interruptroutine veränderst. Das funktioniert nie 100%tig exakt, weil deine Interruptroutine auch mal verzögert ausgeführt werden kann, weil gerade ein anderer Interrupt dazwischenfunkt. Ausserdem musst Du die Zeit berücksichtigen zwischen dem Interruptereignis, und dem Ändern des Zählers. Deshalb kommst Du immer auf so krumme Werte und kannst es nicht genau berechnen. Je kürzer deine Interruptintervalle sind, umso größer ist die Ungenauigkeit.

Wenn Du es exakt haben willst, darfst Du den Timerwert nie per Software ändern, sondern musst alles per Timerkonfiguration einstellen, und dann den Timer sozusagen sich selbst überlassen. Der Interrupt dient dann nur noch dazu, deinen Zähler intCnt hochzuzählen.

Franz-Peter

P.S. Wenn Du die Timerregister direkt mit den Werten beschreibst, die den gewünschten Modus einstellen, musst Du sie auch nicht vorher 'Nullen'. Du musst dann aber eine direkte Zuweisung machen (keine Bitmanipulation), und auch alle notwendigen Controlregister einstellen ( auch die, die tatsächlich Null sein müssen).

Verwende statt dem Overflow Interrupt den CTC Modus (Clear Timer on Compare Match). Dann muss man das Zähl-Register nicht anfassen. Er fängt immer an bei 0 zu zählen und wird automatisch zurückgesetzt wenn ein eingestellter Vergleichswert erreicht wird.

Vielen Dank für die Tips.
Den CTC Mode wollte ich morgen sowieso mal ausprobieren.

Ich habe das Ganze noch mal synchron mit Timer 2 probiert

#include <LiquidCrystal_I2C.h>
#include <Wire.h>
#include <Arduino.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/atomic.h>

#define I2C_ADDRESS 0x20
// LiquidCrystal_I2C lcd(I2C_ADDRESS, 2, 1, 0, 4, 5, 6, 7,3,POSITIVE); // Neuer Adapter
LiquidCrystal_I2C lcd(I2C_ADDRESS, 4, 5, 6, 0, 1, 2, 3, 7, NEGATIVE);

volatile uint32_t intCnt1 = 0;
volatile uint32_t intCnt2 = 0;

uint32_t oldTime1 = 0;
void setup()
{
  lcd.begin(16,2);
  lcd.print("TTEST 1");
  pinMode(7,OUTPUT); 
  digitalWrite(7,LOW);
  cli();
  
  TCCR2A = 0; // RESET TIMER 2 
  TCCR2B = 0;

  TCCR2B = bit(CS22);          // Prescaler 64
  // TCCR2B = bit(CS21) | bit(CS20); // Prescaler 32
  // TCCR2B = bit(CS21);          // Prescaler  8

  // Overflow Interrupt erlauben

  TIMSK2= bit(TOIE2);

  TCNT2 = 231; // Fuer Prescaler 64
  // TCNT2 = 206; // Fuer Prescaler 32
  // TCNT2 = 56;
  
  // Nochmal für Timer 1
 
 
  TCCR1A = 0; // RESET TIMER 2 
  TCCR1B = 0;

  TCCR1B = bit(CS11) | bit(CS10);          // Prescaler 64
  // TCCR2B = bit(CS21) | bit(CS20); // Prescaler 32
  // TCCR2B = bit(CS21);          // Prescaler  8

  // Overflow Interrupt erlauben

  TIMSK1= bit(TOIE1);

  TCNT1 = 65511; // Fuer Prescaler 64
  // TCNT2 = 206; // Fuer Prescaler 32
  // TCNT2 = 56;
  
  sei();
  oldTime1 = millis();
}


ISR (TIMER2_OVF_vect)
{
  TCNT2 = 231; // Fuer Prescaler 64
  // TCNT2 = 206; // Fuer Prescaler 32
  // TCNT2 = 56;
 
  intCnt2++;
  

  //digitalWrite(7,!digitalRead(7));
  // Interrupt Aktion alle
  //(1000000/8)/256 Hz = 488,28125 Hz
  //bzw.
  //1/488,28125 s = 2,048 ms  
}

//------------------------------------------------------------------

ISR (TIMER1_OVF_vect)
{
  TCNT1 = 65511; // Fuer Prescaler 64
  // TCNT2 = 206; // Fuer Prescaler 32
  // TCNT2 = 56;
 
  intCnt1++;
  

  //digitalWrite(7,!digitalRead(7));
  // Interrupt Aktion alle
  //(1000000/8)/256 Hz = 488,28125 Hz
  //bzw.
  //1/488,28125 s = 2,048 ms  
}


uint32_t secs0    = 0;
uint32_t oldSecs0 = 0;
uint32_t secs1    = 0;
uint32_t oldSecs1 = 0;
uint32_t secs2    = 0;
uint32_t oldSecs2 = 0;

uint8_t Buffer[16];
void loop()
{
  if ((millis() - oldTime1) >= 1000)
  {
    oldTime1 = millis();
    secs0++;
  }  
  if (secs0 != oldSecs0)
  {
    oldSecs0 = secs0;
    digitalWrite(7,!digitalRead(7));
    lcd.setCursor(8,0);
    sprintf((char *) Buffer,"T1:%04ld",secs0);
    lcd.print((char *) Buffer);
  }
  
  if (secs2 != oldSecs2)
  {
    oldSecs2 = secs2;
    lcd.setCursor(0,1);
    sprintf((char *) Buffer,"T2:%04ld",secs2);
    lcd.print((char *) Buffer);
  }
  
  if (secs1 != oldSecs1)
  {
    oldSecs1 = secs1;
    lcd.setCursor(8,1);
    sprintf((char *) Buffer,"T3:%04ld",secs1);
    lcd.print((char *) Buffer);
  }

  
  if (intCnt2 >= 10000) 
  {
    secs2++;
    intCnt2 = 0;
  }
  
  if (intCnt1 >= 10000) 
  {
    secs1++;
    intCnt1 = 0;
  }
}

Da ergibt sich das gleiche Bild. Meine beiden eigenen Timer mit 1 und 2 laufen immer
syncron, der mit millis läuft nach einer Stunde weg (um eine Sekunde).
Probiere morgen mal den CTC Mode.

Erstaunlich finde ich das das selbe Programm auf einem Pro Mini das gleich verhalten zeigt, aber
dort alle Timer nach knapp einer Stunde verglichen mit dem UNO etwas über eine Sekunde
hinterherhinken. Ich denke das ist den Resonatoren geschuldet, oder !?

Ach so, die Preloads habe ich aus dem Programm AVR-Timercalculator 1.2. !
Ulli

Hallo,

hast du einen Link zum AVR-Timercalculator 1.2? Die Suche ergibt zu viele verschiedene Treffer.

Abweichungen unter verschiedenen Boards sind normal, eben auf Grund der Resonatoren, die nicht so genau takten wie ein Quarz. Stabil takten die schon, keine Frage. Nur eben nicht genau auf Soll-Frequenz.

Der Zugriff auf volatile Variablen außerhalb der ISR wo sie verändert werden sollten mit atomic gemacht werden. Hatte ich schon erwähnt.

beeblebrox:
Den CTC Mode wollte ich morgen sowieso mal ausprobieren.

Du kannst auch den fast PWM Mode nehmen, da funktioniert auch dein Overflow-Interrupt.
Mal auf die Schnelle etwas 'schmutzig' die Controlregister:

  TCCR2A = 0b00000011;  // Fast PWMMode
  TCCR2B = 0b00001101;  // Prescaler = 64 -> 8µsec pro Tic
  OCR2A = 124;          // 125 tics -> 1000µsec

Dann musst Du nur noch dein TCNT2 = 131; in der ISR löschen

Dass deine Messung mit millis() und dem Timer auseinanderläuft liegt daran, dass deine millis() Variante nicht exakt ist: Wenn Du genau 1ms-Schritte haben willst, darfst Du deine oldTime1 Variable nicht neu mit einem millis() Wert laden, sondern Du musst sie immer exakt um 1000 vergrößern ( oldTime1 += 1000; ).

Ausserdem ist der Hinweis von Doc_Arduino wichtig, die Auswertung von inCnt atomar zu machen. D.h. dass da kein IRQ dazwischenfunken darf.

Ich hab' deine Variante mal etwas angepasst:

#include <LiquidCrystal_I2C.h>
#include <Wire.h>
#include <Arduino.h>
#include <avr/io.h>
#include <avr/interrupt.h>

#define I2C_ADDRESS 0x27
LiquidCrystal_I2C lcd(0x3f,16,2); // Neuer Adapter
//LiquidCrystal_I2C lcd(I2C_ADDRESS, 4, 5, 6, 0, 1, 2, 3, 7, NEGATIVE);

volatile uint16_t intCnt = 0;
uint32_t oldTime1 = 0;
uint32_t secs1    = 0;
uint32_t secs2 = 0;
uint8_t Buffer[16];

void setup()
{
  lcd.init();
  delay(500);
  lcd.backlight();
  delay(1000);
  
  lcd.print("TIMERTESTER V1");
  pinMode(7,OUTPUT);
  digitalWrite(7,LOW);
  cli();
 

  TCCR2A = 0b00000011;  // Fast PWMMode
  TCCR2B = 0b00001101;  // Prescaler = 64 -> 8µsec pro Tic
  OCR2A = 124;          // 125 tics -> 1000µsec

  TIMSK2 &= ~(1<<OCIE2A);
 
  // Overflow Interrupt erlauben

  TIMSK2|=(1<<TOIE2);
  TCNT2 = 131;
  sei();
  oldTime1 = millis();
}


ISR (TIMER2_OVF_vect)
{
 
  intCnt++;
 
  //TCNT2 = 131;
 
  //digitalWrite(7,!digitalRead(7));
  // Interrupt Aktion alle
  //(1000000/8)/256 Hz = 488,28125 Hz
  //bzw.
  //1/488,28125 s = 2,048 ms 
}


void loop()
{

  if ((millis() - oldTime1) >= 1000)
  {
    oldTime1 += 1000;
    lcd.setCursor(0,1);
    secs1++;
    sprintf((char *) Buffer,"T1:%04ld",secs1);
    lcd.print((char *) Buffer);
  }
  cli();
  if (intCnt >= 1000)
  {
    intCnt = 0;
    sei();
    secs2++;
    lcd.setCursor(8,1);
    sprintf((char *) Buffer,"T2:%04ld",secs2);
    lcd.print((char *) Buffer);
  }
  sei();
}

Da laufen jetzt beide Zähler synchron.
(Ich musste die LCD-Befehle etwas anpassen, da ich eine ander Lib verwende)

Hallo,

da muss ich leider nochmal korrigieren. Jede Interrupt Unterbrechung sollte so kurz sein wie nur möglich. Das bedeutet hier, Interrupt unterbrechen, volatile Variable auslesen in andere Variable, Interrupt freigeben. Mehr nicht. Ganz richtig wird es mit SREG Sicherung. Dafür gibts eine fertige Funktion. SREG ist ein Statusregister für die Interruptsteuerung im µC.

#include <util/atomic.h>    

...
...
...

ATOMIC_BLOCK (ATOMIC_RESTORESTATE) {    // cli()+sei() mit SREG Sicherung
  var = intCnt;                         // volatile Variable auslesen bzw. sichern 
}
... weiter mit var ...

OK, kann man so machen. Ist in dem Beispiel aber absolut unnötig. Ausserdem sollte auch das intCnt=0; geschützt sein.

Doc_Arduino:
SREG ist ein Statusregister für die Interruptsteuerung im µC.

Na ja, der Interruptsteuerung dient da genau 1 Bit, das mit sei() und cli() beeinflusst wird. Alle anderen Bits haben mit Interrupts nichts zu tun.

Hallo,

das sollte nur ein Bsp. sein wie man generell anwendet.
Der atmic Block schaltet die Interrupts an der Stelle ganz sicher erstmal aus. Ja.
Aber er bringt das Register danach wieder in genau den Zustand in dem vorher war.
Die Interrupts können vorher auch schon abgeschalten gewesen sein.
Da möchte man diese nicht unkontrolliert einfach so wieder einschalten.
Deshalb kann ich diese Methode nur weiterempfehlen.

So ich habe jetzt bis auf die Umstellung auf CTC alles eingebaut.
Es hat sich nix geändert. Ich denke auch das es nur der falsche Mode für eine Zeitmessung
ist. Dafür gibt es, denke ich, den CTC-Mode.
So ist alles wie vorher. Ich mache sowenig im Int ich glaube nicht das da wirklich Ticks verloren
gehen. Ich denke das ist ein systematischer Fehler weil ich einfach mit dem falschen Modus arbeite.
Aber so war das Projekt ja auch gedacht. Ich wollte einfach Erfahrung mit den ATMEGA Timern
sammeln.

Code jetzt :

#include <LiquidCrystal_I2C.h>
#include <Wire.h>
#include <Arduino.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/atomic.h>

#define I2C_ADDRESS 0x27
LiquidCrystal_I2C lcd(I2C_ADDRESS, 2, 1, 0, 4, 5, 6, 7,3,POSITIVE); // Neuer Adapter
// LiquidCrystal_I2C lcd(I2C_ADDRESS, 4, 5, 6, 0, 1, 2, 3, 7, NEGATIVE);

volatile uint32_t intCnt1 = 0;
volatile uint32_t intCnt2 = 0;

uint32_t oldTime1 = 0;
void setup()
{
  lcd.begin(16,2);
  lcd.print("TTEST 1");
  pinMode(7,OUTPUT); 
  digitalWrite(7,LOW);
  cli();
  
  TCCR2A = 0; // RESET TIMER 2 
  TCCR2B = 0;

  TCCR2B = bit(CS22);          // Prescaler 64
  // TCCR2B = bit(CS21) | bit(CS20); // Prescaler 32
  // TCCR2B = bit(CS21);          // Prescaler  8

  // Overflow Interrupt erlauben

  TIMSK2= bit(TOIE2);

  TCNT2 = 231; // Fuer Prescaler 64
  // TCNT2 = 206; // Fuer Prescaler 32
  // TCNT2 = 56;
  
  // Nochmal für Timer 1
 
 
  TCCR1A = 0; // RESET TIMER 2 
  TCCR1B = 0;

  TCCR1B = bit(CS11) | bit(CS10);          // Prescaler 64
  // TCCR2B = bit(CS21) | bit(CS20); // Prescaler 32
  // TCCR2B = bit(CS21);          // Prescaler  8

  // Overflow Interrupt erlauben

  TIMSK1= bit(TOIE1);

  TCNT1 = 65511; // Fuer Prescaler 64
  // TCNT2 = 206; // Fuer Prescaler 32
  // TCNT2 = 56;
  
  sei();
  oldTime1 = millis();
}


ISR (TIMER2_OVF_vect)
{
  TCNT2 = 231; // Fuer Prescaler 64
  // TCNT2 = 206; // Fuer Prescaler 32
  // TCNT2 = 56;
 
  intCnt2++;
  

  //digitalWrite(7,!digitalRead(7));
  // Interrupt Aktion alle
  //(1000000/8)/256 Hz = 488,28125 Hz
  //bzw.
  //1/488,28125 s = 2,048 ms  
}

//------------------------------------------------------------------

ISR (TIMER1_OVF_vect)
{
  TCNT1 = 65511; // Fuer Prescaler 64
  // TCNT2 = 206; // Fuer Prescaler 32
  // TCNT2 = 56;
 
  intCnt1++;
  

  //digitalWrite(7,!digitalRead(7));
  // Interrupt Aktion alle
  //(1000000/8)/256 Hz = 488,28125 Hz
  //bzw.
  //1/488,28125 s = 2,048 ms  
}


uint32_t secs0    = 0;
uint32_t oldSecs0 = 0;
uint32_t secs1    = 0;
uint32_t oldSecs1 = 0;
uint32_t secs2    = 0;
uint32_t oldSecs2 = 0;
uint32_t var      = 0;
uint8_t Buffer[16];

void loop()
{
  if ((millis() - oldTime1) >= 1000)
  {
    oldTime1 = oldTime1 + 1000;
    secs0++;
  }  
  if (secs0 != oldSecs0)
  {
    oldSecs0 = secs0;
    digitalWrite(7,!digitalRead(7));
    lcd.setCursor(8,0);
    sprintf((char *) Buffer,"T1:%04ld",secs0);
    lcd.print((char *) Buffer);
  }
  
  if (secs2 != oldSecs2)
  {
    oldSecs2 = secs2;
    lcd.setCursor(0,1);
    sprintf((char *) Buffer,"T2:%04ld",secs2);
    lcd.print((char *) Buffer);
  }
  
  if (secs1 != oldSecs1)
  {
    oldSecs1 = secs1;
    lcd.setCursor(8,1);
    sprintf((char *) Buffer,"T3:%04ld",secs1);
    lcd.print((char *) Buffer);
  }

  ATOMIC_BLOCK (ATOMIC_RESTORESTATE)
  {    // cli()+sei() mit SREG Sicherung
    var = intCnt2;                         // volatile Variable auslesen bzw. sichern
  }
  
  if (var >= 10000) 
  {
    secs2++;
    ATOMIC_BLOCK (ATOMIC_RESTORESTATE)
    {
      intCnt2 = 0;
    }
  }
    
  ATOMIC_BLOCK (ATOMIC_RESTORESTATE)
  {    // cli()+sei() mit SREG Sicherung
    var = intCnt1;                         // volatile Variable auslesen bzw. sichern
  }
  
  if (var >= 10000) 
  {
    secs1++;
    ATOMIC_BLOCK (ATOMIC_RESTORESTATE)
    {
      intCnt1 = 0;
    }
  }
}

Der Calculator stammt von hier :

Ulli

Hallo,

der Timer Calculator ist leider etwas nichts sagend. Man weiß nicht für welchen Timer-Mode er berechnet.
Ich würde den ganz schnell vergessen und mir an deiner Stelle eine Exceltabelle anlegen für jeden Timer-Mode.
Die Formeln stehen im Datenblatt.

Wie du auf die 131 kommst weiß ich allerdings jetzt. Ich nehme es an. Du verwechselst bestimmt die Periodendauer einer Frequenz mit der Aufruf-Frequenz der ISR. Diese ist nämlich doppelt so schnell. Halbe Periodendauer = doppelte Frequenz. Also wenn du in der OVF_ISR ein 1ms Intervall haben möchtest, dann musst du mit 500Hz rechnen.

Dann komme ich auf einen errechneten Wert von 249 mit Prescaler 64. Von 255 abgezogen erhalte ich 6 zum TimerCounter vorladen. Eine Formel steht dazu leider nicht im Datenblatt. Textaufgabe. :wink:

Jedenfalls habe ich das mal in Code gefasst. Kannst das ja mal Langzeit testen ob beide Werte wegdriften. Normalerweise dürfte das nicht abdriften.

/*
 *  Doc_Arduino - german Arduino Forum
 *  IDE 1.6.13
 *  Arduino Mega2560
 *  19.04.2017
 * 
 *  Timer 2 - Mode 0 - Normal-Mode mit aktivierten Overflow Interrupt
 * 
 *  Takt = 500Hz
 *  Periodendauer 2ms
 *  Overflow Interrupt wird aller 1ms aufgerufen
 */

#include <util/atomic.h> 

volatile unsigned long count_Timer2;
unsigned long count_millis;


void setup() {

  Serial.begin(9600);
       
  set_Timer2();   // Timer 2 Setting

} 

 
void loop() {

  ATOMIC_BLOCK (ATOMIC_RESTORESTATE) {    // cli()+sei() mit SREG Sicherung
                                          // volatile Variable auslesen bzw. sichern
    count_millis = count_Timer2;
  }

  serieller_Monitor();
}


// *** Funktionen *** //

ISR(TIMER2_OVF_vect)     // Overflow Interrupt Service Routine
{
  TCNT2 = 6;             // Timer Counter vorladen (255-249)
  count_Timer2++;
} 


void set_Timer2()  // Timer 1 Normal-Mode mit OVF enable
{
  cli();
  TCCR2A = 0;     // Reset TCCR1A Register
  TCCR2B = 0;     // Reset TCCR1B Register
  TIMSK2 = 0;     // Reset Interrupts
  TCNT2  = 6;     // Timer Counter vorladen
  TIMSK2 = (1<<TOIE2);  // Timer Overflow Interrupt enable
  TCCR2B = (1<<CS22);   // Prescaler 64 und Timer starten
  sei();
}


void serieller_Monitor ()
{
  unsigned long _milli = millis();
 
  static unsigned int intervall = 1000;            // Ausgabeintervall [ms]
  static unsigned long last_millis = 0;

  if ( _milli - last_millis > intervall )  {    // aller x [ms] frische Daten
    last_millis += 1000;
    Ueberschriftszeile();
    Serial.print(count_millis);  Serial.print('\t');
    Serial.println(_milli);
  }
}


void Ueberschriftszeile ()
{
  static int counter = 33;

  counter++;
 
  if (counter<25) return; // Zeit noch nicht erreicht, Funktion abbrechen
 
  counter = 0;
  Serial.print("count"); Serial.print('\t');
  Serial.print("ms");
  Serial.println();
}

Also ganz blicke ich deine Antwort noch nicht.
Wenn ich in dem Calculator Prescaler 64 und Interrupttime 1000ms eingebe komme
ich doch genau wie du auf einen Preload von 6 !
(ich hatte doch Oben schon geschrieben das ich mich an ein paar Stellen vertan hatte)

Bei einem Prescaler von 128 den es gar nicht gibt komme die 131. Das schrieb ich doch.

Zum Anderen scheine ich mich so sehr ja nicht zu irren denn bis so ca einer 3/4 Stunde stimmt
ja alles. Kann sich also nur um vereinzelte Interruptverluste oder einen winzigen Rechenfehler handeln.

Das mit Frequenz und Periodendauer ist mir bei einem Sinus (oder Rechteck) schon klar.
Wie du das jetzt meinst müsstest du mir mal aufzeichnen.

Wenn ich 1000 mal einen Interrupt / Sec haben will dann ist das für mich die Interruptfrequenz.

Ich denke es ist wichtig die gleiche Sprache zu sprechen.

Ulli

@Doc-Arduino:

ich habe dein Beispiel auch mal ausprobiert und eine Variante mit LC-Display erstellt:

Die Version mit I2C-LCD-Lib verliert in einer Stunde ca. 13 Interrupts.
Aber auch deine Variante mit serieller Ausgabe verliert in der Stunde ca. 2 Interrupts.

Also sind die Timer richtig Programmiert nur irgendwo schluckt jemand Interrupts.

Noch mal Danke für die Unterstützung.

Ulli

beeblebrox:
@Doc-Arduino:

ich habe dein Beispiel auch mal ausprobiert und eine Variante mit LC-Display erstellt:

Die Version mit I2C-LCD-Lib verliert in einer Stunde ca. 13 Interrupts.
Aber auch deine Variante mit serieller Ausgabe verliert in der Stunde ca. 2 Interrupts.

Also sind die Timer richtig Programmiert nur irgendwo schluckt jemand Interrupts.

Damit klärt sich auch warum meine Testprogramme nach einer Weile falsch gehen.

Noch mal Danke für die Unterstützung.

Ulli

Jetzt habe ich Timer 2 mal auf Compare umgestellt und auch da liefert mir der Prescaler-Rechner
korrekte Werte. Mal schauen wie der in einer Stunde aussieht !

Welche Modi gibt es eigentlich noch für die Timer.
PWM ok und dann gibt es ja noch diesen Zählermodus für externe Pinne.
Wenn ich diesen externen Mode richtig verstehe brauche ich dann um Zeiten zu bestimmen einen
zweiten Timer !?

Gab es nicht auch einen Modus wo der Counter einfach zählt (mit OVs die man mitzählen muss)
und bei einem externen Interrupt stoppt so das man dann den Wert auslesen kann ?

Ulli