Port mit 200 kHz einlesen

Gast #3984360
Lesenswert?

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ß
Gast #3984373
Lesenswert?

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 ;-)
Gast #3984401
Lesenswert?

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.
#3984439
Lesenswert?

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......
Gast #3984456
Lesenswert?

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?
Gast #3984502
Lesenswert?

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.
Gast #3984508
Lesenswert?

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.
(Firma: matzetronics) #3984524
Lesenswert?

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.
Gast #3984561
Lesenswert?

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...
Gast #3984562
Lesenswert?

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.
Gast #3984673
Lesenswert?

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...
#3984699
Lesenswert?

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.
Gast #3985816
Lesenswert?

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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren