精华内容
下载资源
问答
  • 多线程串口通信

    2020-03-10 19:04:55
    串口通信类似于udp通信,是无连接的,所以也要考虑编写心跳等协议 实际开发中根据给定的数据协议和给定的显示功能进行开发,制定相应的数据解析 这里我用通用的串口通信讲解如何快速搭建一个显示单片机或者其他...

    串口通信类似于udp通信,是无连接的,所以也要考虑编写心跳等协议

    实际开发中根据给定的数据协议和给定的显示功能进行开发,制定相应的数据解析

    这里我用通用的串口通信讲解如何快速搭建一个显示单片机或者其他计算机的各种信息


    1.设计协议包的操作类

    #ifndef AGREEMENT_H
    #define AGREEMENT_H
    
    #include <QObject>
    #include <qbytearray.h>
    #include <QMap>
    #include <QVector>
    
    
    
    namespace  协议名{ //可用json读取进行配置
    enum Function {
        WAVE =0x01
    ....
    };
    }
    #define Q_NOTZERO(x) ((x!=0))
    
    typedef struct SendPage{    //发送包
        qint16 initial1 = 0x55;//字头
        qint16 initial2 = 0xbe;//字头
        qint16 packetlength = 0x0c;//包长
        qint16 expressCommand = 0x0;//表示命令
        qint16 function = 0x0;//功能
        ....
        qint16 checkH;//效验H
        qint16 checkL;//效验L
    public:
        QString number(qint16 number);//QString::number
    
        void setExpressCommand(qint16 expressCommand) {
            this->expressCommand = expressCommand;
        }
    
        void  setFunction(qint16 function) {
            this->function = function;
        }
    
        void setData(QVariant data);
    
        SendPage(); 
        void commit( QByteArray & byte) {//根据校验方式进行校验 同样可用使用json配置
    
            checkH = 0;
    
            switch (checkL)
            {
            case 方法1:
                
            case 方法2:
    
                break;
            default:
    
                break;
            }
    
    
            QString _str = QString(number(initial1) + ' '+
                                   number(initial2) + ' '+
                                   number(packetlength) + ' ' +
                                   number(expressCommand) + ' ' +
                                   number(function) + ' ' +
                                    ......
                                   number(fill[0]) + ' ' +
                    number(checkH) + ' ' +
                    number(checkL));
    
    
            SerialPort::String2Hex(_str, byte); //转化为16进制
    
        }
    
    
        void setCheckL(qint16 checkL) {
            this->checkL = checkL;
        }
    
        void setFill(QVector<qint16> data){
            for(int i=0;i<6 && i<data.length();++i)
                this->fill[i]=data.at(i);
        }
    
    
    
    
        qint16 getInitial2() const;
        void setInitial2(const qint16 &value);
    }SendPage;
    
    
    namespace PageHelp //xx为默认校验
    {
    
    void set协议1(QByteArray & byte, qint16 type, QVariant data, qint16 expressCommand, qint16 checkL = xx);
    void set协议2(QByteArray &byte,qint16 type, QVariant data, qint16 expressCommand , qint16 checkL = xx);
    }
    
    
    
    class Agreement :public QObject
    {
        Q_OBJECT
    public:
        explicit  Agreement(){
    
        }
    
        explicit Agreement(QVector<qint16> &vector,QObject *parent = nullptr):QObject(parent) {
            m_vector = vector;
        }
        explicit  Agreement(QObject *parent = nullptr):QObject(parent) {
    
        }
        QVector<qint16> values() const;
        void setVector(const QVector<qint16> &values);
        void append(qint16 value);
    
        Agreement(const Agreement & agreement){
            this->setVector(agreement.values());
        }
        qint16 indexOf(quint16 index){
            if(index>=this->m_vector.length() || index<0){
                return 0;
            }
            return m_vector[index];
        }
    
        void setList(QVector<qint16> & list);
    
        void set(qint16 index,QVariant value);
    
    signals:
        void setupSuccess(qint16 function,qint16 value);
    private:
        QVector<qint16> m_vector;
    };
    
    class 协议1:public Agreement
    {
        Q_OBJECT
    public:
    
        void  onSet(QVector<qint16> data);
    
        qint16 value(AMMONOX::Function function);
        协议1命名空间::Function key(qint16 value);
        协议1(QVector<qint16> &vector,QObject *parent = nullptr);
    signals:
        void sendPage(QByteArray page);
    
    private:
    
        QMap<协议1命名空间::Function,qint16> m_mapFunction;
    };
    
    class 协议2:public Agreement
    {
        Q_OBJECT
    public:
    
        void onSet(QVector<qint16> data);
        协议2命名空间::Function key(qint16 value);
        qint16 value(协议2::Function function);
    
        协议2(QVector<qint16> &vector,QObject *parent = nullptr);
    signals:
        void sendPage(QByteArray page);
    
    private:
    
        QMap<协议2命名空间::Function,qint16> m_mapFunction;
    };
    
    #endif //
    
    

    2.设计串口消息接受发送类

    实际开发过程中,PC性能一般大于下位机所有接受并不是一次就接受所有数据,而是和udp一样分片发送,所以再收发的过程中注意收发频率.

     

    展开全文
  • 本次主要写点MFC环境下多线程串口通信相关的东西,这包括线程创建及控制、串口同步异步操作、内存非法访问(或者说是线程同步)、线程通信、Windows消息响应过程等。 遇到问题: 项目中IO传感器通信模块之前...

    写在前面:

             晚上应该继续完成未写完的代码,但Chrome上打开的标签实在太多了,约30个了,必须关掉一些,所以需要把自己看的整理一下然后关掉。本次主要写点MFC环境下多线程串口通信相关的东西,这包括线程创建及控制、串口同步异步操作、内存非法访问(或者说是线程同步)、线程通信、Windows消息响应过程等。

    遇到问题:

             项目中IO传感器通信模块之前直接写在了主线程中,UI代码和串口通信代码搅合在一起,不利于后期维护,而且有个非常严重的问题,IO通信太忙导致整个系统比较卡,特别是当系统接上超过3个摄像机之后,MFC模态对话框使用Domodal()直接无法打开,卡住了,然后用户就无法操作了,这个问题必须要解决。

    解决方案:

             单独开辟一个线程来处理所有的串口通信,该IO线程和主线程(负责UI部分)通信,从而更新状态,不能子线程中直接更新UI,参看《MFC最好不要在子线程中操控界面上的控件》。

    具体步骤:

    1.创立IO线程并完成消息响应

    HANDLE hThread1 = CreateThread( NULL,0,IOControlProc,(LPVOID)(m_pCOMSerialPort),0,&m_dwIOControlThreadId );//创建IO线程
    CloseHandle( hThread1 );//关闭线程句柄
    

      其中

    CSerialPort  *m_pCOMSerialPort;//通信串口
    DWORD m_dwIOControlThreadId;//线程ID
    

      IOControlProc线程函数

    DWORD WINAPI IOControlProc(LPVOID lpParameter)
    {
    	CSerialPort *pSerialPort = (CSerialPort*)lpParameter;
    	UINT_PTR nHUMITimer = 0;
    	UINT_PTR nIOTimer = 0;
    	//::SetTimer(NULL,NULL,200,(TIMERPROC)TimerProc);
    	nHUMITimer = ::SetTimer(NULL,TEMPHUMICOMM_TIMER,500,(TIMERPROC)OnIOTimer);//温湿度
    	nIOTimer =::SetTimer(NULL,IOCOMM_TIMER,100,(TIMERPROC)OnIOTimer);//IO
    	PIOTimerStru pHumiTimer= new IOTimerStru;
    	pHumiTimer->nIdEvent = TEMPHUMICOMM_TIMER;
    	pHumiTimer->pSerialPort = pSerialPort;
    	PIOTimerStru pIoTimer = new IOTimerStru;
    	pIoTimer->nIdEvent = IOCOMM_TIMER;
    	pIoTimer->pSerialPort = pSerialPort;
    	PIOTimerStru pIoControl = new IOTimerStru;
    	pIoControl->nIdEvent = TEMPHUMICOMM_TIMER + IOCOMM_TIMER;
    	pIoControl->pSerialPort = pSerialPort;
    	LPARAM   lParam;
    	MSG msg;
    	while(GetMessage(&msg,NULL,0,0))
    	{
    		TranslateMessage(&msg);
    		if (WM_TIMER == msg.message)
    		{
    			lParam = msg.lParam;//WM_TIMER回调函数的地址
    			if (nHUMITimer == msg.wParam)
    			{
    				msg.wParam = (WPARAM)pHumiTimer;
    			}
    			else if (nIOTimer == msg.wParam)
    			{
    				msg.wParam = (WPARAM)pIoTimer;
    			}			
    		}	
    		else if (WM_IOCOMMDATA == msg.message)
    		{
    			msg.message = WM_TIMER;
    			pIoControl->wParam = msg.wParam;
    			pIoControl->lParam = msg.lParam;
    			msg.wParam = (WPARAM)pIoControl;
    			msg.lParam = lParam;
    		}		
    		DispatchMessage(&msg);
    	}
    	delete pHumiTimer;
    	delete pIoTimer;
    	delete pIoControl;
    	return 0;
    }
    

      这里,IO线程有自己的消息循环队列(虽然没有窗口),参看:《子线程里如何使用定时器》,把这里的代码改成死循环的(参看《是否在子线程内使用SetTimer?》),如下

    VOID CALLBACK TimerProc(          HWND hwnd,
        UINT uMsg,
        UINT_PTR idEvent,
        DWORD dwTime
    ){
             //do some thing
             return;
    }
    
    DWORD WINAPI ThreadProc(LPVOID lpParameter)
    {
          
    	::SetTimer(NULL,NULL,200,(TIMERPROC)TimerProc);
    	MSG msg;
    	while(GetMessage(&msg,NULL,0,0))
    	{
    		TranslateMessage(&msg);
    		DispatchMessage(&msg);
    	}
    	return 0;
    	
    }
    

      我的消息响应函数写成如下

    VOID CALLBACK OnIOTimer(HWND hwnd,UINT uMsg,UINT_PTR idEvent,DWORD dwTime)
    {
    	PIOTimerStru pStru = (PIOTimerStru)idEvent;
    	if (pStru->nIdEvent == TEMPHUMICOMM_TIMER)//温湿度模块
    	{
    		static bool bFlag = true;
    		if (bFlag)
    		{
    			IOSendData(pStru->pSerialPort,2,0x0A,0,0x03);
    		}
    		else
    		{
    			IOSendData(pStru->pSerialPort,2,0x0A,0,0x04);
    		}
    		bFlag = !bFlag;
    	}
    	else if (pStru->nIdEvent == IOCOMM_TIMER)//IO控制模块
    	{
    		IOSendData(pStru->pSerialPort,1,0x10,0,0x03);
    	}
    	else if (pStru->nIdEvent ==  TEMPHUMICOMM_TIMER + IOCOMM_TIMER)//别的线程发送的指令
    	{		
    		WORD highDeviceIndex = HIWORD(pStru->wParam);//设备号
    		WORD lowPortIndex = LOWORD(pStru->wParam);//设备1端口号,设备2指定温度或湿度模块
    		WORD highParam = HIWORD(pStru->lParam);//设备1指定状态1或者0,设备2指定整数部分
    		WORD lowParam = LOWORD(pStru->lParam);//设备1为0,设备2指定小数部分
    
    		IOSendData(pStru->pSerialPort,(int)highDeviceIndex,(BYTE)highParam,(BYTE)lowParam,(BYTE)lowPortIndex);
    	}
    	return;
    }
    

      这里要注意一个问题,OnIOTimer中获得的idEvent并不是我们设置的IOCOMM_TIMER或者TEMPHUMICOMM_TIMER,究其原因,参看《SetTimer在无窗口和有窗口线程的使用》,文中关于原因的解释为“注:只有当hWnd参数为非空时,计时器的ID为设置的 nIDEvent, 系统为你自动生成一个计时器ID,可由返回时值获取.”,可由MSDN得知。

    那么我们的这个IOCOMM_TIMER或者TEMPHUMICOMM_TIMER,怎么传递过去呢?查看《怎么往SetTimer的回调函数传递参数》得知,我们可由msg.wParam传递。

    那么对于我们自定义的消息TEMPHUMICOMM_TIMER + IOCOMM_TIMER,我也想让OnIOTimer来处理怎么办?直接修改msg.message = WM_TIMER;就可以了嘛?答案是不行的!

    为什么呢?参看《消息循环中的TranslateMessage函数和DispatchMessage函数》,原来“如果参数lpmsg指向一个WM_TIMER消息,并且WM_TIMER消息的参数IParam不为NULL,则调用IParam指向的函数,而不是调用窗口程序。”,那么我们直接修改msg.lParam为OnIOTimer函数的地址就行了。这样,所有消息响应完成了。

    2.多线程访问冲突(线程冲突)
    遇到一个问题,系统有一个串口通信端口,IO线程直接使用了,然后我在主线程中也发送数据,然后问题出现了,有时候会出现非法访问,跟踪了一下,原来两个线程使用了同一个串口通信缓冲区,主线程往里面压入数据的时候,可能子线程已经释放了该缓冲区,查询文章《CSerialPort连续发送大量数据时出错原因分析》,而我使用的CSerialPort是同步的,我尝试修改类库代码,报错太多,此方法放弃。最终,我的解决方法是把所有的串口IO通信全部交给子线程来做,那么主线程要做的事,可以通过发送消息给子线程,再由子线程代劳,主线程怎么发送消息给子线程?使用API函数PostThreadMessage来完成,第一个参数就是子线程的Id。

    子线程收到数据后,进行验证,验证通过后,发消息给主线程,通知它更新界面。《主线程与子线程间通信解决办法 - VC/MFC

    3.主线程退出前,关闭子线程

    PostThreadMessage(m_dwIOControlThreadId,WM_QUIT,0,0);
    Sleep(100);//需要等待关闭掉
    

    GetMessage有消息时且消息不为WM_QUIT时返回TRUE,如果有消息且为WM_QUIT则返回FALSE,没有消息时不返回。  

    这里可参看《如何正确的关闭 MFC 线程》和《GetMessage和PeekMessage的联系与区别以及用法 TranslateMessage与DispatchMessage 

    至此,经测试问题全部解决,记录一下。

    转载于:https://www.cnblogs.com/xingrun/p/3587144.html

    展开全文
  • Qt下实现多线程串口通信

    千次阅读 2019-05-20 07:19:37
    Qt下实现多线程串口通信 Qt下无论是RS232、RS422、RS485的串口通信都可以使用统一的编码实现。本文把每路串口的通信各放在一个线程中,使用movetoThread的方式实现。 用SerialPort类实现串口功能,Widget类调用...

    Qt下实现多线程串口通信

    Qt下无论是RS232RS422RS485的串口通信都可以使用统一的编码实现。本文把每路串口的通信各放在一个线程中,使用movetoThread的方式实现。

    SerialPort类实现串口功能,Widget类调用串口。

    serialport.h如下:

    #include <QObject>

    #include <QSerialPort>

    #include <QString>

    #include <QByteArray>

    #include <QObject>

    #include <QDebug>

    #include <QObject>

    #include <QThread>

     

    class SerialPort : public QObject

    {

      Q_OBJECT

    public:

      explicit SerialPort(QObject *parent = NULL);

      ~SerialPort();

     

      void init_port();  //初始化串口

     

    public slots:

      void handle_data();  //处理接收到的数据

      void write_data();     //发送数据

     

    signals:

      //接收数据

      void receive_data(QByteArray tmp);

     

    private:

      QThread *my_thread;

      QSerialPort *port;

    };

    serailport.cpp如下

    #include "serialport.h"

     

    SerialPort::SerialPort(QObject *parent) : QObject(parent)

    {

        my_thread = new QThread();

     

        port = new QSerialPort();

        init_port();

        this->moveToThread(my_thread);

        port->moveToThread(my_thread);

        my_thread->start();  //启动线程

     

    }

     

    SerialPort::~SerialPort()

    {

        port->close();

        port->deleteLater();

        my_thread->quit();

        my_thread->wait();

        my_thread->deleteLater();

    }

     

    void SerialPort::init_port()

    {

        port->setPortName("/dev/ttyS1");                   //串口名 windows下写作COM1

        port->setBaudRate(38400);                           //波特率

        port->setDataBits(QSerialPort::Data8);             //数据位

        port->setStopBits(QSerialPort::OneStop);           //停止位

        port->setParity(QSerialPort::NoParity);            //奇偶校验

        port->setFlowControl(QSerialPort::NoFlowControl);  //流控制

        if (port->open(QIODevice::ReadWrite))

        {

            qDebug() << "Port have been opened";

        }

        else

        {

            qDebug() << "open it failed";

        }

        connect(port, SIGNAL(readyRead()), this, SLOT(handle_data()), Qt::QueuedConnection); //Qt::DirectConnection

    }

     

    void SerialPort::handle_data()

    {

        QByteArray data = port->readAll();

        qDebug() << QStringLiteral("data received(收到的数据):") << data;

        qDebug() << "handing thread is:" << QThread::currentThreadId();

        emit receive_data(data);

    }

     

    void SerialPort::write_data()

    {

        qDebug() << "write_id is:" << QThread::currentThreadId();

        port->write("data", 4);   //发送“data”字符

    }

    widget.h的调用代码

    #include "serialport.h"

    public slots:

      void on_receive(QByteArray tmpdata);

    private:

      SerialPort *local_serial;

    widget.cpp调用代码

    //构造函数中

    local_serial = new QSerialPort();

    connect(ui->pushButton, SIGNAL(clicked()), local_serial, SLOT(write_data()));

    connect(local_serial, SIGNAL(receive_data(QByteArray)), this, SLOT(on_receive(QByteArray)), Qt::QueuedConnection);

    //on_receive槽函数

    void Widget::on_receive(QByteArray tmpdata)

    {

     ui->textEdit->append(tmpdata);

    }

    本文例子实现的串口号是 /dev/ttyS1(对应windows系统是COM1口),波特率38400,数据位8,停止位1,无校验位的串口通信。当然,使用串口程序前,需要在.pro文件中添加 QT += serialport,把串口模块加入程序。

    展开全文
  • 最近在学习Qt编程,为了练手,想做一个串口通信的小软件为之后的项目做准备,经过几天的学习与练习初步搭建起了多线程串口通信的框架及较少的其他功能 功能简介 串口接收 主线程显示ui,子线程接收串口数据,数据...

    最近在学习Qt编程,为了练手,想做一个串口通信的小软件为之后的项目做准备,经过几天的学习与练习初步搭建起了多线程串口通信的框架及较少的其他功能

    功能简介

    1. 串口接收
    2. 主线程显示ui,子线程接收串口数据,数据通过信号和槽发送到主线程
    3. 打开串口启动子线程,关闭串口后关闭子线程
    4. 采用定时器延时方法读取缓存区的数据
    5. 自行选择串口号与波特率,默认数据位为8、停止位为1、无奇偶校验
    6. 16进制显示项目中暂未用到,程序附在最后留存

    界面设计

    由于以后要进行其他数据的解析,用于测试的界面很简单,只设计了很少的控件,UI界面设计如下:
    在这里插入图片描述
    在这里插入图片描述

    子线程

    最开始练习时设计过单线程的串口助手,但是当数据量太大时软件会卡死,因此这次采用了多线程设计。
    首先创建子线程类serialThread,并创建QSerialPort对象及相关初始化方法,主要代码如下:

    serialthread.h

    #ifndef SERIALTHREAD_H
    #define SERIALTHREAD_H
    
    #include<QThread>
    #include<QDebug>
    #include<QSerialPort>
    #include<QSerialPortInfo>
    #include<QString>
    #include<QTimer>
    
    
    class serialThread : public QThread
    {
        Q_OBJECT
    public:
        serialThread();
    
    public:
        QSerialPort *Serial;
        void InitPortName(const QString &portName);                 //初始化串口名
        void InitPortBaudRate(qint32 baudRate);                     //...波特率
        void InitPortDataBits(QSerialPort::DataBits dataBits);      //...数据位
        void InitPortParity(QSerialPort::Parity parity);            //...奇偶校验
        void InitPortStopBits(QSerialPort::StopBits stopbits);      //...停止位
        void InitFlowControl(QSerialPort::FlowControl flowcontrol); //...控制流
        bool OpenPort();                                            //打开串口
        void ClosePort();                                           //关闭串口
    public:
        QByteArray buffer;
    
    signals:
        void dataSend(QByteArray);                                  //发送从串口读取到的数据的信号
        void timeout();
    
    private slots:
        void timeUpdate();
        void data_receive();
        void run();
    
    private:
        QTimer *time;
    };
    
    
    
    #endif // SERIALTHREAD_H
    
    

    然后声明子线程相关方法,连接信号和槽,主要代码如下:
    serialthread.cpp

    #include "serialthread.h"
    
    serialThread::serialThread()
    {
        Serial=new QSerialPort();
        time=new QTimer;
    }
    
    /**
     * @brief 数据接收处理步骤
     * 1.打开串口,开启子线程
     * 2.串口收到数据后启动定时器,延时10ms,防止数据未发送完
     * 3.延时结束后读缓存区的数据
     * 4.将读取到的数据通过信号发送至主线程
     */
    
    void serialThread::run()
    {
        QObject::connect(Serial,&QSerialPort::readyRead,this,&serialThread::data_receive);  //收到数据触发槽函数启动定时器
        QObject::connect(time,SIGNAL(timeout()),this,SLOT(timeUpdate()));                   //延时结束开始读数据
    }
    
    void serialThread::data_receive()
    {
        time->start(10);
    }
    
    void serialThread::timeUpdate()
    {
        time->stop();
        buffer = Serial->readAll();              //读数据
        emit dataSend(buffer);                   //数据发送至主函数
    
    #if 0  
        //以下代码为测试其他功能用的
        for (int i=0;i<(buffer.size()-10);i++)
        {
    
            if(buffer.at(i)==0x55)
            {
                switch (buffer.at(i+1))
                {
                case 0x51:{}break;
                case 0x52:{}break;
                case 0x53:{}break;
                case 0x54:{}break;
                case 0x56:{}break;
                case 0x57:{}break;
                case 0x58:{}break;
                case 0x59:{}break;
                case 0x5A:{}break;
                default:{}break;
                }
            }
        }
    #endif
        buffer.clear();
    }
    
    void serialThread::InitPortName(const QString &portName)
    {
        Serial->setPortName(portName);                          //设置串口名的方法,下同
    }
    
    void serialThread::InitPortBaudRate(qint32 baudRate)
    {
         Serial->setBaudRate(baudRate);
    }
    
    void serialThread::InitPortDataBits(QSerialPort::DataBits dataBits)
    {
        Serial->setDataBits(dataBits);
    }
    
    void serialThread::InitPortStopBits(QSerialPort::StopBits stopbits)
    {
        Serial->setStopBits(stopbits);
    }
    
    void serialThread::InitPortParity(QSerialPort::Parity parity)
    {
        Serial->setParity(parity);
    }
    
    void serialThread::InitFlowControl(QSerialPort::FlowControl flowcontrol)
    {
        Serial->setFlowControl(flowcontrol);
    }
    
    bool serialThread::OpenPort()
    {
        if(Serial->open(QIODevice::ReadWrite))
            return true;
        else
            return false;
    }
    
    void serialThread::ClosePort()
    {
        Serial->close();
    }
    

    主线程

    主线程主要用于UI界面的显示以及启动子线程,其中按钮的槽方法时通过UI编辑器生成的,代码如下:
    mainwindow.h

    #include "serialthread.h"
    
    serialThread::serialThread()
    {
        Serial=new QSerialPort();
        time=new QTimer;
    }
    
    /**
     * @brief 数据接收处理步骤
     * 1.打开串口,开启子线程
     * 2.串口收到数据后启动定时器,延时10ms,防止数据未发送完
     * 3.延时结束后读缓存区的数据
     * 4.将读取到的数据通过信号发送至主线程
     */
    
    void serialThread::run()
    {
        QObject::connect(Serial,&QSerialPort::readyRead,this,&serialThread::data_receive);  //收到数据触发槽函数启动定时器
        QObject::connect(time,SIGNAL(timeout()),this,SLOT(timeUpdate()));                   //延时结束开始读数据
    }
    
    void serialThread::data_receive()
    {
        time->start(10);
    }
    
    void serialThread::timeUpdate()
    {
        time->stop();
        buffer = Serial->readAll();              //读数据
        emit dataSend(buffer);                   //数据发送至主函数
    
    #if 0  
        //以下代码为测试其他功能用的
        for (int i=0;i<(buffer.size()-10);i++)
        {
    
            if(buffer.at(i)==0x55)
            {
                switch (buffer.at(i+1))
                {
                case 0x51:{}break;
                case 0x52:{}break;
                case 0x53:{}break;
                case 0x54:{}break;
                case 0x56:{}break;
                case 0x57:{}break;
                case 0x58:{}break;
                case 0x59:{}break;
                case 0x5A:{}break;
                default:{}break;
                }
            }
        }
    #endif
        buffer.clear();
    }
    
    void serialThread::InitPortName(const QString &portName)
    {
        Serial->setPortName(portName);                          //设置串口名的方法,下同
    }
    
    void serialThread::InitPortBaudRate(qint32 baudRate)
    {
         Serial->setBaudRate(baudRate);
    }
    
    void serialThread::InitPortDataBits(QSerialPort::DataBits dataBits)
    {
        Serial->setDataBits(dataBits);
    }
    
    void serialThread::InitPortStopBits(QSerialPort::StopBits stopbits)
    {
        Serial->setStopBits(stopbits);
    }
    
    void serialThread::InitPortParity(QSerialPort::Parity parity)
    {
        Serial->setParity(parity);
    }
    
    void serialThread::InitFlowControl(QSerialPort::FlowControl flowcontrol)
    {
        Serial->setFlowControl(flowcontrol);
    }
    
    bool serialThread::OpenPort()
    {
        if(Serial->open(QIODevice::ReadWrite))
            return true;
        else
            return false;
    }
    
    void serialThread::ClosePort()
    {
        Serial->close();
    }
    

    mainwindow.cpp

    #include "gpsmap.h"
    #include "ui_gpsmap.h"
    
    gpsMap::gpsMap(QWidget *parent) :
        QMainWindow(parent),
        ui(new Ui::gpsMap)
    {
        ui->setupUi(this);
        portThread = new serialThread();
    
        foreach(const QSerialPortInfo &info,QSerialPortInfo::availablePorts())     //搜索串口并在界面显示
        {
            ui->portBox->addItem(info.portName());
        }
        ui->baudBox->setCurrentIndex(0);                                           //设置波特率初始值
    
        QObject::connect(portThread, SIGNAL(dataSend(QByteArray)),this,SLOT(dataShow(QByteArray)));  //连接子线程发送数据与主线程接收数据的信号和槽
    }
    
    gpsMap::~gpsMap()
    {
        delete ui;
    }
    
    void gpsMap::dataShow(QByteArray buf)    //数据显示
    {
        QByteArray buffer = buf;
        QString recv=QString(buffer);
    
        ui->textEdit->insertPlainText(recv);
    }
    
    void gpsMap::on_searchBtn_clicked()    //搜索按钮的槽方法
    {
        ui->portBox->clear();
        foreach(const QSerialPortInfo &info,QSerialPortInfo::availablePorts())
        {
            ui->portBox->addItem(info.portName());
        }
    }
    
    void gpsMap::on_openBtn_clicked()
    {
        if(ui->openBtn->text()==QString("打开"))
        {
            //串口相关参数设置
            portThread->InitPortName(ui->portBox->currentText());
            portThread->InitPortBaudRate(ui->baudBox->currentText().toInt());
            portThread->InitPortDataBits(QSerialPort::Data8);
            portThread->InitPortStopBits(QSerialPort::OneStop);
            portThread->InitPortParity(QSerialPort::NoParity);
            portThread->InitFlowControl(QSerialPort::NoFlowControl);
            
            //若打开失败则提示
            if(!portThread->OpenPort())
            {
                QMessageBox::about(NULL,"提示","无法打开串口!");
                return;
            }
            
            //串口打开后使相关控件失能
            ui->portBox->setEnabled(false);
            ui->baudBox->setEnabled(false);
            ui->searchBtn->setEnabled(false);
            ui->openBtn->setText(QString("关闭"));
            
            //启动子线程
            portThread->start();
        }
        else {
            portThread->quit();
            portThread->ClosePort();
            ui->portBox->setEnabled(true);
            ui->baudBox->setEnabled(true);
            ui->searchBtn->setEnabled(true);
    
            ui->openBtn->setText(QString("打开"));
        }
    }
    
    

    总结

    软件虽实现了串口接收功能,但是当数据量很大时会发生丢帧现象,搜索相关资料后采用定时器延时方法,虽有所改善但仍未解决,接收缓存区只能够存放99字节数据,不知道是自己检测的原因还是其他原因,留待以后解决。

    由于这是第一次独立编写Qt小软件,走了很多弯路,在此感谢CSDN博主分享的方法及代码,如有错误,望指正。

    附:十六进制显示程序(添加到显示程序)

    	QString recv=QString(buffer);
        if(ui->hexBox->isChecked())//添加QCheckBox控件,判断其状态
        {
            QString strDis;
            QString hexRecv=recv.toUtf8().toHex().toUpper();
            for (int i =0;i<hexRecv.length();i+=2) {
                QString st=hexRecv.mid(i,2);
                strDis+=st;
                strDis+=" ";
            }
            ui->receiveEdit->append(strDis);
        }
    
    展开全文
  • 基于MFC开发的串口通信,技术用到API,和多线程开发串口通信
  • 基于多线程串口通信的生产数据实时采集系统
  • GPS导航系统需要实时获取来自接收机串口的定位数据,在对串口实时监控的同时还可以在前台进行一些其他的操作,利用基于多线程串口通信编程思想方法可以很好地解决这一问题。介绍多线程的基本概念和串口通信编程...
  • 摘 要:GPS导航系统需要实时获取来自接收机串口的定位数据,在对串口实时监控的同时还可以在前台进行一些其他的操作,利用基于多线程串口通信编程思想方法可以很好地解决这一问题。介绍多线程的基本概念和串口通信...
  • {"moduleinfo":{"card_count":[{"count_phone":1,"count":1}],"search_count":[{"count_phone":6,"count":6}]},"card":[{"des":"短信服务为用户提供一种通信服务能力,支持快速发送短信验证码、短信通知等,服务范围...
  • 我做的一个项目,把串口读写采用了一个独立的线程,进行数据的读写,在调试时,发现只要启动另外一个计算量比较大的线程串口通信就出错了,而只要不启动这个线程,可以启动其他线程,串口工作没有任何问题,求原因...
  • Qt多线程串口通信源码

    热门讨论 2010-08-14 00:18:47
    采用Qt4.6.2编写的linux系统下的开源串口通信程序。使用开源的posix_qextserialport类
  • QSerialPort类的串口通讯例程,与单片机通信少不了使用串口进行通信,Qt 也提供了串口通信的类,使用的时候在 pro 添加这句导入模块 QT += serialport
  • 【转】Qt 多线程串口通信问题?

    千次阅读 2019-03-19 10:41:19
    现在我的项目关于线程串口通信这里出了很问题。思路现在就是我首先自己封装了QSerialport的open、read、write函数,然后在通信层中开了 一个线程,在这个线程的构造函数中new了我封的serialport,并且就在这里...
  • win32 官方例程 串行多线程通信程序 MTTTY (Multi-Threaded TTY) 源代码+说明文档
  • Linux 多线程串口通信

    2013-08-09 11:00:00
    大概流程就是打开一个串口、然后进行串口设置。开启二个线程,一个线程写数据,另一个线程读数据。 代码如下: #include <stdio.h> #include <stdlib.h> #include <string.h> #include <...
  • 之前在工作中所涉及的编程几乎都是数值计算(以及计算结果的画图),并且都是在Linux环境下干活的,早已习惯了在Terminal中敲东西,从未涉及过桌面应用的开发,更别提什么RS232串口了(直到不久前才知道这么个玩意儿...
  • QThread这个基于类的子线程,比起传统的基于函数的子线程好处在于,你可以在线程中放任意个对象运行。只要你保证所有对象都是通过信号槽运作,而不是像写c的子线程那样用while(true)运作,那么一个QThread就可以...
  • 先用serialport类串口通信。使用了oncomm方式响应接收数据。但在开启多线程后。在开启的线程中使用该串口发送三次数据,然后另外一台机器发送响应数据,此时主程序接收不到数据。测试后发现如果另一位一台机器一直...
  • 有六个串口设备,并有六个测试通道 2.六个通道可对六个设备选择,可能一个通道一台,也可能一个通道六台 3.这六个通关对仪器有不同的控制要求,并要读回数据作处理,要可以同时运行六个通道(六通道动作不同) 开发...
  • 我的策略:采用一个接收线程和一个发送线程,当接收线程接收到数据后发消息给主线程,主线程在消息响应函数中将接收线程的数据流进行了解析,每次会解析出好条命令,然后根据每一天命令我开始组织要回复的数据,...
  • 基于VC的多线程串口通信程序设计

    千次阅读 2010-07-29 11:01:00
    目前对于上位机串口通信编程采用串口控件mscomm,或者直接调用api函数的形式。其中mscomm控件使用非常简单,但由于其对串口的封装,在一些有特殊要求的情况下使用起来不够灵活。而api函数都是以独立的全局函数的...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 493
精华内容 197
关键字:

多线程串口通信