Forum: Mikrocontroller und Digitale Elektronik Interrupt-Klasse für ATXMegas


von Nicolas G. (Gast)


Lesenswert?

Hi Leute,

ich habe mir eine hübsche Klasse geschrieben, die es mir ermöglicht bei 
OOP-Projekten komfortabler mit Interrupts umgehen zu können.

Zunächst mal die Klasse selbst. Sie ist aufgesplittet in eine .h- und 
eine .cpp-Datei.

InterruptHelper.h
1
/*
2
 * InterruptHelper.h
3
 *
4
 *  Created on: 30.07.2014
5
 *      Author: nicolas
6
 */
7
8
#ifndef INTERRUPTHELPER_H_
9
#define INTERRUPTHELPER_H_
10
11
#include <avr/io.h>
12
#include <avr/interrupt.h>
13
#include <stdlib.h>
14
15
#if (_VECTORS_SIZE / _VECTOR_SIZE > 255)
16
  typedef uint16_t INTERRUPT_NUM_t;
17
#else
18
  typedef uint8_t INTERRUPT_NUM_t;
19
#endif
20
21
class Interrupt;
22
23
// Eine Art LinkedList, falls mehrere Instanzen auf einen Interrupt reagieren sollen.
24
struct interruptFunc_t {
25
    Interrupt* interrupt;
26
    interruptFunc_t* next;
27
};
28
29
extern interruptFunc_t interruptFunctions[_VECTORS_SIZE / _VECTOR_SIZE];
30
31
class Interrupt {
32
  private:
33
    INTERRUPT_NUM_t vectorNum;
34
35
  public:
36
    Interrupt() {
37
      vectorNum = -1;
38
    }
39
40
    virtual ~Interrupt() {
41
      if (vectorNum >= 0) {
42
        removeFromInterrupt();
43
      }
44
    }
45
46
    void addToInterrupt(INTERRUPT_NUM_t vectorNum) {
47
      this->vectorNum = vectorNum;
48
      interruptFunc_t* e = &interruptFunctions[vectorNum];
49
      if (e->interrupt) {
50
        while (e->next) {
51
          e = e->next;
52
        }
53
        e->next = (interruptFunc_t*) malloc(sizeof(interruptFunc_t));
54
        e->next->interrupt = this;
55
        e->next->next = 0;
56
      } else {
57
        e->interrupt = this;
58
        e->next = 0;
59
      }
60
    }
61
62
    void removeFromInterrupt() {
63
      interruptFunc_t* e = &interruptFunctions[vectorNum];
64
      if (e->interrupt == this) {
65
        if (e->next) {
66
          e->interrupt = e->next->interrupt;
67
          interruptFunc_t* tmp = e->next->next;
68
          e->next = tmp;
69
          free(tmp);
70
        }
71
      } else {
72
        interruptFunc_t* previous = e;
73
        e = e->next;
74
        while (e) {
75
          if (e->interrupt == this) {
76
            previous->next = e->next;
77
            free(e);
78
            break;
79
          }
80
          previous = e;
81
          e = e->next;
82
        }
83
      }
84
    }
85
86
    virtual void interrupt() {
87
    }
88
};
89
90
#endif /* INTERRUPTHELPER_H_ */

InterruptHelper.cpp
1
/*
2
 * InterruptHelper.cpp
3
 *
4
 *  Created on: 30.07.2014
5
 *      Author: nicolas
6
 */
7
8
#include "InterruptHelper.h"
9
10
interruptFunc_t interruptFunctions[_VECTORS_SIZE / _VECTOR_SIZE];
11
12
#define declareISR(name)  ISR(name, ISR_BLOCK) { \
13
                interruptFunc_t* e = &interruptFunctions[name##_num]; \
14
                if (e->interrupt) { \
15
                  e->interrupt->interrupt(); \
16
                  while (e->next) { \
17
                    e = e->next; \
18
                    if (e->interrupt) { \
19
                      e->interrupt->interrupt(); \
20
                    } \
21
                  } \
22
                } \
23
              } \
24
25
26
/*
27
 * Im folgenden Abschnitt einfach alle Intrrupts deklarieren, die später mal
28
 * genutzt werden sollen.
29
 */
