Hallo,
Ich verarbeitete innerhalb einer ISR eines ATmega16 ein Datensignal.
Eine Taktleitung triggert die ISR über INT1. Der Takt ist so hoch, dass
ich beim Lesen der Daten keinen komplette 32-Bit Wert schieben kann,
daher schiebe ich die einzelnen Bytes. Nach 29 Zyklen kommen ca. 200ms
Pause auf der Taktleitung.
1) Führe ich die mit '***' gekennzeichnete Zeile innerhalb main() aus,
funktioniert alles.
2) Führe ich sie stattdessen in der ISR aus, wird der Wert in main()
niemals ausgegeben.
3) kapsele ich dann aber die ISR zusätzlich in cli()/sei() wie in den
Kommentaren angedeutet, bekomme ich einen Wert, der halb so groß ist,
wie mit Lösung (1)?
Ich steh grad etwas auf dem Schlauch, kann mir jemand das merkwürdige
Verhalten erklären? Insbesondere verstehe ich nicht, warum cli/sei
innerhalb der ISR eine Auswirkung hat, da 'ISR (INT1_vect)' auch ohne
zusätzliche Maßnahmen in der ISR keine rekursiven Interrupts zulassen
sollte, oder?
Hier der Code (habe hier geschweifte Klammern zwecks kompakterer
Darstellung wegoptimiert):
Arne F. schrieb:> Ich steh grad etwas auf dem Schlauch, kann mir jemand das merkwürdige> Verhalten erklären? Insbesondere verstehe ich nicht, warum cli/sei> innerhalb der ISR eine Auswirkung hat, da 'ISR (INT1_vect)' auch ohne> zusätzliche Maßnahmen in der ISR keine rekursiven Interrupts zulassen> sollte, oder?
Soweit ich weiß, muss man solche rekursiven Szenarien explizit
unterbinden.
Bauchgefühl sagt also, der Controller ist zu langam und kommt ins
Stolpern.
Dummerweise funktioniert das Unterbinden mit cli/sei in diesem Beispiel
ja auch nicht richtig.
Auch hier im AVR-GCC Tutorial finde ich folgendes Statement:
"Während der Ausführung der Funktion sind alle weiteren Interrupts
automatisch gesperrt. Beim Verlassen der Funktion werden die Interrupts
wieder zugelassen."
Daher bin ich weiterhin ratlos...
Arne F. schrieb:> "Während der Ausführung der Funktion sind alle weiteren Interrupts> automatisch gesperrt. Beim Verlassen der Funktion werden die Interrupts> wieder zugelassen."> Daher bin ich weiterhin ratlos...
Ok, hab ich mich geirrt. Mein Fehler.
Also bleiben nur rund 80 Takte zwischen jeden Aufruf übrig, ist auch
nicht unbedingt viel. Wird die Zeit zu knapp verschluckt er einen Takt.
Ich würd der Einfachheit halber einen Quarz dranschrauben und mal die
Taktfrequenz erhöhen.
Die zeitkritische Verarbeitung der 28 Datenbits funktioniert ja. Und
nach dem 29. Takt kommen 200ms Pause, also reichlich Zeit für die das
bisschen 32-Bit Wert verarbeiten.
Letztlich habe ich ja eine funktionierende Lösung (Berechnung in main
statt ISR), möchte jedoch verstehen, warum cli/sei innerhalb der ISR
einen (positiven) Einfluss hat und wie es zu solchen Problemen kommt, um
das in Zukunft zu vermeiden.
Arne F. schrieb:> Letztlich habe ich ja eine funktionierende Lösung (Berechnung in main> statt ISR), möchte jedoch verstehen, warum cli/sei innerhalb der ISR> einen (positiven) Einfluss hat und wie es zu solchen Problemen kommt, um> das in Zukunft zu vermeiden.
Ich denke mal, du bist knapp an der Kippe.
Der sei kommt zu früh, noch ehe der Epilog der compilergenerierten ISR
beginnt an deren Ende dann das ultimative Freigeben der Interrupts
steht. Du bist mit deinem eigenen sei etwas zu früh drann, so dass die
Ausführung der nächsten ISR schon beginnt, noch ehe die vorhergehende
fertig war.
-> deine ISR stapeln sich rekursiv und irgendwann kracht es.
Arne F. schrieb:> Alle in der ISR verwendeten Variablen sind 'volatile' deklariert:
Das ist Unsinn. Variablen, die Du ausschließlich in der ISR nutzt,
brauchen lediglich in der ISR-Funktion als static definiert werden -
ohne volatile. Das spart nicht nur Code in der ISR, sondern sie wird
auch wesentlich flotter abgearbeitet.
Ein Kandidat wäre da zum Beispiel bitCount.
Was kostet es denn an Code/Takten, die volatile-Variable "data" aus 4
Bytes zusammenzusetzen?
Gruß,
Frank
Nachtrag:
Wenn Du data in der ISR zusammenbaust, gehören d1 bis d4 als
static-NON-volatile in die ISR.
Wenn du data in main zusammenbaust, gehört data als lokale Variable ins
main().
Du verbrätst in beiden Varianten jede Menge Code bzw. Taktzeiten. 100kHz
(= 80 Takte) sind nicht von Pappe.
Arne F. schrieb:> Ich steh grad etwas auf dem Schlauch, kann mir jemand das merkwürdige> Verhalten erklären? Insbesondere verstehe ich nicht, warum cli/sei> innerhalb der ISR eine Auswirkung hat, da 'ISR (INT1_vect)' auch ohne> zusätzliche Maßnahmen in der ISR keine rekursiven Interrupts zulassen> sollte, oder?
Gerade durch das cli/sei wird die Rekursion erlaubt, da das Enable schon
vor dem Epilog des Interrupthandlers erfolgt.
Peter
arne schrieb:> Ok, aber es bleibt die Frage, warum es ohne cli/sei noch schlechter> funktioniert
Weil da irgendwas faul ist, zum Beispiel das Zusammenbauen von 4
volatile-Variablen d1,d2,d3,d4 zu einer volatile-Variablen "data". Okay,
das machst Du zwar in der 200ms-Pause, aber das hier ist grausam:
1
if(bitCount<=8)d1=(d1>>1)|databit;
2
elseif(bitCount<=16)d2=(d2>>1)|databit;
3
elseif(bitCount<=24)d3=(d3>>1)|databit;
4
elseif(bitCount<=28)d4=(d4>>1)|databit;
5
elseif(bitCount==29)
Das sind alles volatiles. Das kostet viel Zeit und viel Takte. Und die
hast Du nicht. Ich bin mir sicher, dass Deine ISR mehr Zeit verbrät als
Du hast bei 100kHz-Signal.
Ich habe Deinen Code gerade mal durch den Compüiler geschickt. Der Code
für die ISR ist 166 Bytes groß.
Mach bitCount,d1,d2,d3,d4 static (ohne volatile) und definiere sie
direkt in der ISR. Ich bin mir sicher, dass dann Deine Variante, die
Variable "data" in der ISR zusammenzubauen, auch erfolgreich ist.
EDIT:
Da Du mir die Frage, wieviel Takte das Zusammenbauen von 4
volatile-Bytes zu einer volatile-32-bit-Variblen kostet, nicht
beantwortet hast, habe ich es gerade mal selber getestet:
Die Zeile
@Frank:
1. ich habe alle diese Variablen volatile deklariert, da ich sie zu
Testzwecken in main() auf einem Display ausgegeben habe.
2. nein, auch wenn ich bitCount und d1-d4 nicht volatile deklariere
funktioniert es auch nicht. (Im übrigen ist es aus Laufzeitsicht(!)
völlig unerheblich, ob die Vars static im Scope der ISR oder global
definiert werden)
3. dass die Berechnung von data innerhalb der ISR das Laufzeitproblem
darstellt ist unstrittig und war schon vorher klar. Die Frage zielte
nicht auf eine Codeoptimierung, denn eine funktionierende Variante habe
ich, wie zuvor geschrieben. Stattdessen war die Frage, wie es zu dem
Verhalten kommen kann, obwohl eine ISR nicht rekursiv ausgeführt wird
(wenn man es nicht explizit erzwingt):
Die Berechnung von data wird ca. 4x pro Sekunde ausgeführt, dafür sind
die 150 Byte Code also unerheblich. Trotzdem wird scheinbar entweder
trigger nie gesetzt (eher unwahrscheinlich) oder main() kommt gar nicht
mehr dran. Womit wir wieder beim ursprünglichen Problem wären.
Vielleicht wird's doch mal Zeit, sich einen JTAG-Adapter anzuschaffen...