Arduino Timer0 interrupt nutzen

OP #5369217
Lesenswert?

Hallo,

ich versuche einen Interrupt von Timer0 auszulösen und dabei eine 
Variable alle 1ms zu inkrementieren. Eigentlich sollte der Interrupt 
alle 1ms aufgerufen werden da so wie ich es verstanden habe auch die 
millis() Funktion von diesem Timer bedient wird.

Ich habe mich an diese Anleitung gehalten:

https://learn.adafruit.com/multi-tasking-the-arduino-part-2/timers

Der Interrupt wird auch ausgelöst, nur leider nicht alle 1ms sondern nur 
alle 2ms.

in der Setup Routine habe ich dies eingefügt:
1
  OCR0A = 0xAF;
2
  TIMSK0 |= _BV(OCIE0A);

die ISR:
1
ISR(TIMER0_COMPA_vect) 
2
{
3
  timeout++;
4
  digitalWrite(led, !digitalRead(led));
5
}

Bei dem Arduino handelt es sich um einen Pro Mini 3.3v 8Mhz.

Die delay() Funktion funktioniert und verzögert korrekt.
Beitrag #5369237 wurde von einem Moderator gelöscht.
Gast #5369309
Lesenswert?

R. H. schrieb:
> Ich habe mich an diese Anleitung gehalten:
>
> https://learn.adafruit.com/multi-tasking-the-arduino-part-2/timers
>
> Der Interrupt wird auch ausgelöst, nur leider nicht alle 1ms sondern nur
> alle 2ms.
>
> in der Setup Routine habe ich dies eingefügt:
>   OCR0A = 0xAF;
>   TIMSK0 |= _BV(OCIE0A);
>
> die ISR:
> ISR(TIMER0_COMPA_vect)
> {
>   timeout++;
>   digitalWrite(led, !digitalRead(led));
> }
>
> Bei dem Arduino handelt es sich um einen Pro Mini 3.3v 8Mhz.

Da weiß man echt nicht, wo man anfangen soll...

1.) Ist das bisschen Schnipsel Code in der Setup() keine ordentliche 
Initialisierung des Timer0. Das funktioniert nur, weil die Arduino 
Umgebung schon vorher irgend ein Init gemacht hat.
2.) Da der Timer0 von der Arduino IDE ebenfalls für millis() verwendet 
ist, weißt du nicht, was "unter" der Haube mit der Timer0 Konfiguration 
geschieht. Bleibt denn der Wert von OCR0A bei 0xAF nach dem setup()? 
Nimm lieber den Timer2. Der wird von der Arduino IDE mW nur für die 
tone() Funktion benutzt.
3.) Der Anleitung geht von einem 16(!) MHz Arduino aus, du hast aber nur 
einen 8(!) MHz Arduino. Evtl. liegt es auch daran: "16MHz zu 8MHz 
verhält sich wie 2ms zu 1 ms"
4.) Die Anleitung ist von 2015... Und damit auf einer vermutlich viel 
älteren Arduino IDE geschrieben. Das muss nicht zwangsläufig bedeuten, 
dass es mit der aktuellen immer noch läuft. Das Problem gibt es auch bei 
einigen Libraries
Gast #5369331
Lesenswert?

Hi

>2.) Da der Timer0 von der Arduino IDE ebenfalls für millis() verwendet
>ist, weißt du nicht, was "unter" der Haube mit der Timer0 Konfiguration
>geschieht.

0xAF ist trotzdem Unsinn. 1ms bekommt man, egal ob 8 oder 16MHz nur mit 
Prescaler 64. Und da sind die OCRA-Werte 0x7C bzw. 0xF9.

MfG spess
Gast #5369385
Lesenswert?

spess53 schrieb:
> 0xAF ist trotzdem Unsinn. 1ms bekommt man, egal ob 8 oder 16MHz nur mit
> Prescaler 64. Und da sind die OCRA-Werte 0x7C bzw. 0xF9.

Nicht unbedingt... Wenn du dir den Artikel mal genauer anschaust:

"Timer0 is an 8-bit that counts from 0 to 255 and generates an interrupt 
whenever it overflows. It uses a clock divisor of 64 by default to give 
us an interrupt rate of 976.5625 Hz (close enough to a 1KHz for our 
purposes).  We won't mess with the freqency of Timer0, because that 
would break millis()!

Comparison Registers
Arduino timers have a number of configuration registers.  These can be 
read or written to using special symbols defined in the Arduino IDE. 
For comprehensive description of all these registers and their 
functions, see the links in "For further reading" below.

