2017-02-20 3 views
-2

Ich verwende QT und QCustomPlot, um ein Echtzeit-Plot-Tool zu erstellen, und die Daten des Diagramms werden von der Arduino UNO-Karte gelesen. Meine Anwendung hat es geschafft, zu zeichnen, während die Daten total durcheinander sind. Hier ist mein Code unten ist (einige Code ist aus QCustomPlot Website):Abnormale Ausgabe beim Lesen von der seriellen Schnittstelle in QT

void Dialog::realtimeDataSlot() 
{ 
    bool currentPortNameChanged = false; 

    QString currentPortName; 
    if (currentPortName != portName) { 
     currentPortName = portName; 
     currentPortNameChanged = true; 
    } 

    QString currentRequest = request; 
    QSerialPort serial; 

    if (currentPortNameChanged) { 
     serial.close(); 
     serial.setPortName(currentPortName); 

     if (!serial.open(QIODevice::ReadOnly)) { 
      return; 
     } 
    } 



    static QTime time(QTime::currentTime()); 
    // calculate two new data points: 
    double key = time.elapsed()/1000.0; 
    static double lastPointKey = 0; 
    if (key-lastPointKey > 0.002) // at most add point every 2 ms 
    { 
     // add data to lines: 
     if(serial.waitForReadyRead(-1)){ 
      data = serial.readAll(); 
      QTextStream(stdout) << "HERE:" << data.toDouble() << endl; 
      customPlot->graph(0)->addData(key, data.toDouble()); 
      customPlot->graph(0)->rescaleValueAxis(); //rescale value (vertical) axis to fit the current data: 
      lastPointKey = key; 
      customPlot->xAxis->setRange(key, 8, Qt::AlignRight); 
      customPlot->replot(); 
      static double lastFpsKey; 
      static int frameCount; 
      ++frameCount; 
      if (key-lastFpsKey > 2) // average fps over 2 seconds 
      { 
       lastFpsKey = key; 
       frameCount = 0; 
      } 
     } 
    } 


    // calculate frames per second: 

    if (currentPortName != portName) { 
     currentPortName = portName; 
     currentPortNameChanged = true; 
    } else { 
     currentPortNameChanged = false; 
    } 

} 

Als ich versuchte, die Daten auszudrucken ich von der seriellen Schnittstelle zu lesen, fand ich folgendes:

HERE:1 
HERE:15 
HERE:150 
HERE:149 
HERE:149 
HERE:149 
HERE:150 
HERE:150 
HERE:15 
HERE:150 
HERE:149 
HERE:49 
HERE:150 
HERE:150 
HERE:1 
HERE:150 

Die Werte ungefähr 150 sind normal, während die Werte, die 0, 1 für andere sind, nicht sind. Es wird auch nicht mit einer stabilen Geschwindigkeit ausgedruckt. Ich weiß nicht, was damit passiert ist, und danke an alle, die helfen können, und ich würde es begrüßen, wenn es bessere Möglichkeiten gäbe, dies umzusetzen.

+0

Wir haben keine Ahnung, wie Sie diese Frage beantworten es sei denn, Sie sagen uns, von welchem ​​Gerät Sie gerade lesen. Warum sagst du, dass 150 normal ist, aber 0/1 nicht? – Falmarri

Antwort

1

Das Problem hier ist, dass es nicht garantiert ist, dass die serielle Übertragung auf einmal empfangen wird. So ist es besser, die seriell zu lassen, um irgendwo anders verarbeitet werden, zum Beispiel:

// in the class definition 
    QSerialPort serialPort; 
private slots: 
    void handleReadyRead(); 
private: 
    QByteArray serialBuffer; 
    volatile double lastSerialValue; 

// In the initialization part (not the realtimeDataSlot function) 
lastSerialValue = qQNaN(); 
serialPort.setPortName(currentPortName); 
connect(&serialPort, &QSerialPort::readyRead, this, &Dialog::handleReadyRead, Qt::UniqueConnection); 
if (!serialPort.open(QIODevice::ReadOnly)) { 
    return; 
} 
serialBuffer.clear(); 

