QT同时与多个串口通信

本人才接触QT两周,请问QT做上位机 同时与两个串口通信,将两个串口接收的数据拼接并显示在qTEXTEDIT中,现在只显示其中一个串口的数据是为什么?

参考GPT和自己的思路:

首先,需要检查你的代码是否正确打开了两个串口,并且每个串口都有正确的读取相关的信号槽连接。其次,需要检查每个串口的读取函数中是否正确将数据拼接到同一个 qTEXTEDIT 中显示。如果只显示其中一个串口的数据,可能有以下原因:

  1. 有一些代码逻辑错误,导致没有正确读取一个串口的数据。

  2. 可能存在数据冲突,即两个串口同时发送数据并且在同一时间接收数据,由于数据压栈先后的问题,导致其中一个串口的数据被覆盖或者丢失。

  3. qTEXTEDIT 处理数据的逻辑可能存在问题。

如果以上的建议还不能解决问题,可以尝试使用一些调试的工具,比如调试器或者输出调试信息,来帮助你找到问题的原因。希望能够帮到你!

上代码

该回答引用GPTᴼᴾᴱᴺᴬᴵ
这个问题可能是由于程序中只读取了其中一个串口的数据而没有读取另一个串口的数据,或者读取的顺序不正确,导致最终在QTextEdit中只显示了其中一个串口的数据。你可以检查一下程序中读取串口数据的代码,确保同时读取两个串口的数据,并按照需要的顺序进行拼接和显示。

以下是一个示例代码片段,用于演示如何同时读取两个串口的数据:

// 假设有两个串口对象分别为serialPort1和serialPort2
QByteArray data1, data2;
while (serialPort1.bytesAvailable() > 0) {
    data1 += serialPort1.readAll();
}
while (serialPort2.bytesAvailable() > 0) {
    data2 += serialPort2.readAll();
}
// 将两个串口的数据拼接并显示在QTextEdit中
QString text = QString("%1\n%2").arg(QString(data1), QString(data2));
ui->textEdit->setText(text);

该示例代码中,首先分别读取了两个串口的数据,并将其分别存储在data1和data2变量中。然后,使用QString的arg()函数将两个串口的数据拼接在一起,并将其设置为QTextEdit的文本。在实际使用中,你需要根据实际情况进行修改。

