Qt com порт

QThread + QSerialPort!

Крутим в отдельном потоке работу с СOM портом.

В одном из своих проектов столкнулся с небольшой проблемкой: задача заключается в том, что нужно работать с тремя разными портами одновременно(если быть точнее с двумя, а при настройке и калибровке оборудования 3мя), а самая большая опа в том, что все они общаются по разным протоколам… да в придачу два (в некоторых случаях 3) устройство висит на RS485 с протоколом DCON, это первый порт! Одно устройство на RS232 с жутким самопальным протоколом внеочередной переделкой Wake. И настроечно-калибровочное подключается также по RS232-му, протокол неизвестен (занимаюсь расшифровкой)… Ну это все прелюдия и ОФФТОП…
В такой задаче ИМХО целесообразно крутить работу с каждым портом в отдельном потоке. Вот на этом месте и начались все проблемы… Проведя жуткоскучный и мучительный лит. поиск «ГУГЛ в помощь» результаты были весьма и весьма неоднозначны. Мои подзатыльники самому себе и хочу здесь привести на примере создания простейшей ГУЕвины терминалки с использованием QserialPort и Qthread. Надеюсь топик поможет многим, да и мне возможность получить дополнительных подзатыльников=)))

Постановка задачи: Рассмотреть возможные варианты реализации данной задачи и реализовать наиболее приемлемый и работоспособный. Привести пример простейшей терминалки. Получить подзатыльников и исправить свои ошибки (а их много).
Исходя из лит.анализа, основное: Поток должен быть независим, правильно унаследован. Все обращения к потоку и операции с потоком должны организовываться по сигнально-слотовой системе (никакого прямого обращения к методам в классе).
Краткие теоретические сведенья:
Кьют — бесплатный и кроссплатформенный инструмент для разработки ПО на C++.
QserialPort — дополнение к библиотеке Qt для работы с аппаратными и виртуальными СOM портами. Начиная с 5й версии Qt-а официально в него входит.
Qthread — какая-то жуткая магия для работы с потоками. Независимая задача, которая выполняется внутри процесса и разделяет вместе с ним общее адресное пространство, код и глобальные данные.

COM порт (RS232) — интерфейс с последовательной передачей данных стандарта RS232.
RS485 — стандарт физического уровня. Прикручивается к UART…
Цели: обобщить наработанные результаты и поделиться ими. Получить критический анализ и оценку от сообщества для повышения маны.
Моменты создания приложения в Qt пропускаю… Здесь же приведу код класса и то как его обьявлять.

Создаем новый класс:
файл.h

Теперь необходимо правильно обьявить поток и подключить к нему наш новый класс (здесь находится вся магия):

И соединяем все нужные нам сигналы и слоты:

И остается самое главное! Запустить поток:

В результате получаем вот такое окошко. Единственное что отправленные и принятые данные отображаются в ASCII кодах

Прилеплен архивчик с исходниками!

Комментарии ( 17 )

Писалось под линукс?
На кнопке надпись Serch лучше заменить на Search… Conect = Connect (Disconnect)

Да под линукс, но и на винде тоже должно работать. Не проверял… нету сейчас доступа к ней…
Правки внес, спасибо=)) то все мои глубокие знания англиского=))

RS485 — стандарт физического уровня. Прикручивается к RS232… Обычно таки к UART. RS232 — стандарт физического уровня, аналогично RS485. Алсо, в одном месте ты обозвал его RS323.

>>В такой задаче ИМХО целесообразно крутить работу с каждым портом в отдельном потоке. Вот на этом месте и начались все проблемы…

ИМХО тема не раскрыта — в чём проблемы-то были? О том, как создавать потоки в QT не писал только ленивый. В том числе, была куча дебатов, о том, как их писать правильно и неправильно — правда вот однозначных выводов из них сделано не было. У вас используется метод, продвигаемый в статье «QThread — You are doing it wrong», но почему именно так объяснений не дано. А вот я, решая аналогичную задачу и начитавшись ответной статьи, написал более привычным мне по wxWidgets методом — через наследование. А как НА САМОМ ДЕЛЕ правильно и не правильно — фиг разберёшься.