30
declareISR(TCD1_OVF_vect)
31
declareISR(TCD1_ERR_vect)
32
33
// Nachfolgend ein Beispiel
34
#if false
35
class InterruptExample : Interrupt {
36
  InterruptExample() {
37
    addToInterrupt(TCD1_OVF_vect_num);
38
  }
39
40
  void interrupt() {
41
    /* Diese Methode wird ausgeführt, sobald der
42
     * entsprechende Interrupt ausgeführt wird.
43
     */
44
  }
45
};
46
47
48
/* Nachfolgend ein Beispiel wie man innerhalb einer Klasse
49
 * auf unterschiedliche Interrupts reagieren kann.
50
 */
51
class MyClass {
52
    class Interrupt1 : public Interrupt {
53
        MyClass* parent;
54
55
        Interrupt1(MyClass* _parent) : parent(_parent) {
56
          addToInterrupt(TCD1_OVF_vect_num);
57
        }
58
59
        void interrupt() {
60
          parent->interrupt1();
61
        }
62
    };
63
64
    class Interrupt2 : public Interrupt {
65
        MyClass* parent;
66
67
        Interrupt2(MyClass* _parent) : parent(_parent) {
68
          addToInterrupt(TCD1_ERR_vect_num);
69
        }
70
71
        void interrupt() {
72
          parent->interrupt2();
73
        }
74
    };
75
76
    Interrupt1* int1;
77
    Interrupt2* int2;
78
79
    MyClass() {
80
      int1 = new Interrupt1(this);
81
      int2 = new Interrupt2(this);
82
    }
83
84
    void interrupt1() {
85
      // Wird aufgerufen bei TCD1_OVF_vect
86
    }
87
    void interrupt2() {
88
      // Wird aufgerufen bei TCD1_ERR_vect
89
    }
90
};
91
#endif
In der .cpp-Datei befinden sich auch zwei ausgeklammerte Beispiele, die 
verdeutlichen sollen wie die Klasse funktioniert.

Hat man also eine Klasse geschrieben, die auf genau einen Interrupt 
reagieren soll, so erbt man einfach von der Klasse 'Interrupt', 
definiert den Interrupt, auf den man reagieren will, im Konstruktor und 
überschreibt dann die Methode 'void interrupt()'.

Möchte man innerhalb einer Klasse auf mehr als einen Interrupt reagieren 
können, muss man Unterklassen erstellen, die wiederum die Methoden aus 
der Hauptklasse aufrufen.

Es ist keine gute Idee einfach alle Interrupt zu deklarieren, die es 
gibt. Also man sollte nicht einfach alle möglichen 
'declareISR()'-Varianten einbauen, die man finden kann, da das zu viel 
Flash-Speicher benötigt. Wenn man den Speicher natürlich hat, spricht 
nicht unbedingt etwas dagegen.

Ich hoffe euch gefällt das Konstrukt. Über Kritik und 
Verbesserungsvorschläge freue ich mich aber trotzdem. Ich hoffe hier 
entfacht jetzt keine Diskussion darüber wie sinnvoll es ist mit OOP auf 
einem AVR zu hantieren.

von c-hater (Gast)


Lesenswert?

Nicolas G. schrieb:

> ich habe mir eine hübsche Klasse geschrieben, die es mir ermöglicht bei
> OOP-Projekten komfortabler mit Interrupts umgehen zu können.

Du hast einen Fehler gemacht...

> Es ist keine gute Idee einfach alle Interrupt zu deklarieren, die es
> gibt. Also man sollte nicht einfach alle möglichen
> 'declareISR()'-Varianten einbauen, die man finden kann, da das zu viel
> Flash-Speicher benötigt.

Und das zeigt ihn überdeutlich...

Mehr ist dazu eigentlich nicht zu sagen.

OO ist nett (für den Programmierer) auf fetten Systemen mit viel 
Reserven. Ja ich gebe zu, daß auch ich auf solchen Zielsystemen 
größtenteils reinen OO-Code produziere (allerdings nur unter 
fürchterlichem Zwang in C++, wenn schon gemütlich, dann richtig 
gemütlich: also "managed" Code).

