Forum: Mikrocontroller und Digitale Elektronik Interupt im Hintergrund ausführen


von Peter S. (peter_smart)


Lesenswert?

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(double x=0; x<=50000;x++)
7
  {
8
    ;; //Kommentar k
9
  }
10
  uart_puts("--Trap-Funktion aktiv / Keine Eingabe-- ");
11
  
12
    }
13
  
14
15
16
void Timer(int b)    //Timer bekommt 0 oder 1 übergeben
17
{
18
  
19
  if (b==1)
20
  {
21
  sei();
22
  TIMSK |= (1<<TOIE0);
23
  
24
  
25
  }
26
  else if (b==0)
27
  {
28
  cli();
29
  TIMSK = 0x00;
30
  TCCR0 = 0x00;
31
  }
32
}
danke schonmal :)

: Bearbeitet durch User
von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

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.

: Bearbeitet durch User
von Peter S. (peter_smart)


Lesenswert?

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?

von Kaj (Gast)


Lesenswert?

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

von Kaj (Gast)


Lesenswert?

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

von Kaj (Gast)


Lesenswert?

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.

von Peter II (Gast)


Lesenswert?

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.

von Peter S. (peter_smart)


Lesenswert?

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?

von Peter S. (peter_smart)


Lesenswert?

Peter II schrieb:

> denn schreib doch einfach ein return rein.
>


wohin springt das Programm in dem Fall?

von Der Rächer der Transistormorde (Gast)


Lesenswert?

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.

von Helmut H. (helmuth)


Lesenswert?

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

von Peter II (Gast)


Lesenswert?

Peter Smart schrieb:
> wohin springt das Programm in dem Fall?

woher es herkam, wie bei jeder andere Funktion auch.

von Der Rächer der Transistormorde (Gast)


Lesenswert?

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.

von Der Rächer der Transistormorde (Gast)


Lesenswert?

Der Rächer der Transistormorde schrieb:
> static int TransmitCountdown = 50000;

Puristen schreiben da natürlich:
static unsigned int TransmitCountdown = 50000;

von Werner B. (werner-b)


Lesenswert?

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 ;-)

von Der Rächer der Transistormorde (Gast)


Lesenswert?

Werner B. schrieb:
> Zu was soll denn denn das gut sein?

Puristen anlocken ;-)
but i hope that you know what I mean.

von Hans (Gast)


Lesenswert?

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
static bool flagPrintMsg = false;
2
3
ISR(TIMER0_OVF_vect)
4
{
5
  flagPrintMsg = true;
6
}
7
8
int main(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.

von c-hater (Gast)


Lesenswert?

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.

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
Noch kein Account? Hier anmelden.