在等待用户输入时避免无限循环? [英] Avoid an infinite loop while waiting for user input?
问题描述
我陷入了一个逻辑22.让我澄清一下我要做什么:按下按钮将触发电动机运行,直到感觉到传感器为止(向我的Rpi GPIO发送3.3V电压),这时它将反转方向.一切都很好;问题是,它卡在一个无限循环中,因此,如果我想按另一个按钮(例如,提高速度或在运行过程中将其停止),那我就不能. 我试图将"wiringPiISR()"实现为中断,但这似乎也在循环内做出反应.
I'm caught in a logical catch-22. Let me clarify what I am trying to do: A button press will trigger a motor to move until a sensor is felt (sends 3.3V to my Rpi GPIO), at which point it will reverse direction. This all works fine; the problem is, it is stuck within an infinite loop, so if I want to press another button, for example to increase the speed, or stop it in the middle of a run, well, I can't. I have tried to implement "wiringPiISR()", as an interrupt, but that seems to also be within a loop to react.
请记住,以下内容只是为了使某些功能正常工作以适应更大的代码而进行的测试.
Keep in mind, the following is just a test to get something to work, to be adapted to a much larger piece of code.
#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++;
}
}
再次,我只想以某种方式让该程序不断检查"24,INT_EDGE_RISING" ...对于您不熟悉的人来说,意味着有一些电压传送至第24 GPIO引脚(从低高电压)...而不会完全被这样做迷住了.背景循环,或者我真的不知道,这就是为什么我在这里.任何想法将不胜感激!
So again, I just want some way to have this program constantly checking for "24,INT_EDGE_RISING" ...which for those of you unfamiliar means that there is some voltage being delivered to the 24th GPIO Pin (Rising from low-high volt)...without being completely enthralled by doing so. A background loop, or I really don't know, which is why I'm here. Any ideas would be much appreciated!
推荐答案
无需执行任何显式循环.事件循环已经为您完成了.您可以在某些事件发生时执行操作,例如选中或取消选中按钮时.
There's no need to do any explicit looping. The event loop already does it for you. You execute actions when certain events happen, e.g. when a button is checked or unchecked.
这有助于从UI中剔除控制器,并使用UML状态图正式指定其行为.下面的代码与状态图1:1相对应.
It'd help to factor out the controller from the UI, and formally specify its behavior using a UML statechart. The code below corresponds 1:1 to the statechart.
s_moving
复合状态没有初始状态,因为它从未直接进入过,只是在进入其子状态时才隐式进入.
The s_moving
composite state has no initial state since it's never entered directly, only implicitly when entering its substates.
// 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()
中断处理程序是由connectionPi库从高优先级工作线程中调用的,该工作线程等待内核报告的GPIO转换.为了最大程度地减少反转电机的等待时间,我们利用了此线程.由于sensorHit()
已经在高优先级线程中运行并且尽可能接近GPIO转换,因此我们立即设置电动机反向,并发出信号以指示状态机转换为s_reverse
状态.由于此信号是从与Controller
实例所在的主线程所在的线程不同的线程发出的,因此插槽调用在主线程的事件队列中排队.
The sensorHit()
interrupt handler is called by the wiringPi library from a high-priority worker thread that waits for GPIO transitions as reported by the kernel. To minimize the latency of reversing the motor, we leverage this thread. Since sensorHit()
already runs in a high-priority thread and is as close to the GPIO transition as possible, we immediately set the reverse motor direction, and emit a signal to instruct the state machine to transition to the s_reverse
state. Since this signal is emitted from a thread different than the one the main thread the Controller
instance lives in, the slot call is queued in the main thread's event queue.
/// 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
链接它们,否则UI和控制器对象都无法直接相互了解:
The UI is decoupled from the controller: neither UI nor controller objects are directly aware of each other until you link them using connect
:
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"
为了便于在台式机平台上进行测试,我们可以添加一个简单的WiringPi样机以使其完全独立:
To facilitate testing on desktop platforms, we can add a trivial WiringPi mockup to make it all self-contained:
// 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;
}
这篇关于在等待用户输入时避免无限循环?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!