Aber auf Systemen mit eng begrenzten Resourcen knallt der Overhead 
einfach viel zu stark rein. Und das nicht nur bezüglich des 
Speicherbedarfs. Mach einfach mal eine Analyse bezüglich des 
Rechenzeitbedarfs deines OO-Konstruktes. DEN Overhead braucht man echt 
nicht, ganz sicher jedenfalls nicht in ISRs. Da ist oft genug schon der 
Overhead der Runtime von plain C schon Show-Stopper.

von Moby (Gast)


Lesenswert?

Nicolas G. schrieb:
> Ich hoffe hier
> entfacht jetzt keine Diskussion darüber wie sinnvoll es ist mit OOP auf
> einem AVR zu hantieren.

Oh ja, die wär aber bitter nötig... Natürlich aus Performance- und 
Platzgründen. Und als Asm-Progger stell ich mir natürlich auch gleich 
die Frage, warum es komplizierte Konstrukte wie diese hier für 
Interrupts braucht, wo solche doch oft aus nur wenigen Assemblerzeilen 
bestehen (können).

von Nicolas G. (Gast)


Lesenswert?

Ich kenne die Nachteile. Aber bei unserem Quadcopter sind die größten 
Geschwindigkeitsfresser die Fließkommaberechnungen. Die Klassen werden 
scheinbar gut umgesetzt. Wobei natürlich keine Lust hatte das mit einer 
reinen C-Version zu vergleichen. Immerhin müsste ich dann alles 
umschreiben.
Das einzige, was noch in C geschrieben ist, ist die main-Methode. Die 
wird aber am Ende eh noch mal aufgeräumt.

Falls es dich interessiert: https://github.com/NicolasGoeddel/Quadcopter

von Moby (Gast)


Lesenswert?

Nicolas G. schrieb:
> Aber bei unserem Quadcopter sind die größten
> Geschwindigkeitsfresser die Fließkommaberechnungen. Die Klassen werden
> scheinbar gut umgesetzt.

Na das zeigt ja wenigstens wieder mal die Leistungsfähigkeit der 
AVR/Xmegas ;-)

von c-hater (Gast)


Lesenswert?

Nicolas G. schrieb:

> bei unserem Quadcopter sind die größten
> Geschwindigkeitsfresser die Fließkommaberechnungen.

Dann wäre es doch sinnvoll, den Fließkommascheiß auszumerzen. Das ist in 
aller Regel nur eine Krücke für Programmierer, die zu faul oder zu doof 
sind, die Wertebereiche ihrer Algorithmen gut genug abschätzen zu 
können.

Aber OK, es gibt Ausnahmen von dieser Regel. Wenn du mir nachweist, daß 
du in deinem Copter zwingend einen solchen Algorithmus mit nicht 
abschätzbarem Wertebereich verwenden mußt, dann darfst du ungestört 
weiter mit dem Werkzeug der Dummen und Faulen arbeiten...

Um aber auf's Thema zurückzukommen: ISRs bringen bei hinreichend 
häufigem Aufruf der ISR absolut *jedes* System zum Erliegen. Und 
selbst wenn die Aufruffrequenz nicht derart hoch ist: Jeder in einer ISR 
sinnlos verschwendete Takt fehlt genau mit der Häufigkeit ihres Aufrufs 
an jeder anderen Stelle des Systems.

Also: Nur 10 Takte in einer ISR sinnlos verschwendet und diese ISR 
100000mal pro Sekunde aufgerufen, mindert den effektiv nutzbaren 
Systemtakt schon um ein ganzes 1MHz.

Das kann doch nicht so schwer zu verstehen sein, oder?

von Peter II (Gast)


Lesenswert?

Nicolas G. schrieb:
> Ich kenne die Nachteile.

ist ja nicht nur das C++. Schon das malloc zieht haufenweise Probleme 
nach sich. So mal du nicht mal eine Fehlerbehandlung eingebaut hast.

Und wie wahrscheinlich ist es wohl das man Interrupts zur Laufzeit ein 
oder aushängt?

Wenn überhaupt sollte man das als Template und alles static umsetzen, 
dann dürfte der Overhead sehr gut zu vermeiden sein.

Auch die Stabilität vom removeFromInterrupt wage ich zu bezweifeln, wenn 
gleichzeitig auch Interrupts auftreten.

von Peter D. (peda)


Lesenswert?

