Hallo zusammen, ich habe zu den Interrupts (ATmega32) zwei Fragen. 1. Nach meiner Recherche muss man das SREG in einer Interrupt-Routine immer selbst sichern und vor Beendigung wieder zurückspeichern, ansonsten kann es je nach Operation in der Interrupt-Routine und Änderung der SREG-Flags beim Rücksprung zum Bruch führen. Besteht aber auch die Gefahr, dass Operationen mit 16 bit oder 32 bit Variablen, die NICHT in der Interrupt-Routine verwendet und verändert werden, korrupt werden, weil ein Interrupt zw. den Bytes auftritt, d.h. muss hier das Interrupt Flag gesperrt werden oder ist dies überflüssig, z.B.: void main (void) uint16_t (oder uint32_t) sum, buffer; uint8_t save_sreg; save_sreg = SREG; cli(); … buffer = (((sum * 5)/10) - (sum/100) - (sum/1000)); …. SREG = save_sreg; 2.Die Interrupt-Priorität ist im ATmega fest vergeben, wenn aber das global I-Flag im Interrupt sofort wieder freigeben wird, könnte theoretisch auch ein Interrupt mit niedrigerer Priorität dazwischen ausgeführt werden oder liege ich hier falsch, z.B.: ISR (TIMER0_OVF_vect) uint8_t sreg_temp; sreg_temp = SREG; sei(); ….. // z.B. SPI Interrupt (geringere Priorität) SREG = sreg_temp; Vielen Dank für eine Rückmeldung. Andreas
1. Was die ISR nicht ändert, muss gegenüber dieser ISR nicht abgesichert werden. Ausnahme: Man schreibt die ISR in Assembler und vergisst, alle verwendeten Register zu sichern. 2. Die "Prioritäten" bei AVRs (ohne XMega) beziehen sich nur auf gleichzeitig beim Vektorzugriff anstehende Interrupts. Gibt man in der ISR Interrupts frei, kommt jeder Interrupt durch. Das unterscheidet AVR von Architekturen mit komplexem über Prioritäten verschachtelbaren Interruptsystemen wie den ARM Cortex M. Aber auch von 8051 und 8-Bits PICs mit zwei Ebenen.
:
Bearbeitet durch User
Vielen Dank für die Antwort, nur nochmals zur Sicherheit. zu Frage 1: SREG in ISR sichern ist erforderlich, dass Sperren von Interrupts, solange die Variable nicht im Interrupt bearbeitet wird ist nicht erforderlich. zu Frage 2: d.h. der SPI Interrupt kann innerhalb der des Timer-Interrupts ausgeführt werden und muss nicht warten bis der Timer ISR abgearbeitet wurde, d.h. er wäre für die SPI Kommunikation unterbrechbar.
Andi_T T. schrieb: > 1. Nach meiner Recherche muss man das SREG in einer Interrupt-Routine > immer selbst sichern und vor Beendigung wieder zurückspeichern, > ansonsten kann es je nach Operation in der Interrupt-Routine und > Änderung der SREG-Flags beim Rücksprung zum Bruch führen. Ja, das SREG musst du sichern. > Besteht aber auch die Gefahr, dass Operationen mit 16 bit oder 32 bit > Variablen, die NICHT in der Interrupt-Routine verwendet und verändert > werden, korrupt werden, weil ein Interrupt zw. den Bytes auftritt, d.h. > muss hier das Interrupt Flag gesperrt werden oder ist dies überflüssig, Nein, wenn die ISR sie nicht verwendet, besteht da keine Gefahr. > 2.Die Interrupt-Priorität ist im ATmega fest vergeben, wenn aber das > global I-Flag im Interrupt sofort wieder freigeben wird, könnte > theoretisch auch ein Interrupt mit niedrigerer Priorität dazwischen > ausgeführt werden oder liege ich hier falsch, z.B.: Nein, da liegst du richtig. Wenn das I-Flag, das Enable-Flag des Interrupts und das Interrupt-Flag selbst gesetzt sind, wird in der nächsten Instruktion die entsprechende ISR angesprungen, egal was da sonst gerade läuft.
Andi_T T. schrieb: > zu Frage 1: SREG in ISR sichern ist erforderlich, Nur, wenn deine ISR auch Operationen ausführt, die das SREG ändern. Lässt deine ISR beispielsweise nur via OUT eine LED aufleuchten, ist das nicht der Fall. Das dürfte allerdings nahezu der einzige praktikable Fall sein, bei dem das so ist, d.h. jede komplexere ISR wird das benötigen. Da du für das Sichern des SREG selbst noch ein CPU-Register brauchst, musst du das natürlich vorher ebenfalls sichern. > dass Sperren von > Interrupts, solange die Variable nicht im Interrupt bearbeitet wird ist > nicht erforderlich. "die Variable"? Falls es deine Frage ist, ob die Interrupts beim Eintritt in die ISR automatisch gesperrt worden sind: ja, dem ist so. Ansonsten könnte dir ja schon während der Präambel-Abarbeitung der ISR der nächste Interrupt reinknalllen. Der übliche Weg, sie nach der ISR wieder zu gestatten, ist via RETI. > zu Frage 2: d.h. der SPI Interrupt kann innerhalb der des > Timer-Interrupts ausgeführt werden und muss nicht warten bis der Timer > ISR abgearbeitet wurde, d.h. er wäre für die SPI Kommunikation > unterbrechbar. Wenn du innerhalb der ISR die Interrupts manuell wieder frei gibst, kann dir jeder Interrupt ab diesem Moment deine ISR unterbrechen – inklusive des eigenen (kann bspw. bei UARTs passieren). Daher lässt man die Interrupts beim AVR üblicherweise in der ISR gesperrt und hält lieber die ISR kurz.
Wenn du, wie oben zu sehen, die ISR in C schreibst, dann kümmert sich der Compiler um das SREG, in den neueren GCC-Versionen (ab 9, WIMRE) läst er das auch weg, wenn die ISR das SREG nicht braucht. Einzig die vorzeitige Freigabe weiterer Interrupts obliegt dem Programmierer. Der sollte aber dann auch wissen, was er tut. Variablen, die von der ISR verändert und in einer "Main-Loop" gelesen werden, müssen "volatile" sein, sonst erkennt der Compiler nämlich fälschlicherweise, daß sich die Variable "nie" ändert. Aber bitte nur die. "volatile" für alles "zur Sicherheit" deutet nur auf ein fehlerhaftes Programm hin.
Andi_T T. schrieb: > 1. Nach meiner Recherche muss man das SREG in einer Interrupt-Routine > immer selbst sichern und vor Beendigung wieder zurückspeichern, In Assembler ja, in C nein. Der Compiler weiß alleine, was zerstört werden könnte und daher gesichert werden muß. Der Compiler sorgt quasi dafür, daß der Interrupthandler für die Mainloop komplett unsichtbar bleibt. Es ist nur eben deutlich mehr Zeit vergangen, als für die letzte Instruktion nötig gewesen wäre. D.h. Delayloops können sich durch den Interrupt verlängern. Andi_T T. schrieb: > ISR (TIMER0_OVF_vect) > uint8_t sreg_temp; > sreg_temp = SREG; > sei(); Das sei(); sollte man in Interrupts extrem vorsichtig benutzen. Der AVR kennt keine Interruptlevel, d.h. absolut jeder enablete Interrupt kann dann reingrätschen, sogar der, der gerade in Behandlung ist.
Andi_T T. schrieb: > 1. Nach meiner Recherche muss man das SREG in einer Interrupt-Routine > immer selbst sichern und vor Beendigung wieder zurückspeichern, Nur in Assembler, nicht in C. > Besteht aber auch die Gefahr, dass Operationen mit 16 bit oder 32 bit > Variablen, die NICHT in der Interrupt-Routine verwendet und verändert > werden, korrupt werden, weil ein Interrupt zw. den Bytes auftritt, d.h. > muss hier das Interrupt Flag gesperrt werden oder ist dies überflüssig, Dein Problem nennt man atomaren Zugrifft. Wie man den richtig macht, steht hier. https://www.mikrocontroller.net/articles/Interrupt#Atomarer_Datenzugriff > 2.Die Interrupt-Priorität ist im ATmega fest vergeben, wenn aber das > global I-Flag im Interrupt sofort wieder freigeben wird, könnte > theoretisch auch ein Interrupt mit niedrigerer Priorität dazwischen > ausgeführt werden oder liege ich hier falsch, z.B.: Ja, man kann per Software verschachtelte Interrupts auf dem AVR machen, wenn man weiß was man tut.
Falk B. schrieb: > Dein Problem nennt man atomaren Zugrifft. Wie man den richtig macht, > steht hier. Er hatte allerdings nach Auswirkungen der ISR auf Variablen gefragt, die in der ISR nicht angesprochen werden. Da ist atomarer Zugriff unnötig.
:
Bearbeitet durch User
(prx) A. K. schrieb: > Er hatte allerdings nach Auswirkungen der ISR auf Variablen gefragt, die > in der ISR nicht angesprochen werden. Da ist atomarer Zugriff unnötig. Das ist eine Tautologie. Aber trotzdem schön, daß wir drüber gesprochen haben . . .
Andi_T T. schrieb: > 1. Nach meiner Recherche muss man das SREG in einer Interrupt-Routine > immer selbst sichern und vor Beendigung wieder zurückspeichern, > ansonsten kann es je nach Operation in der Interrupt-Routine und > Änderung der SREG-Flags beim Rücksprung zum Bruch führen. Das SREG hat auf den Rücksprung keine Auswirkung. Der Compiler sichert das SREG, um u.A. folgendes zu verhindern: > Besteht aber auch die Gefahr, dass Operationen mit 16 bit oder 32 bit > Variablen, die NICHT in der Interrupt-Routine verwendet und verändert > werden, korrupt werden, weil ein Interrupt zw. den Bytes auftritt, d.h. > muss hier das Interrupt Flag gesperrt werden oder ist dies überflüssig, Ist also überflüssig.
Gcc schrieb: > Wenn du, wie oben zu sehen, die ISR in C schreibst, dann kümmert sich > der Compiler um das SREG, in den neueren GCC-Versionen (ab 9, WIMRE) > läst er das auch weg, wenn die ISR das SREG nicht braucht. Vielen Dank für die Unterstützung, im Disassembly sieht man, dass das SREG automatisch gesichert wird und nicht händisch nochmals durchgeführt werden muss. GNU Compiler Invoking: AVR/GNU C Compiler : 5.4.0 gcc version 5.4.0 (AVR_8_bit_GNU_Toolchain_3.6.2_1778)
Jörg W. schrieb: > Andi_T T. schrieb: >> gcc version 5.4.0 > > Der ist übrigens uuuuuuralt inzwischen (3. Juni 2016). Das haben auch die Versionen vorher schon alle so gemacht. WinAVR in seiner letzten Version von 2010 mit gcc 3.x dürfte ja heute noch hier und da im Einsatz sein, noch ältere findet man wohl eher selten. Aber schön, daß das auch in Jahre 2021 immer noch mal diskutiert wird. Oliver
:
Bearbeitet durch User
Andi_T T. schrieb: > 1. Nach meiner Recherche muss man das SREG in einer Interrupt-Routine > immer selbst sichern und vor Beendigung wieder zurückspeichern, > ansonsten kann es je nach Operation in der Interrupt-Routine und > Änderung der SREG-Flags beim Rücksprung zum Bruch führen. Nein. Sichern und Wiederherstellen muss man SREG nur dann, wenn man in der ISR irgendwas an den Flags (AUSSER dem I-Flag) ändert. Das I-Flag wird automatisch durch die Hardware vor dem Eintritt in die ISR gelöscht und durch das reti am Ende der ISR genauso automatisch auch wieder gesetzt, darum braucht man sich also normalerweise überhaupt nicht zu kümmern, nur um den Rest der Flags.
Oliver S. schrieb: >> Der ist übrigens uuuuuuralt inzwischen (3. Juni 2016). > > Das haben auch die Versionen vorher schon alle so gemacht. Ja, sicher. Aktuelle sind aber etwas intelligenter beim Generieren der ISR-Umgebung:
1 | #include <avr/interrupt.h> |
2 | #include <avr/io.h> |
3 | |
4 | ISR(INT0_vect) |
5 | {
|
6 | PIND = 1; |
7 | }
|
1 | $ avr-gcc -Os -c -mmcu=atmega328 isrtest.c |
2 | $ avr-objdump -d isrtest.o |
3 | |
4 | isrtest.o: file format elf32-avr |
5 | |
6 | |
7 | Disassembly of section .text: |
8 | |
9 | 00000000 <__vector_1>: |
10 | 0: 8f 93 push r24 |
11 | 2: 81 e0 ldi r24, 0x01 ; 1 |
12 | 4: 89 b9 out 0x09, r24 ; 9 |
13 | 6: 8f 91 pop r24 |
14 | 8: 18 95 reti |
:
Wiederhergestellt durch Moderator
Jörg W. schrieb: > Aktuelle sind aber etwas intelligenter beim Generieren der ISR-Umgebung: Es wäre ja auch schlimm, wenn zig Compilergenerationen nicht doch irgend eine Verbesserung gebracht hätten. Den kleinen Codegrößen vom gcc 3 weinen aber heute noch viele nach ;) Oliver
Oliver S. schrieb: > Den kleinen Codegrößen vom gcc 3 weinen aber heute noch viele nach Ich nicht, denn ich denke, dass die nur gefühlt "klein" waren. Sicher wirst du immer pathologische Fälle finden, aber im Großen und Ganzen sind vor allem durch Johanns Arbeit (ich glaube, es war ab GCC 4.9) die Optimierungen für AVR deutlich besser geworden.
Bei ISRn mit der Besonderheit, dass das SREG darin nicht verändert (also geschrieben) wird, muss man dieses auch nicht Sichern und Wiederherstellen. Der dazu gewissermaßen entgegengesetzte Grenzfall wäre ein Programm, in dessen Nicht-ISR-Code ("Main"-Ebene) das SREG nirgendwo ausgewertet (also gelesen) wird. Meistens ist die Main-Schleife dann einfach leer ("Loop: rjmp Loop"), d. h. das Programm läuft komplett in ISRn. Manche Leute finden Gefallen daran, das so zu machen, und unter bestimmten Bedingungen ist das auch völlig OK. Auch in diesem Fall kann man auf das Sichern und Wiederherstellen des SREG in den ISRn verzichten (solange man nicht mit verschachtelten Interrupts operiert).
Jörg W. schrieb: > Oliver S. schrieb: >> Den kleinen Codegrößen vom gcc 3 weinen aber heute noch viele nach > > Ich nicht, denn ich denke, dass die nur gefühlt "klein" waren. > > Sicher wirst du immer pathologische Fälle finden, aber im Großen und > Ganzen sind vor allem durch Johanns Arbeit (ich glaube, es war ab GCC > 4.9) die Optimierungen für AVR deutlich besser geworden. Johanns Arbeit ist unbestritten wertvoll, und ich weine dem 3er gcc auch nicht nach. Den Codegrößenvergleich müsste man aber doch mal richtig machen. Leider ging’s damit nach Johanns Ausscheiden ja wild hin und her, aber vor allem wieder rauf. Oliver
Oliver S. schrieb: > Den Codegrößenvergleich müsste man aber doch mal richtig machen. Würde voraussetzen, den alten Compiler überhaupt noch rumliegen zu haben, außerdem eine Version der avr-libc, die damit compiliert ist. Interessant wäre mal der Vergleich mit Clang, aber dazu habe ich mich auch noch nicht aufraffen können.
Andi_T T. schrieb: > ISR (TIMER0_OVF_vect) > > uint8_t sreg_temp; > > sreg_temp = SREG; > > sei(); > > ….. // z.B. SPI Interrupt (geringere Priorität) > > SREG = sreg_temp; Boah. Ist im besten Fall nicht nötig, weil ein Compiler für den AVR das Sichern des SREG selbst hinbekommen muß. Im schlechtesten Fall führt das in die "Katastrophe", weil C-Compiler durchaus Code optimieren (u.a. verschieben) dürfen und dann irgendein Quatsch passiert. Auf alle Fälle den Assembler-Output des Compilers kontrollieren, wenn man so was macht. Aber in 99.99995% der Fälle gilt: Lass es den Compiler machen. Dafür ist er da. Zu den sei(); im ISR: Wenn der SPI Interrupt aber länger braucht, als der TIMER0_OVF_vect wieder überläuft, dann wird der TIMER0_OVF_vect nach dem RETI des SPI-Interrupt sofort nochmal aufgerufen, bevor der "1. TIMER0_OVF_vect" vollständig abgearbeitet ist. Hat das Potential u.a. zu Stack-Overflow und anderen sehr unerfreulichen und ekeligen Fehlern. Tip von mir: Halte alle Interrupts so kurz, daß die kleine Verzögerung durch den Interrupt bei anderen verzögerten Interrupts nicht stört. Üblich ist z.B. nur einen Zähler hochzählen oder ein Flag zu setzten, welches dann in der main() abgearbeitet wird. Dann kann man sich das sei(); im Interrupt sparen. Es lohnt sich aus hier immer mal wieder einen kritischen Blick auf den Assembler-Output zu haben. Man kann durch geschicktes Programmieren hier einiges erreichen. Faustformel: Im Interrupt nur so viel wie absolut nötig, so kurz wie irgendwie möglich. Gruß Robert P.S. Lesenswert zu "kurzen code" ist: http://ww1.microchip.com/downloads/en/AppNotes/doc1497.pdf http://ww1.microchip.com/downloads/en/AppNotes/doc8453.pdf
Jörg W. schrieb: > Andi_T T. schrieb: >> gcc version 5.4.0 > > Der ist übrigens uuuuuuralt inzwischen (3. Juni 2016). Ich habe das Atmel Studio 7 2020 installiert, wundert mich dann das diese alte Version hinterlegt ist, d.h. ich sollte ein Upgrade durchführen. Wie wird dies am Besten durchgeführt damit kein Bruch entsteht, unter Extensions and Updates oder einfach die aktuelle Version unter https://gnutoolchains.com/avr/ downloaden und installieren!?
Andi_T T. schrieb: > oder einfach die aktuelle Version unter > https://gnutoolchains.com/avr/ downloaden und installieren!? Da da die Version 5.3.0 angeboten wird, ist das wohl weniger sinnvoll. Den (fast) aktuellsten avr-gcc findest du immer hier: https://blog.zakkemble.net/avr-gcc-builds/ Aber wenn du mit dem Studio bisher klargekommen bist, und du da die aktuellste Version hast, gibts wenige Gründe für ein Update. Oliver
Andi_T T. schrieb: > Ich habe das Atmel Studio 7 2020 installiert, wundert mich dann das > diese alte Version hinterlegt ist Das kommt halt davon, wenn man sich als Hersteller seine eigenen Patches zusammen hackt (nennt sich bei Microchip "Device Pack"). Irgendwann läuft das dann mal mit den Upstream-Änderungen so weit aus dem Ruder, dass man es praktisch nicht mehr schafft, da alle eigenen Änderungen auf dem Laufenden zu halten. Für dich als Endanwender hast du nun zwar eine Hersteller-gepflegte Software, die allerdings an einigen Ecken und Enden der Community-gepflegten (eben bspw. beim Prolog/Epilog der ISRs) deutlich hinterher hinkt.
:
Bearbeitet durch Moderator
Jörg W. schrieb: > Das kommt halt davon, wenn man sich als Hersteller seine eigenen Patches > zusammen hackt (nennt sich bei Microchip "Device Pack"). Irgendwann > läuft das dann mal mit den Upstream-Änderungen so weit aus dem Ruder, > dass man es praktisch nicht mehr schafft, da alle eigenen Änderungen auf > dem Laufenden zu halten. Hmm ok und was kann ich nun dagegen tun oder was wäre sinnvoll?
googlen ? https://microchipsupport.force.com/s/article/Atmel-Studio---Changing-Toolchain-Compiler Oliver
Oliver S. schrieb: > googlen ? Ok, wollte nur nix falsch machen. Vielen Dank an ALLE hier für den sehr guten Support. Andreas
Andi_T T. schrieb: > Ok, wollte nur nix falsch machen. Warum? Wird man bei euch dann öffentlich geteert und gefedert? Man lernt aus solchen Aktionen, aber nur, wenns erst mal schiefgeht, und man dann herausfindet, warum. Oliver
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.