Hallo,
leider kann ich grade nichts testen deswegen frage ich.. (sorry)
Ich möchte ein Tacho bauen, dass so funktioniert:
Ein 8bit-Timer läuft durch. Der Timer Overflow Interrupt erhöht einen
Counter. Wenn an INT0 eine Flanke kommt, wird der counter wert und die
Zahl der Timer-'Ticks' gesamplet.
Die zahl der Ticks ist dann Counter*256+Rest-Ticks
Wenn der Controller nun die INT0-ISR betritt - die ISR ist ja naked,
oder?, wie viele Ticks sind da vergangen bis der Erste Befehl ausgeführt
wurde?
1
ISR(...){
2
sample=TCNTx
3
...
4
}
also wieviele Takte vergehen bis die erste Zeile erreicht bzw.
durchgelaufen ist? Der Befehl ld register, TCNTx , zählt der takt noch
zu TCNTx dazu oder wird TCNTx erst nachdem der Befehl ausgeführt wurde
inkrementiert?
Oder anders: Wieviele Takte muss ich von TCNTx abziehen damit ich die
Korrekte anzahl der Ticks zum Zeitpunkt des Auftretens der Flanke habe?
Und: Springt INT0 direkt im Takt der Flankenerkennung in die ISR oder
einen Takt später?
Vielen Dank,
Elmo
elmo schrieb:> leider kann ich grade nichts testen deswegen frage ich.. (sorry)
warum nicht, geht dein simulator nicht?
Egal ob es nun 4 oder 5 Takte sind, es ist zuminest nicht konstant. Es
kommt darauf an welcher befehl gerade ausgeführt wird. Es kann sogar
viel mehr sein wenn gerade eine andere ISR ausgeführt wird. Dann Spielt
auch noch eine rolle viele Variable in der ISR sind, dann es müssen
Register gesichert werden. Es wird also so nicht gehen.
> Oder anders: Wieviele Takte muss ich von TCNTx abziehen damit ich die> Korrekte anzahl der Ticks zum Zeitpunkt des Auftretens der Flanke habe?
Wenn Du die mit ein paar simplen Klimmzügen als hinreichend konstant
annimmst, dann kannst Du die getrost unterschlagen.
> Und: Springt INT0 direkt im Takt der Flankenerkennung in die ISR oder> einen Takt später?
Weder noch. Die Details stehen im Datenblatt.
Einfacher wirds vermutlich wenn Du keinen externen Interrupt nimmst um
die Zeitdifferen zu messen, sondern das Tachosignal an den Zähler hängst
und den dann per Timer regelmäßig abfragst. Zumindest dann wenn Du keine
aberwitzig kurzen Reaktionszeiten brauchst - so wie bei einem 'Tacho'
eben, da langt es locker flockig die Ausgabe ein paar mal pro Sekunden
zu aktualisieren.
HTH
Angenommen ich mache das mit Assembler nicht inline sondern seperat.
Dann müsste ich keine Variablen pushen. Angenommen in der anderen ISR
(in der, der Counter inkrementiert wird) lasse ich das interrupt-flag
eingeschaltet... Dann wäre das einzigste taktverlängernde Moment doch
wenn ein 2- oder 3-Takt Befehl läuft?
Angenommen es kann sofort gesprungen werden:
Dann habe ich die Flanke -> Detektor erkennt das im nächsten Takt -> jmp
zur Interrupt address -> jmp von da in die ISR -> register samplen.
das wären 1+2+2+2 = 7 Takte?
elmo schrieb:> lasse ich das interrupt-flag> eingeschaltet... Dann wäre das einzigste taktverlängernde Moment doch> wenn ein 2- oder 3-Takt Befehl läuft?
nein, es kann sein das eine andere ISR schon läuft
Hallo elmo,
es spricht viel dafür, dass du von Atmel Controllern sprichst. ;-)
Allerdings machst du keine weitere Angaben. "Größere" ATTinys (z.B.
ATTiny24) und neuere ATMEGAs haben eine "Input Capture"-Funktion. Damit
wird auf ein externes Signal hin der aktuelle Timer1-Wert gespeichert
(16-Bit). Dann kannst du dir "Zeit lassen" mit dem Eintritt in die ISR
ohne Genauigkeit zu verlieren.
elmo schrieb:> Angenommen ich mache das mit Assembler nicht inline sondern seperat.> Dann müsste ich keine Variablen pushen. Angenommen in der anderen ISR> (in der, der Counter inkrementiert wird) lasse ich das interrupt-flag> eingeschaltet... Dann wäre das einzigste taktverlängernde Moment doch> wenn ein 2- oder 3-Takt Befehl läuft?
Nein, das I-Flag wir automatisch beim Sprung in einen Interrupt
gelöscht. Du könntest es höchstens dort wieder manuell setzen. Ist aber
nicht gerade zu empfehlen.
ICU geht leider nicht, weil ich mehr als ein Signal habe. Der Controller
ist irgendeiner aus der 'älteren' ATmega-Reihe (8,16,32) - sorry, dass
ich das vergessen habe.
>Wenn Du die mit ein paar simplen Klimmzügen als hinreichend konstant>annimmst, dann kannst Du die getrost unterschlagen.
Würde ich das alles Relativ betrachten ja, aber ich möchte auf ca. 0,1Hz
genau messen. Das Problem ist, dass der Messbereich mit 10-60Hz relativ
niedrig liegt, daher die Flanken-Methode. Ansonsten würde ich es ja
anders machen.
> Weder noch. Die Details stehen im Datenblatt.
schön und gut, aber ich dachte es geht schneller und einfacher, wenn ich
hier frage, anstatt den Haupt-Takt durch diverse Ebenen zu verfolgen und
zu raten, wann der Flankendetektor zum Interrupt springt.
ist es nicht viel einfacher einfach einen Timer laufen zu lassen. Jeder
Flankenwechsel erhöht eine Variable. Nach einem festen Zeitintervall
fragt der Timer die Variabelen ab und setzt sie auf 0 zurück. Wenn du es
aller 1s machst hast du sogar gleich die Angaben in Hz.
> elmo schrieb:> > lasse ich das interrupt-flag> > eingeschaltet... Dann wäre das einzigste taktverlängernde Moment doch> wenn ein 2- oder 3-Takt Befehl läuft?> nein, es kann sein das eine andere ISR schon läuft
ja und? wenn das globale Interrupt-flag gesetzt ist?
@Detlev, ja ich spreche von Atmel ATmega, nur dumm, dass ich den
16bit-Timer für eine relativ schnelle PWM brauche und der OC0-Eingang
vom 8bit Timer nicht ausreicht (ATmega8,32..)
>Nein, das I-Flag wir automatisch beim Sprung in einen Interrupt>gelöscht. Du könntest es höchstens dort wieder manuell setzen. Ist aber>nicht gerade zu empfehlen.
Warum nicht? Wenn ich meine Takte abgezählt habe und genau weiß, dass
die ISR nicht unendlich tief schachteln? aber ok, das I-Flag setzen wäre
dann 1-Takt mehr.
elmo schrieb:>> nein, es kann sein das eine andere ISR schon läuft> ja und? wenn das globale Interrupt-flag gesetzt ist?
wann willst du es denn setzen, du kannst es ja nur setzen nach dem du in
der ISR bist, aber da sind ja schon wieder ein paar takte vergangen.
> @Peter: nein das geht nicht, weil die zu messende Frequenz sehr niedrig> ist. Mit dieser Methode bekomme ich hier keine gute Auflösung hin.
du kannst du Timer ja auch 1 Stunde festlegen, dann wird es sehr genau.
Du must schon mal schreiben wie oft die aktuelle werte brauchst.
> wann willst du es denn setzen, du kannst es ja nur setzen nach dem du in> der ISR bist, aber da sind ja schon wieder ein paar takte vergangen.
Es dürften ja Takte vergehen, ich möchte nur wissen wieviele. 6? 7? 8?
Damit ich die vom TCNTx abziehen kann.
> du kannst du Timer ja auch 1 Stunde festlegen, dann wird es sehr genau.> Du must schon mal schreiben wie oft die aktuelle werte brauchst.
10Hz - 60Hz, 0.1Hz Auflösung mindestens. 16Mhz Quarz, 16bit-Timer ist
vergeben und durch sehr kleinen TOP-Wert unbrauchbar.
elmo schrieb:> 10Hz - 60Hz, 0.1Hz Auflösung mindestens. 16Mhz Quarz, 16bit-Timer ist> vergeben und durch sehr kleinen TOP-Wert unbrauchbar.
Die Frage war wie Oft du neue Werte brauchst, nicht in welchen
Frequenzbereich du messen willst.
elmo schrieb:> Es dürften ja Takte vergehen, ich möchte nur wissen wieviele. 6? 7? 8?> Damit ich die vom TCNTx abziehen kann.
Einen festen Wert gibt es nicht, da es von der Laufzeit des grad aktiven
Befehls abhängt.
Ausserdem sollte dir das einigermassen egal sein, denn wenn du die Zeit
zwischen zwei Interrupts misst, dann tritt diese Latenz beidesmal auf
und subtrahiert sich automatisch raus. Nur die Variabilität dieser Zeit
wird mit gemessen und die kannst du nicht vermeiden.
willst du mir jetzt 1024-bit integer aufdrücken um mit 16Mhz Quartz
genau zu messen oder was?
ich will doch bloß wissen, wie viele Takte vergehen von der Flanke bis
zur Dispatcher Table und, ob TCNTx vor oder nach dem Befehl, der in
diesem Takt bzw. den nächsten 2 ausgeführt wird inkrementiert wird,
verdammt.
Apropos Latenz: Eine laufende ISR blockiert den Interrupt, d.h. zu den
erwähnten Takten und der unwägbarkeit der Latenz kommt noch die Laufzeit
der Timer-ISR hinzu.
>Ausserdem sollte dir das einigermassen egal sein, denn wenn du die Zeit>zwischen zwei Interrupts misst, dann tritt diese Latenz beidesmal auf>und subtrahiert sich automatisch raus. Nur die Variabilität dieser Zeit>wird mit gemessen und die kannst du nicht vermeiden.
ja, dass stimmt natürlich, danke für den Gedankenanstoß, 1 oder 2 Takte
sind mir egal, das dürfte trotzdem passen.
elmo schrieb:> ich will doch bloß wissen, wie viele Takte vergehen von der Flanke bis> zur Dispatcher Table und, ob TCNTx vor oder nach dem Befehl, der in> diesem Takt bzw. den nächsten 2 ausgeführt wird inkrementiert wird,> verdammt.
das kann dir niemand sagen, weil es nicht konstant ist. Es ist auch mit
AMS nicht Konstant, du kannst nichts tun damit es Konstant wird.
> willst du mir jetzt 1024-bit integer aufdrücken um mit 16Mhz Quartz> genau zu messen oder was?
60Hzv eingangsfrequenz, abfrage aller 10min. Dafür reicht ein 16bit int.
Und dann bist du genauer als 0.01Hz
PS: Dazu kommt noch die nicht uninteressante Aufgabe, bei annähernd
gleichzeitigem TimerOverflow und ExtInt den halb in Hardware und halb in
Software gegossenden Gesamtzähler wirklich korrekt auszulesen, ohne
dabei ab und zu 256 zu viel oder zu wenig zu erhalten.
>Apropos Latenz: Eine laufende ISR blockiert den Interrupt, d.h. zu den>erwähnten Takten und der unwägbarkeit der Latenz kommt noch die Laufzeit>der Timer-ISR hinzu.
die Timer-ISR läuft schneller als 60Hz, von daher aktiviere ich in der
Timer-ISR einfach das interrupt-flag, dann läuft der Flanken-interrupt
meinetwegen, aber der ist nur so kurz, dass diese ISR und die Timer-ISR
durchlaufen können, bis die Timer-ISR das nächste mal aufgerufen wird.
also kein problem.
elmo schrieb:> die Timer-ISR läuft schneller als 60Hz, von daher aktiviere ich in der> Timer-ISR einfach das interrupt-flag, dann läuft der Flanken-interrupt
Klar, aber damit sind es nicht mehr ein paar Takte Variabilität des
laufenden Befehls, sondern deutlich mehr. Die Laufzeit der Timer-ISR ab
Reaktion des Prozessors bis einschliesslich des Befehls nach SEI geht
voll in die Ungenauigkeit ein.
>60Hzv eingangsfrequenz, abfrage aller 10min. Dafür reicht ein 16bit int.>Und dann bist du genauer als 0.01Hz
Stimmt du hast recht, sorry.
>PS: Dazu kommt noch die nicht uninteressante Aufgabe, bei annähernd>gleichzeitigem TimerOverflow und ExtInt den halb in Hardware und halb in>Software gegossenden Gesamtzähler wirklich korrekt auszulesen, ohne>dabei ab und zu 256 zu viel oder zu wenig zu erhalten.
deshalb schalte ich das i-Flag auch erst wieder ein, wenn ich den
overflow vermerkt habe ;)
vielleicht wäre es auch sinnvoll, die ganze dispatch table einfach mit
den befehlen für den interrupt vollzuklatschen, dann spare ich einen jmp
im timer overflow, aber da brauche ich genug platz, damit ich nicht mit
anderen Interrupts die ich brauche kollidiere --- lol
>Klar, aber damit sind es nicht mehr ein paar Takte Variabilität des>laufenden Befehls, sondern deutlich mehr. Die Laufzeit der Timer-ISR ab>Reaktion des Prozessors bis einschliesslich des Befehls nach SEI geht>voll in die Ungenauigkeit ein.
deswegen packe ich gleich an die stelle in der dispatch table ein ld
gefolgt von sei, dann habe ich nur 3 takte, ab dem vierten kann wieder
gesprungen werden.
dann kann ich ein paar vektoren später aus der dispatch table in die
restliche ISR springen ;)
elmo schrieb:> deshalb schalte ich das i-Flag auch erst wieder ein, wenn ich den> overflow vermerkt habe ;)
Was die maximale Latenz weiter erhöht.
Aber ich fürchte es wird schon ein bischen komplizierter. Zwischen
Auslösung des ExtInt und dem Auslesen des Timer-Registers vergehen
einige Takte. Wenn dazwischen der Timer überläuft, dann wird die Sache
interessant. Selbst SEI im ExtInt-Handler bringt dich nicht wirklich
weiter. Da musst du schon ein bischen mehr Grips reinstecken.
elmo schrieb:> deswegen packe ich gleich an die stelle in der dispatch table ein ld> gefolgt von sei, dann habe ich nur 3 takte, ab dem vierten kann wieder> gesprungen werden.
geht aber nur wenn du die nächsten 2 ISR nicht brauchst.
>Ich fürchte es wird schon ein bischen komplizierter. Zwischen Auslösung>des ExtInt und dem Auslesen des Timer-Registers vergehen einige Takte.>Wenn dazwischen der Timer überläuft, dann wird die Sache interessant.>Selbst SEI im ExtInt-Handler bringt dich nicht wirklich weiter
hmm, naja, aber das wäre ja ein ausreißer, den man durch genügend
trägheit ausgleicht oder? außerdem tritt der auch nur alle 256 / 3
durchläufe auf? alternativ könnte man zusätzlich das Timer register
auslesen in der ExtInt ISR dann feststellen, ob ein Überlauf währen der
Laufzeit dieser ISR erfolgt und dementsprechen den Timer zurücksetzen
und die übrigen Ticks manuell akkumulieren. oder?
elmo schrieb:> alternativ könnte man zusätzlich das Timer register> auslesen in der ExtInt ISR dann feststellen, ob ein Überlauf währen der> Laufzeit dieser ISR erfolgt
Müsste so gehen: Im ExtInt-Handler Interrupts gesperrt lassen. Timer
auslesen, danach TimerOvf-Intflag testen. Wenn gesetzt und ausgelesener
Timer-Wert hinreichend klein, dann hat man ihn verpasst.
elmo schrieb:> alternativ könnte man zusätzlich das Timer register> auslesen in der ExtInt ISR dann feststellen, ob ein Überlauf währen der> Laufzeit dieser ISR erfolgt und dementsprechen den Timer zurücksetzen> und die übrigen Ticks manuell akkumulieren. oder?
je mehr code man schreib je ungenauer wird das timing. Teste es doch
einfach mal im Simulator und geht jede Situation mal durch. Du wirst
merken das du damit nicht zum ziel kommst.
Alter Schalter, was für ein S.. natürlich ist das unpraktikabel und
dummer Scheiß den niemand debuggen möchte, auch ich nicht, aber sich
mindestens(!) theorethisch mit solchen Basteleien auseinanderzusetzen,
das ist doch spannend. Besser als die Interrupt-Tabelle mit einer ISR
vollzuklatschen ist doch nur ein Programm, dass sich aus einer
mathematischen Funktion erzeugt und per SPM erstmal den Controller neu
flasht bevor es losgeht...
Ich entscheide mich dann wohl doch lieber für Peters methode oder etwas
ähnlich 'simples'..
Danke für die Hilfe.
Peter schrieb:> je mehr code man schreib je ungenauer wird das timing. Teste es doch> einfach mal im Simulator und geht jede Situation mal durch. Du wirst> merken das du damit nicht zum ziel kommst.
Code an dieser Stelle zählt nicht. Kritisch ist der Code im
TimerOvf-Handler bis SEI+nächster und im ExtInt-Handler bis zum Auslesen
vom Timer. Der Rest dahinter geht nicht mit ins Ergebnis ein.