Nicolas G. schrieb:
> ich habe mir eine hübsche Klasse geschrieben, die es mir ermöglicht bei
> OOP-Projekten komfortabler mit Interrupts umgehen zu können.

Kannst Du mal erläutern, worin der Komfort besteht.

Warum soll ich erst eine Zwischeninstanz schreiben müssen und nicht 
einfach gleich:
1
ISR( PCINT1_vect )
2
{
3
  // Code
4
}


Ich vermute mal stark, daß diese ganze Pointerei nicht zur Compilezeit 
aufgelöst werden kann und daher ein erheblicher Overhead an Flash, RAM 
und CPU-Belastung entsteht.

von Dr. Sommer (Gast)


Lesenswert?

Nicolas G. schrieb:
> while (e->next) {
>           e = e->next;
>         }
>         e->next = (interruptFunc_t*) malloc(sizeof(interruptFunc_t));
>         e->next->interrupt = this;
>         e->next->next = 0;
Füg doch am Anfang der Liste ein, das ist dann O(1) und nicht O(n).

Nicolas G. schrieb:
> e->next = (interruptFunc_t*) malloc(sizeof(interruptFunc_t));
Warum "malloc" und nicht "new"?

Was auch interessant wäre, statt virtueller Funktionen std::function zu 
verwenden. Dann könnte man auch Lambdas hinzufügen, zB so:
1
int main () {
2
  Interrupts::add (TCD1_ERR_vect_num, [] () {  /* do something */ });
3
}
Dazu müsste diese add Funktion ein std::function<void()> entgegennehmen 
und in die Linked List einfügen. Beim Interrupt dann die Liste 
durchgehen und den operator() auf den std::function Objekten aufrufen.

von Nicolas G. (Gast)


Angehängte Dateien:

Lesenswert?

c-hater schrieb:
>> bei unserem Quadcopter sind die größten
>> Geschwindigkeitsfresser die Fließkommaberechnungen.
>
> Dann wäre es doch sinnvoll, den Fließkommascheiß auszumerzen. Das ist in
> aller Regel nur eine Krücke für Programmierer, die zu faul oder zu doof
> sind, die Wertebereiche ihrer Algorithmen gut genug abschätzen zu
> können.
>
> Aber OK, es gibt Ausnahmen von dieser Regel. Wenn du mir nachweist, daß
> du in deinem Copter zwingend einen solchen Algorithmus mit nicht
> abschätzbarem Wertebereich verwenden mußt, dann darfst du ungestört
> weiter mit dem Werkzeug der Dummen und Faulen arbeiten...

Zeig mir schöne Routinen, mit denen mein atan schneller wird und genau 
genug bleibt. Außerdem hätte ich dann gerne noch Routinen, die den 
Komplementärfilter und die PID-Regelung sauber ohne Fließkommazahlen 
berechnen.
Und da wir schon bei Beleidigungen angekommen sind: Du bist wohl selber 
dumm, wenn du so eine Gülle schreibst.
Schau dir einfach mal andere Quadrocopter-Projekte an. Die nutzen auch 
alle floats. Außerdem habe ich ja noch Rechenzeit übrig. Der Xmega darf 
ruhig arbeiten. Der muss nicht idlen.


Peter II schrieb:
> Nicolas G. schrieb:
>> Ich kenne die Nachteile.
>
> ist ja nicht nur das C++. Schon das malloc zieht haufenweise Probleme
> nach sich. So mal du nicht mal eine Fehlerbehandlung eingebaut hast.

Es sind noch nicht alle Dinge abgesichert. Aber ich bin hier ja auch 
sozusagen noch in der Alpha-Phase.

Dr. Sommer schrieb:
> Nicolas G. schrieb:
>> while (e->next) {
>>           e = e->next;
>>         }
>>         e->next = (interruptFunc_t*) malloc(sizeof(interruptFunc_t));
>>         e->next->interrupt = this;
>>         e->next->next = 0;
> Füg doch am Anfang der Liste ein, das ist dann O(1) und nicht O(n).

