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.
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
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
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.
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
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
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.
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...
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!
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"
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....
R. H. schrieb:> Seltsamerweise funktionieren ja alle Funktionen wie millis() und delay()> korrekt, ich verstehe es nicht....
Schwer zu sagen. Wie hasst du denn gemessen?
Ist die Variable timeout als volatile deklariert?
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
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 :-)
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...
S. Landolt schrieb:> Nicht ganz exakt 1ms, das wissen wir ja. Aber nochmal, welche Frequenz> wird mit dem Oszilloskop gemessen?
Bei jedem Aufruf der ISR wird der Pin umgeschaltet...
Gemessen habe ich jeweils die on oder off Zeit, sind 2,02 ms laut Oszi
R. H. schrieb:> Bei jedem Aufruf der ISR wird der Pin umgeschaltet...
Ja, das ist schon klar...
Aber von wo bis wo sind es auf dem Oszi 2ms?
Von einem Einschalten bis zum nächsten, oder ist das die Dauer für Der
Ein- bzw. Aus-Phase?
Vielleicht liege ich auch falsch, aber simpel gefragt: auf dem
Oszilloskop ist ein Rechtecksignal zu sehen, wie lang ist ein Strich
oben und wie lang unten?
Reiner_Gast schrieb:> Hast du das Board auch als 8 MHz Board vor dem Flashen in der Arduino> IDE eingestellt?
Ja die IDE ist auf ATmega328p (3.3V, 8MHz) eingestellt.
Wie gesagt funktionieren ja auch die millis() und delay() Funktionen
korrekt.
R. H. schrieb:> PINB = PINB | 0b00100000;
Unnötig und tut sicherlich was anderes, als du erreichen willst.
Aus 2 Gründen:
1: digitalRead() und digitalWrite() ist deutlich schneller als 1 ms
2: PINB = 0b00100000; // wäre korrekt
Zitat:
"Ich vermute einen Interpretationsfehler: die ISR wird alle 1 ms
aufgerufen, wegen des Hin- und Herschaltens wird daraus eine Frequenz
von 500 Hz."
da steht doch die Lösung, warum geht darauf keiner ein?
Gruß
Hans
> 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:
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?
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.
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!
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();
> Denn delay() verwendet intern micros();
Nein, ich frage jetzt nicht, wie Arduino micros erzeugt - hier wendet
sich der (Gast) mit Grausen.
Frohe Feiertage!
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...
Hi
>Ich hätte gedacht, dass die IDE den prescaler entsprechend auf 32>anpasst oder so...Ein Blick ins Datenblatt hätte dir gezeigt, das es keinen Prescaler
von 32 gibt.
MfG Spess
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.
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
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.
R. H. schrieb:> 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.
Damit bekommst du dann deine 2 Interrupts auf 2ms.
Pro 1ms einen.
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!
>> 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.
PS:
Das geht allerdings nur, wenn Timer0 im Normal-Modus läuft, nur dann
(und bei CTC) gilt "Update of OCRx at 'Immediate'". Ob das hier der Fall
ist, kann unser Arduino-Fanboy beantworten.
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.
Übrigens...
Hier wurde es schon geklärt:
Reiner_Gast schrieb:> und die> initalisiert den Timer0 auf den Modus "Fast PWM" (siehe Funktion init(),> wiring.c