Es ist nicht erforderlich, explizite Schleifen durchzuführen. Die Ereignisschleife erledigt das schon für Sie. Sie führen Aktionen aus, wenn bestimmte Ereignisse eintreten, z. wenn eine Schaltfläche aktiviert oder deaktiviert ist.
Es würde helfen, den Controller aus der Benutzeroberfläche herauszufiltern und sein Verhalten mit einem UML-Statechart zu spezifizieren. Der folgende Code entspricht 1: 1 dem Statechart.

Der s_moving
Verbund Zustand hat keinen Anfangszustand, da sie nie direkt eingegeben hat, nur implizit, wenn seine substates eingeben.
// https://github.com/KubaO/stackoverflown/tree/master/questions/wiringpi-isr-38740702
#include <QtWidgets>
#include <wiringpi.h>
class Controller : public QObject {
Q_OBJECT
QStateMachine m_mach{this};
QState s_stopped{&m_mach};
QState s_moving {&m_mach};
QState s_forward{&s_moving};
QState s_reverse{&s_moving};
static QPointer<Controller> m_instance;
enum { sensorPin = 24, motorPinA = 10, motorPinB = 11 };
// These methods use digitalWrite() to control the motor
static void motorForward() {
digitalWrite(motorPinA, HIGH);
digitalWrite(motorPinB, LOW);
}
static void motorReverse() { /*...*/ }
static void motorStop() { /*...*/ }
//
Q_SIGNAL void toStopped();
Q_SIGNAL void toForward();
Q_SIGNAL void toReverse();
void setupIO() {
wiringPiSetupSys();
pinMode(sensorPin, INPUT);
wiringPiISR(sensorPin, INT_EDGE_RISING, &Controller::sensorHit);
}
sensorHit()
Die Interrupt-Handler durch die wiringPi Bibliothek aus einem hochprioren Arbeiter-Thread aufgerufen wird, die für die Übergänge GPIO wartet, wie es durch den Kernel berichtet. Um die Latenz beim Umkehren des Motors zu minimieren, setzen wir dieses Thema ein. Da sensorHit()
bereits in einem Thread mit hoher Priorität läuft und so nahe wie möglich am GPIO-Übergang ist, setzen wir sofort die umgekehrte Motorrichtung und geben ein Signal aus, um den Zustandsautomaten anzuweisen, in den Zustand s_reverse
überzugehen. Da dieses Signal von einem anderen Thread als dem Hauptthread ausgegeben wird, in dem sich die Instanz Controller
befindet, wird der Slot-Aufruf in die Ereigniswarteschlange des Hauptthreads eingereiht.
/// This method is safe to be called from any thread.
static void sensorHit() {
motorReverse(); // do it right away in the high-priority thread
emit m_instance->toReverse();
}
public:
Controller(QObject * parent = nullptr) : QObject{parent} {
Q_ASSERT(!m_instance);
// State Machine Definition
m_mach.setInitialState(&s_stopped);
s_stopped.addTransition(this, &Controller::toForward, &s_forward);
s_moving.addTransition (this, &Controller::toStopped, &s_stopped);
s_forward.addTransition(this, &Controller::toReverse, &s_reverse);
s_reverse.addTransition(this, &Controller::toForward, &s_forward);
connect(&s_stopped, &QState::entered, this, [this]{
motorStop();
emit isStopped();
});
connect(&s_forward, &QState::entered, this, [this]{
motorForward();
emit isForward();
});
connect(&s_reverse, &QState::entered, this, [this]{
motorReverse();
emit isReverse();
});
m_mach.start();
//
m_instance = this;
setupIO();
}
Q_SLOT void forward() { emit toForward(); }
Q_SLOT void stop() {
motorStop(); // do it right away to ensure we stop ASAP
emit toStopped();
}
Q_SIGNAL void isStopped();
Q_SIGNAL void isForward();
Q_SIGNAL void isReverse();
};
QPointer<Controller> Controller::m_instance;
Die UI von der Steuerung abgekoppelt ist: weder UI noch Controller-Objekte voneinander direkt bewusst sind, bis Sie sie connect
mit verlinken:
int main(int argc, char ** argv) {
using Q = QObject;
QApplication app{argc, argv};
Controller ctl;
QWidget ui;
QVBoxLayout layout{&ui};
QLabel state;
QPushButton move{"Move Forward"};
QPushButton stop{"Stop"};
layout.addWidget(&state);
layout.addWidget(&move);
layout.addWidget(&stop);
Q::connect(&ctl, &Controller::isStopped, &state, [&]{ state.setText("Stopped"); });
Q::connect(&ctl, &Controller::isForward, &state, [&]{ state.setText("Forward"); });
Q::connect(&ctl, &Controller::isReverse, &state, [&]{ state.setText("Reverse"); });
Q::connect(&move, &QPushButton::clicked, &ctl, &Controller::forward);
Q::connect(&stop, &QPushButton::clicked, &ctl, &Controller::stop);
ui.show();
return app.exec();
}
#include "main.moc"
Zur Prüfung auf Desktop-Plattformen zu erleichtern, können wir hinzufügen ein triviales WiringPi-Modell, um alles in sich geschlossen zu machen:
// A rather silly WiringPi mockup
std::function<void()> isr;
int wiringPiSetupSys() { return 0; }
void pinMode(int, int) {}
void digitalWrite(int pin, int value) {
if (pin == 10 && value == HIGH)
QTimer::singleShot(1000, isr);
}
int wiringPiISR(int, int, void (*function)()) {
isr = function;
return 0;
}
Geringfügige Anmerkung: Da diese Methode im Controller statisch ist, können Sie Controller :: sensorHit als Callback zu WiringPiISR verwenden, aber IMHO fügen Sie weitere Motoren hinzu wird auf diese Weise Ihren Code wirklich komplizieren. (Wie der Autor erwähnt: "Denken Sie daran, dass das Folgende nur ein Test ist, um etwas zur Arbeit zu bringen, um an ein viel größeres Stück Code angepasst zu werden"). Vielleicht möchten Sie in diesem Fall eine statische Klassenmethode überdenken. – Elijan9
@ Elijan9 Leider ist die 'wiringPiISR' API kaputt. Callbacks sind nicht besonders nützlich, wenn Sie keine Benutzerdaten an den Rückruf weitergeben können. Sie benötigen also für jeden Pin einen separaten Callback. Das ist keine große Sache. Es gibt keine Möglichkeit, benutzerdefinierte Daten an den Rückruf zu übergeben, und da der Rückruf in einem anderen Thread ausgeführt wird, können Sie nicht direkt auf die Methoden des Controllers zugreifen, da sie nicht threadsicher sind. Nur die Signale sind (von Design). Und das machen wir. Es ist alles in Ordnung. Es gibt nichts zu überdenken, da der Callback ** statisch sein muss: Er muss in eine C-Funktion umgewandelt werden können. –
@ Elijan9 Ich wünschte, Sie könnten 'std :: bind (& Class :: method, instance)' an 'wiringPiISR' übergeben , oder dass man dem Rückruf ein Argument liefern könnte, wie es in jeder anderen nicht gebrochenen Callback-API auf dem Planeten üblich ist, aber das können wir nicht tun. Ich werde diesen Mangel wahrscheinlich bald in 'wiringPi' beheben; Der Patch ist fast kürzer als jede Prosa, die ihn beschreibt. –