*Kopf->Tisch* Du hast natürlich Recht. Lag wohl an der späten Uhrzeit. 
:D Hintergrund war wahrscheinlich, dass ich wollte, dass die Interrupts 
auch in der Reihenfolge ausgeführt werden, in der sie sich eingeklingt 
haben.
Und bezüglich der Lambdas: Das habe ich so noch nie gemacht. Klingt aber 
interessant, wenn das mit avr-g++ geht.

Peter Dannegger schrieb:
> Kannst Du mal erläutern, worin der Komfort besteht.
>
> Warum soll ich erst eine Zwischeninstanz schreiben müssen und nicht
> einfach gleich:ISR( PCINT1_vect )
> {
>   // Code
> }
> Ich vermute mal stark, daß diese ganze Pointerei nicht zur Compilezeit
> aufgelöst werden kann und daher ein erheblicher Overhead an Flash, RAM
> und CPU-Belastung entsteht.

Eine statische Funktion weiß nichts über die Datenstruktur, auf der sie 
arbeiten soll. Man müsste diese also auch global machen. Von der 
Interrupt-Klasse oben wird einem das abgenommen und man hat automatisch 
Zugriff auf alle Klassen-Attribute und weitere Methoden.

Moby schrieb:
> Nicolas G. schrieb:
>> Aber bei unserem Quadcopter sind die größten
>> Geschwindigkeitsfresser die Fließkommaberechnungen. Die Klassen werden
>> scheinbar gut umgesetzt.
>
> Na das zeigt ja wenigstens wieder mal die Leistungsfähigkeit der
> AVR/Xmegas ;-)

Naja, was willst du ohne FPU anders machen. ;) Mit reiner 
Ganzzahlarithmetik komme ich nicht besonders weit. Das habe ich damals 
schon mit dem PID-Regler versucht und die Wertebereiche waren einfach zu 
groß. Das nimmt mir der float-Typ eben alles ab.

Falls es interessiert: Im Anhang ist der grobe Ablauf der Regelung vom 
Quadcopter drin.

von Peter II (Gast)


Lesenswert?

Nicolas G. schrieb:
> Zeig mir schöne Routinen, mit denen mein atan schneller wird und genau
> genug bleibt.

welche Winkel können denn vorkommen?

fixe Tabelle bietet sich doch an. Bis 1/10 Grad ist das doch kein 
Problem.

von Nicolas G. (Gast)


Lesenswert?

Peter II schrieb:
> Nicolas G. schrieb:
>> Zeig mir schöne Routinen, mit denen mein atan schneller wird und genau
>> genug bleibt.
>
> welche Winkel können denn vorkommen?
>
> fixe Tabelle bietet sich doch an. Bis 1/10 Grad ist das doch kein
> Problem.

Für Tangens würde sich eine Lookup-Table anbieten, aber bei ArcusTangens 
ist es problematisch, da der Definitionsbereich von -Unendlich bis 
Unendlich geht. Bei Zwischenwerten kann man sehr schlecht auf eine 
Tabelle mappen, da das keine lineare Funktion ist. Würde man jetzt die 
Lookup-Tabelle mit gleichen Abständen zwischen den Werten erstellen, 
dann wäre es im niedrigen Bereich sehr ungenau und im oberen sehr genau. 
Und beim Quadcopter können durchaus alle Werte vorkommen.
Abgesehen davon ist die bereits vereinfachte Atan-Funktion sehr klein. 
Natürlich wird hier auch nur das Polynom 3. Grades genommen, aber es ist 
genauer als eine Lookup-Table.
1
float myAtan(float x) {
2
  if (x > 1.0) {
3
    return M_PI_2 - (x / (x * x + 0.28));
4
  } else if (x < -1.0) {
5
    return -M_PI_2 - (x / (x * x + 0.28));
6
  }
7
  return x / (1 + 0.28 * x * x);
8
}
Oder wie würdest du es mit einer Lookup-Tabelle machen?

von Peter II (Gast)


Lesenswert?

Nicolas G. schrieb:
> Oder wie würdest du es mit einer Lookup-Tabelle machen?

ich hatte nur tan gelesen, für atan scheint deine Formel schon recht 
kompakt zu sein.

von Peter D. (peda)


Lesenswert?

Nicolas G. schrieb:
> Von der
> Interrupt-Klasse oben wird einem das abgenommen und man hat automatisch
> Zugriff auf alle Klassen-Attribute und weitere Methoden.

