Solved Problem with threads QObject::startTimer: Timers cannot be started from another thread
-
Hi,
I'm having problem with QThread class. My application has two different threads: the main thread manage the GUI, the second thread sends and receives message on the SerialPort using QSerialPort.
The problem is that when I start the second Thread I get this error:
"QObject::startTimer: Timers cannot be started from another thread"I searched for this error on the net but I still can't find the problme on my code.
Here is the code:mainwindow.h
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include "newcard.h" #include "newcircuitnumber.h" #include "t_serialport.h" namespace Ui { class MainWindow; } class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = nullptr); ~MainWindow(); private slots: void on_buttonNewCard_clicked(); void on_buttonNewImpiant_clicked(); private: Ui::MainWindow *ui; NewCard *setNewCard; newCircuitNumber *circuitNum; T_SerialPort *serialObject; bool connectDB(); void disconnect(); };
mainwindow.cpp
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); setWindowTitle("NFC"); if(!connectDB()) { qDebug() << "Error opening the db"; } else { qDebug() << "db opened!"; } serialObject = new T_SerialPort; serialObject->start(); status_MFRC522 = 0; }
t_serialport.h
/#ifndef T_SERIALPORT_H #define T_SERIALPORT_H #include <QThread> #include <QSerialPort> typedef struct { uint8_t size; // Number of bytes in the UID. 4, 7 or 10. uint8_t uidByte[10]; uint8_t sak; // The SAK (Select acknowledge) byte returned from the PICC after successful selection. }UID_PICC; class T_SerialPort : public QThread { Q_OBJECT QSerialPort *portCom; void run(); public: explicit T_SerialPort(QObject *parent = nullptr); ~T_SerialPort(); bool startSearch = true; void MFRC522_AntennaOn(); void MFRC522_Reset(); void ReadRegister_MFRC522(uint32_t address, uint8_t *buff, uint32_t size); void WriteRegister_MFRC522(uint32_t address, uint8_t buff, uint32_t size, uint8_t mask); void MFRC522_ClearRegister(uint8_t reg, uint8_t mask); void MFRC522_SetRegister(uint8_t reg, uint8_t mask); void WriteRegisterBuffer_MFRC522(uint32_t address, uint8_t *buff, uint32_t size); int MFRC522_CalculateCRC(uint8_t *data, uint8_t length, uint8_t *result); int MFRC522_CommunicateWithTag(uint8_t command, uint8_t waitIRq, uint8_t *bufferToSend, uint8_t bufferToSendLen, uint8_t *bufferToReceive, uint8_t bufferToReceiveLen, uint8_t *validBits, uint8_t rxAlign, uint8_t CheckCRC); int TAG_read(uint8_t *buffer_req, uint8_t buffer_len); int PICC_IsNewCardPresent(); void ReadRegisterBuffer_MFRC522(uint32_t address, uint8_t *buff, uint32_t size); UID_PICC UidPicc; int PICC_Select( UID_PICC *uid, uint8_t validBits ); int PCD_Authenticate_Ultralight(); void Tag_ReadInfo(uint8_t tagType); int MIFARE_Ultralight_Read(uint8_t page, uint8_t *buffer, uint8_t buffersize); int MIFARE_Read(uint8_t blockAddr, uint8_t *buffer, uint8_t bufferSize); int MIFARE_Ultralight_Write(uint8_t page, uint8_t *buffer, uint8_t bufferSize); int PCD_MIFARE_Transceive(uint8_t *sendData, uint8_t sendLen, bool acceptTimeout); void RicavaCredito(); int PICC_HaltA(); int PICC_GetType(uint8_t piccNum); uint8_t ControlloPicc(uint8_t tag); void Tag_WriteInfo(uint8_t tagType); uint8_t ScaloCredito(); signals: void openPortOk(bool); }; #endif // T_SERIALPORT_H
t_serialport.cpp
#include "t_serialport.h" #include "globs.h" #include <QDebug> #include <QThread> #include "globs.h" #include <string.h> #include <stdlib.h> #include <stdio.h> STATUS PICC_IsNewCardPresent(void); uint8_t ControlloStato(uint8_t status); T_SerialPort::T_SerialPort(QObject *parent) : QThread(parent) { portCom = nullptr; if(portCom == nullptr) { qDebug() << "CREO SERIAL PORT"; portCom = new QSerialPort; portCom->setPortName("COM7"); portCom->setFlowControl(QSerialPort::NoFlowControl); portCom->setBaudRate(QSerialPort::Baud9600); portCom->setDataBits(QSerialPort::Data8); portCom->setParity(QSerialPort::NoParity); portCom->setStopBits(QSerialPort::OneStop); // connect(portCom, SIGNAL(readyRead()), this, SLOT(readyReadSlot())); } if(portCom->isOpen()) portCom->close(); if(portCom->open(QSerialPort::ReadWrite)) { uint8_t prova; emit openPortOk(true); MFRC522_Reset(); // When communicating with a PICC we need a timeout if something goes wrong. // f_timer = 13.56 MHz / (2*TPreScaler+1) where TPreScaler = [TPrescaler_Hi:TPrescaler_Lo]. // TPrescaler_Hi are the four low bits in TModeReg. TPrescaler_Lo is TPrescalerReg. WriteRegister_MFRC522(TModeReg, 0x80, 2, 0xff); WriteRegister_MFRC522(TPrescalerReg, 0xA9, 2, 0xff); // TPreScaler = TModeReg[3..0]:TPrescalerReg, ie 0x0A9 = 169 => f_timer=40kHz, ie a timer period of 25�s. WriteRegister_MFRC522(TReloadRegH, 0x03, 2, 0xff); // Reload timer with 0x2710 = 10000, ie 250ms before timeout. WriteRegister_MFRC522(TReloadRegL, 0xE8, 2, 0xff); WriteRegister_MFRC522(TxASKReg, 0x40, 2, 0x40); // Default 0x00. Force a 100 % ASK modulation independent of the ModGsPReg register setting WriteRegister_MFRC522(ModeReg, 0x3D, 2, 0x88); // Default 0x3F. Set the preset value for the CRC coprocessor for the CalcCRC command to 0x6363 (ISO 14443-3 part 6.2.4) MFRC522_AntennaOn(); // Enable the antenna driver pins TX1 and TX2 (they were disabled by the reset } else emit openPortOk(false); } T_SerialPort::~T_SerialPort() { wait(); } void T_SerialPort::run() { while(1) { uint8_t exit_timer, result, piccType; if(status_MFRC522 == 0) { qDebug() << "OK"; int result = PICC_IsNewCardPresent(); if(result == STATUS_OK || result == STATUS_COLLISION) { qDebug() << "OK TROVATO QUALCOSA"; startSearch = false; status_MFRC522 = 1; } else { qDebug() << "NotFound"; } } qDebug() << "PROVA"; QThread::msleep(1000); } } int T_SerialPort::PICC_IsNewCardPresent(void){ uint8_t bufferATQA[2]; int result; result = TAG_read(&bufferATQA[0], sizeof(bufferATQA)); // effettuo semplicemente una richeista di lettura se risponde uno o piu tag vuol dire che lo status è ok return (result == STATUS_OK || result == STATUS_COLLISION); } int T_SerialPort::TAG_read(uint8_t *buffer_req, uint8_t buffer_len){ uint8_t validBits; int status; uint8_t byte; if((buffer_req == nullptr) || (buffer_len < 2)){ qDebug() << "ERROR0"; return CR_ERR; } MFRC522_ClearRegister(CollReg, 0x80); validBits = 7; byte = PICC_CMD_REQA; status = MFRC522_CommunicateWithTag(MFRC522_Transceive, 0x30, &byte, 1, buffer_req, buffer_len,&validBits,0, 0); //THE PROGRAM STOPS HERE AND GIVE ME THE OUTPUT WRITTEN ABOVE if (status != STATUS_OK) { return status; } // if (*buffer_req != 2 || validBits != 0) { // ATQA must be exactly 16 bits. // return STATUS_ERROR; //capire quanto deve essere il valid bits // } return STATUS_OK; } int T_SerialPort::MFRC522_CommunicateWithTag(uint8_t command, uint8_t waitIRq, uint8_t *bufferToSend, uint8_t bufferToSendLen, uint8_t *bufferToReceive, uint8_t bufferToReceiveLen,uint8_t *validBits,uint8_t rxAlign ,uint8_t CheckCRC){ uint16_t i; uint8_t n,_validBits; uint8_t errorRegValue; uint8_t txLastBits; uint8_t bitFraming; //*1 = se non funziona verificare con un a maschera diversa //*2 = Qui la maschera è diversa volutamente perchè il registro è dinamico q quando faccio partire il comando non so cosa fa nell'immediato txLastBits = validBits ? *validBits : 0; bitFraming = (rxAlign << 4) + txLastBits; WriteRegister_MFRC522(CommandReg, MFRC522_Idle, 2, 0x2F); //*1 // Stop any active command. WriteRegister_MFRC522(ComIrqReg, 0x7F, 2, 0x00); //1* // Clear all seven interrupt request bits MFRC522_SetRegister(FIFOLevelReg, 0x80); WriteRegisterBuffer_MFRC522(FIFODataReg, bufferToSend, bufferToSendLen); // Write sendData to the FIFO WriteRegister_MFRC522(BitFramingReg, bitFraming , 2, 0x77); // Bit adjustments se settati a 0 WriteRegister_MFRC522(CommandReg, command, 2, 0x00); //*2 // Execute the command if(command == MFRC522_Transceive){ MFRC522_SetRegister(BitFramingReg, 0x80); // StartSend=1, transmission of data starts } // Wait for the command to complete. // In PCD_Init() we set the TAuto flag in TModeReg. This means the timer automatically starts when the PCD stops transmitting. // Each iteration of the do-while-loop takes 17.86�s. i = 3000; //2000 while (1) { ReadRegister_MFRC522(ComIrqReg, &n , 1); // ComIrqReg[7..0] bits are: Set1 TxIRq RxIRq IdleIRq HiAlertIRq LoAlertIRq ErrIRq TimerIRq if (n & waitIRq) { // One of the interrupts that signal success has been set. break; } if (n & 0x01) { qDebug() << "QUITIMOUT1" << n;// Timer interrupt - nothing received in 25ms return STATUS_TIMEOUT; } if (--i == 0) { qDebug() << "QUITIMOUT2";// The emergency break. If all other condions fail we will eventually terminate on this one after 35.7ms. Communication with the MFRC522 might be down. return STATUS_TIMEOUT; } } // Stop now if any errors except collisions were detected. ReadRegister_MFRC522(ErrorReg, &errorRegValue , 1); // ErrorReg[7..0] bits are: WrErr TempErr reserved BufferOvfl CollErr CRCErr ParityErr ProtocolErr if (errorRegValue & 0x13) { // BufferOvfl ParityErr ProtocolErr qDebug() << "ERROR1" << errorRegValue << n << waitIRq; return STATUS_ERROR; } qDebug() << "VALORE" << n << "BUFFER TO RECEIVE" << bufferToReceiveLen; // If the caller wants data back, get it from the MFRC522. if (bufferToReceive && bufferToReceiveLen) { ReadRegister_MFRC522(FIFOLevelReg, &n , 1); // Number of bytes in the FIFO if (n > bufferToReceiveLen) { return STATUS_NO_ROOM; } bufferToReceiveLen = n; // Number of bytes returned ReadRegisterBuffer_MFRC522(FIFODataReg, bufferToReceive, n); // Get received data from FIFO // ReadRegisterBuffer_MFRC522(FIFODataReg, bufferToReceive, n); // Get received data from FIFO ReadRegister_MFRC522(ControlReg, &_validBits,1); // ReadRegister_MFRC522(ControlReg, &_validBits,1); _validBits &= 0x07; // _validBits = PCD_ReadRegister(ControlReg) & 0x07; // RxLastBits[2:0] indicates the number of valid bits in the last received byte. If this value is 000b, the whole byte is valid. if (validBits) { *validBits = _validBits; } } // Tell about collisions if (errorRegValue & 0x08) { // CollErr return STATUS_COLLISION; } //// Perform CRC_A validation if requested. if (bufferToReceive && bufferToReceiveLen && CheckCRC) { // In this case a MIFARE Classic NAK is not OK. if (bufferToReceiveLen == 1 && _validBits == 4) { return STATUS_MIFARE_NACK; } // We need at least the CRC_A value and all 8 bits of the last byte must be received. if (bufferToReceiveLen < 2 || _validBits != 0) { return STATUS_CRC_WRONG; } // Verify CRC_A - do our own calculation and store the control in controlBuffer. uint8_t controlBuffer[2]; n = MFRC522_CalculateCRC(&bufferToReceive[0], (bufferToReceiveLen - 2), &controlBuffer[0]); if (n != STATUS_OK) { return n; } if ((bufferToReceive[bufferToReceiveLen - 2] != controlBuffer[0]) || (bufferToReceive[bufferToReceiveLen - 1] != controlBuffer[1])) { return STATUS_CRC_WRONG; } } return STATUS_OK; } void T_SerialPort::ReadRegister_MFRC522(uint32_t address, uint8_t * buff, uint32_t size){ uint8_t devadd; devadd = address | 0x80; portCom->write((char*)&devadd,1); if(portCom->waitForReadyRead()) { QByteArray datareceived = portCom->readAll(); qDebug() << "RICEVUTO" << datareceived; *buff = static_cast<uint8_t>(datareceived.at(0)); } } /**************************************************************************************************************************************** * Funzione dove scrivo nei registri del modulo. * address: è l'indirizzo a cui devo scrivere * buff: è il dato da scrivere * size: non la utilizzo * mask: indica dei vari registri i bit che posso effettivamente leggere perchè sono disponibili in lettura, quelli ceh sono diposnibili * solo in scrittura li trascuro ****************************************************************************************************************************************/ void T_SerialPort::WriteRegister_MFRC522(uint32_t address, uint8_t buff, uint32_t size, uint8_t mask){ uint8_t buffer [2]; uint8_t buffer_2; uint8_t received; // buffer[0] = (address << 1) & 0x7F; buffer[0] = address & 0x7F; //l'ultimo bit deve essere settato a 0 per la scrittura buffer[1] = buff; buffer_2 = address | 0x80; //l'ultimo bit deve essere settato a 1 per la lettura bool writecorrect = false; do{ bool equal = false; do{ portCom->write((char*)&buffer[0], 1); portCom->waitForBytesWritten(10); if(portCom->waitForReadyRead()) { QByteArray datareceived = portCom->readAll(); if(datareceived.at(0) != buffer[0]) { qDebug() << "ERRORE DURANTE LA SCRITTURA" << datareceived.at(0) << address << datareceived.size(); if(datareceived.size() > 1) { qDebug() << "ALTRI DATI"; for(int k = 0; k < datareceived.size(); k++) { qDebug() << "DATO" << k << QString::number(datareceived.at(k)); if(datareceived.at(k) == buffer[1]) { equal = true; break; } } } if(datareceived.at(0) == buffer[1]) { //vuol dire che ha sbagliato in qualche modo gli allinamenti e deve riallinearsi portCom->write((char*)&buffer[0], 1); if(portCom->waitForReadyRead()) { QByteArray datareceived = portCom->readAll(); if(datareceived.at(0) != buffer[0]) { qDebug() << "ANCORA DIVERSI" << QString::number(datareceived.at(0)) << address << datareceived.size(); } else equal = true; } } } else equal = true; } portCom->write((char*)&buffer[1], 1); portCom->waitForBytesWritten(10); }while(!equal); //a questo punto dovrei aver scritto il dato correttamente, vado a verificare eseguendo una lettura if(mask != 0x00) // se il mask è uguale a 0x00 allora significa che nessuno dei bit del registro è disponibile in lettura quindi lo trascuro e setto writecorrect a true per farlo uscire dal ciclo { portCom->write((char*)&buffer_2, 1); if(portCom->waitForReadyRead()) { QByteArray datareceived = portCom->readAll(); //rileggo il registro che ho appena settato e controllo che effettivamente sia settato nel modo corretto if((datareceived.at(0)&mask) == (buffer[1]&mask)) //qui suppongo di ricevere solo 1 byte dalla seriale, dovrebbe essere cosi, se ci sono più byte significa che c'è qualcosa che non va { writecorrect = true; qDebug() << "Scritto correttamente"; } else qDebug() << "NON E SETTATO NEL MODO GIUSTO"; } else qDebug() << "NON HA RISPOSTO SULLA SERIALE"; } else writecorrect = true; }while(!writecorrect); }
I didn't put all the function because they're a lot, but if somebody needs additional code I can send the other.
Basically the second thread starts and enter the while loop but it never reach the end and it gives me "QObject::startTimer: Timers cannot be started from another thread".I don't have timers in my program and I don't access the GUI from the other thread. Somebody can help me?
Thanks in advance! -
You have to create your QSerialPort inside run() - otherwise the object is created in the main thread which is not what you want. And maybe QSerialPort is using a QTimer inside which results in the message you're seeing.
-
@Christian-Ehrlicher Thank you a lot! QSerialPort was the problem and moving it into the run() function made it works.