// Other functions: 
void Dialog::realtimeDataSlot() 
{ 
    ... 
    if (key-lastPointKey > 0.002) // at most add point every 2 ms 
    { 
     if (!qIsNaN(lastSerialData)) 
     { 
      // use lastSerialValue as the data.toDouble() you had before, then, at the end 

      lastSerialValue = qQNaN(); 
     } 
    ... 
} 

void Dialog::handleReadyRead() 
{ 
    serialBuffer.append(serialPort.readAll()); 
    int serPos; 
    while ((serPos = serialBuffer.indexOf('\n')) >= 0) 
    { 
     bool ok; 
     double tempValue = QString::fromLatin1(serialBuffer.left(serPos)).toDouble(&ok); 
     if (ok) lastSerialValue = tempValue; 

     serialBuffer = serialBuffer.mid(serPos+1); 
    } 
} 

Erklärung: Wenn Sie etwas aus dem Arduino erhalten die Bytes in einen Puffer angehängt werden. Dann wird das Bytearray analysiert, wobei nach einem Terminator gesucht wird, und falls gefunden, wird das Bytearray aufgeteilt und analysiert. Wenn die andere Funktion die Daten benötigt, zieht sie einfach die letzte in der Variablen gespeicherte Funktion.

HINWEIS 1: Ich habe gesehen, dass Sie eine binäre Übertragung verwendet haben. Das Problem ist, dass Sie auf keine Weise feststellen können, wo die Daten beginnen und enden. Zum Beispiel, wenn Sie 0x01 0x02 0x03 0x04 erhalten und Sie wissen, dass es 3 Bytes gibt, sind sie 01.03 oder 02..04 oder 03, 04 und eine fehlende oder ...? Die von mir implementierte Version erfordert, dass Sie Daten im String-Format mit einem New-Line-Terminator senden (einfachste Version, Sie müssen nur Serial.println(doubleValue); im Arduino-Code schreiben), aber wenn Sie die binäre Version benötigen, kann ich Ihnen einige Hinweise geben

HINWEIS 2: Der Code, den ich geschrieben habe, ist NICHT Thread-sicher. Es funktioniert nur, wenn der realtimeDataSlot und der handleReadyRead im selben Thread aufgerufen werden. Beachten Sie, dass dies gewährleistet ist, wenn sie zum selben Objekt gehören und durch Signale aufgerufen werden.

Jetzt sollte dies funktionieren. Aber ich rate Ihnen dringend davon ab. Ich weiß nicht, wer die realtimeDataSlot() aufrufen muss, aber ich denke, dass die meisten richtige Version so etwas wie dieses:

// in the class definition 
    QSerialPort serialPort; 
private slots: 
    void handleReadyRead(); 
    void receivedData(double val); 
private: 
    QByteArray serialBuffer; 
signals: 
    void newData(double data); 

// In the initialization part (not the realtimeDataSlot function) 
serialPort.setPortName(currentPortName); 
connect(&serialPort, &QSerialPort::readyRead, this, &Dialog::handleReadyRead, Qt::UniqueConnection); 
connect(this, &Dialog::newData, this, &Dialog::receivedData, Qt::UniqueConnection); 
if (!serialPort.open(QIODevice::ReadOnly)) { 
    return; 
} 
serialBuffer.clear(); 

// Other functions: 
void Dialog::receivedData(double val) 
{ 
    double key = time.elapsed()/1000.0; 
    static double lastPointKey = 0; 
    if (key-lastPointKey > 0.002) // at most add point every 2 ms 
    { 
     QTextStream(stdout) << "HERE:" << data.toDouble() << endl; 
     customPlot->graph(0)->addData(key, data.toDouble()); 
     customPlot->graph(0)->rescaleValueAxis(); 
     ... 
    } 
} 

void Dialog::handleReadyRead() 
{ 
    serialBuffer.append(serialPort.readAll()); 
    int serPos; 
    while ((serPos = serialBuffer.indexOf('\n')) >= 0) 
    { 
     bool ok; 
     double tempValue = QString::fromLatin1(serialBuffer.left(serPos)).toDouble(&ok); 
     if (ok) emit newData(tempValue); 

     serialBuffer = serialBuffer.mid(serPos+1); 
    } 
} 

So halten Sie die Grafik, die auf Ereignisse (empfangene neue Daten) anstelle von einem Zeitgeber .

Noch eine Sache: Ich entfernte den Portwechsel absichtlich. Ich empfehle Ihnen, es auf eine andere Art und Weise zu handhaben: Legen Sie eine Schaltfläche fest, um die serielle Schnittstelle zu starten und zu stoppen. Wenn die serielle Schnittstelle gestartet wird, können Sie den Portnamen nicht ändern. Auf diese Weise muss der Benutzer ihn explizit ausschalten, wenn er den Port ändern muss. Wenn Sie Ihre Version jedoch nicht in Ihrem Code enthalten möchten, aber einen Steckplatz für sich selbst erstellen, um den Portnamen zu ändern:

void changeSerialPortName(QString newName) 
{ 
    if (newName != serialPort.portName()) { 
     if (serialPort.isOpen()) 
      serialPort.close(); 

     serialPort.setPortName(newName); 

     if (!serialPort.open(QIODevice::ReadOnly)) { 
      return; 
     } 
    } 
} 
+0

Vielen Dank! –

+0

Eine andere Sache ist, was macht NewData? Sie haben das in der Header-Datei definiert, aber ich habe nicht gesehen, dass Sie es irgendwo für das zweite Beispiel verwenden? Tut mir leid, ich bin wirklich neu in QT und C++. –

+1

@RuofanLiu 'newData' ist ein ** Signal **, es wird in' HandleReadyRead' ausgegeben, nachdem ein neuer Wert erfolgreich gelesen wurde, mit 'emit newData (tempValue);'. Dieses * Signal * führt dazu, dass der * Slot * 'receivedData (...)' aufgerufen wird, da er in diesem Befehl mit ihm verbunden war: 'connect (this, & Dialog :: newData, this, & Dialog :: receivedData, Qt: : UniqueConnection) ' –

Verwandte Themen