Probleme mit Timer bei Arduino Uno

Hallo zusammen,

wie im Titel schon erwähnt, habe ich ein Problem mit dem Timer1 von meinem Arduino. Und zwar wenn ich einen Prescaler von 1024 oder 256 auswähle, dann dauert es ziemlich lang bis ein Interrupt ausgelöst wird (habe bei PS 1024 ca. 20 Minuten mit einer Stoppuhr gemessen :astonished: ).
Habe auch versucht niedrigere Prescaler einzustellen und die Overflows in der Interrupt Service Routine zu zählen und die Zeit zu stoppen aber das funktionierte wunderbar, nur die 2 oben erwähnten machen Probleme.

Hat jemand eine Idee woran das liegen könnte? Danke schonmal im Voraus.

LG Montaja

Und wie stellst du fest dass ein Interrupt ausgelöst wird? Hoffentlich nicht mit Serial.print()

Ne, ich hab eine LED an ein I/O Port angeschlossen und die soll nach ca. 20 Sekunden leuchten. Dafür hab ich in der ISR eine Zählvariable inkrementiert die eben die Overflows zählen soll. Und nach einer bestimmten Anzahl Overflows soll eben diese LED angehen.

Für 20 Sekunden und LEDs brauchst du keinen extra Timer. Verwende einfach millis(). Siehe BlinkWithoutDelay Beispiel.

Timer braucht man wenn Sachen sehr schnell gehen müssen (µs oder ms) oder sehr regelmäßig

Damit kann ich aber keinen Interrupt erzeugen oder? Ich müsste dann immer abfragen, ob die Zeit abgelaufen ist. Und auch wenn ich die Funktion in diesem Fall natürlich benutzen könnte, würde mich trotzdem interessieren warum der Timer bei höheren Prescalern einfach austeigt.

Ich müsste dann immer abfragen, ob die Zeit abgelaufen ist.

Ja.
Wo ist das Problem?

Und auch wenn ich die Funktion in diesem Fall natürlich benutzen könnte, würde mich trotzdem interessieren warum der Timer bei höheren Prescalern einfach austeigt.

Naja...
Fehler im Code, vermutlich....

PS:
Es ist eine ganz tolle Idee, den fehlerhaften Code geheim zu halten.
Damit nimmst du den Helfern jegliche Chance.

Montaja:
Damit kann ich aber keinen Interrupt erzeugen oder? Ich müsste dann immer abfragen, ob die Zeit abgelaufen ist.

Und wo ist dabei das Problem? 16MHz mag sich selbst nach heutigen µC-Maßstäben nach nicht viel anhören, aber man kann trotzdem in sehr kurzer Zeit viel machen. Man muss nur den restlichen Code so programmieren, dass er nicht blockiert.

Interrupts nicht so trivial wie sich auf den ersten Blick erscheinen und man muss viele Sachen beachten.

Du hast recht, dass es trotzdem gehen müsste, aber es ist keine gute Lösung. Wieso es nicht geht kann man ohne Code nicht sagen.

Also Hintergrund ist, dass wir gerade in der Schule die Grundlagen der Microcontroller-Programmierung beim ATMega kennengelernt haben (eben auch mit Interrupts). Ich wollte den Stoff den wir dort gemacht haben wiederholen und dafür eben ein wenig mit dem Timer rumspielen.

Timer.txt (703 Bytes)

Hallo,

a) für 20s braucht man eigentlich keinen Timer, dafür reicht millis
b) wenn du dich mit Timern Zwecks Lerneffekt befassen möchtest, dann bräuchten wir deinen Code
c) wurde alles schon erwähnt :wink:

Edit:
Codeanhang übersehen. Den kannste auch nachträglich in Code Tags einbetten. Button </>
Timersettings in Hexzahlen, sehr fehleranfällig, kannste das Bitweise schreiben?