Ich muß zugeben, von C++ verstehe ich nur Bahnhof.
Wenn ich das richtig sehe, soll dieses Klassendingens nur die Variablen 
zusammen fassen, die der Interrupt und damit zusammen arbeitende 
Funktionen benutzen. Ich kann mir nur schwer vorstellen, daß es in C++ 
keinen besseren Weg geben sollte ohne diesen riesen Overhead.

Vielleicht kannst Du ja mal ein konkretes Beispiel mit Variablen zeigen, 
wie das mit den Klassen gedacht ist, z.B. UART Interrupt mit FIFO, init, 
putchar und getchar.

von Nicolas G. (Gast)


Lesenswert?

Peter Dannegger schrieb:
> Vielleicht kannst Du ja mal ein konkretes Beispiel mit Variablen zeigen,
> wie das mit den Klassen gedacht ist, z.B. UART Interrupt mit FIFO, init,
> putchar und getchar.

Vielleicht hilft dir das hier weiter:
https://github.com/NicolasGoeddel/Quadcopter/blob/master/Quadcopter/Clock.h
https://github.com/NicolasGoeddel/Quadcopter/blob/master/Quadcopter/Clock.cpp


Im Übrigen hat hier im Wiki schon einmal jemand etwas gemacht bezüglich 
Interrupts und OOP. Allerdings erschien mir das viel zu kompliziert und 
am Ziel vorbei. Da gefällt mir meine Lösung wesentlich besser. ;)
https://www.mikrocontroller.net/articles/AVR_Interrupt_Routinen_mit_C%2B%2B

von Dr. Sommer (Gast)


Lesenswert?

Peter Dannegger schrieb:
> Ich kann mir nur schwer vorstellen, daß es in C++
> keinen besseren Weg geben sollte ohne diesen riesen Overhead.
Ja, indem man den "next" Pointer direkt in die "Interrupt" Klasse packt, 
und sich somit das extra struct und den 2. malloc Aufruf spart.

von Nicolas G. (Gast)


Lesenswert?

Dr. Sommer schrieb:
> Peter Dannegger schrieb:
>> Ich kann mir nur schwer vorstellen, daß es in C++
>> keinen besseren Weg geben sollte ohne diesen riesen Overhead.
> Ja, indem man den "next" Pointer direkt in die "Interrupt" Klasse packt,
> und sich somit das extra struct und den 2. malloc Aufruf spart.

Das Problem ist eben der einmalige Aufruf im globalen Scope von 
'declareISR'. Der erstellt erst die ISR-Funktion. Wenn ich den 
next-Pointer in die Interrupt-Klasse packe, dann weiß eine weitere 
Instanz nichts mehr von der ersten und kann sich nicht dahinter hängen. 
Oder wie hast du das gemeint?

von Dr. Sommer (Gast)


Lesenswert?

Nicolas G. schrieb:
> Oder wie hast du das gemeint?
So:

InterruptHelper.h:
1
void runISR (INTERRUPT_NUM_t i);
2
3
class Interrupt {
4
    friend void runISR (INTERRUPT_NUM_t i);
5
  private:
6
    INTERRUPT_NUM_t vectorNum;
7
    Interrupt* next;
8
9
  public:
10
    Interrupt ();
11
    virtual ~Interrupt ();
12
    void addToInterrupt (INTERRUPT_NUM_t vectorNum);
13
    void removeFromInterrupt ();
14
    virtual void interrupt () = 0;
15
};
16
extern Interrupt* interruptFunctions [_VECTORS_SIZE / _VECTOR_SIZE];