We'll set up a up a comparison register for Timer 0 (this register is 
known as OCR0A) to generate another interrupt somewhere in the middle of 
that count.  On every tick, the timer counter is compared with the 
comparison register and when they are equal an interrupt will be 
generated."

D.h. die Arduino IDE hat den Teiler von 64 eingestellt (reicht wohl von 
der Geneuigkeit ;-)) und der Compare IRQ hängt sich quasi in das Zählen 
rein, ohne den eigentlichen IRQ der Arduino IDE zu stören. D.h. egal 
welchen Wert OCR0A bekommt, der Compare IRQ kommt dann auch alle ca. 1ms 
vor

Wenn du den Timer frei programmierst und exakt alle 1ms den IRQ haben 
willst, dann gebe ich dir recht, dann müssen es die beiden OCR0A Werte 
sein.
Gast #5369554
Lesenswert?

Hi

>Nicht unbedingt... Wenn du dir den Artikel mal genauer anschaust:

>"Timer0 is an 8-bit that counts from 0 to 255 and generates an interrupt
>whenever it overflows. It uses a clock divisor of 64 by default to give
>us an interrupt rate of 976.5625 Hz (close enough to a 1KHz for our
>purposes).

976.5625 Hz ergeben bei 16MHz eine Comparewert von 0xF3. Passt auch 
nicht zu
0xAF. Das ergäbe nämlich 1,41 ms.

Ehrlich gesagt, halte ich die 0xAF für einen Schreib- plus Rechenfehler.

Erstens sollte es 0xFA heissen. In der Formel im Datenblatt steht im 
Nenner ein 2 x N x (1 + OCRnx). Bei der Umstellung der Formel kommt dann 
OCR0A = (..)-1 heraus. Und 0xFA-1 = 0xF9. Genau der Wert den ich für 1ms 
errechnet habe.

>D.h. die Arduino IDE hat den Teiler von 64 eingestellt (reicht wohl von
>der Geneuigkeit ;-)) und der Compare IRQ hängt sich quasi in das Zählen
>rein, ohne den eigentlichen IRQ der Arduino IDE zu stören. D.h. egal
>welchen Wert OCR0A bekommt, der Compare IRQ kommt dann auch alle ca. 1ms
>vor

Muss ich das verstehen?

MfG Spess
(Firma: @home) #5369599
Lesenswert?

Reiner_Gast schrieb:
> D.h. die Arduino IDE hat den Teiler von 64 eingestellt (reicht wohl von
> der Geneuigkeit ;-)) und der Compare IRQ hängt sich quasi in das Zählen
> rein, ohne den eigentlichen IRQ der Arduino IDE zu stören. D.h. egal
> welchen Wert OCR0A bekommt, der Compare IRQ kommt dann auch alle ca. 1ms
> vor

... das klingt nach esoterik.

timer0 hat zwei compare match units, einen hat arduino belegt und der 
andere ist frei belegbar. die taktfrequenz gibt arduino quasi vor, somit 
sind je nach hw prescaler und osc bereits festgelegt.

dann lässt sich der compare wert wie immer berechnen.

hier wird nichts irgendwo reingehängt sondern ganz normal eine isr auf 
compare macth unit a aufgerufen.


mt
Gast #5369664
Lesenswert?

Vielleicht habe ich es auch nicht richtig verstanden, kenne den Arduino 
nicht, aber ich versuche es trotzdem mal: Timer0 läuft durch, Arduino 
nutzt die Overflow-ISR. Wohlgemerkt, kein CTC. Also kann man jetzt 
noch Compare-ISRs aufsetzen, wobei die OCR-Werte nur die Phasenlage 
bestimmen, die Frequenz ist immer die gleiche, nämlich die des Overflow. 
So zumindest verstehe ich Reiner_Gast.
Gast #5369665
Lesenswert?

spess53 schrieb:
>>"Timer0 is an 8-bit that counts from 0 to 255 and generates an interrupt
>>whenever it overflows. It uses a clock divisor of 64 by default to give
>>us an interrupt rate of 976.5625 Hz (close enough to a 1KHz for our
>>purposes).
>
> 976.5625 Hz ergeben bei 16MHz eine Comparewert von 0xF3. Passt auch
> nicht zu
> 0xAF. Das ergäbe nämlich 1,41 ms.
>
> Ehrlich gesagt, halte ich die 0xAF für einen Schreib- plus Rechenfehler.

Hi,

du hast es nicht verstanden, fürchte ich...

Du musst das ganze im Kontext der Arduino IDE sehen... und die 
initalisiert den Timer0 auf den Modus "Fast PWM" (siehe Funktion init(), 
wiring.c library der aktuellen Arduino IDE), und dieser Modus zählt nun 
mal von 0 bis 255, egal, was in OCR0A/B steht (sagt das Datenblatt zum 
ATMega328, nicht ich ;-))

