Interrrupt Funktion

Hallo,

ich stehe gerade richtig auf dem Schlauch. Problem ist die ISR-Funktion bei attachInterrupt. Bekomme immer die Fehlermeldung:

Das Argument vom Typ ““void (MultiStepper::)()"" ist mit dem Parameter vom Typ ""void ()()”” inkompatibel.

Hat jemand eine Idee wie ich das fixen kann?

Danke
Janik

boolean MultiStepper::homing(){
	
	long newPositions[_num_steppers];
	uint8_t i;
    for (i = 0; i < _num_steppers; i++) {
		newPositions[i] = - 1500;  
		attachInterrupt(digitalPinToInterrupt(18), stop(i), RISING); // Endschalter Pins die Interrupt können 2, 3, 18, 19, 20, 21
		_steppers[i]->moveTo(newPositions[i]); // New target position (resets speed)
	    _steppers[i]->setSpeed(HomingSpeed); // New speed
		newPositions[i] = 0;
	}

}
void MultiStepper::stop(uint8_t in) {
	_steppers[in] -> stop();
}

Du kannst nur Adressen von Funktionen übergeben, nicht von Methoden.

Außer von statischen Methoden.

Methoden haben den this-Zeiger versteckt als Parameter. Deshalb ist die Signatur anders als Funktionen.

Es gibt einen Workaround mit einer statischen Wrapper-Funktion oder einer Lambda-Funktion. Allerdings geht das dann praktisch nur mit Singletons, da eben der Bezug zum aktuellen Objekt der Klasse verloren geht.

Hi

Warum die Methode nicht in einer eigenen Funktion aufrufen?
Dann habe ich den Zeiger zu meiner Funktion und den Aufruf von der gewünschten Methode, oder?

MfG

postmaster-ino:
Warum die Methode nicht in einer eigenen Funktion aufrufen?

Man muss das Problem aber mit "static" umgehen. Und kann dann nur auf statische Member zugreifen. Im Prinzip geht das. Das ist eine Wrapper-Funktion. Und schöner lässt es sich mit einer anonymen Lambda-Funktion schreiben, was aber letztlich auf das Gleiche rausläuft.

Aber du brauchst so oder so einen Zeiger aus das aktuelle Objekt. Wodurch das nicht geht wenn man mehrere Objekte der Klasse hat. Kommt halt auf die Anwendung an. Im Arduino Bereich gibt es viele Singeltons, aber man kann auch leicht mehrere Motoren haben

Auf ESPs ist das übrigens viel einfacher. Da gibt es die Mittel aus der STL um das zu handhaben. Functional, bind, etc.

Hier sieht man auch schön, dass Funktionen und Methoden 2 Paar Stiefel sind. Oftmals geht das ja ziemlich durcheinander und man hat oft den Eindruck, viele meinen 'Methode' sei nur der neuere und 'modernere' Name für 'Funktion'.

Mir ist schon oft aufgefallen, dass da in den Threads nicht wirklich unterschieden wird. Vielleicht sollte man da doch öfter drauf hinweisen und das richtigstellen, dann werden solche Probleme wie hier auch verständlicher.

Auch mir kamen bei diesem Thema Zweifel an der Vermittlung des Grundlagenwissens über Klassen. Früher war es noch einfach, dem C Programmierer eine Methode als Funktion mit einem Pointer auf eine Struktur als Parameter erklären - solche Konstrukte gab es vielfach in größeren C Programmen. Heute meint jeder, daß Klassen das abolute Woodoo sind, undurchschaubar für das Fußvolk. Wobei die C++ Klassen zu dieser Undurchschaubarkeit sehr viel beigetragen haben :frowning:

Erstens:
Ich bin auch für Lambda Funktionen, denn die decken den Bedarf hier prächtig ab.
Und sind recht leicht zu händeln.

Zweitens:

Heute meint jeder, daß Klassen das abolute Woodoo sind, undurchschaubar für das Fußvolk.
Mir fehlt "hier" vielfach der Ehrgeiz, der Einsteiger, die Sprache zu verstehen welche sie verwenden.
Wobei ich mir bei jan981 da gar nicht sicher bin, denn bis zu so einem Problem, wie diesem, muss man auch erstmal kommen. Das geht nicht ohne Ehrgeiz+Wissen.