InterruptHelper.cpp:
1
Interrupt* interruptFunctions [_VECTORS_SIZE / _VECTOR_SIZE];
2
3
Interrupt::Interrupt () {
4
  vectorNum = -1;
5
}
6
7
Interrupt::~Interrupt () {
8
  if (vectorNum >= 0) {
9
    removeFromInterrupt ();
10
  }
11
}
12
13
void Interrupt::addToInterrupt (INTERRUPT_NUM_t vectorNum) {
14
  this->vectorNum = vectorNum;
15
  Interrupt* first = &;
16
  if (interruptFunctions [vectorNum]) {
17
    next = interruptFunctions [vectorNum];
18
    interruptFunctions [vectorNum] = this;
19
  } else {
20
    interruptFunctions [vectorNum] = this;
21
    next = nullptr;
22
  }
23
}
24
25
void Interrupt::removeFromInterrupt () {
26
  // ...
27
}
28
29
void runISR (INTERRUPT_NUM_t i) {
30
  Interrupt* scan = interruptFunctions [i];
31
  while (scan) {
32
    scan->interrupt ();
33
    scan = scan->next;
34
  }
35
}
36
37
ISR(TCD1_OVF_vect, ISR_BLOCK) {
38
  runISR (TCD1_OVF_vect_num);
39
}

Nutzungs-Beispiel:
1
class MyInterrupt : public Interrupt {
2
  public:
3
    MyInterrupt () {
4
      addToInterrupt(TCD1_OVF_vect_num);
5
    }
6
    void interrupt () {
7
      // ...
8
    }
9
} myInterrupt;

So hat man sich malloc/new sogar komplett gespart.

von Nicolas G. (Gast)


Lesenswert?

Dr. Sommer schrieb:
> So:

Sehr cool. Darf ich das so übernehmen? Das bleibt eh OpenSource. :)

von Ex-C++-Programmierer (Gast)


Lesenswert?

Peter Dannegger schrieb:
> Ich kann mir nur schwer vorstellen, daß es in C++
> keinen besseren Weg geben sollte ohne diesen riesen Overhead.

Naja, Templates sind schon erfunden. Und declareISR() ist, ähm, auch 
weit entfernt vom Optimum. Die hier vorgestellte Lösung ist eben, sagen 
wir mal, etwas infantil.

Die wichtigste Frage wurde weiter oben schon gestellt: Ist es wirklich 
nötig, auf einem Mikrocontroller Interrupt-Handler dynamisch zur 
Laufzeit zu ändern?

Die zweite Frage könnte lauten, muss das (Mini-) Framework wirklich 
mehrere Handler pro Interrupt unterstützen?

Es gibt viele Überlegungen, die hier augenscheinlich nicht gemacht 
wurden. Klar, es funktioniert für den Ersteller erstmal soweit. Elegant 
und wiederverwendbar ist aber anders.

von Ex-C++-Programmierer (Gast)


Lesenswert?

Nicolas G. schrieb:
> Im Übrigen hat hier im Wiki schon einmal jemand etwas gemacht bezüglich
> Interrupts und OOP. Allerdings erschien mir das viel zu kompliziert und
> am Ziel vorbei.

NIH-Syndrom?

von Nicolas G. (Gast)


Lesenswert?

Ex-C++-Programmierer schrieb:
> Peter Dannegger schrieb:
>> Ich kann mir nur schwer vorstellen, daß es in C++
>> keinen besseren Weg geben sollte ohne diesen riesen Overhead.
>
> Naja, Templates sind schon erfunden. Und declareISR() ist, ähm, auch
> weit entfernt vom Optimum. Die hier vorgestellte Lösung ist eben, sagen
> wir mal, etwas infantil.
Templates nutze ich an einigen anderen Stellen auch, also sind mir 
bekannt. Aber mir ist so auf die Schnelle nicht eingefallen wie ich die 
an dieser Stelle geschickt einsetzen könnte. Ich würde gerne den ISR 
erst an der Stelle erstellen, an der er auch benutzt wird. Soll heißen, 
wenn ich nirgendwo eine Klasse habe, die einen bestimmten Interrupt 
benutzen möchte, dann soll auch nicht die entsprechende ISR-Funktion 
erstellt werden. Momentan muss man eben mit 'declareISR()' jeden ISR 
zunächst erstellen, die irgendwo anders mal benutzt werden soll.

> Die wichtigste Frage wurde weiter oben schon gestellt: Ist es wirklich
> nötig, auf einem Mikrocontroller Interrupt-Handler dynamisch zur
> Laufzeit zu ändern?
Vermutlich nicht. Zur Compile-Zeit reicht ja schon. Zur Laufzeit geht es 
ja auch gar nicht. Man kann ihn nur während der Compile-Zeit erstellen 
und dann währender Laufzeit entscheiden, ob man ihn benutzen möchte oder 
nicht. Bei manchen Interrupts hat man dann eben das Problem, dass 
bestimmte Bits gesetzt werden, wenn er ausgeführt wurde, auch wenn man 
ihn nicht wirklich nutzt. Das heißt man muss schon genau wissen, ob man 
'declareISR(x)' wirklich braucht oder nicht und das händisch festlegen.