Demnach wird: 16000000(MHz) / 64 (Prescaler) = 976,5625 Hz

Damit die Arduino IDE trotzdem die 1ms richtig zählt, wird für die 
Differenz ein Counter mitgezählt und dann zu der Anzahl der 
Millisekunden zugefügt, wenn die Abweichung zu groß geworden ist (steht 
etwas weiter oben in der ISR; auch wieder wiring.c lib)

Ich schlage vor, siehe selbst mal dort nach...
Gast #5369666
Lesenswert?

S. Landolt schrieb:
> Vielleicht habe ich es auch nicht richtig verstanden, kenne den Arduino
> nicht, aber ich versuche es trotzdem mal: Timer0 läuft durch, Arduino
> nutzt die Overflow-ISR. Wohlgemerkt, kein CTC. Also kann man jetzt
> noch Compare-ISRs aufsetzen, wobei die OCR-Werte nur die Phasenlage
> bestimmen, die Frequenz ist immer die gleiche, nämlich die des Overflow.
> So zumindest verstehe ich Reiner_Gast.

Bingo!
Gast #5369675
Lesenswert?

Apollo M. schrieb:
> timer0 hat zwei compare match units, einen hat arduino belegt und der
> andere ist frei belegbar. die taktfrequenz gibt arduino quasi vor, somit
> sind je nach hw prescaler und osc bereits festgelegt.

Sicher?... Dann schau doch mal selber nach in den Arduino sourcen... 
(siehe meinen vorherigen Post)

Die Taskfrequenz gibt der Quarz vor und nicht der Arduino vor... Die 
Platform heißt nur so...

Der Prescaler und Countermodus wird per Software gesetzt... und damit 
die Aufruffrequenz für die entsprechende ISR

> dann lässt sich der compare wert wie immer berechnen.
>
> hier wird nichts irgendwo reingehängt sondern ganz normal eine isr auf
> compare macth unit a aufgerufen.

Der Compare Match wird in der Frequenz durchgeführt, die die 
Timereinstellung der Arduino IDE vorgibt, und damit hängt die quasi 
"hinten dran"
OP #5369686
Lesenswert?

Hallo,

danke für die Antworten. So wie es  S. Landolt beschrieben hat habe ich 
es auch verstanden, deswegen ist es egal welcher Wert im Compare 
Register steht.

Leider beantworten alle Kommentare noch nicht warum die ISR nun alle 2ms 
aufgerufen wird und nicht alle 1ms.

Seltsamerweise funktionieren ja alle Funktionen wie millis() und delay() 
korrekt, ich verstehe es nicht....
Gast #5369722
Lesenswert?

Hi

>Demnach wird: 16000000(MHz) / 64 (Prescaler) = 976,5625 Hz

16000000/64 ist bei mir 250000 kHz. Bei dir fehlt noch ein /256. Dann 
stimmt es.

>Damit die Arduino IDE trotzdem die 1ms richtig zählt, wird für die
>Differenz ein Counter mitgezählt und dann zu der Anzahl der
>Millisekunden zugefügt, wenn die Abweichung zu groß geworden ist (steht
>etwas weiter oben in der ISR; auch wieder wiring.c lib)

Ach so, also 'von hinten durch die Brust in Auge'.

>Ich schlage vor, siehe selbst mal dort nach...

Muss nicht sein. Ich programmiere schon seit 20 Jahren erfolgreich AVRs 
(ohne Ardudings). Warum sollte ich mich durch das Arduino-Gedödel davon 
abbringen lassen vernünftig zu programmieren?

MfG Spess
Gast #5369724
Lesenswert?

Hi

spess53 schrieb:
> Ach so, also 'von hinten durch die Brust in Auge'.

ja, habe mich auch erschrocken, als ich das gesehen habe...

>
>>Ich schlage vor, siehe selbst mal dort nach...
>
> Muss nicht sein. Ich programmiere schon seit 20 Jahren erfolgreich AVRs
> (ohne Ardudings). Warum sollte ich mich durch das Arduino-Gedödel davon
> abbringen lassen vernünftig zu programmieren?

Das stimmt... bin auch davon weg...

Frohes Osterfest :-)
OP #5369752
Lesenswert?

Reiner_Gast schrieb:
> Schwer zu sagen. Wie hasst du denn gemessen?

Gemessen habe ich mittels Pin toggeln und Oszi, sind ziemlich genau 2 ms
1
digitalWrite(led, !digitalRead(led));

