Hallo,
arbeite gerade an einem Terminalserver, bei dem man über Hterm Befehle
eingeben kann, welche dann vom Board (mit Atmega16) ausgeführt werden.
Jetzt soll zB eine Trapfunktion aktiviert werden, die regelmäßig (also
mit Timer) eine Message ausgegeben wird. Leider blockiert mir diese
Funktion meine weitere Eingabe und kann deshalb auch nicht beendet
werden. MEine Frage deshaln: wie schaffe ich es, einen Interupt parallel
zum normalen Programm laufen zu lassen?
Das sind die Unterprogramme:
1
ISR(TIMER0_OVF_vect)
2
{
3
4
_delay_ms(250);
5
6
for(doublex=0;x<=50000;x++)
7
{
8
;;//Kommentar k
9
}
10
uart_puts("--Trap-Funktion aktiv / Keine Eingabe-- ");
Gar nicht. Ein MC der AVR Klasse hat nur einen Kern und kann damit
prinzipbedingt nur eine Aufgabe bearbeiten. Entweder du verlagerst eine
Aufgabe auf intelligente Peripherie oder du optimierst dein Programm.
Es ist z.B. eine höchst dumme Idee, in eine ISR Delays einzubauen. Eine
ISR blockt damit alles andere und kann normalerweise nicht mal durch
andere ISR unterbrochen werden.
Auch UART Ausgaben oder LCD Zeugs hat in einer ISR nichts verloren. Das
kostet alles nur Zeit und blockt den Rest.
Peter Smart schrieb:> wie schaffe ich es, einen Interupt parallel> zum normalen Programm laufen zu lassen?
Solange du nur einen CPU-Kern hast, gar nicht! Das betrifft nicht nur
Mikrocontroller aonder auch normale CPUs. Auf einem
Single-Core-Prozessor kann es keine parallaelitaet geben, wie auch. Was
bei Single-Core-Prozessoren gemacht wird, ist, das man jedem Programm
immer ein bisschen Zeit zum Arbeiten gibt, und dann das naechste
Programm dran kommt. Durch das schnelle Umschalten der Programme
entsteht der Eindruck einer "quasie parallaelitet". Das passiert auch
auf Multi-Core-Prozessoren. Bei einem Quad-Core-Prozessor z.B. koennen
tatsaechlich vier Programme parallel, also wirklich gleichzeitig laufen.
Da da aber nicht nur vier Programme laufen, sondern viiiiiele, wird auch
da wieder pro Kern zwischen den Programmen hin und her geschaltet.
Gruesse
Peter Smart schrieb:> Ich muss auf jeden Fall die Möglichkeit haben, aus der ISR rauszukommen,
Eine ISR ist eine besondere Funktion, das ist dir hoffentlich klar.
WENN du die ISR von Hand beenden willst (was meiner Meinung nach keinen
Sinn macht) dann musst du dich auch um die ganzen Aufraeumarbeiten
kuemmern, Stack aufraeumen, ruecksprungadresse, stackpointer etc.
An deiner Stelle wuerde ich ueber eine Umstrukturierung des Programms
nachdenken.
Poste dochmal deinen gesamten Code.
Gruesse
Edit:
Peter Smart schrieb:> Ich muss auf jeden Fall die Möglichkeit haben, aus der ISR rauszukommen,
Ich bin mir nicht mal sicher, ob das ueberhaupt moeglich ist.
Peter Smart schrieb:> Ich muss auf jeden Fall die Möglichkeit haben, aus der ISR rauszukommen,
denn schreib doch einfach ein return rein.
Kaj schrieb:> Eine ISR ist eine besondere Funktion, das ist dir hoffentlich klar.> WENN du die ISR von Hand beenden willst (was meiner Meinung nach keinen> Sinn macht) dann musst du dich auch um die ganzen Aufraeumarbeiten> kuemmern, Stack aufraeumen, ruecksprungadresse, stackpointer etc.
nö, denn wir reden hier über C und macht dann der Compiler wenn man
einfach return schreibt.
Danke hierfür schonmal. ICh dachte, dass man vielleicht die Ausgabe nur
einmal laufen lässt und danach eine Abfrage macht, ob die Funktion
beendet wird und ansonsten wieder startet. Oder man lässt den Timer
dauerhaft laufen und macht die Ausgabe nur, wenn die Funktion Timer
aktiviert wurde und lässt ihn ansonsten einfach nur hochzählen.
(vielleicht mit einer globalen Variablen?) Wären das vielleicht
umsetzbar?
Peter Smart schrieb:> Ich muss auf jeden Fall die Möglichkeit haben, aus der ISR rauszukommen,> um sie per Eingabe zu beenden. Wie könnte ich das realisieren?
einfach wie eine normale Funktion beenden. Der Compiler macht dann ein
Return from Interrupt daraus und setzt das Programm dort fort wo es
Unterbrochen wurde.
Alles kein Hexenwerk sondern nur der simple Sprung zu einer anderen
Adresse dem sichern von ein paar Registern und umgekehrt am Ende.
Vom Konzept her spart dir ein Interrupt nur das ständige Abfragen von
externen Ereignissen. Du kannst dann schnell darauf reagieren (oder eben
nur ein Flag setzen und das ganze verarbeiten wenn du Zeit hast).
Alles was Zeit kostet gehört da nicht rein weil es schlicht dem Konzept
wiederspricht und dir den Prozessor blockiert.
Sinnvoll wäre evtl. deinen Trap per Timer zu starten und dann bei jedem
Interrupt ein Zeichen zu senden (in dem Status kannst du z.B. die
Intervalle des Timers Interrupts verkürzen). Dann brachst du nur
jeweils einen Befehl um das Zeichen in das Uart Subsystem zu schieben,
den Rest macht die Hardware dann parallel zu deiner Software.
Timer rückwärts zählen lassen, bei 0 wird ein interrupt ausgelöst, der
nur ein Flag setzt.
Timner Interrupt:
set Flag
mainloop:
Wenn Befehl bekommen:
setze Timer auf Timeout Wert
Wenn Flag gesetzt:
clear Flag
Gib Text Aus
endloop
Peter Smart schrieb:> _delay_ms(250);> for(double x=0; x<=50000;x++)> {> ;; //Kommentar k> }
Die längere Zeit im Interrupt kannst du z.B. mit
interrupt(){
static int TransmitCountdown = 50000;
if (~TransmitCountdown--){
uart_puts("--Trap-Funktion aktiv / Keine Eingabe-- ");
TransmitCountdown = 50000;
}
} // RTI
auf die Sendezeit verkürzen, besser ist aber du sendest einzelne Zeichen
bei jedem Interrupt und nachst den Zähler dann wieder scharf.
Der Rächer der Transistormorde schrieb:> static int TransmitCountdown = 50000;
Puristen schreiben da natürlich:
static unsigned int TransmitCountdown = 50000;
Der Rächer der Transistormorde schrieb:> if (~TransmitCountdown--){
Zu was soll denn denn das gut sein?
Da kannst du auch "if (1)" hinschreiben.
Siehe mal in deinem C/C++ Buch nach was "~" macht ;-)
Du musst das Programm grundsätzlich anders aufbauen, denn auf
Mikrocontrollern ohne Betriebssystem gibt es nicht mehrere Threads, die
man schlafen legen kann, wie auf dem PC.
Die bewährte Vorgehensweise ist:
In der Timer-ISR wird nur ein Flag gesetzt, das in der Hauptschleife
ausgewertet wird. Die Hauptschleife kann dann wieder von anderen
Interrupts (z.B. UART-Zeichen empfangen) unterbrochen werden.
1
staticboolflagPrintMsg=false;
2
3
ISR(TIMER0_OVF_vect)
4
{
5
flagPrintMsg=true;
6
}
7
8
intmain(void)
9
{
10
// Initialisierung ...
11
while(1)
12
{
13
// Andere Aufgaben ...
14
if(flagPrintMsg)
15
{
16
uart_puts("--Trap-Funktion aktiv / Keine Eingabe-- ");
17
flagPrintMsg=false;
18
}
19
}
20
}
Die _delay_ms()-Funktion brauchst Du nicht, wenn Du den Timer
verwendest. Du musst ihn nur so einstellen, dass er alle 250 ms feuert,
sprich die ISR aufgerufen wird.
Peter Smart schrieb:> arbeite gerade an einem Terminalserver, bei dem man über Hterm Befehle> eingeben kann, welche dann vom Board (mit Atmega16) ausgeführt werden.> Jetzt soll zB eine Trapfunktion aktiviert werden, die regelmäßig (also> mit Timer) eine Message ausgegeben wird. Leider blockiert mir diese> Funktion meine weitere Eingabe und kann deshalb auch nicht beendet> werden. MEine Frage deshaln: wie schaffe ich es, einen Interupt parallel> zum normalen Programm laufen zu lassen?
Ganz streng gesehen: Garnicht. Da die AVRs nur eine MCU besitzen, können
sie auch nur an einer Aufgabe zu einer Zeit rechnen, solange sie also
irgendeine ISR abarbeiten, können sie keinen Handschlag am Hauptprogramm
machen. Alles, was möglich ist, ist die ISR durch eine weitere ISR
unterbrechen zu lassen. Dann rechnen sie halt an dieser neuen ISR, bis
die fertig ist, dann an der unterbrochenen ISR, bis diese fertig ist und
erst dann geht's mit dem Hauptprogramm weiter.
Generell geht also nur eine Quasi-Gleichzeitigkeit, die in Wirklichkeit
eine Abfolge von (ggf. geschachtelten) kurzen Unterbrechungen
darstellt. Der Name "Interrupt" drückt das auch aus, denn das heißt
nichts anderes als Unterbrechung.
Es gibt mehrere Möglichkeiten, so eine Quasi-Gleichzeitigkeit zu
erreichen. Alle sind mit Vor- und Nachteilen behaftet. Für alle gilt
aber gleichermaßen:
1) delay() ist verboten. Auch jedes andere Konstrukt, was den Verbrauch
von Rechenzeit zur Zeitmessung bzw. Verzögerung einsetzt. Erlaubt ist es
nur für sehr kurze, technisch notwendige Wartezeiten (sinnvolle
Obergrenze etwa die Dauer von zwei Interruptframes, also ca. 16 Takte)
2) Alle ISRs sind so kurz wie irgend möglich zu halten. Zumindest der
Zeitraum, in dem exklusiv ausgeführt werden.
Die verschiedenen Ansätze kann man etwa so charakterisieren:
A) Alles passiert in main().
ISRs werden entweder komplett vermieden (reine Trigger-Events, typisch
z.B. Timer-Overflow) oder extrem kurz (Wert aus der Hardware holen, in
Puffer legen und Ereignisflag setzen, typisch z.B. UART-Receive).
Vorteile: sehr einfach zu programmieren, oft auch ziemlich effizient.
Nachteile: Reaktionszeit hängt von der längsten Ereignisbehandlung in
main() ab. Polling schluckt wertvolle Rechenzeit. Sinnvoller Einsatz von
Schlafmodi relativ schwierig.
B) Alles passiert in ISRs.
Einzige Aufgabe des Hauptprogramms: Initialisierung des Systems und ggf.
Eintritt in Sleepmodus. ISRs sinnvollerweise oft in exklusiven Teil und
kooperativen Teil gespalten.
Vorteile: Reaktionszeit auf äußere Ereigniss meist sehr viel geringer.
Keine sinnloser Aufwand von Rechenzeit und Energie für Polling.
Einfache, sich sozusagen auf natürliche Art ergebende Nutzung von
Schlafmodi.
Nachteile: Weitaus schwieriger zu programmieren, Gefahr von
Stacküberlaufen bei unsachgerechter Programmierung.
Diese beiden Varianten stellen aber letztlich gleichermaßen Formen von
kooperativem Multitasking dar und sind sozusagen zwei Extremformen ein-
und derselben Sache. Dementsprechend sind auch beliebige Mischformen
zwischen den beiden Extremen möglich. Ich würde aber empfehlen,
möglichst keine Mischformen zu benutzen, sondern sich je nach konkreter
Anwendung für eins der beiden Extreme zu entscheiden, denn i.A. ist es
so, daß man sich bei Mischformen die Nachteile beider Ansätze
einhandelt, die Vorteile beider Ansätze aber mehr oder weniger flöten
gehen.
Was bleibt noch? Richtig, präemptives Multitasking:
C) (Fast) alles passiert in einer ISR, typischerweise in einer
Timer-ISR.
Genau genommen handelt es sich hierbei um eine Sonderform von Ansatz B).
Vorteile: einfache Programmierung der einzelnen Tasks, auch dümmliche
delays und ungewollte Endlosschleifen schaden der Funktionsweise des
Restsystems nicht, sie drücken nur dessen Performance.
Nachteile: Interaktionen mit anderen Interrupts und parallel laufenden
Tasks ist schwierig zu programmieren, Gefahr von Deadlocks. System ist
insgesamt sehr viel ineffizienter als bei kooperativem MT.