Probleme mit dem Timer1 Sketch

Hallo Zusammen !

ihr hattet mir ja vor einiger Zeit geholfen, ein Sketch zu basteln, um über den Timer1 etwas ein bzw aus zu schalten.
Jetzt ist mir aber aufgefallen, das etwas an diesem Sketch nicht so ganz funktioniert.

Der Sketch funktioniert im Bereich von Sekunden ganz gut, aber wenn jetzt was im Millisekundenbereich dazu kommt, wird der tausender Bereich (also Sekunden) einfach ignoriert.

hier ist zb die AUS-Zeit kürzer als die AN-Zeit:

const byte Pin = 11;   //OC1A = Mega Pin 11

volatile int offTime = 0;       //Aus-Zeit in ms
volatile int onTime = 0;       //An-Zeit in ms


void setup()
{
  pinMode(Pin, OUTPUT); 

  offTime = 1100;       
  onTime = 1000;
  timerOn();
}

void loop()
{

}


void timerOn()
{
  cli();                            // stop interrupts
  TCCR1A = _BV(COM1A0);   // Timer automatic für Timerpin
  TCCR1B = _BV(WGM12) | _BV(CS12);  //CTC Mode, Prescaler = 256 (= 16µs pro Tick)
  TCNT1  = 0;                       // reset counter value
  TIMSK1 = _BV(OCIE1A);             //Enable Output Compare Match A Interrupt
  sei();           // allow interrupts
}




ISR(TIMER1_COMPA_vect)
{
  static bool onPhase;

  if (onPhase)
  {
  OCR1A = (offTime * (1000UL / 16)) -1;    //Zeit in ms * 1000ms / 16µs (Zeit pro Timer-Tick) 
  }
  else
  {
  OCR1A = (onTime * (1000UL / 16)) -1;      //Zeit in ms * 1000ms / 16µs (Zeit pro Timer-Tick)
  }

  onPhase = !onPhase;
}

Die Zeit wird hier berechnet:

 OCR1A = (offTime * (1000UL / 16)) -1;

Hast du die Klammern da selbst hinzugefügt? 1000 / 16 = 62. Es wäre aber 62,5 wenn es keine Ganzzahl Division wäre. Wenn man die Klammern weglässt wird erst * 1000 gemacht und danach durch 16 dividiert. So war es eigentlich gedacht und so stand es auch in dem Thread. Das hatte schon seinen Grund

Und Zeiten die viel größer als 1 Sekunde sind gehen nicht!! Du hattest gesagt es soll von 0-1000ms gehen. In 16 Bit gehen 2 ^ 16 - 1 = 65535

65535 * 16µs = 1,04856s

Mach den Prescaler höher dann gehen auch längere Zeiten. Am Anfang war es ja glaube ich mal 64µs pro Tick. Dann gehen 65535 * 64µs = 4,19424s

Du wolltest aber eine höhere Auflösung. Das war ok, aber du hattest auch gesagt es soll nur von 0-1000ms gehen. Und in diesen Grenzen habe ich gesagt dass es geht.

Hallo,

Ursprungs-Thread.

Stichpunkt Prescaler ändern für andere Zeitbereiche wurde schon erwähnt.

Du hast Recht !
Ich weiß absolut nicht, wie ich auf 1000ms gekommen bin, ich vermute mal, es war ein sich wiederholender Tippfehler........
Ich brauche 10s also 10000ms ! in 100ms Schritten........

Das müßte doch über ein Zähler gehen ?

Also den Timer auf 100ms einstellen und dann über ein (bzw zwei) Zähler die Anzahl der 100ms "Ticks" zählen und bei Anzahl X dann die Ausgänge schalten und Zähler zurücksetzen.....
oder spricht da was gegen ?

Die KLammer hab ich nachträglich hinzugefügt, um den Fehler zu suchen bzw wollte ich damit probieren, obs so funktioniert.

Hallo,

ganz ehrlich. Das mit dem Tippfehler glaube ich dir nicht. Das wäre damals schon längst aufgefallen. Du mußt hier keine Geschichten erfinden. Wer ordentlich fragt, bekommt ordentliche Antworten. :wink:

Mit einem Timer 1 Durchlauf mit größten Prescaler 1024 kann man "nur" 4,194" Sekunden abdecken.
Um jetzt nicht umständlich die Timer Durchläufe zu zählen und wenn Du seriell nicht brauchst, dann kannste den CPU Takt generell ändern. Von 16 auf 4MHz. Dann könntest du mit einem Timerdurchlauf 16,777 Sekunden abdecken und hast eine Auflösung von 512µs.
Wenn du delay und millis im Code verwendest, mußte immer "Faktor" 4 umrechnen/umdenken.

gleich zu Beginn den Takt ändern. Und den Timer 1 mit Prescaler 1024 initialisieren.

void CPU_4MHz()
{
 //int oldClkPr = CLKPR;  // save old system clock prescale
 CLKPR = 0x80;          // Tell the AtMega we want to change the system clock
 CLKPR = 0x02;          // Clock Prescaler 4 ( Kapitel 10.13 )
 delay(25);             // warte 100ms (100/4)
}

es war keine erfundene Geschichte, sondern eine Vermutung (;
…ich weiß jedenfalls wirklich nicht mehr wie ich da auf 1000ms Sekunden gekommen bin !!!, echt nicht ! hät ich “Mist” gebaut, würd ich dazu stehen…aber ich weiß es nicht…

ich hab in der Anleitung, an der ich mich orientiert hab, 99,9s (aufgerundet 10s) stehen !
Und die sollen es auch sein, in 100ms Schritten…

“Die Rücklaufzeiten und Abnahmezeiten können zwischen 0,1 Sekunden und 99,9 Sekunden unabhängig voneinander gewählt werden.”

“Einstellbereich für Rücklaufzeit 0,1 s bis 99,9 s
Einstellbereich für Abnahmezeit 0,1 s bis 99,9 s”

wie dem auch sei, der Threat war nicht “umsonst”, ihr habt mir sehr geholfen und ich hab viel gelernt (;

ich habs jetzt folgendermaßen gelöst und auf den ersten Blick klappts:

const byte Pin = 11;   

volatile int TIMER1Time = 100; // ms
volatile int TIMER1Counter = 0; // Zähler
volatile int OFFTime = 0; //Aus-Zeit in ms (min. 100ms, in 100ms Schritten!)
volatile int ONTime = 0; //An-Zeit in ms (min. 100ms, in 100ms Schritten!)

byte OFFcounter = 0; // Um Tasterdruck,zum umstellen der Werte, nach x sekunden Laufzeit, zu simulieren
byte ONcounter = 0; // Um Tasterdruck,zum umstellen der Werte, nach x sekunden Laufzeit, zu simulieren


void setup()
{
  pinMode(Pin, OUTPUT);
  digitalWrite(Pin, LOW); // emFt -> aus = totaler Rückfluss

  timerOff(0);

  OFFTime = 0;       
  ONTime = 2500;
  timerOn();
}

void loop()
{

}


void timerOn()
{
if ((OFFTime > 0) && (ONTime < 1))
 {
 timerOff(0); 
 }
 else if ((OFFTime < 1) && (ONTime > 0))
 {
 timerOff(1);
 }
 else
 {
  cli(); // stop interrupts
  TCCR1A = 0;    
  TCCR1B = _BV(WGM12) | _BV(CS12); //CTC Mode, Prescaler = 256 (= 16µs pro Tick)
  TCNT1  = 0; // reset counter value
  TIMSK1 = _BV(OCIE1A); //Enable Output Compare Match A Interrupt
  OCR1A = (TIMER1Time * 1000UL / 16) -1; //Zeit in ms * 1000ms / 16µs (Zeit pro Timer-Tick)
  sei(); // allow interrupts
}
    }

void timerOff(byte X)
{
  cli(); // stop interrupts   
  TCCR1A = 0;         
  TCCR1B = 0;
  TCNT1  = 0; // reset counter value
  TIMSK1 = 0; //Disable Output Compare Match A Interrupt
  sei(); // allow interrupts

  if (X == 0)
  {
  digitalWrite(Pin, LOW);
  }
  else if (X == 1)
  {
  digitalWrite(Pin, HIGH);
  }
}


ISR(TIMER1_COMPA_vect)
{
  static bool onPhase;

  TIMER1Counter++;
  
  if (onPhase)
  {
  if (TIMER1Counter >= (ONTime/100))
  {
  digitalWrite(Pin, LOW);
  TIMER1Counter = 0;
  onPhase = !onPhase;
  }
  }
  else if (!onPhase)
  {
  if (TIMER1Counter >= (OFFTime/100))
  {
  digitalWrite(Pin, HIGH);
  TIMER1Counter = 0;
  onPhase = !onPhase;
  }
  }

}

ich vermute jetzt mal, das mit dem CPU-Takt ändern ist in meinem Fall nicht so die beste Lösung ?

ich verwende in dem Sketch recht oft Millis, dann ist der Sketch auch ziehmlich lang (es werden edliche Sensoren abgefragt, auf SD-Karte geschrieben usw usw) und dann brauche ich Serial für das WLan-Modul....
und der ATMega hat ein externees 16Mhz Quarz

Hallo,

okay, will dir mal glauben. Nur solltest du dich jetzt wirklich festlegen welchen Zeitbereich zu abdecken möchtest. Mittlerweile bist du bei 100 (hundert) Sekunden.

Eine grobe Idee habe ich, ob das klappt weis ich noch nicht. Zeitvorgabe [ms] durch 4194ms teilen, damit die Anzahl der notwendigen Timerdurchläufe abwarten + den Rest davon und dann erst den Pin schalten.

Hallo,

denk jetzt bitte nochmal genau nach welche Zeiten und welche Auflösung du wirklich brauchst! Wenn es wirklich nur eine Auflösung von 0,1sec, also 100ms sein soll, dann können wir uns den gesamten "Timer Quatsch" sparen. Machste alles mit millis() und fertig. Außer die Zeiten sollen genau wiederholbar sein, falls die loop mal wieder länger dauert.

Sorry für die "Verwirrung", aber wie gesagt, es war nicht "umsonst" ! Ihr habt euch keine Mühe vergebens gemacht ! (:

das ist aus dem Datenblatt:

"Die Rücklaufzeiten und Abnahmezeiten können zwischen 0,1 Sekunden und 99,9 Sekunden unabhängig voneinander gewählt werden."

"Einstellbereich für Rücklaufzeit 0,1 s bis 99,9 s
Einstellbereich für Abnahmezeit 0,1 s bis 99,9 s"

Mit Millis wird das glaub ich zu ungenau, wegen der Loop.

aber was spricht gegen mein Sketch ? Der ist doch "einfach", funktioniert(auf den ersten Blick), wird durch die Loop nicht beeinträchtigt und ich kann den leicht in den bestehenden Sketch einbinden......

Ich würde sagen, der ist gut und brauchbar, ich frage euch nur lieber nochmal, ob da aus "professioneller" Sicht, was dagegen spricht oder ob der irgendwie anders Komplikationen verursachen kann.....

Es wäre wahrscheinlich auch keine große Sache die den Overflow Interrupt noch auszuwerten. Dann zählt man darin die Überläufe mit. Dann kann man im Compare Match Interrupt abfragen wie viele Überlaufe man schon hatte. Dann muss man aber den CTC Modus abschalten, da der Timer weiterlaufen muss! Also TNCT1 immer weiter laufen lassen und wenn nötig per Hand auf 0 setzen.

100s mit 100ms Auflösung sind halt Zeiten wo man sich einen Timer langsam sparen kann. Das kann man wirklich mit millis() erschlagen, wenn der Rest sauber programmiert ist

klingt aber komplizierter, als das was ich jetzt gemacht hab ?

ja aber Auflöung ist ja nicht gleich Genauigkeit ! Die Genauigkeit sollte so genau, wie möglich sein.......

mit Millis is mir das zu ungenau.
Hatte damals, als der Sketch noch lange nicht fertig war, ma versucht, eine normale Stoppuhr mit Millis anzusteuern, die ist messbar danebengelaufen.....
jetzt steuer ich die mit der RTC an.

ist das denn "zuviel", für ein Interrupt ?:

ISR(TIMER1_COMPA_vect)
{
static bool onPhase;

TIMER1Counter++;

if (onPhase)
{
if (TIMER1Counter >= (ONTime/100))
{
digitalWrite(Pin, LOW);
TIMER1Counter = 0;
onPhase = !onPhase;
}
}
else if (!onPhase)
{
if (TIMER1Counter >= (OFFTime/100))
{
digitalWrite(Pin, HIGH);
TIMER1Counter = 0;
onPhase = !onPhase;
}
}

}

Hallo,

hab da mal was vorbereitet. :slight_smile:
Funktioniert bis max. Zeitbereich von 429.496.729.600 ms
Ist nicht an den Timer Hardware Pin gebunden.
Der Timer löst genau aller 100ms aus. Das wird gezählt und ausgewertet und entsprechend geschaltet.
Die "aller 100ms" entspricht deiner Auflösung. Wenn die wieder mal anders sein soll, mußte entsprechend OCR1A anders vorladen. Und dann wieder davon Vielfache verwenden für deine Schaltzeiten.

/*
Doc Arduino - german Arduino Forum
IDE 1.6.7
Arduino Mega 2560
taktet an Pin 11
*/


const byte emFt_Pin = 11;  // Pin 11, emFt = elektromagnetischer Flüssigkeitsteiler

volatile unsigned long offTime = 600;  // Aus-Zeit in ms, Vielfache von 100
volatile unsigned long onTime  = 1600;  // An-Zeit in ms, Vielfache von 100
volatile unsigned long offCountInterrupts;  // notwendige Timer 1 Überläufe OFF
volatile unsigned long onCountInterrupts;   // notwendige Timer 1 Überläufe ON

void setup()
{
 Serial.begin(9600);
 digitalWrite(emFt_Pin, LOW); // emFt -> aus = totaler Rückfluss
 pinMode(emFt_Pin, OUTPUT);

 calculate_Timer();
 
 set_Timer_1();
}


void loop()  {

}

void calculate_Timer ()  {        //  gültig für Prescaler 256 und OCR1A 6250

 offCountInterrupts = offTime / 100;  
 onCountInterrupts =   onTime / 100;  
 Serial.print("off Count "); Serial.println(offCountInterrupts);
 Serial.print(" on Count "); Serial.println(onCountInterrupts);
}


ISR(TIMER1_COMPA_vect)  {          //  gültig für Prescaler 256 und OCR1A 6250
 static bool statusPin = false;
 static unsigned long onCount = 0;
 static unsigned long offCount = 0;

 onCount++;
 offCount++;

 if ( statusPin == false && offCount >= offCountInterrupts )  {
   statusPin = true;
   offCount = 0;
   onCount = 0;
 }
 
 if ( statusPin == true && onCount >= onCountInterrupts )  {
   statusPin = false;
   onCount = 0;
   offCount = 0;
 }

 digitalWrite(emFt_Pin, statusPin);
}


void set_Timer_1()  {              // aller 100ms 
 cli();                            // stop interrupts
 TCCR1A = 0;                       // Reset TCCR1A Register
 TCCR1B = 0;                       // Reset TCCR1B Register
 TIMSK1 = 0;                       // Reset TIMSK1 Register (disable Timer Compare Interrupts)
 TCNT1  = 0;                       // initialize counter value to 0
 //TCCR1A = _BV(COM1A0);           // Toggle OC1A on compare match
 TCCR1B = _BV(WGM12) | _BV(CS12);  // CTC Mode, Prescaler 256
 OCR1A = 6250;                     // Interrupt aller 100ms mit Prescaler 256
 TIMSK1 = _BV(OCIE1A);             // Enable Output Compare Match A Interrupt
 sei();                            // allow interrupts
}

Ja, das ist vielleicht die unkomplizierte Lösung :slight_smile:

Genaugenomen zählst du aber nicht die Überlaufe, sondern die Vergleiche (Compare Matches). Ein Überlauf findet statt wenn der Timer von selbst am Ende des Wertebereiches wieder auf 0 geht. Das hat man bei CTC nicht.

Hallo,

Danke. Ja, der Kommentar ist falsch. Ich hatte erst anders angefangen und dann gemerkt, dass es doch noch einfacher geht. Ich wollte erst zusätzlich mit Modulo den Rest berechnen und solchen "Quatsch". :slight_smile:

Eine kleine Korrektur noch. OCR1A muß mit 6249 geladen werden. -1 laut Formel im Datenblatt. Um ganz genau zu sein. Jetzt sollten die Kommentare passen.

/*
Doc Arduino - german Arduino Forum
IDE 1.6.7
Arduino Mega 2560
schaltet am Pin 11
*/

const byte emFt_Pin = 11;  // Pin 11, emFt = elektromagnetischer Flüssigkeitsteiler
unsigned long offTime = 1100;  // Aus-Zeit in ms, Vielfache von 100
unsigned long onTime  = 5800;  // An-Zeit in ms, Vielfache von 100
volatile unsigned long offCountInterrupts;  // notwendiger Timer 1 Interruptzähler OFF
volatile unsigned long onCountInterrupts;   // notwendiger Timer 1 Interruptzähler ON

void setup()
{
 Serial.begin(9600);
 digitalWrite(emFt_Pin, LOW); // emFt -> aus = totaler Rückfluss
 pinMode(emFt_Pin, OUTPUT);

 calculate_Timer();
 
 set_Timer_1();
}


void loop()  {

}

void calculate_Timer ()  {        //  gültig für Prescaler 256 und OCR1A 6249
 offCountInterrupts = offTime / 100;  
 onCountInterrupts =   onTime / 100;  
 Serial.print("off Count "); Serial.println(offCountInterrupts);
 Serial.print(" on Count "); Serial.println(onCountInterrupts);
}


ISR(TIMER1_COMPA_vect)  {          //  gültig für Prescaler 256 und OCR1A 6249
 static bool statusPin = false;
 static unsigned long onCount = 0;
 static unsigned long offCount = 0;

 onCount++;
 offCount++;

 if ( statusPin == false && offCount >= offCountInterrupts )  {
   statusPin = true;
   offCount = 0;
   onCount = 0;
 }
 
 if ( statusPin == true && onCount >= onCountInterrupts )  {
   statusPin = false;
   onCount = 0;
   offCount = 0;
 }

 digitalWrite(emFt_Pin, statusPin);
}


void set_Timer_1()  {              // aller 100ms 
 cli();                            // stop interrupts
 TCCR1A = 0;                       // Reset TCCR1A Register
 TCCR1B = 0;                       // Reset TCCR1B Register
 TIMSK1 = 0;                       // Reset TIMSK1 Register (disable Timer Compare Interrupts)
 TCNT1  = 0;                       // initialize counter value to 0
 TCCR1B = _BV(WGM12) | _BV(CS12);  // CTC Mode, Prescaler 256
 OCR1A = 6249;                     // Interrupt aller 100ms mit Prescaler 256
 TIMSK1 = _BV(OCIE1A);             // Enable Output Compare Match A Interrupt
 sei();                            // allow interrupts
}

Formel für Timer ISR Zeit.
OCR1A + 1 = 16.000.000Hz / (1/ Auflösung [sec] ) / Prescaler // Einheiten beachten !

Vielen Dank !

aber in wie weit unterscheidet sich der Sketch von meinem ? ......vom Prinziep her.......

ich hab nur ein Zähler und ein niedrigeren Prescaler (= bessere Auflösung?).

und ich hab die Funktion mit drin um den Timer ganz aus (und wahlweise Ausgang HIGH oder LOW) zu schalten.....

was ist dann an meinem Sketch falsch ?

Eigentlich ist es das gleiche. Nur etwas anders geschrieben :slight_smile:

OK :slight_smile:

wie sagt man so schön:
"viele Wege führen nach Rom"........ das is mir bisher nirgends so krass aufgefallen, wie beim "Programmieren".....

Ich danke euch ! Ihr habt mir wiedermal sehr geholfen ! :slight_smile:

Und sorry für die Umstände !

Hallo,

ganz ehrlich, ohne Code Tags und ohne Klammern einrücken hab ich das erst gar nicht versucht zulesen. :slight_smile:
Entschuldigung. :o
Hauptsache das was du möchtest funktioniert jetzt. Den Rest an Änderungen solltest du nun selbst hinbekommen.
Den Vorladewert für OCR1A kannste noch abseits der Formel fein tunen, wenn Du ein genaues Meßgerät hast. Ich messe immer Abweichungen vom errechneten. Mag am 16MHz Resonator liegen, keine Ahnung.