S. Landolt schrieb:
> Ich vermute einen Interpretationsfehler: die ISR wird alle 1 ms
> aufgerufen, wegen des Hin- und Herschaltens wird daraus eine Frequenz
> von 500 Hz.


Unwarscheinlich das das Pin toggeln exakt 1ms dauert oder?


Ich werde mich dann wohl damit abfinden müssen mit den 2 ms zu leben, 
das funktioniert ja...
Gast #5369844
Lesenswert?

D.h., das Programm sieht jetzt so aus:
1
void setup()
2
{
3
 pinMode(LED, OUTPUT);
4
 TIMSK0 |= _BV(OCIE0A);
5
}
6

7
void loop()
8
{
9
}
10

11
ISR(TIMER0_COMPA_vect) 
12
{
13
PINB = PINB | 0b00100000;
14
}
Oder?
Gast #5369882
Lesenswert?

> warum geht darauf keiner ein?
Im Oszillogramm ist doch ein Rechtecksignal mit 2+2 ms zu sehen, oder 
nicht?


Eine Idee hätte ich noch, aber wirklich daran glauben kann ich nicht: es 
mit B versuchen, also:
1
void setup()
2
{
3
 pinMode(LED, OUTPUT);
4
 TIMSK0 |= _BV(OCIE0B);
5
}
6

7
void loop()
8
{
9
}
10

11
ISR(TIMER0_COMPB_vect) 
12
{
13
PINB = PINB | 0b00100000;
14
}
Gast #5369890
Lesenswert?

S. Landolt schrieb:
> ISR(TIMER0_COMPB_vect)
> {
> PINB = PINB | 0b00100000;
> }

Da wird mir ja (schon fast) schlecht von....

 PINB = 0b00100000;

aber nicht:
 PINB = PINB | 0b00100000;
Denn so lernen die Mitleser doch das Falsche, und denen fällt das dann 
auf die Füße.
Denn die Folgen sind doch sicherlich nicht beabsichtigt, oder?
Gast #5369906
Lesenswert?

S. Landolt schrieb:
> an ufuf:
>
> Also das
>> PORTB = PORTB | 0b00100000;
> ist garantiert falsch.
>
> Und wo beim Anderen der Unterschied liegen soll, ist mir unklar.

Das zitierte habe ich wieder gelöscht, weil falsch.
Warst nur schneller...

> Und wo beim Anderen der Unterschied liegen soll, ist mir unklar.
Weil andere Pins mittogglen, wenn sie Outputs, und High sind.

Das mag in diesem konkreten Fall egal sein.
Kann einem aber in anderen Anwendungen auf die Füße fallen.
Gast #5369998
Lesenswert?

Welches Rätsel?
Warum die ISR bei einem 16MHz Arduino alle ca 1ms aufgerufen wird, aber 
bei einem 8MHz Arduino nur alle ca. 2ms?

Dann ist die Antwort doch offensichtlich!
Sie schreit doch!

Der Teiler ist 64.
Was soll der Prescaler/Timer denn sonst machen, als bei halbierter 
Eingangsfrequenz auch die halbierte Ausgangsfrequenz zu liefern?
Würde er das nicht tun, dann wäre er ein Fall für den Müll.

Also, das zu erwartende Verhalten!
Kein Grund sich zu wundern.


--
PS:
S. Landolt schrieb:
> Prima, jetzt sitzt ja ein Arduino-Fachmann am Tisch, da wird sich doch
> das Rätsel lösen lassen.
Danke für die Blumen!
Gast #5370044
Lesenswert?

S. Landolt schrieb:
> Klingt pausibel. Soll das dann heißen, dass die Funktionen delay
> und
> millis hier eine Auflösung von 2 ms haben?
Schätze mal, dass du mit dieser Annahme den Nagel auf den Kopf triffst.

---
Getestet:
delay(1) ist auch eine ms
Denn delay() verwendet intern  micros();
Beitrag #5370058 wurde von einem Moderator gelöscht.
Gast #5370097
Lesenswert?

Grausen ohne Grund, ist nur halbes Grausen!

Hier ein Grund:
1
unsigned long micros() {
2
  unsigned long m;
3
  uint8_t oldSREG = SREG, t;
4
  
5
  cli();
6
  m = timer0_overflow_count;
7
#if defined(TCNT0)
8
  t = TCNT0;
9
#elif defined(TCNT0L)
10
  t = TCNT0L;
11
#else
12
  #error TIMER 0 not defined
13
#endif
14

15
#ifdef TIFR0
16
  if ((TIFR0 & _BV(TOV0)) && (t < 255))
17
    m++;
18
#else
19
  if ((TIFR & _BV(TOV0)) && (t < 255))
20
    m++;
21
#endif
22

23
  SREG = oldSREG;
24
  
25
  return ((m << 8) + t) * (64 / clockCyclesPerMicrosecond());
26
}
OP #5370394
Lesenswert?

