Wollte den harmonischen Thread nicht kapern, daher hier neu:
Mi N. schrieb:> Alternativ folgende Überlegung:> CAPT und OVL passieren gleichzeitig.> Auf Grund der AVR Hardware hat TIMER1_CAPT höhere Priorität als> TIMER1_OVL.> Folglich wird TIMER1_CAP bedient und ICP = 0 gelesen. T1OVF_Ctr ist noch> auf 0 und das Gesamtergebnis ist auch 0, müßte aber 65536 sein.
Habe das mal mit einem Arduino Pro Mini 16 MHz nachgestellt.
Dazu den OC1B Ausgang (PB2 pin 10) mit dem Capture Eingang (PB0 pin 8)
verbinden (D4).
Aufruf der ISRs setzt pin 12 (Overflow, D2) und pin 13 (Capture, D3),
als letztes in der ISR werden diese wieder 0 gesetzt.
OC1A (PB1 pin 9) wird auf D0 dargestellt.
Fast PWM 8 bit (wgm 5) wird mit folgenden Parametern ausgeführt:
1
_a OCR1A 252 match at 252
2
_b OCR1B 4 match at 4
3
_c cs 2 clock 2 eg /8
4
_d cmoA 3 Set OC1A/OC1B on Compare Match, clear at BOTTOM
5
_e cmoB 3
6
_f ocie 33 Interrupts ICE1 und TOIE1
7
_m ica 0 no noise canceling
8
_w wgm 5 Fast PWM 8 bit
D.h. T1 zählt von 0 bis 255, beim Übergang auf 0 wird Overflow und über
die fallende Flanke von OC1B das Input Capture "gleichzeitig"
getriggert.
Was ich nicht verstehe, warum manchmal die Overflow-ISR vor und manchmal
nach der Capture-ISR ausgeführt wird?
Hallo,
wie sehen dazu die Messwerte aus? Pendeln die um den Sollwert oder
springen diese manchmal völlig daneben? Könnte am Compare Match
Interrupt Timer 1 liegen. Nimm einmal für die Takterzeugung einen
anderen freien Timer, Takterzeugung rein in Hardware ohne ISRs, und
befeuer damit Timer 1.
Veit D. schrieb:> wie sehen dazu die Messwerte aus?
Wie zu erwarten (da im Programm noch nicht gefixt) stimmen manche nicht:
Hi ist Overflow-Counter, Lo ist ICR1
1
P 16 auto 1 avg 65536 uspt 0.062550 clo 0.500400 30.493
Hallo,
deine eigentliche Frage ist demnach nur die, warum der OVF Interrupt
manchmal vor dem Capt Interrupt kommt?
Noch eine Anmerkung. Dir muss bewusst sein das dein Programm durch die
vielen Eigenkreationen der Bitnamen kaum bis nicht lesbar ist. Um
herauszufinden wie die Timer wirklich konfiguriert sind ist viel Aufwand
notwendig. Warum verwendest du nicht das _BV() Makro?
Helmut H. schrieb:> D.h. T1 zählt von 0 bis 255, beim Übergang auf 0 wird Overflow und über> die fallende Flanke von OC1B das Input Capture "gleichzeitig"> getriggert.> Was ich nicht verstehe, warum manchmal die Overflow-ISR vor und manchmal> nach der Capture-ISR ausgeführt wird?
Es gibt kein "gleichzeitig". Der Ausgang wird verzögert gesetzt und der
capture-Eingang verzögert erkannt. Im Datenblatt kann man sich die D-FFs
ansehen.
So kann es passieren, daß die OVF-ISR schon angesprungen wird, während
'capture' noch nicht erkannt ist.
Wenn Du Dein 'to do' eingefügt hast
wird richtig gezählt.
Warum das "// ovcnt++;" ganz schlecht ist hatte ich ja beschrieben:
Beitrag "Re: AVR T1 Capture Problem - Meinung erbeten"
Anmerkung:
Beim Wechsel auf einen anderen µC kann einem das noch deutlicher auf die
Füsse fallen.
Fall A ) Jeder Interrupt is unabhaengig vom Anderen und macht was er tun
muss, Eine (fast-)gleichzeitigkeit wird nachher korrigiert.
Fall B) Jeder Interrupt is unabhaengig vom Anderen und macht was er tun
muss, Eine (fast-)gleichzeitigkeit wird im 2.Interrupt korrigiert.
Fall C) Die beiden Interrupts schauen, jeweils ob der Andere grad kam,
und verarbeiten den auch grad.
Fall C, Dabei wird meines Erachtenz zuviel Zeit verplempert. Ich wuerd
nach Fall A arbeiten.
Veit D. schrieb:> deine eigentliche Frage ist demnach nur die, warum der OVF Interrupt> manchmal vor dem Capt Interrupt kommt?
Ja.
> Noch eine Anmerkung. Dir muss bewusst sein das dein Programm durch die> vielen Eigenkreationen der Bitnamen kaum bis nicht lesbar ist. Um> herauszufinden wie die Timer wirklich konfiguriert sind ist viel Aufwand> notwendig.
Ja. Habe zunächst mal showRegs() angepasst:
1
TCCR1A: 0xF1 TCCR1B: 0x0A TIMSK1: 0x21
2
_a OCR1A 252
3
_b OCR1B 4
4
_c CS 2
5
_d COM1A 3
6
_e COM1B 3
7
_f TIMSK1 33
8
_m ICNC1/ICES1 0
9
_w WGM 5
> Warum verwendest du nicht das _BV() Makro?
jetzt z.B.
1
const byte povf = 12; // pb4
2
const byte dovfS = _BV(4); // to set/reset pin fast
Mi N. schrieb:> Warum das "// ovcnt++;" ganz schlecht ist hatte ich ja beschrieben:
Beim Timer Overflow flag wird jetzt ovcnt+1 gespeichert, ovcnt wird ja
in der nachfolgenden Overflow ISR erhöht.
1
if ( (TIFR1 & 1 << TOV1)) { // if pending timer verflow
Mi N. schrieb:> Es gibt kein "gleichzeitig". Der Ausgang wird verzögert gesetzt und der> capture-Eingang verzögert erkannt. Im Datenblatt kann man sich die D-FFs> ansehen.> So kann es passieren, daß die OVF-ISR schon angesprungen wird, während> 'capture' noch nicht erkannt ist.
Was ich halt nicht verstehe, warum hier (OC1B Ausgang mit dem Capture
Eingang des gleichen Timers verbunden) manchmal zuerst die Capture-ISR
und manchmal zuerst die Overflow-ISR aufgerufen wird.
Hatte das nur gemacht um die von Dir vorgeschlagene Behandlung der
"Gleichzeitigkeit" zu testen, es kommt jetzt ja in beiden Fällen das
richtige Ergebnis.
NB: wenn ich den ICNC1: Input Capture Noise Canceler einschalte, wird
immer zuerst der Timer-Overflow aufgerufen, ist ja klar weil die
Bearbeitung des Captures um 4 Takte verzögert wird.
Helmut H. schrieb:> Was ich halt nicht verstehe, warum hier (OC1B Ausgang mit dem Capture> Eingang des gleichen Timers verbunden) manchmal zuerst die Capture-ISR> und manchmal zuerst die Overflow-ISR aufgerufen wird.
Weil halt aus irgendwelchen Gründen mal beide, und mal nur ein ISR-Flag
gesetzt ist, wenn die ISR aufgerufen wird.
Wenn da im Test des Programm mal die Interrupts gesperrt und wieder
freigegeben werden, was bei dem ganzen Arduino-Funktionen, die du da ja
aufrufst, vielleicht der Fall ist, dann sind halt die Flags mal beide
gesetzt, und mal nicht.
Oliver
Helmut H. schrieb:> Was ich halt nicht verstehe, warum hier (OC1B Ausgang mit dem Capture> Eingang des gleichen Timers verbunden) manchmal zuerst die Capture-ISR> und manchmal zuerst die Overflow-ISR aufgerufen wird.
Das wird gerade auf der Kippe sein. Wann auf Interrupts reagiert wird,
hängt ja auch von dem gerade bearbeitetem Befehl ab.
Vermutung:
Man könnte jetzt spekulieren, daß ein Befehl mit einem Zyklus der
OVF-ISR den Vorrang gibt. Bei >= zwei Zyklen für den aktuellen Befehl
und beiden anstehenden Interrupts, hat die CAPT-ISR die höhere
Priorität.
Setze mal OCR1B auf 0x0001 oder 0x0002, dann wird vermutlich immer die
OVF-ISR gewinnen. Den OC1B-Ausgang mit 1 - 10 nF zu belasten, könnnte
dafür auch schon reichen.
Wichtig ist letztendlich, daß es keine Fehler bei der Erkennung gibt.
Und das wird ja erreicht.
Oliver S. schrieb:> Wenn da im Test des Programm mal die Interrupts gesperrt und wieder> freigegeben werden, was bei dem ganzen Arduino-Funktionen, die du da ja> aufrufst, vielleicht der Fall ist, dann sind halt die Flags mal beide> gesetzt, und mal nicht.
Das ist auch noch ein wichtiger Punkt: Bei den Arduino-Programmen ist
immer noch (zumindest mir) unbekannte Hintergrundaktivität vorhanden.
Das läßt sich vermeiden, indem man setup() und loop() wegläßt und die
Initialisierungen wie gewohnt durchführt.
Beim Versuch, meine Programme auf "Arduino-Niveau" zu bringen, bin ich
zum Beispiel daran gescheitert, daß TIMER0 schon anderwärtig verwendet
wird.
Das liebe ich ja sowas von ...
Helmut H. schrieb:> Was ich nicht verstehe, warum manchmal die Overflow-ISR vor und manchmal> nach der Capture-ISR ausgeführt wird?
Der Overflow kann nur dann zuerst kommen, wenn das Capture noch nicht
erfolgt ist. Da kann schon ein Zyklus später ausreichen (muß aber
nicht).
Der kritische Fall ist immer nur, wenn das Capture kurz nach dem
Overflow erfolgt, dann wurde noch nicht der Overflow gezählt, d.h. das
TOV1 ist noch gesetzt. Das muß man dann, wie im Thread von Gerhard
gezeigt, behandeln.
Ob die Statemaschine in der Capture-ISR keine Seiteneffekte hat, kann
ich nicht einschätzen.
Ich lasse die ISR nur die Erweiterung auf 32 Bit machen und bilde dann
in der Mainloop die Differenz von 2 Capture-Werten.
Für sehr kurze Intervalle läßt man noch einen Capture-Counter
hochzählen, durch den man dann teilen kann (Messung über n Perioden).
Das erhöht die Auflösung.
Ich sehe nirgends eine atomare Kapselung der Zugriffe auf
Interruptvariablen in der Mainloop.
Du mußt zuerst mal alle interessierenden Variablen als atomaren Snapshot
puffern, ehe Du sie einzeln verarbeiten kannst.
Mi N. schrieb:> Beim Versuch, meine Programme auf "Arduino-Niveau" zu bringen, bin ich> zum Beispiel daran gescheitert, daß TIMER0 schon anderwärtig verwendet> wird.> Das liebe ich ja sowas von ...
Weil der für micros(), millis() und delay() verwendet wird. Braucht man
das nicht oder stört einem das legt man Timer0 still. Beim Verzicht auf
setup() und loop() fällt auch alles andere weg. Serial bspw. Wer bei
Arduino in die Tiefe eintaucht muss wissen was er tut. Dafür ist Arduino
so nicht vorgesehen. Muss man auch dazu sagen. Man kann aber alles
machen wenn man die Defaultconfig kennt und abschaltet. Woran bist du
denn gescheitert?
Übrigens ist das timer1Init von Helmut schon gefährlich.
1
voidtimer1Init(){
2
TIMSK1=0;// no timer ints during init
3
TCNT1=0;
4
ovcnt=0;
5
TCCR1A=(tim1cmoA<<6)|(tim1cmoB<<4)|(tim1wgm&3);
6
TCCR1B=(tim1ica<<6)|tim1cs|((tim1wgm<<1)&0x18);
7
TIMSK1=tim1ocie;// Timer interrupts
8
}
Ohne gestoppten Timer wird konfiguriert und läuft auch ohne fertige
Konfig los bzw. weiter. Das heißt der Timer läuft für einen Moment
unkontrolliert.
Veit D. schrieb:> Weil der für micros(), millis() und delay() verwendet wird. Braucht man> das nicht oder stört einem das legt man Timer0 still.
Wenn ich mich richtig erinnere, sind die TIMER0_xxx-Vektoren schon
belegt und kollidieren mit den eigenen.
> Man kann aber alles> machen wenn man die Defaultconfig kennt und abschaltet. Woran bist du> denn gescheitert?
Man muß sie erst einmal finden. Da geht es schneller, die eigenen
Routinen hinzuzuklicken. Bei einer 'richtigen' IDE klicke ich auf 'Goto
Definition' und die Sache wird angezeigt. Das soll aber hier nicht das
Thema werden.
Veit D. schrieb:> Übrigens ist das timer1Init von Helmut schon gefährlich.> Ohne gestoppten Timer wird konfiguriert und läuft auch ohne fertige> Konfig los bzw. weiter. Das heißt der Timer läuft für einen Moment> unkontrolliert.
Ist es so besser?
1
voidtimer1Init(){
2
TCCR1B=0;// stop timer by setting CS to 0
3
TIMSK1=0;// no timer ints during init
4
TCNT1=0;
5
ovcnt=0;
6
TCCR1A=(tim1COM1A<<6)|(tim1COM1B<<4)|(tim1WGM&3);
7
TIMSK1=tim1TIMSK1;
8
TCCR1B=(tim1IC<<6)|tim1CS|((tim1WGM<<1)&0x18);
9
}
NB: Da alle tim1... Parameter zur Laufzeit eingegeben werden können,
kann ich die übliche Schreibweise z.B.
Hallo,
Michael, dass muss ich nun schon richtig stellen. Es kollidiert erstmal
so gesehen nichts. Man muss nur, eben wegen der Defaultkonfig des
Arduino Frameworks, am Besten alle Register nullen. Also in deinem Fall
alle vom Timer 0. So wie es auch in "bare metal" wäre. Danach den Timer
0 nach seinen Wünschen konfigurieren. Die ISRs kann man jederzeit selbst
verwenden. An den Vectoren muss man nicht rumfummeln. Um die Konfig vom
Arduino Framework zu sehen muss man nur sich den Inhalt der Register
ausgeben lassen. Das geht schneller als sich durch die Dateien zu
wühlen. Wenn man die Timerregister sowieso resetet, muss man auch das
nicht machen. Wäre nur zur eigenen Information was default ist.
Das Arduino Framework bereitet alle Timer vor für micros(), millis(),
delay() und analogWrite(). Im Fall von analogWrite() heißt das aber
nicht das alle schon aktiv sind, nur vorbereitet. Deswegen bei Arduino
erste Grundregel bei eigener Konfig der Hardware immer erst alle
Register reseten. Bei Timer 0 hat man dann natürlich kein micros(),
millis(), delay() mehr.
Helmut, dass sieht schon besser aus. Ich habe begonnen einzelne
Funktionen zum konfigurieren zu schreiben. Siehe Anhang. Einmal großer
Aufwand danach nur noch anwenden. Die Nummern der Timer Modi entsprechen
dem Manual, dass ist wichtig, für sich selbst und für andere. Statt der
Nummern kann man noch sprechende Variablennamen anlegen welche dem
Timermode klar erkennen lassen.
> Nimm einmal für die Takterzeugung einen anderen> freien Timer, Takterzeugung rein in Hardware ohne ISRs.
Dann doch gleich direkt per Setzen von PB0: Timer1 mit einem Wert knapp
unterhalb des Uberlaufs starten, eine veränderliche Anzahl von 'nop's
warten, dann 'sbi PORTB,0' - ja, ich halte bei dieser Thematik Assembler
für das geeignete Werkzeug, aber in C geht das wohl auch.
S. L. schrieb:> Dann doch gleich direkt per Setzen von PB0: Timer1 mit einem Wert knapp> unterhalb des Uberlaufs starten, eine veränderliche Anzahl von 'nop's> warten, dann 'sbi PORTB,0'
Das wäre ja noch ungenauer, alle Interrupts würden darin ihren Jitter
abladen. Und selbst, ob ein Befehl im Main 1..4 Zyklen braucht.
> alle Interrupts
Es gibt ja nur, gegebenenfalls, die OVF1-ISR, deren Einfluss eindeutig
im Zählwert erkennbar ist.
> 1..4 Zyklen
Das verstehe ich nicht: es sind nur nops, und damit eine Auflösung von 1
Takt.
Veit D. schrieb:> Michael, dass muss ich nun schon richtig stellen. Es kollidiert erstmal> so gesehen nichts.
Wenn Du Erfahrung damit hast, wird es sicherlich kein großes Problem
sein. Wegen besserer Alternativen habe ich bei wiederholten Anläufen
relativ schnell das Handtuch geworfen.
Die Arduino-IDE habe ich immer dann verwendet, wenn der schon weitgehend
getestete Sourcecode (AVR Studio oder IAR) vom Anwender mit einfachen
Mitteln modifizierbar sein sollte.
[OT]
Meine letzten Versuche diesbezüglich habe ich mit Quellcode für ein
RP2040 Pico-Board gemacht. Bei Arduino ist wohl das Pico-SDK unterlegt.
Viele, viele Bäume und kein Wald zu sehen. Die übliche 'RP2040.h' Datei
ist überall angeeckt und die verwunschenen Pfade vom Pico-SDK wollte ich
nicht gehen.
Zudem brauchen schon kleinste Programme eine kleine Ewigkeit, bis sie
übersetzt sind. Das macht einfach keinen Spaß; erst recht nicht mit
fehlendem Degugging. Schade.
[/OT]
S. L. schrieb:> Dann doch gleich direkt per Setzen von PB0: Timer1 mit einem Wert knapp> unterhalb des Uberlaufs starten, eine veränderliche Anzahl von 'nop's> warten, dann 'sbi PORTB,0' - ja, ich halte bei dieser Thematik Assembler> für das geeignete Werkzeug, aber in C geht das wohl auch.
Was soll das denn jetzt noch bringen? Das Problem ist in C vollständig
und fehlerfrei handhabbar. Auf Assembler umzusteigen bringt - gerade
auch für Anfänger - eher Verwirrung und vermittelt den Eindruck, es wäre
kompliziert.
Mi N. schrieb:> Das wird gerade auf der Kippe sein. Wann auf Interrupts reagiert wird,> hängt ja auch von dem gerade bearbeitetem Befehl ab.
Das ist für mich die Erklärung des Verhaltens, weil zwischen Ereignis
und Bearbeitung zufällig 1 bis 4 Takte liegen. Da hier der Overflow (O)
das Capture-Ereignis (C) auslöst, wird es immer (1 oder 2 Takte?) später
erkannt.
Wenn der aktuelle Befehl nach O aber vor C fertig ist, wird die
Overflow-ISR zuerst ausgeführt. Wenn nach C, wird die Capture-ISR zuerst
ausgeführt.
Vielen Dank an alle für die freundliche Beratung.