So kann man es machen:

using functionPointer = void(*)();      //Alias für Funktionszeiger
void setCallback(functionPointer ptr);  //Prototyp

class MyClass
{
public:
  MyClass()
  {
    currentObject = this;           //Zeiger auf Objekt setzten
    setCallback(functionWrapper);   //Callback auf Wrapper setzten
  }

  void run()          //Das ist die eigentliche Methode die von der ISR aufgerufen werden soll
  {
    Serial.println("Run");
  }

  static void functionWrapper()     //statische Wrapper Methode die über den Zeiger auf die Methode zugreift
  {
    Serial.println("Wrapper");
    currentObject->run();
  }
private:
  static MyClass* currentObject;      //Zeiger auf Objekt
};

MyClass* MyClass::currentObject;      //muss extra definiert werden da statisch. Sonst gibt es Mecker vom Linker

functionPointer callback;
MyClass myObj;

void setup(void)
{
  Serial.begin(9600);

  interrupt();
}

void loop(void)
{ }

void setCallback(functionPointer ptr)   //entspricht attachInterrupt()
{
  callback = ptr;
}

void interrupt()            //Nachbildung einer ISR
{
  Serial.println("ISR");
  if (callback) callback();
}

Aber: Die ISR kann nur einen Zeiger verwalten der auf ein Objekt zeigt. Und die Klasse hat intern einen statischen Zeiger auf ein Objekt der Klasse. Das kann also nicht mit mehreren Objekten gehen

Und mit einer Lambda-Funktion kann man sich den Wrapper und die Zeiger-Variable sparen:

class MyClass
{
public:
  MyClass()
  {
    static MyClass* currentObject = this; 
    setCallback(
      []() { currentObject->run(); }
    ); 
  }

  void run()
  {
    Serial.println("Run");
  }
private:
};

Ist schöner, aber man sollte trotzdem vorher den Wrapper verstanden haben, da es dadurch deutlicher wird.

@combie Was war mit deinem letzten Post? Geht die Variante nicht? :slight_smile:

Geht nicht so.
Zumindest nicht unmittelbar.
Ein Schnelltest hat es bewiesen.

Schade:/

Hab mich jetzt den ganzen Nachmittag mit Lambda-Funktionen beschäftigt, bin aber noch nicht ganz schlau geworden wie ich das letztendlich umsetzen soll.

Bei der Lambda-Funktion geht es eigentlich nur darum dass eine Lambda-Funktion mit äquivalent zu einem void(*)(void) Funktionszeiger ist. Dadurch kann man so eine Funktion direkt an attachInterrupt() übergeben.

Der eigentliche Trick steckt bei der Sache steckt eher darin dass man mit static die Bindung der Methode an ein Objekt aufhebt. Auch mit einer Lambda-Funktion ist das nötig. Da sind wird dann wieder beim grundlegenden Unterschied zwischen Funktionen und Methoden: dem this Zeiger

#8 zeigt beide Varianten. Da wird einfach im Konstruktor der Klasse ein Zeiger auf das aktuelle Objekt abgespeichert. Und zwar als static. Das ist nötig weil eine statische Funktion eben nur auf statische Elemente zugreifen kann. Die Lambda-Funktion macht auch nichts anders. Nur die Syntax ist vereinfacht.

Was auch noch auffällt ist dass du bei attachInterrupt() eine Methode mit Parameter übergeben willst. Das geht eigentlich generell nicht. Egal wie man es macht:

void attachInterrupt(uint8_t, void (*)(void), int mode);

Ich verstehe Lambda-Funktionen so, daß sie Kopien aller benötigten Werte erhalten und daraus ein Ergebnis basteln.

Keine Rekursionen, keine Nebenwirkungen, keine globalen oder statischen Variablen, keine Funktionsaufrufe außer weiterer Lambda-Funktionen. Wie die Berechnung der linken Seite einer Gleichung, deren rechte Seite vollständig gegeben ist.

Ich verstehe sie eher als anonyme Funktionen.