тема не раскрыта Я как бы привел пример работоспособный, а не проводил анализ всех ошибок со способами их решения.
как создавать потоки в QT Отчасти вы правы=)) Вот только никто не писал про всовывание в поток QSerialPort.

Именно он!

обьяснения 2: 1-проблемы с наследованием всего что в классе; 2 — мне он больше понравился.

Первоначально я сделал тоже как в ответной статье, но куча проблем выскочила, опять с тем же наследованием.

Вот самая моя большая головная боль!

Вот тут Вы правы, черт разберешь=)) Поэтому правильно в данном случае — так как удобно именно Вам и чтобы все работало!

>>Первоначально я сделал тоже как в ответной статье, но куча проблем выскочила, опять с тем же наследованием.

Вооо! Вот именно этой информации и не хватает в статье — было бы неплохо дополнить. У меня, кстати, такой проблемы не возникало, потому что я не через QSerialPort работал, а через простую с-шную обёртку вокруг нативного API.

>>Поэтому правильно в данном случае — так как удобно именно Вам и чтобы все работало!

Ещё бы быть уверенным, в том что оно действительно работает, а не только делает вид и любой чих его может сломать…

Вот именно этой информации и не хватает в статье Как чуть расгребусь с делами насущными… постараюсь поправить.
что оно действительно работает проверяю на разных системах работоспособность кода, с разными устройствами и с использованием разных переходников USB <-> RS232, USB <-> RS485…
+ гонял несколько суток в очень тяжелых условиях (в цеху) со всеми положенными помехами: сварка, станки, лебедки, перепады напряжения и.т.д.

вродибы не выскочило никаких критических ощибок…

Как подругому как проверить не придумал еще…

Объявление структуры настроек порта Settings, лучше перенести а public секцию самого класса Port, а объявление экземпляра структуры (Settings SettingsPort;) в pivate секцию.
Передавать настройки порту можно через указатель на структуру типа Port::SettingsPort.

Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.

EasyEDA: бесплатный Облачный CAD

Когда угодно. Где угодно! На Linux, Mac, Windows, Android, PC, планшете или смартфоне. Откройте браузер, войдите в систему и продолжайте работать. Можно вести приватную или коллективную разработку, а также расшаривать свои проекты всему миру.

Прямой эфир

qserialport,qt,qthread,com порт,терминал:В одном из своих проектов столкнулся с небольшой проблемкой: задача заключается в том, что нужно работать с тремя разными портами

работа с ком портом в qt

Доброго времени суток.

Скачал я со страницы qt-project.org из git-а этот самый QtSerialPort и начинаю его компилировать как написано на той же странице, но возникает ошибка undefined reference to winMain@16.

Компилирую я его на winXp правда, но надеюсь это не станет камнем преткновения. Вот что выдаёт компилятор:

