Hi!
In meinem aktuellen Bastelprojekt (Atmega32, avr-gcc) möchte ich eine
Funktion, die als "scheduler" abwechselnd verschiedene Dinge tut, mehr
oder weniger regelmäßig aufrufen. Dies soll immer geschehen, egal wo das
Programm gerade ist.
Normalerweise würde ich dafür einen Timer mit ISR nehmen. Das Problem
ist aber, dass die Jobs des "schedulers" durch ISRs unterbrochen werden
dürfen sollen müssen. Wenn ich den Scheduler aus einer ISR heraus
aufrufe, würden ja auch alle vom Scheduler aufgerufenen Funktionen/Jobs
innerhalb der ISR laufen. Mit allen Konsequenzen (volatile-Deklarationen
usw).
Meine aktuelle Lösung ist im Moment, aus jeder Schleife im Programm, die
längere Zeit laufen kann (z.B. Warten auf Eingaben usw.) die
Scheduler-Funktion aufzurufen. Das finde ich aber nicht wirklich
elegant.
Geht das auch anders? Wie funktionieren die unterbrechbaren Interrupts?
Gruß,
Chris
Chris schrieb:> jeder Schleife im Programm, die> längere Zeit laufen kann (z.B. Warten auf Eingaben usw.)
Besser die Programmstruktur ändern und diese Schleifen ersetzen
Das Programm kann doch immer:
Eingaben abfragen
Ausgaben abhängig von Programmzuständen machen
Sceduler aufrufen wenn ein Blinktakt gerade 1 ist
usw.....
>Wie funktionieren die unterbrechbaren Interrupts?
Die werden mit INTs höherer Prio unterbrochen. (Der mit der höchsten
Prio kann nicht mehr unterbrochen werden)
Du kannst alles einfach nur INT-basiert aufrufen, auch mit (mehreren)
Timer-INT.
GGfs mehrere Timer-INTs, damit bei längeren Intervallen auch 'Platz' für
mehrere Befehle ist.
(Main hätte sozusagen die niedrigste Prio)
MCUA schrieb:>>Wie funktionieren die unterbrechbaren Interrupts?> Die werden mit INTs höherer Prio unterbrochen. (Der mit der höchsten> Prio kann nicht mehr unterbrochen werden)
Nicht auf dem AVR. Da entscheidet die Priorität nur darüber, welche ISR
aufgerufen wird wenn mehrere Interrupts GLEICHZEITIG anliegen. Eine ISR
wird nicht durch eine andere ISR unterbrochen. (Es sei denn, man setzt
das I-Flag in der ISR. Dann kann es aber auch passieren, das sie sich
selbst unterbricht. Nur für Profis, die genau wissen was sie tun!)
>Nicht auf dem AVR. Da entscheidet die Priorität nur darüber...
Von wegen. Der einfache AVR hat nichtmal verschiedene INT-Prio-Ebenen!!
(XMEGAS habens)
(Die Aufruf-Priorität innerhalb gleicher INT-Prio-Ebenen ist dazu
untergeordnet)
So besonders schwierig ist das nicht eine ISR unterbrechbar zu machen.
Man gibt per SEI() interrupts wieder frei - entweder von Hand, oder man
sagt dem Compiler das das gleich noch in der Einleitung so kommt.
Aufpassen muss man vor allem das der selbe Interrupt nicht noch einmal
kommt, bevor die eine Version des Interrupts abgearbeitet ist. Bei
einem Timer Interrupt geht das meist über eine nicht zu hohe Frequenz
für den Interrupt. Einen externen Interrupt, der schneller kommen kann,
muss man ggf. von Hand abschalten.
Man braucht zusätzlichen Platz auf den Stack für eine zusätzliche ISR.
Hinsichtlich volatiler Variablen gibt es eigentlich nichts weiter zu
beachten - so weit ich weiss macht das GCC keinen Unterschied in der ISR
zu normalem Code. Es wird also nicht angenommen oder ausgenutzt das die
ISR nicht unterbrechbar ist. Für andere CPUs wäre das ja ggf. auch ein
Fehler.
Chris schrieb:> In meinem aktuellen Bastelprojekt (Atmega32, avr-gcc) möchte ich eine> Funktion, die als "scheduler" abwechselnd verschiedene Dinge tut, mehr> oder weniger regelmäßig aufrufen. Dies soll immer geschehen, egal wo das> Programm gerade ist
Die Denkweise ist falsch.
Einfacher und nachvollziehbarer wird es so:
in deiner Software gibt es eine Haupptschleife, die dauernd
schnellstmöglich durchlaufen wird. Keine Abzweigung aus dieser Schleife
darf länger als z.B. 10ms dauern. D.h. wenn du 10 Abzweigungen hast,
kannst du dann sicher sagen: jede Abzweigung in der Hauptschleife wird
mindestens alle 100ms abgefragt.
Und diese Abzweigungen werden dann von kurzen und knackigen Interrupts
bestimmt: "ein Zeichen ist von der SIO gekommen", "schon wieder eine
Sekunde vorbei", usw. Andere Gründe für eine Abzweigung können sein: ein
Eingang hat sich geändert, eine Taste wurde gedrückt...
So funktionieren weltweit SPSen zur Zufriedenheit zigtausender Anwender.
Und lustigerweise wissen da viele nicht mal, dass sie einen Automaten
programmiert haben (die nennen das dann Merkerkette). Also:
Hauptschleife:
Eingangssabild einlesen.
Berechnungen durchführen.
Ausgangsabbild schreiben.
Springe zur Hauptschleife
Chris schrieb:> Meine aktuelle Lösung ist im Moment, aus jeder Schleife im Programm, die> längere Zeit laufen kann ... die> Scheduler-Funktion aufzurufen.
In Häppchen aufteilen. Nach Abschluss eines Happens ggf. prüfen was
ansteht und was das wichtigste ist.
Die ISR setzt Flags was zu tun ist.
Chris schrieb:> z.B. Warten auf Eingaben usw.
Die ISR erkennt die Eingabe und setzt ein Flag. Wenn das Flag "neue
Eingabe" gesetzt ist wird die mit eher hoher Priorität abgearbeitet.
Auf Eingabe warten würde ich nie im Hauptprogramm, außer es kann bis zur
Eingabe definitiv nichts anderes anfallen.
In die ISR kommt nur Code mit schlankem Fuß, oder Dinge die zwingend
sofort gemacht werden müssen (z.B. Software-PWM).
Verschachtelte ISRs machen Programme schnell zum Glücksspiel. Eine
Zusatzfunktion, eine Verschachtelung kommt dazu und schon gibts schwer
reproduzierbare Fehler.
>So funktionieren weltweit SPSen zur Zufriedenheit zigtausender Anwender.
..die aber alle rel. langsame Zeitanforderungen haben
Ein spezielles uC-System kann aber viel zeitkritischer (ms, us, einige
ns) sein, als die typ. SPS, die alle Eingänge u Ausgänge mit gleicher
Zeitspanne (typ einige zig ms) bearbeitet. Dafür bräuchte die CPU
nichtmal Interrupts zu können.
Hallo,
man muss halt geeignet strukturieren (wenn man kein RTOS verwenden will,
dass einem das Nachdenken abnimmt): alle Ereignisse, die eine Reaktion
in Echtzeit erfordern, werden in ISRs abgearbeitet, die aber nur so
schnell wie möglich das unbedingt Nötige tun, z.B. auf Rx-Int ein
Zeichen vom UART holen und in den Eingangpuffer schieben. Warteschleifen
in ISRs sind kapitale Fehler, ebenso länger dauernde Programmteile.
Dann kann das Hauptprogramm ganz entspannt in einer Endlosschleife die
nötigen Tasks abarbeiten, darunter z.B. die Reaktion auf ein empfangenes
Zeichen (Interpretation, Änderung des Zustands). Natürlich muss die
Gesamtleistung stimmen, z.B. muss das Hauptprogramm sich um Eingaben
kümmern, bevor der Eingabepuffer überläuft.
Man kann auch die hardwareseitigen Ein/Ausgaben in einer einzigen
Timer-ISR abarbeiten, wenn der Zeitrahmen reicht. Wenn etwa die Baudrate
max. 9600 Baud beträgt, kann ein Timerinterrupt mit 1 ms durchaus einige
UARTs bedienen. Die ISR darf nur nicht auf ein Zeichen warten, sondern
nur prüfen, ob eins da ist, wenn nicht, weiterspringen, kriegen wir
nächstesmal.
Gruss Reinhard
Die oben schon aufgeführten Methoden werden in diesem Artikel sehr gut
erklärt bis es dann tatsächlich zum "echten" Multitasking übergeht.
Das muß vielleicht nicht sein für deine Anwendung aber die Erklärung zu
Hauptschleife + "Call der einzelnen Funktionen" ist sehr gut erklärt.
http://www.mikrocontroller.net/articles/Multitasking
Hm, ein interessantes Thema :)
Ich werde versuchen, mein Programm zumindest in Teilen umzubauen.
Dazu habe ich z.B. schon die 1wire-Funktionen zerlegt, da die
Dallas-Temperaturfühler ja fast eine Sekunde für die Wandlung benötigen.
Bisher war mein Programm in dieser Zeit blockiert. Jetzt startet ein Job
des Schedulers die Wandlung, ein weiterer rund 2 Sekunden später holt
das Ergebnis.
MCUA schrieb:>>Wie funktionieren die unterbrechbaren Interrupts?> Die werden mit INTs höherer Prio unterbrochen. (Der mit der höchsten> Prio kann nicht mehr unterbrochen werden)
Das halte ich aber auch für ein Gerücht zumal hier von Atmega32
die Rede war. Mir fällt das auf, da ich diesem Irttum auch schon
aufgessen war. Im Atmega Datenblatt gibt es ja die schöne IRQ Tabelle.
Offenbar hatte ich das ungeprüft ins Hirn übernommen und gedacht
es wäre eine Prioritätentabelle (so wie damals beim 8086 PC).
Es ist wohl nicht so.
Ulrich, Lothar, Stefan sagten ja schon einiges dazu.
Auch ich möchte meine Betrachtungsweise mal beitragen.
Wenn die Hauptschleife nur aus :
Do Loop
bestehen würde wäre das doch schon Prima.
Alle IRQ laufen dann schön hintereinander ab.
Nun könnten wir das ganze noch etwas verfeinern indem wir
in einer Timed-IRQ (Scheduler) einen Satz ToDo-Flags abragen (nur
abfragen)
Die müssen dann je nach Priorität auf einen kleinen hausgemachten Stack
(ein array z.B.) .
Nun könne wir, wohl überlegt in der Hauptschleife noch ein paar
If Abfragen einbauen die dann diesen ToDostack bearbeiten.
Bin nicht sicher, ob mein Beitrag hilft aber es vielleicht ein
denkansatz indem man das Hauptprogramm leer lässt.
Gruss
Klaus de Lisson
Klaus De lisson schrieb:> Im Atmega Datenblatt gibt es ja die schöne IRQ Tabelle.> Offenbar hatte ich das ungeprüft ins Hirn übernommen und gedacht> es wäre eine Prioritätentabelle (so wie damals beim 8086 PC).> Es ist wohl nicht so.
In gewissem Sinne doch. Liegen mehrere Intterruptbedingungen
gleichzeitig vor, werden die mit niedrigerer Vectornummer zuerst
behandelt.
Ich schrieb:
die Erklärung zu Hauptschleife + "Call der einzelnen Funktionen" ist
sehr gut erklärt.
Oh je, sollte heißen : die Erklärung zur Hauptschleife + " Call der
einzelnen Funktionen " ist sehr gut geschrieben.
... schrieb:> Klaus De lisson schrieb:>> Im Atmega Datenblatt gibt es ja die schöne IRQ Tabelle.>> Offenbar hatte ich das ungeprüft ins Hirn übernommen und gedacht>> es wäre eine Prioritätentabelle (so wie damals beim 8086 PC).>> Es ist wohl nicht so.>> In gewissem Sinne doch. Liegen mehrere Intterruptbedingungen> gleichzeitig vor, werden die mit niedrigerer Vectornummer zuerst> behandelt.
Muss man sich dann auch nochmal explizit merken.
.. und die verschiedenen Intterruptbedingungen können sich ja
ordenlich anhäufen.
allerdings gibt es keinen IRQ Stack. Dadurch werden
Mehrfachanforderungen
ignoriert
klaus de lisson
>Das halte ich aber auch für ein Gerücht zumal hier von Atmega32>die Rede war.
sagte ich doch oben:
>>Nicht auf dem AVR. Da entscheidet die Priorität nur darüber...>Von wegen. Der einfache AVR hat nichtmal verschiedene INT-Prio-Ebenen!!