Wenn ich das richtig im Kopf hab, sind If Abfragen relativ
takt-intensiv, oder?
Und falls jemand eine Quelle kennt wo man die Takte verschiedener
Befehle nachlesen kann, immer her damit.
MfG Andre
schau dir die *.lss Datei an. Da siehst du zu deiner ISR jeden einzelnen
Assembler-Befehl. Mit dem Datenblatt (da gibts irgendwo eine Liste mit
allen Assemblerbefehle und die benötigten Takte zum ausführen) kannst du
dann die Takte zusammenzählen.
Die Takte kann mit dem Debugger des AVR-Studios schnell bestimmen.
> Wenn ich das richtig im Kopf hab, sind If Abfragen relativ> takt-intensiv, oder?
Deine erste wohl nicht. Für die letzte würde ich meine Hand nicht ins
Feuer legen.
Es ist für eine Programm (fast) immer besser, wenn die IRQ-Routine das
Ereignis z. B. in einer Variablen ablegt und im Hauptprogramm alles
weitere bearbeitet wird.
cli() und sei() haben innerhalb von ISR nicht zu suchen. Damit bewirkst
du ganz sicher nicht das, was du wolltest, sondern eher einen Stack
Überlauf.
Schreibe Konstanten in Großbuchstanben, damit man sie als solche
Erkennt. Bei den Schiebe-Operationen spielt es eine sehr große Rolle, ob
die Zahl rechts von "<<" konstant ist, oder variabel.
Martin schrieb:>> Es ist für eine Programm (fast) immer besser, wenn die IRQ-Routine das> Ereignis z. B. in einer Variablen ablegt und im Hauptprogramm alles> weitere bearbeitet wird.
So allgemein ist das Quatsch. Es kommt auch darauf an, wie oft die ISR
aufgerufen wird (werden kann). Der TE fragt schon richtig. Wenn man
sicherstellen kann, daß die ISR auch im worst case weniger Zeit
braucht, als minimal zwischen zwei Triggerungen des Interrupts liegt,
ist man auf der sicheren Seite.
Danke für die Antworten
Schrittzaehler ist eine uint16_t Variable, alles andere sind uint8_t.
Den Hinweis mit den den Großbuchstaben für Konstanten merke ich mir, in
dem Fall sind in der Tat alles Konstanten im Zusammenhang mit
Schiebeoperatoren.
Ich werde mir das im Simulator anschauen, mal schauen ob ich es finde.
Andre schrieb:> Ich werde mir das im Simulator anschauen, mal schauen ob ich es finde.
Poste doch mal bitte den Abschnitt aus dem LSS file.
Dann kann einer von uns die Takte dazuschreiben und du siehst, die
andere Möglichkeit.
Norbert schrieb:> Zu Beginn der ISR einen Pin auf High, zum Schluss wieder auf Low.> Oszilloskop dran, auf'n Bildschirm schauen, fertig
Ja, das bringt es - bei enthaltenen IF-Abfragen.
Stefan ⛄ F. schrieb:> cli() und sei() haben innerhalb von ISR nicht zu suchen. Damit bewirkst> du ganz sicher nicht das, was du wolltest, sondern eher einen Stack> Überlauf.
Einen Stacküberlauf gibt es nicht, aber es ist doppelt gemoppelt, weil
das globale IRQ Flag sowieso beim ISR Eintritt gelöscht und am Ende
wieder gesetzt wird.
Möchte man die ISR wirklich durch eine weitere unterbrechen, hat es
Sinn, am Anfang ein sei() zu schreiben oder (besser) die ISR als NOBLOCK
zu deklarieren.
Matthias S. schrieb:> Einen Stacküberlauf gibt es nicht
Na ja, wenn die Interrupt-Bedingung längere Zeit besteht dann sorgt RETI
dafür dass der nächste IRQ-Aufruf erst nach dem Rücksetzen des Stacks
passiert und nicht davor zwischen SEI und RETI mit entsprechendem
Wachstum des Stack ...
LG, Sebastian
Andre schrieb:> Wenn ich das richtig im Kopf hab, sind If Abfragen relativ> takt-intensiv, oder?> Und falls jemand eine Quelle kennt wo man die Takte verschiedener> Befehle nachlesen kann, immer her damit
Grob gesagt: 10 Takte pro Zeile mit 1 Befehl.
Ja, deine Interruptroutine ist lang und zeitaufwändig.
Genauer: Assemblerausgabe angucken und Takte danebenschreiben, stehen ja
im Assemblermanual des AVR.
Ich glaubt Atmel Studio zeigt die sogar im Listing an (eventuell nur
beim Debuggen...)
Sebastian schrieb:> Na ja, wenn die Interrupt-Bedingung längere Zeit besteht dann sorgt RETI> dafür dass der nächste IRQ-Aufruf erst nach dem Rücksetzen des Stacks> passiert und nicht davor zwischen SEI und RETI mit entsprechendem> Wachstum des Stack ...
Um es nochmal zu sagen. Das cli() am Anfang einer ISR und das sei() am
Ende macht der AVR von alleine in Hardware. Das hat nichts mit dem Stack
zu tun und ist bei TE einfach nur doppelt gemoppelt und unnötig.
Norbert schrieb:> Zu Beginn der ISR einen Pin auf High, zum Schluss wieder auf Low.> Oszilloskop dran, auf'n Bildschirm schauen, fertig!
besser noch mit einem USB Logic Analyzer aufzeichnen und ansehen. Eine
komplette Fahrt mit Beschleunigungs- und Bremsrampe kann man komplett in
hoher Auflösung speichern. LA Software wie von Saleae kann mit wenigen
Klicks die Min/Max/Mittelwerte der Pulslängen anzeigen.
Norbert schrieb:> Zu Beginn der ISR einen Pin auf High, zum Schluss wieder auf Low.> Oszilloskop dran, auf'n Bildschirm schauen, fertig!
falls kein oszilloguck vorhanden, einen timer auf 0 und nach isr
auslesen & ausgeben, event. prescaler anpassen
VlG
charly
Andre schrieb:> if(sflags & (1<<beschleunigungsrampe))
Wenn der shift zur Laufzeit berechnet wird, ist die Zeile teuer, und
davon gibts ja ein paar mehr. Wenn das zur Compilezeit berechnete Masken
sind, dann ist das ok.
Der Rest ist auch nicht wirklich kritisch. Die ISR wird ja nicht im
MHz-Takt laufen müssen, und für einige KHz reicht es allemal, wenn der
Prozessor nicht gerade mit einem Uhrenquarz getaktet wird.
Wie schon gesagt wurde, zählt dir der Atmel/Microchip-Simulator im
Studio die Takte.
Oliver
Hugo H. schrieb:> Ja, das bringt es - bei enthaltenen IF-Abfragen.
Genau, den kann man sich auch gleich die statistische Verteilung der
ISR-Laufzeiten angucken.
Axel S. schrieb:> Martin schrieb:>>>> Es ist für eine Programm (fast) immer besser, wenn die IRQ-Routine das>> Ereignis z. B. in einer Variablen ablegt und im Hauptprogramm alles>> weitere bearbeitet wird.>> So allgemein ist das Quatsch. Es kommt auch darauf an, wie oft die ISR> aufgerufen wird (werden kann). Der TE fragt schon richtig. Wenn man> sicherstellen kann, daß die ISR auch im worst case weniger Zeit> braucht, als minimal zwischen zwei Triggerungen des Interrupts liegt,> ist man auf der sicheren Seite.
Auch das ist so pauschal Unsinn.
Matthias S. schrieb:> Sebastian schrieb:>> Na ja, wenn die Interrupt-Bedingung längere Zeit besteht dann sorgt RETI>> dafür dass der nächste IRQ-Aufruf erst nach dem Rücksetzen des Stacks>> passiert und nicht davor zwischen SEI und RETI mit entsprechendem>> Wachstum des Stack ...>> Um es nochmal zu sagen. Das cli() am Anfang einer ISR und das sei() am> Ende macht der AVR von alleine in Hardware. Das hat nichts mit dem Stack> zu tun und ist bei TE einfach nur doppelt gemoppelt und unnötig.
Das cli() und sei() des TE hat aber mit dem Stack zu tun. Das ist nicht
nur unnötig, sondern potenziell schädlich. Denn damit wird sei()
ausgeführt, bevor alle in der ISR auf dem Stack gesicherten Register
wieder zurückgeschrieben werden. Wenn die Interrupt-Bedingung in dem
Moment wieder anliegt, wird dann gleich wieder in die ISR gesprungen,
ohne dass vorher die Register zurückgeschrieben werden. Damit erhöht
sich die Stacknutzung. Deshalb macht man niemals ein sei() in einer ISR,
es sei denn, man hat einen ganz konkreten Grund, weshalb man das
unbedingt braucht. Und selbst dann überlegt man dreimal, ob man es auch
wirklich braucht.
Hugo H. schrieb:> Norbert schrieb:>> Zu Beginn der ISR einen Pin auf High, zum Schluss wieder auf Low.>> Oszilloskop dran, auf'n Bildschirm schauen, fertig>> Ja, das bringt es - bei enthaltenen IF-Abfragen.
Vielleicht probierst du es erst mal bevor du mit dem Meckern anfängst.
Dann wirst du nämlich feststellen, das es mehrere unterschiedlich stark
ausgeprägte (je nach Häufigkeit) abfallende Flanken gibt. Daran kannst
du sogar die einzelnen Wege erkennen welche in der ISR eingeschlagen
werden.
Norbert schrieb:> Zu Beginn der ISR einen Pin auf High, zum Schluss wieder auf Low.> Oszilloskop dran, auf'n Bildschirm schauen, fertig!
Jein. Dann Fehlen aber noch die PUSH/POP Befehle, welche vor bzw. nach
der "ersten und letzten C-code" Zeile ausgeführt werden. Aber es stimmt
schon, man bekommt ein vernünftiges Gefühl, wie lange der ISR dauert.
Trotzdem schließe ich mich an: der Ersteller der Anfrage sollte sich die
Tipps und Tricks zum effizienten Programmieren von ISR´s mal zur Gemüte
führen. Ohne den vollständigen Code zu kennen, scheint es aber trotzdem
so, daß hier "noch was zum Optimieren" ist.
Auf dem Ersten Blick scheint es auch so, daß alle Variablen global (und
dann hoffentlich auch volatile deklariert) sind. Ist das wirklich nötig?
Könnte man vielleicht die eine oder andere Variable fest an ein
Prozessor-Register binden?
Gruß
Robert
Norbert schrieb:> Zu Beginn der ISR einen Pin auf High, zum Schluss wieder auf Low.> Oszilloskop dran, auf'n Bildschirm schauen, fertig!Norbert schrieb:> Vielleicht probierst du es erst mal bevor du mit dem Meckern anfängst.> Dann wirst du nämlich feststellen, das es mehrere unterschiedlich stark> ausgeprägte (je nach Häufigkeit) abfallende Flanken gibt. Daran kannst> du sogar die einzelnen Wege erkennen welche in der ISR eingeschlagen> werden.
Nach Deinem Vorschlag gibt genau eine abfallende Flanke zum Ende jeder
ISR. Anhand der Ausführungszeit der ISR kann man - wenn man denn
ungefähr weiß, welcher Weg welche Zeiten verbrät - schließen, welche
Zweige durchlaufen wurden. Das beinhaltet, dass man wissen sollte
(zumindest grob) wie viele Takte jeder Zweig benötigt. -> LSS lesen und
Takte zählen oder mehr Pins wackeln lassen.
Andre schrieb:> Schrittzaehler ist eine uint16_t Variable, alles andere sind uint8_t.
DAS ist eine ganz entscheidende Information, da 16-Bit-Variablen auf
einem ATMEGA328 grob doppelt so lang brauchen wie 8-Bit-Variablen und
32-Bit wiederum doppelt so lange. Dagegen sind die ca. 2-10T für eine
if-Abfrage Peanuts.
Es fehlt aber noch die Angabe des Microprozessors.
Trotzdem kann man schon jetzt sagen, dass die ISR in jedem ihrer Zweige
vergleichsweise schnell beendet wird, auf einem ATMEGA328 sicher unter
10µs (woher ich das weiss? Weil ich das schon bei vielen ISRs
ausgerechnet habe und hier auch, wenn auch nur grob überschlagsmäßig).
Was ist denn Dein kritischer Wert?
MaWin schrieb:> Ja, deine Interruptroutine ist lang und zeitaufwändig.
Das ist alles relativ und lässt sich so nicht sagen. Z.B. auf dem
ATMEGA328 kann man kaum eine sinnvolle ISR machen, die weniger als 2-3µs
braucht. So gesehen sind weniger 10µs nicht viel.
Und es wurde schon oft gesagt, cli/sei weg. Sind zwar nur zwei gesparte
Takte, aber eben völlig überflüssig.
Justin S. schrieb:> Trotzdem kann man schon jetzt sagen, dass die ISR in jedem ihrer Zweige> vergleichsweise schnell beendet wird, auf einem ATMEGA328 sicher unter> 10µs (woher ich das weiss? Weil ich das schon bei vielen ISRs> ausgerechnet habe und hier auch, wenn auch nur grob überschlagsmäßig).
Aha. Und was sagt Deine Glaskugel zum Prozessortakt?
Hugo H. schrieb:> Nach Deinem Vorschlag gibt genau eine abfallende Flanke zum Ende jeder> ISR. Anhand der Ausführungszeit der ISR kann man - wenn man denn> ungefähr weiß, welcher Weg welche Zeiten verbrät - schließen, welche> Zweige durchlaufen wurden. Das beinhaltet, dass man wissen sollte> (zumindest grob) wie viele Takte jeder Zweig benötigt. -> LSS lesen und> Takte zählen oder mehr Pins wackeln lassen.
Also mein Oszilloskop leuchtet nach. Da sehe ich eine helle positive
Flanke (immer an der gleichen Stelle, da hier getriggert wird) und
mehrere negative Flanken an verschiedenen Stellen. Die relative
Häufigkeit des Vorkommens wird durch die unterschiedliche Helligkeit
angezeigt.
Wenn ich dann noch weiß wie der Prozessor-Takt bzw. die Periodendauer
ist, kann ich ablesen und mittels der Grundrechenarten die Taktzyklen
bestimmen.
Beileibe kein Hexenwerk, insbesondere da der TO explizit nach einem ›ca‹
Wert gefragt hatte.
Lass einen Timer laufen, schreibe dir am Anfang den Counter in eine
Variable und am Ende den Counter in eine andere Variable. Am besten
zählt der Counter bis zum Maximum und die Variablen haben die gleiche
Größe, womit du auch Fälle abfängst, in denen der Zähler innerhalb der
ISR über Null geht. Wenn die ISR durch einen Timer getriggert wird,
verwendest du aber am besten direkt diesen Timer.
Irgendwo in der Main bildest du die Diffeenz und speicherst diese wieder
in einer Variable. Die guckst du dir dann über die
Debugging-Möglichkeiten der IDE an. Damit hast du zumindest die Zeit
innerhalb der ISR. Willst du es genau für die ganze ISR wissen, gibt es
in der Richtung auch Möglichkeiten, aber ich vermute so reicht das
schon.
Wichtig: Du darfst dir nicht die Differenz in der IDE bilden lassen, das
ist fehlerbehaftet, da die Werte beider Variablen selten
zusammengehören.
Andre schrieb:> Wenn ich das richtig im Kopf hab, sind If Abfragen relativ> takt-intensiv, oder?
Ich habe letztens die Anzahl der Takte für eine einfache if-Abfrage
gestet und kam auf 6 Takte. Im Argument stand nur eine einzelne
Variable. Logische Verknüpfungen benötigen natürlich weitere Takte.
Also:
1
if(variable)
2
{
3
// mein Code
4
}
Es handelte sich dabei um einen Mikrocontroller von Texas Instruments,
ein F2838x. Wie übertragbar das auf andere ist, weiß ich nicht.
Terence S. schrieb:> und kam auf 6 Takte. Im Argument stand nur eine einzelne> Variable.
Aus dem Speicher in die Register holen, irgendwie einen Vergleich auf
"not Null" machen und ein bedingter Sprung.
6 Takte sind da schon gut.
Ohne es geprüft zu haben: Ein AVR schaffts auch nicht schneller.
Ein AT89LP4052 schafft ein "DJNZ Adresse" in 4 Zyklen (= 200ns @20MHz).
Will man nur auf 0 testen ohne Decrement, muß noch ein "INC Adresse"
davor, dann sind 6 Zyklen.
Vorteilhaft für Interrupts ist dabei, daß kein Register benötigt wird
und das Statuswort nicht beeinflußt wird, d.h. kein PUSH/POP nötig.