> Die zweite Frage könnte lauten, muss das (Mini-) Framework wirklich
> mehrere Handler pro Interrupt unterstützen?
Das ist recht unwahrscheinlich, aber ich wollte mir die Möglichkeit mal 
offen halten. Und eigentlich spricht ja auch nicht viel dagegen, oder?

> Es gibt viele Überlegungen, die hier augenscheinlich nicht gemacht
> wurden. Klar, es funktioniert für den Ersteller erstmal soweit. Elegant
> und wiederverwendbar ist aber anders.
Ich lerne ja gerne dazu. Aber elegant im Sinne der Objektorientierung 
ist es doch eigentlich schon, oder etwa nicht?

Ex-C++-Programmierer schrieb:
>> Im Übrigen hat hier im Wiki schon einmal jemand etwas gemacht bezüglich
>> Interrupts und OOP. Allerdings erschien mir das viel zu kompliziert und
>> am Ziel vorbei.
>
> NIH-Syndrom?
Nicht wirklich. Ich erfinde zwar gerne mal ein Rad neu, aber die 
Möglichkeit wie sie im Wiki beschrieben ist, hat mir wegen der vielen 
Code-Dopplungen nicht so recht gefallen. Aber naja, im Grunde kann man 
da wohl wieder weiter drüber streiten. ;)

von Peter D. (peda)


Lesenswert?

Was passiert eigentlich, wenn der Interrupt freigegeben wird, bevor die 
Interruptklasse aufgerufen wurde, bzw. nach Aufruf von 
removeFromInterrupt.

Wird dann der BADISR_vect ausgeführt oder ein RETI?

von Nicolas G. (Gast)


Lesenswert?

Peter Dannegger schrieb:
> Was passiert eigentlich, wenn der Interrupt freigegeben wird, bevor die
> Interruptklasse aufgerufen wurde, bzw. nach Aufruf von
> removeFromInterrupt.
>
> Wird dann der BADISR_vect ausgeführt oder ein RETI?

Ich verstehe nicht ganz. Der eigentliche ISR besteht ja immer. Ob sich 
jetzt eine Interrupt-Instanz nun damit verbunden hat oder nicht, spielt 
da keine Rolle.

Oder ging es einfach nur um die Frage, was passiert, wenn man 
'removeFromInterrupt()' aufruft, ohne vorher 'addToInterrupt()' 
aufzurufen? Das geht nämlich gar nicht erst, weil man die Methode 
'removeFromInterrupt()' erst aufrufen kann, wenn eine Instanz von der 
Interrupt-Klasse existiert.

von Peter D. (peda)


Lesenswert?

In plain C werden alle Handler zur Compilezeit eingetragen und alle 
nicht benutzten Vectoren rufen BADISR_vect auf

Mit Deiner Klasse wird aber erst zur Laufzeit der Zeiger initialisiert.
Wohin zeigt er vor dem "addToInterrupt" bzw. nach dem 
"removeFromInterrupt"?

von Yalu X. (yalu) (Moderator)


Lesenswert?

Peter Dannegger schrieb:
> In plain C werden alle Handler zur Compilezeit eingetragen und alle
> nicht benutzten Vectoren rufen BADISR_vect auf

Das ist hier genau so. Die Interrupthandler werden mit
1
declareISR(IRGENDEIN_vect)

festgelegt und zu Laufzeit auch nicht mehr geändert. Beim Eintreffen
eine Interrupts werden die interrupt()-Methoden der mit addToInterrupt()
registrierten Interrupt-Objekte aufgerufen. Sind keine solchen Objekte
registriert, tut der Interruphandler nicht viel mehr als diese Tatsache
festzustellen (das entsprechende Element in interruptFunctions[] ist
dann NULL) und wieder zurückzukehren.

Alle Vektoren, die nicht mit declareISR() definiert werden, werden zu
BADISR_vect.

: Bearbeitet durch Moderator
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.