我陷入了一个合乎逻辑的陷阱-22。让我澄清一下我要做的事情:按下按钮将触发电机移动直到感应到传感器(向我的Rpi GPIO发送3.3V电压),此时它将反转方向。这一切都很好;问题是,它被卡在一个无限循环中,所以如果我想按下另一个按钮,例如提高速度,或者在跑步过程中停止它,那么,我不能。 我试图实现" wiringPiISR()",作为一个中断,但这似乎也在一个循环中作出反应。
请记住,以下只是一个测试,可以使某些东西工作,以适应更大的代码。
#include <libraries>
using namespace std;
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
ui->label->setText("Nothing");
}
MainWindow::~MainWindow()
{
delete ui;
}
void myInterruptLEFT(void)
{
qDebug() << "SENSOR HIT";
}
void mainip(void)//this doesn't currently do anything.
{
wiringPiISR(24,INT_EDGE_RISING,&myInterruptLEFT);
}
void MainWindow::on_pushButton_clicked()
{
qDebug() << "Loop Exited";
}
void MainWindow::on_checkBox_clicked(bool checked)
{ int i = 0;
wiringPiSetupGpio();
while((checked =! 0))
{
ui->label->setNum(i);
i++;
}
}
所以再一次,我只想要一些方法让这个程序不断检查&#34; 24,INT_EDGE_RISING&#34; ...对于那些你不熟悉的人来说,有一些电压被传送到第24个GPIO引脚(从低 - 高电压上升)......没有被这样做完全迷住。一个背景循环,或者我真的不知道,这就是我在这里的原因。任何想法将不胜感激!
答案 0 :(得分:1)
我不确定我是否完全理解你的问题,但也许你可以建模一个状态机,只有一个循环检查所有按钮按下并执行所有必要的操作,即你的onCreate
只会设置一个然后在主循环中检查标志。这样的东西(用pseude代码):
null
通过这种方式,没有任何状态阻止新按钮按下或其他转换的到来。
答案 1 :(得分:1)
恕我直言,你应该在它可用的时候使用中断,而不是连续轮询任何东西的当前状态。在这种情况下,需要在中断处理程序内部发出Qt信号(显然你需要连接到该Qt信号)。这样构建你不需要循环。
但是有一个小问题,对于Qt,你只能从QObject派生类中发出非静态函数,而中断处理程序不能是任何类的一部分。您可以通过使用指向当前MainWindow的指针(Qt中只能有一个)并向该类添加信号来轻松解决此问题,例如:
static MainWindow* mainWindow = nullptr;
void ISRSensorDetected()
{
if (mainWindow)
emit mainWindow->SensorDetected();
}
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent), ui(new Ui::MainWindow)
{
ui->setupUi(this);
mainWindow = this;
wiringPiISR(24, INT_EDGE_RISING,&ISRSensorDetected);
connect(this, SIGNAL(SensorDetected()), this, SLOT(ReverseMotor()));
}
MainWindow::~MainWindow()
{
mainWindow = nullptr;
}
由于您只使用信号和插槽而没有阻塞循环,因此您可以轻松处理其他任何操作。
注意: WiringPi实际上并不使用中断,而是为每个回调创建一个单独的线程。使用实际中断时,您可能应该通过使用QMetaObject::invokeMethod(..., Qt::QueuedConnection)
来改进,以使中断处理程序尽快返回 ,并且只立即执行关键操作。 SensorDetected插槽不会立即在中断处理程序中调用,但稍后将由Qt主循环调用。例如:
void ISRSensor1Detected()
{
StopMotor1();
if (mainWindow)
QMetaObject::invokeMethod(mainWindow, "Sensor1Detected", Qt::QueuedConnection);
}
如果你真的需要在Qt中使用循环,你可以通过调用QCoreApplication::instance()->processEvents(QEventLoop::ExcludeUserInputEvents)
定期更新GUI更改。但在这种情况下你不应该这样做。
答案 2 :(得分:0)
没有必要进行任何明确的循环。事件循环已经为你做了。您可以在某些事件发生时执行操作,例如选中或取消选中按钮时。
它有助于从UI中分解出控制器,并使用UML状态图正式指定其行为。下面的代码对应于状态图1:1。
s_moving
复合状态没有初始状态,因为它从不直接输入,只有在输入其子状态时才会隐式输入。
// 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);
}
来自高优先级工作线程的wiringPi库调用sensorHit()
中断处理程序,该线程等待内核报告的GPIO转换。为了最大限度地减少电机反转的延迟,我们利用这个线程。由于sensorHit()
已经在高优先级线程中运行并且尽可能接近GPIO转换,我们立即设置反向电机方向,并发出信号以指示状态机转换到{{1}国家。由于此信号是从与s_reverse
实例所在的主线程不同的线程发出的,因此槽调用在主线程的事件队列中排队。
Controller
UI与控制器分离:在使用 /// 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;
链接它们之前,UI和控制器对象都不会直接相互识别:
connect
为了便于在桌面平台上进行测试,我们可以添加一个简单的WiringPi模型,使其全部自包含:
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"