Hallo zusammen, ich fange gerade an, mich mit Interruptprogrammierung beim ATMega324 zu beschäftigen. In dem Zusammenhang bin ich etwas verunsichert, weil ich immer wieder von Interruptprioritäten lese. Wenn ich das, was im Datenblatt steht bzw. nicht steht, richtig zusammenzähle, denke ich, dass der eigentliche Prozessorkern keine Interruptprioritäten im klassichen Sinne kennt. Mit klassisch meine ich wie z.B. beim 80C166, wo der Prozessorkern schon allein durch Annahme eines Interrupts von z.B. Priorität 5 (ohne weitere Beeinflussung durch die Software) dafür sorgt, dass keine anderen Interrupts der Priorität 5 oder niedriger angenommen werden. Höher priorisierte Interrupts aber schon. Dort gibt es nicht ein I-Flag im Status, sondern IMHO vier, um den Level der gerade erlaubten Interrups abzubilden. Dem Datenblatt entnehme ich, dass bei Annahme des Interrupts beim AVR lediglich das I-Bit gelöscht wird. Die Prioritäten hier sind nur dafür wichtig, welcher Interrupt als erstes bedient wird, sofern mehrere gleichzeitig anstehen. Wenn ich in der ISR das I-Flag nicht beeinflusse, sind während der Ausführung alle weiteren Interrupts gesperrt, egal wie hoch deren Priorität im Vergleich zu der "meiner" gerade laufenden Routine ist. Sehe ich das richtig? Danke und schönen Gruß – Peter
Ja, das siehst Du richtig. Das Einzige, was Du machen kannst: das I-Bit in Interrupt-Routinen zu löschen, die von anderen Interrupts unterbrochen werden sollen. Mit allen negativen Konsequenzen. Gruß, Stefan
Stefan schrieb: > Das Einzige, was Du machen kannst: das I-Bit in Interrupt-Routinen zu > löschen, die von anderen Interrupts unterbrochen werden sollen. Mit > allen negativen Konsequenzen. Es gibt eigentlich nur eine wirklich negative Konsequenz und das ist die Gefahr eines Stacküberlaufs. Und die kann man sehr leicht dadurch ausschließen, daß man möglichst schnell in der ISR den exklusiv zu erledigenden Kram abarbeitet, um dann von der globalen Interruptsperre mit der Holzhammermethode via I-Flag auf die lokale via *IE-Bit des Kontrollregisters der entsprechenden interruptauslösenden Einheit zu wechseln. Damit hat man eigentlich nur noch die üblichen Probleme (und Vorteile!) "paraller" Programmierung. Natürlich: Wenn die Last insgesamt zu hoch ist, wird's nicht mehr richtig funktionieren. Aber das ist bei jeder anderen Methode der Ereignisbehandlung ganz genauso der Fall, bis herunter zu den Systemen ganz ohne Interrupt, die alles nur in main() pollen. Das Problem beim Umswitchen auf lokale Interruptsperren ist vor allem der zusätzliche Overhead, der dadurch eingeführt wird. Man muß hier abwägen, wo es sinnvoll (im Sinne des Gesamtsystems) ist und wo nicht. Auf jeden Fall hat man damit erstmal eine einfache und gut beherrschbare Möglichkeit, zwei Prioritätsebenen für Interrupts zu schaffen. Das besonders blöde bei größeren AVRs ist, daß hier leider längst nicht mehr alle Register mit *IE-Flags per sbi/cbi erreichbar sind, was dazu zwingt, den exklusiven Teil der ISRs durch das Retten von Registern und Flags noch signifikant weiter zu verlängern. Da ist es dann sehr nützlich, wenn man eine Sprache/Compiler verwendet, wo es möglich ist, zwei Register extra für solche Zwecke in ISRs zu reservieren. Die stehen dann allen ISRs zur Verfügung und verkürzen für alle die Zeit, in denen sie exklusiv laufen müssen und senken damit auch die Latenzen für alle ISRs, die oft viel wichtiger sind als die Laufzeit an sich.
Danke für die Bestätigung. Ich möchte gar nicht wirklich eine Priorisierung. Andere Interrupts sollen aber kommen können; es soll halt nur nicht die eigene IRQ-Quelle nochmal kommen können. In meinem Beispiel geht es Timer0, Overflow. Ich schreibe also am Anfang von meiner ISR ein sei hin. Weil aber beim TIFR0 (Timer/Counter 0 Interrupt Flag Register) steht: "TOV0 is cleared by hardware when executing the corresponding interrupt handling vector" (verstehe ich so, dass in dem Moment, wo meine ISR betreten wird, es schon gelöscht ist), bleibt mir nichts anderes übrig, als vor dem sei noch ein TOIE0=0 (TIMSK0-Register) zu veranlassen. Am Ende der ISR setze ich das Bit wieder. Damit ein ggf. vorher aufgelaufener wiederholter Timer-Überlauf nun nicht reinhackt, kommt vorher noch ein cli. Also:
1 | ISR(TIMER0_OVF_vect, ISR_BLOCK) |
2 | {
|
3 | TIMSK0 &= ~(1<<TOIE0) // Weitere Timer-IRQs verhindern |
4 | sei(); // Andere IRQs erlauben |
5 | MeinCode(); |
6 | cli(); // Neuer Timer-IRQ soll nicht reingrätschen |
7 | TIMSK0 |= (1<<TOIE0) // Timer-IRQ wieder erlauben |
8 | }
|
Was passiert, wenn MeinCode so lange braucht, dass währenddessen ein neuer Timer-Überlauf stattfindet? Ich hoffe/verstehe das so: Bit in TIFR0 wird gesetzt, es passiert erstmal nichts, weil ich Bit in TIMSK0 gelöscht habe. Erst nach reti sind I-Flag und Bit in TIMSK0 beide gesetzt. D.h. dann wird sofort erneut die ISR aufgerufen, ohne Stacküberlauftendenzen. Die angesprochenen Laufzeitoptimierungen durch exklusive Register habe ich erstmal hintenangestellt. Ist das in anderen Worten das, was c-hater (Gast) meinte? Oder habe ich noch was übersehen? Gruß – Peter
Gerade gefunden: http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Unterbrechbare_Interruptroutinen Meine Vorschläge scheinen dem zu entsprechen.
Peter M. schrieb: > Also: >
1 | ISR(TIMER0_OVF_vect, ISR_BLOCK) |
2 | > { |
3 | > TIMSK0 &= ~(1<<TOIE0) // Weitere Timer-IRQs verhindern |
4 | > sei(); // Andere IRQs erlauben |
5 | > MeinCode(); |
6 | > cli(); // Neuer Timer-IRQ soll nicht reingrätschen |
7 | > TIMSK0 |= (1<<TOIE0) // Timer-IRQ wieder erlauben |
8 | > } |
Würde ich nur im Notfall machen. Du erhebst doch lediglich den Anspruch, dass ein anderer Interrupt auf jeden Fall auch ausgeführt werden soll. Das passiert doch auch ohne sei() und cli() in der ISR. Wenn während Ausführung der ISR "A" ein weiterer Interrupt für ISR "B" eintrifft, wird das mittels Flag gespeichert. Sobald sich ISR "A" beendet, wird dann ISR "B" angesprungen. Es geht also nichts verloren - außer wenn die ISR "A" so lahm ist, dass während der Ausführung mehr als ein Interrupt für ISR "B" eintrifft. Dann wird die ISR "B" nur einmal statt zum Beispiel 3x ausgeführt. Aber das halte ich eher für einen Vorteil als Nachteil. Bei diesem Fall müsste man den Code für ISR "A" halt nachbessern. > Was passiert, wenn MeinCode so lange braucht, dass währenddessen ein > neuer Timer-Überlauf stattfindet? Wird als Flag gespeichert. Nach Beendigung wird direkt wieder die ISR angesprungen. Damit hast Du dann den µC zu 100% dichtgemacht. ;-)
Peter M. schrieb: > ich fange gerade an, mich mit Interruptprogrammierung beim ATMega324 zu > beschäftigen. In den allermeisten Fällen gibt es bei typischen AVR-Programmen keinen Grund, ISRs unterbrechbar zu machen, wenn man den ebenso AVR-typischen Ratschlägen folgt, ISRs kurz zu halten, und Datenbearbeitung in der main-Loop durchzuführen. Die verfügbare Rechenleistung wird ja durch Einsatz von ISRs nicht größer. Mehr Zyklen, als der Prozessor pro Zeiteinheit ausführen kann, kann er nicht ausführen. In den ganz seltenen und wenigen Ausnahmefällen, bei denen es auf zyklengenaue und extrem schnelle Reaktion auf (externe) Ereignisse ankommt, mögen unterbrechbare ISRs erforderlich sein. Dann weiß man aber sehr genau, warum das nötig ist. 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.