Den eigenen Timercounter darfste nicht stellen. Und nullen mußte den auch nicht.
Der wird übrigens mit jeden Overflow genullt. In deinem ISR ist das demnach nicht notwendig.

In der Timereinstellung mußte einen Vergleichswert setzen, bis dahin zählt der Counter,
dann wird ein Overflow ISR ausgelöst. Welcher Modus soll das sein? CTC?

"Normal Operation Mode" stimmt auch irgendwie nicht. Wo haste die Einstellungen her?
Datenblatt Kapitel 17 kennste?

Hm das mit der TXT hat nicht so funktioniert wie gedacht xD Hier nochmal der Code direkt im Post:

#include<avr/interrupt.h>
#include<avr/io.h>

volatile int counter=0;
void setup() {
// put your setup code here, to run once:
DDRD |= 0x8F; //10001111
DDRB |= 0x1E; //00011110

TCNT1 = 65536-32500; //Set Timer to 4s
TCCR1A = 0x00; //Normal-Mode-Operation
TCCR1B |= 0x85; //Input Capture: NoiseCanceller Enabled, Falling Edge Timer1 Prescaler 64
TIMSK1 |= 0x21; //Input Capture & Overflow Interrupt Enabled
sei(); //Set I-Flag in SREG
}

ISR(TIMER1_OVF_vect)
{
counter++;
TCNT1=0;
}

void loop() {
// put your main code here, to run repeatedly:

if(counter >= 5) //>20s
{
PORTB |= (1<<4);
}

}

Was willst du mit dem Input Capture Modus? Das ist falsch. Input Capture ist dafür da die Länge von externen Pulsen zu messen.

TCCR1B |= 0x85;       //Input Capture: NoiseCanceller Enabled, Falling Edge  
TIMSK1 |= 0x21;       //Input Capture & Overflow Interrupt Enabled

Es ist übrigens eine gute Idee die Bit Bezeichnungen zu verwenden.

Also statt TCCR1B = 0x01 schreibt man TCCR1B = (1 << CS10) oder TCCR1B = _BV(CS10). So sieht man sofort was gemacht wird

Dann muss du unbedingt beachten, dass die du Timer Register am Anfang auf 0 setzt! Auf einem normalen Atmega wäre das nicht nötig. Aber die Arduino Software konfiguriert die Timer auf PWM. Auch wenn man das nicht verwendet! Sonst bist du leicht ein einem anderen Modus als du denkst. Damit meine ich vor allem die TCCR Register. Es reicht auch dabei = statt |= zu machen. Dann wird der alte Wert überschrieben.

Montaja:
Hallo zusammen,

wie im Titel schon erwähnt, habe ich ein Problem mit dem Timer1 von meinem Arduino. Und zwar wenn ich einen Prescaler von 1024 oder 256 auswähle, dann dauert es ziemlich lang bis ein Interrupt ausgelöst wird (habe bei PS 1024 ca. 20 Minuten mit einer Stoppuhr gemessen :astonished: ).
Habe auch versucht niedrigere Prescaler einzustellen und die Overflows in der Interrupt Service Routine zu zählen und die Zeit zu stoppen aber das funktionierte wunderbar, nur die 2 oben erwähnten machen Probleme.

Hat jemand eine Idee woran das liegen könnte?

Das liegt an Deinem f(falschen!) Code und offfenbar daran, dass Du von extrem falschen Voraussetzungen ausgehst. So langsame Timer-Interrupts, ie nur alle paar Minuten feuern, kannst Du mit 16 MHz Atmegas gar nicht programmierenn. Weder absichtlich noch aus Versehen. Mit Prescaler 1024 feuert ein 16-Bit Timer auf einem 16 MHz Atmega nach spätestens32768*1024/16000000 =2 Sekunden nach meiner Rechnung.

Die Interrupts kommen öfters. Aber er will ja die Anzahl der Überlaufe zählen. Das geht schon. Wobei damit es richtig genau wird, müsste man den CTC Modus nehmen.