E:\Qt\serialport-build\serialport-build-release\src\serialport>make mingw32-make -f Makefile.Release mingw32-make[1]: Entering directory `E:/Qt/serialport-build/serialport-build-rel ease/src/serialport’ g++ -Wl,-s -o release\libSerialPort.a release/serialport.o release/serialportinf o.o release/serialport_win.o release/serialportinfo_win.o -L".\lib" -L"e:\Qt\4. 8.3\lib" -lsetupapi -ladvapi32 -lQtCore4 e:/qt/mingw/bin/../lib/gcc/i686-mingw32/4.4.5/../../../libmingw32.a(main.o):main .c:(.text+0xd2): undefined reference to `WinMain@16′ collect2: ld returned 1 exit status mingw32-make[1]: *** [release\libSerialPort.a] Error 1 mingw32-make[1]: Leaving directory `E:/Qt/serialport-build/serialport-build-rele ase/src/serialport’ mingw32-make: *** [release] Error 2

кто-нить сталкивался с такой бедой? может даже её побороли? помогите плз

seijuurou


QextSerialPort и потоки



#include "serial.h" int port_fd; //int must_stop; //int f_rx, f_timeout; //u_int8_t msg1 [100]; loadcells::loadcells(clbDialog *c1) { // must_stop=0; clb1=c1; setWindowTitle (tr("Comm. setup")); editPortName = new QLineEdit(this); editN1 = new QLineEdit(this); editN2 = new QLineEdit(this); editN3 = new QLineEdit(this); editN4 = new QLineEdit(this); editN5 = new QLineEdit(this); editN6 = new QLineEdit(this); editN7 = new QLineEdit(this); editN8 = new QLineEdit(this); labelPortName = new QLabel(tr("Serial port name"), this); labelNN = new QLabel(tr("Adresses of loadcells"), this); labelP1 = new QLabel(tr("Platform 1"), this); labelP2 = new QLabel(tr("Platform 2"), this); labelN1 = new QLabel(tr("Loadcell’s N1 adress"), this); labelN2 = new QLabel(tr("Loadcell’s N2 adress"), this); labelN3 = new QLabel(tr("Loadcell’s N3 adress"), this); labelN4 = new QLabel(tr("Loadcell’s N4 adress"), this); labelN5 = new QLabel(tr("Loadcell’s N5 adress"), this); labelN6 = new QLabel(tr("Loadcell’s N6 adress"), this); labelN7 = new QLabel(tr("Loadcell’s N7 adress"), this); labelN8 = new QLabel(tr("Loadcell’s N8 adress"), this); buttonStart = new QPushButton(tr("Start comm."), this); buttonStop = new QPushButton(tr("Stop comm."), this); buttonSave = new QPushButton(tr("Save"), this); buttonClose = new QPushButton(tr("Close"), this); checkAutostart = new QCheckBox(tr("Auto start comm."), this); // port1 = new QextSerialPort(); layout1 = new QGridLayout(this); tc1 = new threadComm(this); loadSettings(); layout1->setSpacing(20); layout1->addWidget(labelNN,0,2); layout1->addWidget(labelP1,1,0); layout1->addWidget(labelP2,1,4); layout1->addWidget(labelN1,2,0); layout1->addWidget(editN1,2,1); layout1->addWidget(editN2,2,2); layout1->addWidget(labelN2,2,3); layout1->addWidget(labelN3,3,0); layout1->addWidget(editN3,3,1); layout1->addWidget(editN4,3,2); layout1->addWidget(labelN4,3,3); layout1->addWidget(labelN5,2,4); layout1->addWidget(editN5,2,5); layout1->addWidget(editN6,2,6); layout1->addWidget(labelN6,2,7); layout1->addWidget(labelN7,3,4); layout1->addWidget(editN7,3,5); layout1->addWidget(editN8,3,6); layout1->addWidget(labelN8,3,7); layout1->addWidget(labelPortName, 4, 0); layout1->addWidget(editPortName, 4, 1); layout1->addWidget(checkAutostart, 5, 0); layout1->addWidget(buttonStart, 6, 0); layout1->addWidget(buttonStop, 6, 1); layout1->addWidget(buttonSave, 6, 6); layout1->addWidget(buttonClose, 6, 7); connect(buttonClose, SIGNAL(clicked()), this, SLOT(close())); connect(buttonSave, SIGNAL(clicked()), this, SLOT(saveSettings())); connect(buttonStart, SIGNAL(clicked()), tc1, SLOT(startC())); connect(buttonStop, SIGNAL(clicked()), tc1, SLOT(stopC())); connect(tc1, SIGNAL(updated()), this, SLOT(updatedP())); if (checkAutostart->checkState()==Qt::Checked) tc1->startC(); // connect(this, SIGNAL(tcStop()), tc1, SLOT(stop())); } void loadcells::updatedP() { emit updated(); } QString loadcells::getPortName() { QString s1 = editPortName->text(); return s1; } void loadcells::loadSettings() { QSettings settings(QSettings::IniFormat, QSettings::UserScope, "CMA", "QVD"); //Debug // QSettings s1(QSettings::IniFormat, QSettings::SystemScope, "CMA", "QVD"); //Release editN1->setText(settings.value("comm/adress1", "0").toString()); editN2->setText(settings.value("comm/adress2", "0").toString()); editN3->setText(settings.value("comm/adress3", "0").toString()); editN4->setText(settings.value("comm/adress4", "0").toString()); editN5->setText(settings.value("comm/adress5", "0").toString()); editN6->setText(settings.value("comm/adress6", "0").toString()); editN7->setText(settings.value("comm/adress7", "0").toString()); editN8->setText(settings.value("comm/adress8", "0").toString()); editPortName->setText(settings.value("comm/portName", " ").toString()); if(settings.value("comm/autostart", "0").toString() == "0")checkAutostart->setCheckState(Qt::Unchecked); if(settings.value("comm/autostart", "0").toString() == "2")checkAutostart->setCheckState(Qt::Checked); } void loadcells::saveSettings() { QSettings settings(QSettings::IniFormat, QSettings::UserScope, "CMA", "QVD"); //Debug // QSettings s1(QSettings::IniFormat, QSettings::SystemScope, "CMA", "QVD"); //Release settings.setValue("comm/adress1", editN1->text()); settings.setValue("comm/adress2", editN2->text()); settings.setValue("comm/adress3", editN3->text()); settings.setValue("comm/adress4", editN4->text()); settings.setValue("comm/adress5", editN5->text()); settings.setValue("comm/adress6", editN6->text()); settings.setValue("comm/adress7", editN7->text()); settings.setValue("comm/adress8", editN8->text()); settings.setValue("comm/portName", editPortName->text()); settings.setValue("comm/autostart", checkAutostart->checkState ()); } //int init_port(char* portname) { // port_fd = open (portname, O_RDWR | O_NOCTTY | O_NDELAY); // if (port_fd == -1) return -1; // fcntl(port_fd, F_SETFL, 0); // struct termios options; // tcgetattr(port_fd, &options); // cfsetispeed(&options, B115200); // cfsetospeed(&options, B115200); // options.c_cflag |= (CLOCAL | CREAD); // options.c_cflag &= ~CSIZE; // options.c_cflag |= CS8; // options.c_cflag &= ~PARENB; // options.c_cflag &= ~CSTOPB; ///* options.c_cflag |= CRTSCTS; */ // options.c_cflag &= ~CRTSCTS; // options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG | IEXTEN); // options.c_oflag &= ~OPOST; // options.c_cc[VMIN] = 0; // options.c_cc[VTIME] = 10; // options.c_iflag &= ~(IXON | IXOFF | IXANY | INLCR | ICRNL); // options.c_iflag &= ~(INPCK | ISTRIP | PARMRK | IGNCR); // options.c_iflag |= IGNPAR; // tcsetattr(port_fd, TCSANOW, &options); // tcflush (port_fd, TCIOFLUSH); // return 0; //} //void close_port (void) { close (port_fd); } //void* u_timeout(){ //// f_timeout = 1; // pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, NULL); // int count = 0; // while ((count < 1000) && (f_rx != 1)) { usleep (500); count++; }; //// usleep (500000); // f_timeout = 0; // return NULL; //} //void* u_read(){ // f_rx = 0; // int count_bytes=0; // msg1[0] = 1; // while ((f_timeout == 1)&&(count_bytes < msg1[0]+3)){ // int len1 = read (port_fd, &msg1[count_bytes], 1); //// if (len1 != -1)fprintf (stdout, "\n msg1[%X] = %X", count_bytes, msg1[count_bytes]); // if ((len1 != -1) && (count_bytes <= 30)) count_bytes += len1; // } //// fprintf (stdout, "\n count_bytes = %X, f_timeout = %X", count_bytes, f_timeout); //// } // if (f_timeout == 0) f_rx = -1; // else f_rx = 1; // return NULL; //} //Sending request, whaiting and testing answer //Return codes: //0 — OK //-1 — Time-out //-2 — Checksum error //*/ //int req_ans(u_int8_t* req) { // pthread_t thread_read, thread_timeout; // f_timeout = 1; // pthread_create (& thread_read, NULL, &u_read, NULL); // pthread_create (& thread_timeout, NULL, &u_timeout, NULL); // int req_l = req[0]+3; //// fprintf(stdout, "\n req = %X, %X, %X, %X, %X, %X, %X, %X, %X,", req[0], req[1], req[2], //req[3], req[4], req[5], req[6], req[7], req[8]); //// fprintf(stdout, "\n req_l = %X", req_l); // int res = write (port_fd, req, req_l); // if (res == -1) { // pthread_join (thread_read, NULL); // pthread_join (thread_timeout, NULL); // return -1; // } //// if (ans_l > 2) sleep (1); //// res = read (port_fd, ans, ans_l); // pthread_join (thread_read, NULL); //// if (f_rx == 0) pthread_cancel (thread_timeout); // pthread_join (thread_timeout, NULL); //// fprintf(stdout, "\n f_timeout = %X, f_rx = %X", f_timeout, f_rx); //// if (f_rx != 1) return -1; //// if (test_cs_msg (msg1) == 0) return 0; //// return -2; // return -2; //} void threadComm::startC() { start(QThread::LowestPriority); emit started(); } threadComm::threadComm(loadcells *p2) { parent=p2; } void threadComm::stopC() { qDebug("Stop signal"); exit(0); // emit tcStop(); if(wait(5000)==TRUE)qDebug("Normal exit"); else qDebug("Timeout"); emit stopped(); } QString loadcells::getNum(int i) { QString res("0"); switch(i) { case 1: { res=editN1->text(); break; } case 2: { res=editN2->text(); break; } case 3: { res=editN3->text(); break; } case 4: { res=editN4->text(); break; } case 5: { res=editN5->text(); break; } case 6: { res=editN6->text(); break; } case 7: { res=editN7->text(); break; } case 8: { res=editN8->text(); break; } } return res; } void threadComm::run() { port1= new QextSerialPort(); // init_port((editPortName->text()).unicode()); port1->setPortName(parent->getPortName()); port1->setBaudRate(BAUD9600); port1->setDataBits(DATA_8); port1->setFlowControl(FLOW_OFF); port1->setParity(PAR_EVEN); port1->setStopBits(STOP_1); port1->setTimeout(0,100); port1->setTextModeEnabled(FALSE); if(port1->open(QIODevice::ReadWrite) == TRUE)qDebug("Open TRUE"); else qDebug("Open False"); port1->reset(); timer1 = new QTimer(); connect(timer1, SIGNAL(timeout()), this, SLOT(update())); timer1->start(3000); exec(); port1->close(); } void threadComm::update() { port1->write("S98;"); port1->write("MSV?;"); QString s1("S"); QByteArray s2; s1+=parent->getNum(1); s1+=(";"); port1->write(s1.toAscii()); s2=port1->read(8); parent->setValue(s2.toFloat(),0); s1.truncate(1); s1+=parent->getNum(2); s1+=(";"); port1->write(s1.toAscii()); s2=port1->read(8); parent->setValue(s2.toFloat(),1); s1.truncate(1); s1+=parent->getNum(3); s1+=(";"); port1->write(s1.toAscii()); s2=port1->read(8); parent->setValue(s2.toFloat(),2); s1.truncate(1); s1+=parent->getNum(4); s1+=(";"); port1->write(s1.toAscii()); s2=port1->read(8); parent->setValue(s2.toFloat(),3); s1.truncate(1); s1+=parent->getNum(5); s1+=(";"); port1->write(s1.toAscii()); s2=port1->read(8); parent->setValue(s2.toFloat(),4); s1.truncate(1); s1+=parent->getNum(6); s1+=(";"); port1->write(s1.toAscii()); s2=port1->read(8); parent->setValue(s2.toFloat(),5); s1.truncate(1); s1+=parent->getNum(7); s1+=(";"); port1->write(s1.toAscii()); s2=port1->read(8); parent->setValue(s2.toFloat(),6); s1.truncate(1); s1+=parent->getNum(8); s1+=(";"); port1->write(s1.toAscii()); s2=port1->read(8); parent->setValue(s2.toFloat(),7); emit updated(); } float loadcells::lastValue(int n1) { return values[n1]; } void loadcells::setValue(float i1, int n1) { values[n1]=clb1->clb(i1*40/1000000, n1); // qDebug("loadcell 1 %f" ,values[n1]); } //void threadComm::stop() //{ // qDebug("Stop signal"); // exit(0); //} //#include <termios.h> //#include <stdio.h> //#include <string.h> //#include <unistd.h> //#include <fcntl.h> //#include <errno.h> //#include <pthread.h> //#include <stdlib.h> #define _TTY_POSIX_ #include <QDialog> #include <QLineEdit> #include <QLabel> #include <QGridLayout> #include <QPushButton> #include <QShowEvent> #include <QSettings> #include <QCheckBox> #include <QTimer> #include <QThread> #include "qextserialport/qextserialport.h" #include "c16c.h" //int init_port (char*); //void close_port (void); //int req_ans (u_int8_t*); //extern int port_fd; //extern u_int8_t msg1 [100]; //int rx (u_int8_t*, u_int8_t, u_int8_t*, int*, u_int8_t*, u_int8_t*); //void create_msg (u_int8_t*, u_int8_t); //void add_cs_msg (u_int8_t*); //void add_data_msg (u_int8_t*, void*, u_int8_t); //int tx_msg (u_int8_t*, int); class loadcells; class threadComm : public QThread { Q_OBJECT public: threadComm(loadcells *); public slots: void startC(); void stopC(); private slots: void update(); signals: void started(); void stopped(); void updated(); protected: void run(); private: QTimer *timer1; QextSerialPort *port1; loadcells *parent; void update1(int); }; class loadcells : public QDialog { Q_OBJECT public: loadcells(clbDialog*); QString getNum(int); float lastValue(int); //After calibration void setValue(float, int); // int lastError(); //0 — No error, 1…8 — no answer from loadsell 1..8, 9 — error opening serial port; QString getPortName(); private slots: void saveSettings(); void loadSettings(); void updatedP(); // void update(); // void commStart(); // void commStop(); signals: // void tcStop(); void started(); void stopped(); void updated(); // void updated(); // void error(); private: QLineEdit *editPortName; QLineEdit *editN1; QLineEdit *editN2; QLineEdit *editN3; QLineEdit *editN4; QLineEdit *editN5; QLineEdit *editN6; QLineEdit *editN7; QLineEdit *editN8; QLabel *labelPortName; QLabel *labelNN; QLabel *labelP1; QLabel *labelP2; QLabel *labelN1; QLabel *labelN2; QLabel *labelN3; QLabel *labelN4; QLabel *labelN5; QLabel *labelN6; QLabel *labelN7; QLabel *labelN8; QGridLayout *layout1; QPushButton *buttonStart; QPushButton *buttonStop; QPushButton *buttonSave; QPushButton *buttonClose; QCheckBox *checkAutostart; threadComm *tc1; QextSerialPort *port1; clbDialog *clb1; float values[8]; };


Reply to:


Добавить комментарий

Закрыть меню