Also ist es jetzt tatsächlich so, dass die millis() Funktion bei einem 8 
MHz Arduino nur eine Auflösung von 2 ms besitzt?

Ich hätte gedacht, dass die IDE den prescaler entsprechend auf 32 
anpasst oder so...

Dann muss ich wohl mit den 2 ms leben, geht auch, wäre aber schön 
gewesen das irgendwo zu lesen, ich habe da nichts gefunden...
OP #5370409
Lesenswert?

spess53 schrieb:
> Ein Blick ins Datenblatt hätte dir gezeigt, das es keinen Prescaler
> von 32 gibt.

ah ok, ja dann wird ein Schuh draus, dann geht es wohl tatsächlich nicht 
anders.

Leider ist das aber gar nicht dokumentiert auf:

https://www.arduino.cc/reference/en/language/functions/time/millis/

Schade, bei der micros() Funktion steht was zu der Auflösung in 
Abhängigkeit der verwendeten Taktquelle.
Gast #5370462
Lesenswert?

Hi

>Schade, bei der micros() Funktion steht was zu der Auflösung in
>Abhängigkeit der verwendeten Taktquelle.

Kann schon sein, aber ich kenne dieses Arduingeödel nicht. Aber ich 
würde dir raten Programmierung ohne dieses vorgekaute Zeugs zu lernen.

MfG Spess
OP #5370499
Lesenswert?

spess53 schrieb:
> Kann schon sein, aber ich kenne dieses Arduingeödel nicht. Aber ich
> würde dir raten Programmierung ohne dieses vorgekaute Zeugs zu lernen.
>
> MfG Spess

Sonst nutze ich auch eher STM32, aber Arduino hat auch seinen Charme.

Hier handelt es sich um eine relativ einfache Steuerungsaufgabe mit 
einigen Sensoren, da es für Arduino massig Librarys für diverse 
Sensoren/Boards gibt hat man einfach schnelle Fortschritte.

Für STM32 z.b. gibt es nicht viel und dann fängt man wieder an die 
entsprechenden Arduino Libs anzupassen was viel Zeit kostet.
Gast #5370647
Lesenswert?

spess53 schrieb:
> Ein Blick ins Datenblatt hätte dir gezeigt, das es keinen Prescaler
> von 32 gibt.

Ist Dir schon EINMAL ein Arduino-Nutzer begegnet, der einen Blick ins 
Datenblatt des ATmega328 geworfen hätte?

Die meisten sind schon damit überfordert, wenn Du sagst 
"AVR-Controller". Sie haben doch einen Arduino!
Gast #5370695
Lesenswert?

>> Dann muss ich wohl mit den 2 ms leben,

> Warum?
> Es gibt nicht nur COMPB sondern auch COMPA!
> Damit kannst du 2 Schaltschwellen im Abstand von 0x80 definieren.

Wie wäre es nur mit der COMPA-ISR, und dort OCRA 'vor sich her 
schieben'? Also einfach OCR0A = OCR0A + 128.
Gast #5370918
Lesenswert?

S. Landolt schrieb:
> Ob das hier der Fall ist, kann
> unser Arduino-Fanboy beantworten.

Könnte ich, aber wie alle anderen Arduino User auch, schau ich nie in 
irgendwelche Datenblätter.
Siehe: Beitrag "Re: Arduino Timer0 interrupt nutzen"
Nicht EINEN gibt es.

Also kann ich euch nur die Werte liefern, die bei einem 16MHz UNO in den 
Registern stehen.
TCCR0A 0x03
TCCR0B 0x03

Außerdem wird das langsam langweilig...
Denn der Arduino Core/Quellcode liegt öffentlich aus.
https://github.com/arduino/Arduino/tree/master/hardware/arduino/avr/cores/arduino
Es kann sich also jeder aus eigener Kraft allumfassend kundig machen.

//----

R. H. schrieb:
> Leider ist das aber gar nicht dokumentiert auf:
>
> https://www.arduino.cc/reference/en/language/functions/time/millis/
Wenn sich in der Arduino Welt Fehler befinden, kann man sie melden, oder 
in vielen Fällen auch selber korrigieren.
Die Arduino Welt ist in der Sache auf externe Helfer angewiesen.
Also bitte nicht auf die Doku schimpfen, sondern helfen sie besser zu 
machen.

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