Die Hauptgründe weshalb es nicht geht sind folgende:
1.) Falsche Einstellungen mit Interrupt Capture Modus
2.) Die Timer Register haben noch ihre PWM Konfiguration von der Arduino Software

Vor allem Punkt 2

Okay das werd ich gleich mal ausprobieren, vielen Dank schonmal!

Der Input Capture soll hier einfach einen Interrupt auslösen sobald auf einen Taster gedrückt wird. Und da dieser einen eben über einen NoiseCanceller verfügt muss ich mir keine debounce Funktion selbst schreiben um den Taster zu entprellen.

Und die Gesamtzeit berechnet sich doch aus (2^16*1024)/16MHz ? das wären dann ca 4,2s

Input Capture macht nicht was du denkst! Das kopiert den aktuellen Zählerstand in das Input Capture Register. Das ist wie gesagt dazu da Puls-Längen zu messen. z.B. zur Messung einer Frequenz oder um das Tastverhältnis eines PWM-Signals zu berechnen.

Taster per Interrupt abzufragen (das geht über die externen Interrupt Pins) ist eine ganz schlechte Idee da sie mechanisch prellen. Also bei einem Druck werden mehrere Impulse ausgelöst. Dazu muss man sie erst mal per Hardware (RC-Glied o.ä.) entprellen.

Wenn du das Entprellen nicht selbst machen willst, gibt es eine fertige Bibliothek dafür (Bounce2)

Soweit ich das verstanden habe wird der Inhalt von TCNT1 bei einem Capture Event in das ICR1 geschrieben aber das stört mich ja eigentlich nicht. Mir gehts es ja nur darum, dass in diesem Moment ein Interrupt ausgelöst wird und ich in der ISR(TIMER1_CAPT_vect)-Funktion sagen was der MC tun soll wenn dieses Event auftritt. Der Timer an sich wird dabei soweit ich das verstanden habe gar nicht beeinflusst. So haben wir es zumindest bei unseren Programmen in der Schule gemacht. Und wie gesagt der Vorteil daran ist eben, dass das Entprellen des Schalters vom MC übernommen wird.

Also nachdem ich das Register TCCR1B überschrieben habe scheint es zu funktionieren, vielen Dank nochmal für die schnelle Hilfe :slight_smile:

Wenn man ins Datenblatt schaut:

The noise canceler input is monitored over four samples, and all four must be equal for changing the
output that in turn is used by the edge detector.

Entprellen sieht anders aus

Man kann Taster wie gesagt in Hardware entprellen:
https://www.mikrocontroller.net/articles/Entprellung#Hardwareentprellung

Die Arduino-Eingänge haben auch schon Schmitt-Trigger Verhalten. Also braucht man kein extra Gatter!

jurs:
Das liegt an Deinem f(falschen!) Code und offfenbar daran, dass Du von extrem falschen Voraussetzungen ausgehst. So langsame Timer-Interrupts, ie nur alle paar Minuten feuern, kannst Du mit 16 MHz Atmegas gar nicht programmierenn. Weder absichtlich noch aus Versehen. Mit Prescaler 1024 feuert ein 16-Bit Timer auf einem 16 MHz Atmega nach spätestens32768*1024/16000000 =2 Sekunden nach meiner Rechnung.

Hallo,

Entschuldigung, ich muß das einfach korrigieren. 16 Bit Timer zählt bis 65535.
Mit 1024er Prescaler kommste auf eine Overflow Zeit von 4,194 Sekunden.
Und wenn man das noch mit einer unsigned long Zählervariable kombiniert, dann kann man einen Timer bis
4,194 * 4.294.967.295 = 571,19 Jahre programmieren
Na wenn das nichts ist. :slight_smile:

if(counter >= 5)

Da counter in einer ISR bearbeitet wird, muss auch das verarbeiten in loop() ATOMIC erfolgen.
Sonst werden dir irgendwann "Race Conditions" einen Streich spielen.