Hallo,
An meinem atmega8 löse ich einen externen interrupt aus. In der ISR wird
lediglich ein Ausgang auf high gesetzt (1 Zeile Code). Die Reaktionszeit
beträgt bei 16MHz ca 1,25 Mikrosekunden. Woran liegt das? Gibt es eine
Möglichkeit das Ganze etwas zu beschleunigen?
Hi
0,063µs / Takt -> ~20 Takte
Sprung zur ISR 2/3 Takte
Setzen des IO ebenfalls um den Dreh
Bis auf dem PC-Int reagiert wird, vergehen auch 1/2 Takte im Simulator
Wie viele Takte hattest Du erwartet?
Was spricht der Simulator, wie viele Takte es sein sollten?
Womit hast Du die 1,25µs ermittelt?
Reproduzierbar?
MfG
Ich habe so an die 5-6 Takte erwartet, sprich ca 400ns. Ist das
realistisch? Mit dem Simulator habe ich nicht getestet. Gemessen habe
ich mit dem Oszi direkt am Ausgangspin.
fixi schrieb:> An meinem atmega8 löse ich einen externen interrupt aus. In der ISR wird> lediglich ein Ausgang auf high gesetzt (1 Zeile Code). Die Reaktionszeit> beträgt bei 16MHz ca 1,25 Mikrosekunden. Woran liegt das?
Schlechte Programmierung, falsche Sprache.
> Gibt es eine> Möglichkeit das Ganze etwas zu beschleunigen?
Klar: Assembler. Damit ist bei Interruptsteuerung ein progagation delay
für eine einzelne Flanke von 6 Takten möglich, bei einer Endlosschleife
sind's sogar nur 4.
Sprich, bei 16MHz: 375 bzw. 250ns.
fixi schrieb:> Gibt es eine Möglichkeit Assembler im C-Code zu verwenden?
Das brauchst Du nicht.
Auf den Prolog und Epilog kannst Du durch das Attribute ISR_NAKED
verzichten.
Dann werden r0, r1 und SREG (und evtl. andere Register) nicht gesichert.
fixi schrieb:> Ich habe so an die 5-6 Takte erwartet, sprich ca 400ns. Ist das> realistisch?
Absolut nein.
Der AVR ist ein RISC, da sind 50 Takte nichts.
Schon der nackte Interrupteinsprung + Return kostet 10 Takte. Dann noch
Prolog, Epilog (Register retten).
Auch muß man damit rechnen, daß ja gerade eine anderer Interrupt in
Arbeit sein könnte. Assembler ist daher nur selten zielführend.
Kalkuliere nen Interrupthandler mit ~200 Takten, das reicht für die
meisten kleineren Sachen.
Peter D. schrieb:> Kalkuliere nen Interrupthandler mit ~200 Takten, das reicht für die> meisten kleineren Sachen.
Das ist aber schon Schwarzmalerei. Sofern keine anderen Interrupts aktiv
sind oder diese passend programmiert werden, ist eher von 30 - 50 Takten
auszugehen.
m.n. schrieb:> Sofern keine anderen Interrupts aktiv> sind
Genau das ist der Pferdefuß.
Eine Applikation soll ja in der Regel noch viel mehr machen.
Ein Interrupthandler darf nicht so tun, als wäre er ganz allein auf der
Welt (im Flash). Er muß den anderen Tasks auch CPU-Zeit zugestehen.
Peter D. schrieb:> m.n. schrieb:>> Sofern keine anderen Interrupts aktiv>> sind
und weiter zitiert: ... oder diese passend programmiert werden.
Wenn man hohe Interruptlast im System hat, muß man sich auch ein paar
Gedanken über die Prioritäten machen.
Nur sehe ich diese Probleme beim TO überhaupt nicht.
Wilhelm M. schrieb:> Auf den Prolog und Epilog kannst Du durch das Attribute ISR_NAKED> verzichten.> Dann werden r0, r1 und SREG (und evtl. andere Register) nicht gesichert.
Das ist selbst schon bei dieser einfachen ISR tödlich!
Transparente und schnelle ISRs programmiert man in Assembler, wo nur die
Register gerettet werden, die unbedingt gebraucht werden mit SREG an
allererster Stelle.
Aber gut, dazu ist schon an anderen Stellen bis zum Abwinken diskutiert
worden.
Wie so oft:
Vielleicht ist es auch an der Zeit zu erörtern was eigentlich
das Ziel des ganzen Unternehmens sein soll, denn die schnelle
ISR zu schreiben soll ja wohl keinen reinen Selbstzweck
erfüllen.
Sonst werden hier ja nur reihenweise die Pferde scheu gemacht
für nix und wieder nix ....
Arduinoquäler schrieb:> denn die schnelle> ISR zu schreiben soll ja wohl keinen reinen Selbstzweck> erfüllen.
Und falls doch, dann ist ein ATmega8 mit 8kB Flash für ein <100Byte
Progrämmchen leicht oversized.
Peter D. schrieb:> Und falls doch, dann ist ein ATmega8 mit 8kB Flash für ein <100Byte> Progrämmchen leicht oversized.
Wenn es um den eigentlichen Sinn der Übung geht (es soll ja
keine Übung sein?) wird es sehr schnell sehr ruhig.
Eine oft gemachte Erfahrung hier ....
fixi schrieb:> Kann jemand meinen Code so umschreiben, dass die Reaktionszeit besser> wird?
fixi schrieb:> Kann jemand meinen Code so umschreiben, dass die Reaktionszeit besser> wird? Wenn es sein muss, kann ich auch mit Assembler in der ISR leben.
Hast Du denn schon selbst mal die Vorschläge (ISR_NAKED) ausprobiert?
Bzw. den Assembler-Code untersucht?
Arduinoquäler schrieb:> Wie so oft:>> Vielleicht ist es auch an der Zeit zu erörtern was eigentlich> das Ziel des ganzen Unternehmens sein soll, denn die schnelle> ISR zu schreiben soll ja wohl keinen reinen Selbstzweck> erfüllen.>> Sonst werden hier ja nur reihenweise die Pferde scheu gemacht> für nix und wieder nix ....
Finde ich nicht! Es ist ein MCVE, das ausreicht. Und ein klare Frage.
Wilhelm M. schrieb:> Es ist ein MCVE, das ausreicht.
Malaysia Commercial Vehicle Expo reicht aus?
Aha ... so so .... sehr interessant .... deine eigene Welt ...
Arduinoquäler schrieb:> Wilhelm M. schrieb:>> Es ist ein MCVE, das ausreicht.>> Malaysia Commercial Vehicle Expo reicht aus?>> Aha ... so so .... sehr interessant .... deine eigene Welt ...
Wohl kaum! Schon mal gegoogelt: MCVE code -> erster Eintrag.
Wilhelm M. schrieb:> Wohl kaum! Schon mal gegoogelt: MCVE code -> erster Eintrag.
Zu deiner eigenen Welt gehört dazu dass du es dem Suchenden
überlässt wie er sucht.
Also: MCVE --> Malaysia Commercial Vehicle Expo
Zu deiner eigenen Welt gehört auch dazu dass du ohne Not
irgedwelche Abkürzungen verwendest die kein Schwein kennt.
m.n. schrieb:> Wilhelm M. schrieb:>> Nö.>> Und Du meinst, man könne R24 mal eben so zerschießen?
In diesem, sehr speziellen Fall ist das aber kein Problem! Schau Dir
hast Listing genau an:
Arduinoquäler schrieb:> Wilhelm M. schrieb:>> Wohl kaum! Schon mal gegoogelt: MCVE code -> erster Eintrag.>> Zu deiner eigenen Welt gehört dazu dass du es dem Suchenden> überlässt wie er sucht.>> Also: MCVE --> Malaysia Commercial Vehicle Expo>> Zu deiner eigenen Welt gehört auch dazu dass du ohne Not> irgedwelche Abkürzungen verwendest die kein Schwein kennt.
Schon mal was von StackOverflow gehört ... (wer ist das Schwein?)
Wilhelm M. schrieb:> Es ist ein MCVE, das ausreicht. Und ein klare Frage.
MCVE nimmt man für Bugreports.
Hier handelt es sich aber um keinen Bug, sondern um falsche Erwartungen
bezüglich der Ausführungszeit.
Und zur Klarheit fehlen noch sämtliche wichtigen Randbedingungen (wie
oft, welche anderen Tasks usw.).
Unter speziellen Bedingungen kann man auf eine externe Flanke innerhalb
2..3 Takten einen Pin setzen:
Timer als Zähler, Auswahl der Flanke, Signal an den Timereingang, Set
Output-Pin on Compare, Comparewert auf Timer+1.
Peter D. schrieb:> Wilhelm M. schrieb:>> Es ist ein MCVE, das ausreicht. Und ein klare Frage.>> MCVE nimmt man für Bugreports.
Nope, generell überall für jede Art von Frage, die mit einem bestimmten
Codeverhalten (bug oder auch nicht) zu tun hat:
FYI: https://stackoverflow.com/help/mcve
FYI: for your interest
HTH
HTH: Hope that helps
AFAIK, hat das aber alles nichts mit der Frage zu tun...
http://www.catb.org/jargon/html
Meiner Meinung nach baut man sich mit ISR_NAKED eine Zeitbombe in den
Code. Da nicht definiert ist, welche Register der Compiler für die
folgenden Befehle benutzt, ist eine manuelle Sichtung des Outputs
eigentlich unumgänglich. Spätestens bei einer Änderung der
Compilereinstellungen oder einem Compilerupdate können ganz andere
Register zu sichern sein.
Und ich finde es auch nicht wirklich sinnvoll. Wenn man nicht gerade
Funktionen im ISR aufruft, die nicht im gleichen C-File implementiert
sind, dann sichert der gcc die Register sehr sparsam. Meiner Erinnerung
sind es ein, höchstens 2 Register, die man händisch einsparen könnte.
Falls man tatsächlich einen derart zeitkritischen Code hat, dann sollte
man auf eingebetteten Asm-Code innerhalb von ISR_NAKED ausweichen.
Gruß, Stefan
Stefan K. schrieb:> Meiner Meinung nach baut man sich mit ISR_NAKED eine Zeitbombe in den> Code.
Sehe ich genauso. Aber die Frage war nach den Möglichkeiten ...
> Und ich finde es auch nicht wirklich sinnvoll. Wenn man nicht gerade> Funktionen im ISR aufruft, die nicht im gleichen C-File implementiert> sind, dann sichert der gcc die Register sehr sparsam.
Oder er macht ein inlining der Funktion. Wenn man Templates (C++)
verwendet sogar (fast) immer (da es dieselbe TU, ups,
Übersetzungseinheit ist).
fixi schrieb:> Ich habe so an die 5-6 Takte erwartet, sprich ca 400ns. Ist das> realistisch?
Minimum:
2 Takte Flankenerkennung
4 Takte Interrupt response time
2 Takte rjmp IRQ-Vector
1 Takt Ausgang setzen (per sbi)
In C ohne "naked" kommen da im Idealfall noch 2 Takte zur Rettung des
Status Registers dazu.
Oliver
Wilhelm M. schrieb:> ... war wohl ursprünglich mein Fehler ...
Dem ist wohl so, ärgere mich dennoch über diesen peinlichen Lapsus, so
entstehen Fehler.
Der Ablauf ist eigentlich recht einfach:
1. Es dauert ein bisschen, bis eine Unterbrechung erkannt wird
(1 bis 2 Takte). Üblicherweise sind die ja auch asynchron.
Siehe Handbuch.
2. Nicht jeder Befehl kann unmittelbar unterbrochen werden, manche
müssen halt beendet werden (1 bis 2 Takte).
3. Die Rücksprungadresse muss auf dem Stapel abgelegt werden
(2 Bytes Push).
4. Die neue "Arbeitsadresse" muss in den Programmzähler geholt werden.
5. Arbeitest Du nicht im Assembler, so wird hier praktisch immer noch
das Statusregister gesichert.
6. MACH DEIN DING. Zusätzlich fast immer 1 X Push und 1 X Pop.
7. Ein Compiler wird hier das Statusregister restaurieren.
8. Interrupt beenden meist mittel IRET. Also mindestens 2 X Pop.
Also: Auch wenn Du in Assembler programmierst, gibt es einige Sachen,
die unumgänglich sind. Die Meisten sind mit etwas Nachdenken einfach
nachzuvollziehen.
Natürlich fällt, bei den heutigen Compilern, kein "alle Register
sichern" und kein "alle Register restaurieren" am Ende der Routine mehr
an.
Habe bestimmt den einen oder anderen Takt vergessen.
Klar ist der Code etwas primitiv. Es geht aber erstmal nur darum die
Reaktionszeit zu testen, bevor es ans eigentliche Projekt geht. Dafür
sollte dieses Beispiel ausreichend sein. Wie es aber scheint, gibt es
(noch) keine Lösung für ein schnelleres ansprechen :(
Später soll das Programm doch etwas umfangreicher werden.
Dorftrottel schrieb:> oh, ich überlas, es ging um den INT1-Interrupt...> dann eben so...ISR(INT1_vect, ISR_NAKED) {> asm volatile (> "push r16" "\n\t"> "ldi r16, 0xFF" "\n\t"> "out %0, r16" "\n\t"> "pop r16" "\n\t"> :> : "M" (_SFR_IO_ADDR (PORTB))> );> asm volatile ( "reti" );> }> Asche über mein Haupt.
das funktioniert so leider nicht ganz - PORTB ist dauerhaft auf high.
Hi
Wie sieht es aus, die scheinbar nicht auszumerzenden Takte durch
brachiale Prozessor-Geschwindigkeit anszutricksen?
Wenn bei Deiner aktuellen Geschwindigkeit 40 Takte vergehen, wo Du mit
maximal 4 Takten leben kannst - Tiger in den Tank und Quarz x10 in den
Slot - die Wartetakte hast Du zwar immer noch, sind aber viel schneller
vorbei.
MfG
PS:
fixi schrieb:> das funktioniert so leider nicht ganz - PORTB ist dauerhaft auf high.
Dann ersetze versuchshalber
"ldi r16, 0xFF" "\n\t"
durch
"ldi r16, 0x00" "\n\t"
Damit beträgt die Reaktionszeit nur noch 600ns. Leider immernoch etwas
zu langsam...
Aber wie ich den Reaktionen entnehmen kann, sollte ich lieber die Finger
von diesem Ansatz lassen?
Hi
Darauf wollte ich hinaus - wenn im ISR steht, daß der Port bei JEDEM
Abarbeiten komplett auf HIGH oder LOW gesetzt wird, wird der Port wohl
so gesetzt.
Sofern das Hauptprogramm da Nichts dran ändert.
Da Das das einzige Register war, was eine Information enthielt und hier
wohl PortB beschrieben wurde, ging ich davon aus, daß das Port nun
'anders herum' bleibt.
Wenn Du nur ein Toggel pro ISR möchtest, kannst Du PinB mit 0xFF
beschreiben - Das sollte Dir alle PortB-Bits 'umdrehen'.
(zumindest beim ATtiny ... vll. reicht der Verwandtschaftsgrad ja bis
hier hin)
MfG
fixi schrieb:> Wie es aber scheint, gibt es> (noch) keine Lösung für ein schnelleres ansprechen :(
Doch, gibt es. Ein STM32F4xx schafft es schneller.
Mit der richtigen Hardware geht es noch viel schneller ;-)
> Später soll das Programm doch etwas umfangreicher werden.
Dann sag besser schon jetzt, was später sein soll. Ein ATmega8 ist
heutzutage ja nicht mehr der absolute Renner - in jeder Beziehung.
fixi schrieb:> Später soll das Programm doch etwas umfangreicher werden.
Dann ist Dein Test vollkommen nutzlos.
Du hast nur eine CPU und die muß auch im worst-case alles in der
geplanten Zeit ausführen können.
Die oben genannten ~200 Zyklen je Interrupthandler sind eine brauchbare
Praxisabschätzung. Anfänger können auch deutlich mehr brauchen.
fixi schrieb:> Damit beträgt die Reaktionszeit nur noch 600ns. Leider immernoch etwas> zu langsam...
Was wäre denn schnell genug? Denn schneller als 562ns geht's halt mit
dem Prozessor bei dem Takt nicht. Klingt blöd, ist aber so.
Oliver
m.n. schrieb:> Wenn R16 vorher 0xff ist, könnte man es auch durch INC R16 auf 0x00> bringen ;-)
Nö.
Denn laut ATMEL beeinflußt die INCrementierung die Flags Z, N & V im
Statusregister. Keine gute Idee in nem naked_Interrupt.
;-)
Danke für die zahlreichen Vorschläge. Ich komme mit dem testen noch
nicht ganz hinterher. Das werde ich aber demnächst ausführlich machen.
Eine Altnernative wäre den Atmega88a einzusetzen. Dieser kann laut
Datenblatt 20MHz. Gibt es abgesehen von der höheren Frequenz evtl
weitere Auswirkungen auf die Reaktionszeit?
Dorftrottel schrieb:> Keine gute Idee in nem naked_Interrupt.
Und darum sollte man ISRs, deren endgültige Funktion noch nicht
feststeht, nicht auf Glück programmieren.
m.n. schrieb:> Transparente und schnelle ISRs programmiert man in Assembler, wo nur die> Register gerettet werden, die unbedingt gebraucht werden mit SREG an> allererster Stelle.
m.n. schrieb:> m.n. schrieb:>> Transparente und schnelle ISRs programmiert man in Assembler, wo nur die>> Register gerettet werden, die unbedingt gebraucht werden mit SREG an>> allererster Stelle.
Je nun, was anderes macht der Compiler auch nicht...
Oliver
Oliver S. schrieb:> Je nun, was anderes macht der Compiler auch nicht...
Doch, das tut er durchaus, es sei denn, es ist ein "guter" UND man ist
sehr mit seinen Eigenheiten vertraut und benutzt sie auch kompetent.
Aber selbst wenn die Kenntnisse da sind und der Compiler zumindest
potentiell wirklich alle wesentlichen Tricks ermöglicht, die in Asm
möglich sind, stellt sich am Ende des Tages immer noch die Frage: warum
zum Teufel muss man das nun unbedingt in C programmieren?
Bilanz:
1.
Der Einsatz von C bringt in einem solchen "engen" Szenario keinerlei
Vorteile, weil's entweder garnicht geht oder zumindest die
"Portabilität" als einziger nennenswerter Vorteil von C gegenüber Asm
praktisch vollständig entfällt.
2.
Es macht mehr Arbeit, weil man neben der allfälligen Kenntnis der
Zielarchitektur und seiner Assemblersprache auch noch die konkreten
Features des Compilers perfekt beherrschen muss (weit jenseits der
C-Sprachstandards).
Fazit:
Wer in solchen Szenarien trotzdem C benutzt, hat einfach irgendwie nicht
alle Latten am Zaun. Das kann nur ein eingefleischter C-Wanker sein, der
nix anderes will (und nix anderes kann)...
Was um alles in der Welt soll:
Out ff
direkt gefolgt von
Out 0
?
Liegt Deine Reaktionszeit nicht in der Größenordnung eines Oszilloskops,
"siehst" Du nur 00.
Inc r16
verkürzt nur die "Ein-zeit".
Sebastian S. schrieb:> Inc r16> verkürzt nur die "Ein-zeit".
Nein, nicht einmal das. Es sollte nur unbemerkt das SREG zerschiessen.
fixi schrieb:> Wie schnell wäre denn die Reaktion bei aktivem Warten durch polling auf> einen Spannungspegel am Pin?
Wie wäre es denn, uns einmal zu sagen, was es werden soll?
@ m.n.
Es ist natürlich ein sehr seltener Sonderfall, aber die Sequenz:
>PUSH R16 Push register on stack>SER R16 Set Register>OUT 0x18,R16 Out to I/O location>LDI R16,0x00 Load immediate>OUT 0x18,R16 Out to I/O location>POP R16 Pop register from stack>RETI Interrupt return
lässt das Statusregister in Ruhe.
Deshalb gilt:
>Nein, nicht einmal das. Es sollte nur unbemerkt das SREG zerschiessen.
nicht.
Du verschweigst auch auf diverse Nachfragen beharrlich Deine genauen
Anforderungen. Da hat man doch keine Lust mehr, auf nachgelegte Fragen
zu antworten.
Die Anforderung ist ganz klar eine möglichst schnelle Reaktion eines
Ausgangs auf ein Eingangssignal :) Alles andere würde nur zu
Abschweifungen führen.
fixi schrieb:> Ist die Reaktion beim aktiven Warten durch polling auf ein High signal> am Pin schneller als bei einem externen Interrupt?
Wie wäre es denn wenn du so einen einfachen Sachverhalt einfach
schnell selbst duch Eigeninitiative in der Praxis klärst. Die
Voraussetzungen dazu hast du ja offensichtlich:
fixi schrieb:> An meinem atmega8 löse ich einen externen interrupt aus. In der ISR wird> lediglich ein Ausgang auf high gesetzt (1 Zeile Code). Die Reaktionszeit> beträgt bei 16MHz ca 1,25 Mikrosekunden.
Andererseit drängst sich bei solchen Fragen und Handlungs-
zusammenhängen deinerseits der Verdacht auf dass hier im Thread
etwas gefaked wird, oder getrollt.
Arduinoquäler schrieb:> Andererseit drängst sich bei solchen Fragen und Handlungs-> zusammenhängen deinerseits der Verdacht auf dass hier im Thread> etwas gefaked wird, oder getrollt.
Ich hab ja sonst nichts zu tun...
Es ist doch ganz einfach. Ich habe kaum Zeit um das auszutesten und will
einfach nur wissen wie die Erfolgsaussichten sind, bevor ich damit
anfange. Darum wende ich mich hier an das Forum. Und genau das ist doch
Sinn und Zweck eines Forums. Es ist doch eine ganz einfach Frage.
Entweder man weiß die Antwort und hilft, oder man lässt es eben sein.
Aussagen wie "such doch selbst" oder "teste es doch" sind hier das
eigentliche rumgetrolle.
fixi schrieb:> Es ist doch ganz einfach. Ich habe kaum Zeit um das auszutesten und will> einfach nur wissen wie die Erfolgsaussichten sind, bevor ich damit> anfange.
Verstehe.
Bei solch einem aufwendigen, umfangreichen Test muss man sich
schon gut überlegen ob, wie und wann man diesen Aufwand treibt.
Ich veranschlage mal ca vier Wochen Arbeit a 50 Stunden für das
Schreiben von 10 Zeilen Code, das Hochladen auf den Controller
und das Anschliessen eines Oszilloskops. Betrachten und
Analysieren des Ergebnisses noch nicht einbezogen.
Alternativ dazu drei Wochen Arbeit für das Schreiben von 10
Zeilen Code und das analysieren des compilierten C-Codes auf
Assembler-Ebene.
fixi schrieb:> Die Anforderung ist ganz klar eine möglichst schnelle Reaktion eines> Ausgangs auf ein Eingangssignal
Wenn das alle Anforderungen sind, dann ist Dein Ansatz falsch.
Dieses Verhalten kannst Du mit einem Timer-Eingang und seinem
OCR-Ausgang lösen. Dazu ist keine CPU-Interaktion erforderlich.
Grundsätzlich würde ich erst mal auf:
ISR(INT1_vect, ISR_NAKED) {
PORTB = ~PORTB;
reti();
}
Umsteigen.
Wie bereits gesagt natürlich nur, wenn Deine Reaktionszeit unter der
eines Oszilloskops liegt;-)
Wie es mit den minimal möglichen Zeiten aussieht wurde bereits gesagt:
"Unterbrechung erkennen" Automatisch, abhängig vom Zeitpunkt
und gerade aktuellem Befehl Variabel
PUSH PC Automatisch Fixzeit
Load Interruptvector -> PC Automatisch Fixzeit
PUSH Work Dein Ding PUSH Fixzeit
MOV Dein Ding Hole Status Fixzeit
PUSH Status Dein Ding PUSH Fixzeit
„Hau rein“ Dein Ding akt. Status holen 1) Fixzeit
Dein Ding invertieren und 2) Fixzeit
Dein Ding zurückschreiben 3) Fixzeit
POP Status Dein Ding POP Fixzeit
MOV Status Dein Ding Fixzeit
POP Work Dein Ding POP Fixzeit
POP PC Dein Ding Indirekt durch IRET Fixzeit
In diesem Falle muss auch das Statusregister gesichert werden, da der
Befehl: COM wahrscheinlich den Status ändert.
Besondere Wünsche sehen weder der Compiler noch die Befehlsliste vor.
fixi schrieb:> Die Anforderung ist ganz klar eine möglichst schnelle Reaktion eines> Ausgangs auf ein Eingangssignal :)
Wie wäre es, einen Draht vom Eingang zum Ausgang zu löten. Schneller
gehts dann nicht mehr.