八成是你把两个串口的数据当做一个串口的数据了
每个串口的数据要单独显示,不要混在一起

  • 这篇文章讲的很详细,请看:QT实例 - 实现串口通信
  • 除此之外, 这篇博客: Qt 编写串口通信程序(接收、发送都可以)中的 以下主要是代码部分 部分也许能够解决你的问题, 你可以仔细阅读以下内容或跳转源博客中阅读:
  • #include <QMainWindow>+
    //通讯串口
    QString comPortName;                        //串口名
    HANDLE idComDev;                            //串口id
    int isComOpen;                              //是否已打开
    DWORD LWpt,LRpt;                            // 写出字节/读取字节的指针
    DWORD dwErrorFlag;                          //定义了错误类型的32位变量
    OVERLAPPED ovlp;                            //OVERLAPPED结构体指针
    COMSTAT state;                              //返回设备状态的控制块 0|1
    unsigned char bufComSend[512],bufComRead[1024];
    unsigned int frmEndTime;                    //等待时间
    QTimer *tmrWatchCom;                        //串口
    
    //构造函数
    isComOpen = 0;			//串口未打开
    
    
    //****************************函数***********************
    /**
     * @brief MainWindow::CheckExistPorts   查询串口的函数
     */
    void MainWindow::CheckExistPorts()
    {
        HKEY   hKey;
        LONG   ret;
        OSVERSIONINFO     osvi;
    //	BOOL   bOsVersionInfoEx;
        char   keyinfo[100],comm_name[200],ValueName[200];
        int   i;
        DWORD   sType,Reserved,cbData,cbValueName;
    
        ZeroMemory(&osvi,sizeof(OSVERSIONINFO));
      osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
        memset(keyinfo,0,100);
        strcpy(keyinfo,"HARDWARE\\DEVICEMAP\\SERIALCOMM");
        i=0;   sType=REG_SZ;  Reserved=0;
        GetVersionEx(&osvi);
    //	bOsVersionInfoEx = GetVersionEx(&osvi);
      ret = RegOpenKeyExA(HKEY_LOCAL_MACHINE,keyinfo,Reserved,KEY_READ,&hKey);
        if(ret == ERROR_SUCCESS){
        ui->comboBoxPorts->clear();
          if(osvi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS){
                for(i=1;i<=128;i++){
                    sprintf(comm_name,"COM%d",i);
                    ui->comboBoxPorts->addItem((QString)comm_name);
                }
        }
        else if(osvi.dwPlatformId == VER_PLATFORM_WIN32_NT){
                do{
                    cbData = 200;
                    cbValueName = 200;
                    memset(comm_name,0,200);
                    memset(ValueName,0,200);
                    ret = RegEnumValueA(hKey,i,ValueName,&cbValueName,NULL,&sType,(LPBYTE)comm_name,&cbData);
                    if(strlen(comm_name) > 0){
                        ui->comboBoxPorts->addItem((QString)comm_name);
                    }
                    i++;
                 }while(ret == ERROR_SUCCESS);
            }
        }
        RegCloseKey(hKey);
        i=ui->comboBoxPorts->count();
    }
    
    /**
     * @brief MainWindow::GetComPortsN  获取下拉列表的元素数量
     * @return
     */
    int MainWindow::GetComPortsN()
    {
        int n;
        n = ui->comboBoxPorts->count();
        return n;
    }
    
    /**
     * @brief MainWindow::GetComPortStr 根据下标获取下拉列表对应内容
     * @param idx
     * @return
     * 获取选择串口
     */
    QString MainWindow::GetComPortStr(int idx)
    {
        QString str1;
        str1 = ui->comboBoxPorts->itemText(idx);
        return str1;
    }
    
    /**
     * @brief MainWindow::initComPort
     * @param pnm
     * @return
     * 串口的初始化
     */
    int MainWindow::initComPort(QString pnm)
    {
        int err;
        DCB dcb;
        QString str1;
        COMMTIMEOUTS to;
        QByteArray ba;
        char *cpt;
    
        if(isComOpen){
            tmrWatchCom->stop();
            CloseHandle(idComDev);
        }
        str1 = comPortName;
        if(str1.length() > 4)
          str1 = "\\\\.\\"+str1;
        ba = str1.toLatin1();
        cpt = ba.data();
        idComDev = CreateFileA(cpt,GENERIC_READ|GENERIC_WRITE,0,NULL,
                 OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,NULL);
    //             FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,NULL);
        if(idComDev==INVALID_HANDLE_VALUE){
            QMessageBox::question(this,tr("警告!"),tr("打开通讯口失败!"),QMessageBox::Yes | QMessageBox::Cancel);
          return 1;
        }
        SetupComm(idComDev,4096,1024);
        SetCommMask(idComDev,0);
        GetCommTimeouts(idComDev, &to);
        to.ReadIntervalTimeout = 4294967295;
        to.ReadTotalTimeoutMultiplier = 0;
        to.ReadTotalTimeoutConstant = 0;
        to.WriteTotalTimeoutMultiplier = 100;
        to.WriteTotalTimeoutConstant = 1000;
        SetCommTimeouts(idComDev, &to);
        err=GetCommState(idComDev,&dcb);
        if(!err){
            QMessageBox::question(this,tr("警告!"),tr("打开通讯口失败!"),QMessageBox::Yes | QMessageBox::Cancel);
          return 1;
        }
        dcb.BaudRate = 38400;                       //波特率
        dcb.ByteSize = 8;                           //数据位数
        dcb.Parity = NOPARITY;                      //奇偶数
        dcb.StopBits = ONESTOPBIT;                  //停止位
        dcb.fBinary = true;
        dcb.fParity = false;
        dcb.fOutxCtsFlow = false;
        dcb.fOutxDsrFlow = false;
        dcb.fDtrControl = DTR_CONTROL_DISABLE;
        dcb.fDsrSensitivity = false;
        dcb.fTXContinueOnXoff = true;
        dcb.fOutX = false;
        dcb.fInX = false;
        dcb.fErrorChar = false;
        dcb.fNull = false;
        dcb.fRtsControl = RTS_CONTROL_DISABLE;
        dcb.fAbortOnError = false;
      /*  dcb.XonLim = ;
        dcb.XoffLim = ;
        dcb.XonChar = ;
        dcb.XoffChar = ;
        dcb.ErrorChar = ;
        dcb.EofChar = ;
        dcb.EvtChar = ;*/
        err=SetCommState(idComDev,&dcb);
        if(!err){
            QMessageBox::question(this,tr("警告!"),tr("打开通讯口失败!"),QMessageBox::Yes | QMessageBox::Cancel);
          return 1;
        }
        isComOpen = 1;
        tmrWatchCom->start(10);
        return 0;
    }
    
    /**
     * @brief MainWindow::comReadBytes
     * @param rbuf
     * @param len
     * @return
     * 读取数据
     */
    int MainWindow::comReadBytes(unsigned char *rbuf, int len)
    {
        int err;
    
        if(!isComOpen){
            err = initComPort(comPortName);
            if(err){
              return 0;
            }
        }
        //从文件指针指向的位置开始将数据读出到一个文件中, 且支持同步和异步操作,
        //1.文件的句柄 2.用于保存读入数据的一个缓冲区 3.要读入的字节数 4.指向实际读取字节数的指针
        //5.如文件打开时指定了FILE_FLAG_OVERLAPPED,那么必须,用这个参数引用一个特殊的结构。
        //5.该结构定义了一次异步读取操作。否则,应将这个参数设为NULL
        ovlp.Internal = 0;
        ovlp.InternalHigh = 0;
        ovlp.Offset = 0;
        ovlp.OffsetHigh = 0;
        ovlp.hEvent = CreateEvent(NULL, true, false, NULL);
        ReadFile(idComDev,rbuf,len,&LRpt,&ovlp);
        return (int)LRpt;
    }
    
    /**
     * @brief MainWindow::comSendBytes
     * @param sbuf
     * @param len
     * @return
     * 发送数据
     */
    int MainWindow::comSendBytes(unsigned char *sbuf, int len)
    {
        if(!isComOpen){
            return 0;
        }
        //可以将数据写入一个文件或者I/O设备
        //1.文件句柄 2.数据缓存区指针 3.你要写的字节数 4.用于保存实际写入字节数的存储区域的指针 5.lpOverlapped  OVERLAPPED结构体指针
        ovlp.Internal = 0;
        ovlp.InternalHigh = 0;
        ovlp.Offset = 0;
        ovlp.OffsetHigh = 0;
        ovlp.hEvent = CreateEvent(NULL, true, false, NULL);
        //void* 在转换为其他数据类型时,赋值给void* 的类型 和目标类型必须保持一致
        WriteFile(idComDev,(void *)sbuf,(DWORD)len,&LWpt,&ovlp);
        return (int)LWpt;
    }
    
    /**
     * @brief MainWindow::comClose
     * 关闭串口
     */
    void MainWindow::comClose()
    {
        if(isComOpen)
            CloseHandle(idComDev);
        isComOpen = 0;
    }
    
    /**
     * @brief MainWindow::comWatchPort
     * @return
     * 串口的检查 清除错误信息,输入缓冲区中的字节数
     */
    int MainWindow::comWatchPort()
    {
        int i;
         //ClearCommError函数的
        //第一个参数hFile是由CreateFile函数返回指向已打开串行口的句柄。
        //第二个参数指向定义了错误类型的32位变量。
        //第三个参数指向一个返回设备状态的控制块COMSTAT。如果函数调用成功,则返回值为非0;若函数调用失败,则返回值为0
        if(!isComOpen)
            return 0;
        ClearCommError(idComDev,&dwErrorFlag,&state);
        //cbInQue 在输入缓冲区尚未被ReadFile函数读取的数据字节数。这个参数经常被用来进行状态检查。
        i = state.cbInQue;
        return i;
    }
    
    /**
     * @brief MainWindow::WaitComData
     * @param nms       2500    超时跳出时间
     * @return
     * 等待数据
     */
    int MainWindow::WaitComData(unsigned int nms)
    {
        int i,k;
        unsigned int strTick,preTick,curTick;               //1 进入程序的时间   2 上一次接收完数据的时间     3 当前的时间
                                                            //  satrt........一帧数据previous ................current现在的时间
    
        k = 0;
        i = 0;
        strTick = GetTickCount();                           //该函数从0开始计时,返回自设备启动后的毫秒数(不含系统暂停时间)。
        preTick = strTick ;
        curTick = strTick;
        do{
            curTick = GetTickCount();
            ClearCommError(idComDev,&dwErrorFlag,&state);
            k = state.cbInQue;                              //返回缓冲区的数据字节数
            if(k > 0)
            {
                if(k != i)                                  //接收到一帧的数据 != i  这里重置
                {
                    i = k;
                    preTick = curTick ;
                }
                else                                        //如果接受到了一帧数据
                {
                    //每帧数据之间
                    if((curTick - preTick) > frmEndTime)    //数据接收结束 20ms 每一个字节的接收时间远远小于20ms  如果大于20ms 证明 一帧数据接收完毕
                    {
                        return i;                           //返回缓冲区的数据字节数
                    }
                }
            }
            if((curTick - strTick)>nms)                     //如果程序开始到
            {
                return 0;                                   //超时
            }
        }while(1);
    }
    

    注释应该已经很明确了,因为是内部使用,所以波特率,数据位等,在这边已经定义,没有在界面定义了。如果出现ui界面的 ,也就是一个comboCox下拉框,显示已经查到的串口名。

    如果需要发送,接收,需要可视化,可以用文本接收发送,调用以上的函数就可以了。

    如果有什么疑问,可以留言讨论,谢谢观看!!!