Hallo zusammen! Ich möchte die Zustände zweier Ports (Port B, Port C) mit einer Frequenz von 200 kHz einlesen. Dazu verwende ich den Timer1 mit entsprechender Vorladung im CTC-Modus: Vergleichswert 80, Prescaler 1, f=16MHz. Laut Simulator benötigt die ISR 31 Takte von der 1. zur letzten Code-Zeile, also knapp 2us. Setzen ich einen Breakpoint auf die 1. Codezeile und messe die Zeit zwischen 2 Aufrufen der ISR, komme aich auf 6,69 us -> ca. 150 kHz. Woher kommen die zusätzlichen 4us? Hat das was mit dem Stack zutun? Was kann ich tun um die Zeit zu reduzieren? Gruß
probiere mal am Anfang und Ende von deinem Code ein Bit zu toggeln und mit Oszi die Zeit zu messen, ohne Hardwarebreakpoints, damit kannst du dein Ergebnis mal verifizieren :) Womöglich braucht dein Debugging ein paar Zyklen, wobei du natürlich gar nicht angegeben hast welchen Mikrocontroller du überhaupt verwendest lg
Bitte schreibe doch wenigstens, welche Programmiersprache du benutzt. In ASM z.B. wäre das Verhalten ungewöhnlich - in C kein bisschen.
Der µC braucht eine gewisse Anzahl an Takte bis er die ISR ausführt (bis zu 50). Beschleunigen kann man das leider nicht wirklich. Aber evtl. heit dein µC ja DMA, dann geht das ganze Hardwareseitig
Gerd schrieb: > Woher kommen die zusätzlichen 4us? Das wird an deinem Programm und der Art liegen, wie dein Prozessor die ISR abarbeitet. Guck im Simulator einfach im Single Step nach, was zwischen den Breakpoints passiert. Dann siehst du vielleich eher, wo dir die Zeit verloren geht. Vermutlich macht der Prozessor alles richtig und du hast einen Fehler in deiner Rechnung ;-)
RTFM! Der Aufruf der ISR benötigt natürlich auch Zeit. Aber wozu überhaupt Interups? Was soll der MC noch in der Loop erledigen?
Thomas schrieb: > welchen > Mikrocontroller du überhaupt verwendest Atmega8535 Matthias Sch. schrieb: > Bitte schreibe doch wenigstens, welche Programmiersprache du benutzt. C - noch... Max Mustermann schrieb: > Aber wozu überhaupt Interups? > Was soll der MC noch in der Loop erledigen? 20 Werte pro Port mit einer (einstellbaren) Frequenz von bis zu 200 kHz einlesen. Da die Frequenz einstellbar ist, verwende ich den Timer. Bis 100 kHz geht es auch. M.O. schrieb: > Das wird an deinem Programm und der Art liegen, wie dein Prozessor die > ISR abarbeitet. Guck im Simulator einfach im Single Step nach, was > zwischen den Breakpoints passiert. Dann siehst du vielleich eher, wo dir > die Zeit verloren geht. Vermutlich macht der Prozessor alles richtig und > du hast einen Fehler in deiner Rechnung ;-) Die Ausführung der ISR dauert 31 Takte. Das Problem ist die Zeit zwischen 2 Aufrufen der ISR, die bei 105 Takten liegt. Mir ist unklar was in den 68 Takten nach Ende der ISR und aufruf der nächsten ISR passiert.
>Mir ist unklar >was in den 68 Takten nach Ende der ISR und aufruf der nächsten ISR >passiert. Wie soll dir jemand helfen ohne deinen Code zu sehen?
> Die Ausführung der ISR dauert 31 Takte. Das Problem ist die Zeit > zwischen 2 Aufrufen der ISR, die bei 105 Takten liegt. Mir ist unklar > was in den 68 Takten nach Ende der ISR und aufruf der nächsten ISR > passiert. Vermutlich wird jeweils ein Interrupt vom Controller verpasst. Ansonsten: Beitrag "Re: Port mit 200 kHz einlesen"
Gerd schrieb: > Mir ist unklar was in den 68 Takten nach Ende der ISR und aufruf der > nächsten ISR passiert. Da pusht und popt der µC Registerinhalte und rennt im Hauptprogramm rum, falls nicht noch andere Interrupts anstehen.
Gerd schrieb: > Mir ist unklar > was in den 68 Takten nach Ende der ISR und aufruf der nächsten ISR > passiert. Der MC tut dass was jeder Prozessor bei einem Interupt tut! Er sichert ALLE Register incl Stackpointer und Programmpointer auf dem Stack. Erhöht den Stackpointer. Setzt den Programmpointer auf den Anfang der ISR. Arbeitet die ISR ab. Holt Alle Register incl Stackpointer und Programmpointer wieder vom Stack. Und versucht mit dem Hauptprogramm weiterzumachen. Wenn nämlich in der Zwischenzeit ein neuer Interupt aufgetreten ist geht das gleich wieder los mit Register sichern......
Max Mustermann schrieb: > Der MC tut dass was jeder Prozessor bei einem Interupt tut! > Er sichert ALLE Register incl Stackpointer und Programmpointer auf dem > Stack. Erhöht den Stackpointer. Setzt den Programmpointer auf den Anfang > der ISR. Arbeitet die ISR ab. Holt Alle Register incl Stackpointer und > Programmpointer wieder vom Stack. Und versucht mit dem Hauptprogramm > weiterzumachen. > Wenn nämlich in der Zwischenzeit ein neuer Interupt aufgetreten ist geht > das gleich wieder los mit Register sichern...... Ja, dass wird mein Problem sein... Es gibt wohl keine Möglichkeit der uC daran zu hindern? ;-) Mir bleibt dann für die besagte Frequenz wohl keine andere Möglichkeit, als das ohne Timer zu realisieren, oder? Also sozusagen im Hauptprogramm. Die erfoderliche Abtastzeit erreiche ich dann durch einsetzen von NOPs. Bessere Idee?
Gerd schrieb: > Max Mustermann schrieb: >> Der MC tut dass was jeder Prozessor bei einem Interupt tut! >> Er sichert ALLE Register incl Stackpointer und Programmpointer auf dem >> Stack. Erhöht den Stackpointer. Setzt den Programmpointer auf den Anfang >> der ISR. Arbeitet die ISR ab. Holt Alle Register incl Stackpointer und >> Programmpointer wieder vom Stack. Und versucht mit dem Hauptprogramm >> weiterzumachen. >> Wenn nämlich in der Zwischenzeit ein neuer Interupt aufgetreten ist geht >> das gleich wieder los mit Register sichern...... > > Ja, dass wird mein Problem sein... Es gibt wohl keine Möglichkeit der uC > daran zu hindern? ;-) Das könnt' schon gerade noch gehen, denn der beschrieben Ablauf ist bestenfalls als ganz grobe Umschreibung ok, in Beziehung auf 8Bit AVRs in mehrfacher Hinsicht falsch. Der ATM8535 schiebt im Interruptfall nur den ProgrammCounter auf den Stack, sonst macht er nichts, er sichert keine Register, er schiebt keinen Stackpointer auf den Stack. C sichert die verwendeten Register selektiv, je nachdem wie gut die ISR geschrieben ist, müssen mehr oder weniger Register gesichert werden. Das ist entscheidend, denn ein Paar Takte gehen noch für den Ein- und Rücksprung in die ISR weg (min. 7 Takte) und dann bleiben bei 31 Takten in der ISR für das Sichern und Wiederherstellen 42 Takte, also 21 beim Aufruf der ISR, 21 beim Ende. Das ist kanpp, aber nicht unmöglich. Du wurdest bereits aufgefordert den Code zu zeigen, nur daran kann man sehen, ob das klappen kann. Scheinbar verstehst Du das nicht so recht.
Max Mustermann schrieb: > Der MC tut dass was jeder Prozessor bei einem Interupt tut! > Er sichert ALLE Register incl Stackpointer und Programmpointer auf dem > Stack. Das höre ich zum ersten Mal. Lediglich das Sichern von Statusregister und Programmpointer wird man dem µC nicht verwehren können. Alles andere ist Sache der Software. Und wenn man genau weiss, welche Register in der ISR verändert werden, reicht es auch nur genau diese beim Einsprung in die ISR zu sichern.
Max Mustermann schrieb: > Der MC tut dass was jeder Prozessor bei einem Interupt tut! Das tut nicht der Prozessor, ein AVR sichert lediglich die Rücksprungadresse auf dem Stack von alleine. Alles andere macht die Kompilerumgebung. Bei avrgcc besteht aber die Möglichkeit, ISR von jeglichem Gedöns zu befreien, wenn du selber weisst, was du tust.
1 | ISR(ADC_vector, ISR_NAKED) {} |
schafft z.B. eine leere ISR, bei dem die automatische Sicherung von Registern wegfällt. Siehe die Doku zur avrlib.
:
Bearbeitet durch User
Matthias Sch. schrieb: > ein AVR sichert lediglich die Rücksprungadresse auf dem Stack von alleine. Das Statusregister wird er sich schon noch zusätzlich sichern müssen. Sonst geht das schief. Die Rücksprungadresse alleine reicht nur bei einer Subroutine.
Die Aufgabe schreit förmlich nach einem MCU mit DMA. Die gibt's doch schon billig, sogar bei den AVRs. Da hast du dann sogar noch genug CPU-Zeit übrig um was anderes nebenbei zu machen.
Max Mustermann schrieb: > Der MC tut dass was jeder Prozessor bei einem Interupt tut! Das muß schon deshalb grandioser Schwachsinn sein, weil das jeder µC anders macht. Da wir inzwischen vom OP wissen, daß es sich um einen Atmega8535 handelt... > Er sichert ALLE Register incl Stackpointer und Programmpointer auf dem > Stack. ...ist zumindest das definitiv Schwachsinn. Ein Atmega sichert genau eine Sache von sich aus auf dem Stack, nämlich die Rücksprungadresse. Dazu braucht er zwischen 4 (Normalfall) und theoretisch maximal 6 Takten. Letzteres erfordert aber schon einen Mega, dessen Stack in externem RAM liegt. Der 8535 hat keinen externen RAM, das wäre aber z.B. beim Mega8515 oder Mega128 immerhin möglich. Also definitiv vier Takte konstante Interruptlatenz. Dazu noch mal vier Takte für reti. Zusammen also genau acht Takte unvermeidlichem Interupt-Overhead der Hardware. Nicht mehr und nicht weniger. Alles, was darüber hinausgeht, ist mehr oder weniger vermeidbar. Wenn man in der einzig wahren Sprache für kleine µC programmiert, der einzigen, die die volle Kontrolle über alle Aspekte der Hardware garantiert. Und die heißt numal einfach nicht C, das ist schon alles, was dazu zu sagen ist. Und irgendwie wohl auch oft genug gesagt wurde...
M.O. schrieb: > Das Statusregister wird er sich schon noch zusätzlich sichern müssen. > Sonst geht das schief. Die Rücksprungadresse alleine reicht nur bei > einer Subroutine. Nö, der Prozessor selbst sichert nur die Rücksprungadresse. Das SREG muss dann nicht gesichert werden, wenn man in der ISR ausschließlich Opcodes verwendet, die das Statusregister nicht beeinflussen.
M.O. schrieb: > Das Statusregister wird er sich schon noch zusätzlich sichern müssen. Nö, tut er nicht. Das musst du selber erledigen.
Was nützt es, Portpins in der Geschwindigkeit abzufragen, wenn bei den Standard-µCs nur für einige 1000 Messwerte Speicherplatz vorhanden ist. Also für einige 100-stel bis 10-tel Sekunden. Kann man mit nem µC machen, wenn man genau weiß, dass nach einer Auslösebedingung, nur so wenig "Aufnahmezeit" nötig ist. Aber, ob da so viel vorher überlegt wurde...
c-hater schrieb: > Also definitiv vier Takte konstante Interruptlatenz. Dazu noch mal vier > Takte für reti. Zusammen also genau acht Takte unvermeidlichem > Interupt-Overhead der Hardware. Nicht mehr und nicht weniger. Nein. Beim Auftreten eines Interrupts muß zunächst mal der aktuell abgearbeitete Befehl zuende gebracht werden, also im Falle z.B. eines RET bis zu 4 (oder 5 je nach Prozessor-Typ) zusätzliche Takte. Wenn das abgeschlossen ist, wird erstmal in die Interrupt-Vektor-Tabelle gesprungen, was die von dir erwähnten 4 Takte benötigt. Da findet sich dann in der Regel ein RJMP, der nochmal 2 Takte braucht. Das heißt, daß es bis zu 10 Takte dauern kann, bevor überhaupt an den Anfang der ISR gesprungen wird. Der Rücksprung braucht dann zusätzlich die erwähnten 4 Takte. Eine komplett leere ISR braucht also schon mal bis zu 14 Takte (mal angenommen, daß kein Sleep-Modus benutzt wird, denn sonst verlängert sich das nochmal deutlich). Dann kommt die Register-Sicherung. Selbst minimale Vertreter dieser Art sichern normalerweise erstmal ein Register auf dem Stack, um dann über dieses das SREG ebenfalls auf dem Stack zu sichern. 2 * PUSH und ein IN, macht 5 Takte, am Ende das umgekehrte, nochmal 5 Takte. So sind wir dann schon bei 24 Takten und können dann auch schon anfangen, der ISR ihren eigentlichen Inhalt zu geben.
Hi >Das heißt, daß >es bis zu 10 Takte dauern kann, bevor überhaupt an den Anfang der ISR >gesprungen wird. Wenn du schon so genau sein willst, sind es maximal 9 Takte (10 bei 22Bit-PC), da ein Befehl schon in Bearbeitung sein muss um noch abgearbeitet zu werden. MfG Spess
Für Interrupts, bei denen das Aufreteten Bedingung an den Takt gebunden ist, gebe ich dir recht. Beim Timer-Interrupt ist das der Fall.
Rolf Magnus schrieb: > So sind wir dann > schon bei 24 Takten und können dann auch schon anfangen, der ISR ihren > eigentlichen Inhalt zu geben. Tja, und woher kam jetzt nochmal das Gerücht her, dass die ARMs besonders lange Interrupt-Latenz haben? Ein Cortex-M3 sichert den gesamten Kontext in 12 Zyklen!
Rolf Magnus schrieb: > c-hater schrieb: >> Also definitiv vier Takte konstante Interruptlatenz. Dazu noch mal vier >> Takte für reti. Zusammen also genau acht Takte unvermeidlichem >> Interupt-Overhead der Hardware. Nicht mehr und nicht weniger. > > Nein. Beim Auftreten eines Interrupts muß zunächst mal der aktuell > abgearbeitete Befehl zuende gebracht werden Das ist die variable Interruptlatenz, nicht die konstante. Und diese variable Interuplatenz ist zwar sicher oft sehr störend bezüglich der Reaktionszeit für die Bearbeitung eines Interrupts (denn sie kann sogar noch sehr viel länger sein als nur die Beendigung der aktuellen Instruktion, z.B. durch konkurrierende Interrupts oder CLI/SEI-Blöcke in main), aber sie ist kein Interrupt-Overhead im engeren Sinne, denn genau die Takte, die die ISR dadurch später zur Auführung kommt, kommt ja der unterbrochene Code derweil auch noch weiter in seiner Arbeit. Die Rechenzeit ist also im Unterschied zur konstanten Latenz nicht verloren, die variable Latenz verringert also im Gegensatz zur konstanten nicht den Durchsatz des Gesamtsystems. > Wenn das > abgeschlossen ist, wird erstmal in die Interrupt-Vektor-Tabelle > gesprungen, was die von dir erwähnten 4 Takte benötigt. Da findet sich > dann in der Regel ein RJMP Und genau das ist schon der erste Punkt, der in Asm nicht mehr zwingend ist. Wenn ich eine im Bezug auf den Durchsatz extrem zeitkritische ISR habe, kann ich die notfalls auch direkt in den Vektorbereich legen. Schlimmstenfalls muß ich dann irgendwas anderes ohne Interrupt lösen, weil der zugehörige Vektor nicht mehr verfügbar ist. > Eine komplett leere ISR braucht also schon mal bis zu 14 Takte Das ist eindeutig falsch. > Dann kommt die Register-Sicherung. Selbst minimale Vertreter dieser Art > sichern normalerweise erstmal ein Register auf dem Stack, um dann über > dieses das SREG ebenfalls auf dem Stack zu sichern Auch wieder eine Sache, die in Assembler oft nicht nötig ist, denn es gibt erstens viele Instruktionen, die die Flags überhaupt nicht anfassen, dementsprechend gibt es dann auch keinen Grund, ebendiese zu retten, wenn man eine ISR nur mit solchen Instruktionen gebacken bekommt und zweitens kann ich in Assembler bei hinreichender Wichtigkeit der ISRs für den Durchsatz des Systems einfach ein Register systemweit für den Zweck reservieren, als Scratchregister zum Sichern der Flags in ISRs zu dienen. Wenn alle ISRs so kurz sind, daß ich sie vollständig exklusiv laufen lassen kann, brauchen dann nichtmal mehr die Flags vom Scratchregister auf den Stack, sondern sie bleiben einfach nur dort und werden auch von dort wiederhergestellt. Zweimal push gespart, zweimal pop gespart, total 8 Takte pro Interrupt. Wenn dabei nur ein einzelner Interrupt mit 400kHz ausgelöst wird und das System mit 20 MHz läuft, spare ich allein durch diese kinderleichten Manipulationen 3,2MTakte/s. Was nichts anderes bedeutet, das ich bezüglich des Durchsatzes mit diesem Ansatz Sachen realisieren kann, für die du sonst mindestens 23,2MHz Systemtakt benötigen würdest. So einfach ist das... Bezüglich der Reaktionszeiten ist das sogar oft noch extremer, denn hier wirkt sich jeder im exklusiven Teil einer ISR unnütz vertrödelte Takt auf die variable Latenz aller anderen ISRs gleichermaßen schädlich aus. Hier kann bei entsprechender Aufgabenstellung schon ein einziger gesparter Takt in irgendeiner ISR über geht oder geht nicht für das Gesamtsystem entscheiden.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.