QT使用串口与RS485设备通讯

  • 背景
  • 开始
  • 小结

背景

近期才开始接触QT编程,以前都是使用MFC编写上位机软件,经常需要通过串口与RS485设备通讯。之所以开始学习QT,是因为看到网上有个人说自从接触了QT之后,就觉得MFC可以放弃,于是我也有了学习QT的冲动。

买了一本QT入门的书籍《Qt Creater快速入门》,简单学习了按钮等基本控件的使用、信号与槽、以及多线程的知识后,开始编写一个简单的串口程序与RS485设备通讯。

这本书上没有关于串口知识,不过好在现在的网络这么发达,随便一搜就有一大堆。

开始

(1)创建一个Qt widgets application应用,包含以下头文件:

#include <QtSerialPort/QtSerialPort>
#include <QList>
#include <QtSerialPort/QSerialPortInfo>

(2)界面上放一个下拉列表comboBox,使用下面几句代码可以获取电脑上可用的串口,并显示在comboBox控件中,简简单单几句话,就列出了所有串口,感觉好方便啊,瞬间觉得好喜欢。

QList<QSerialPortInfo> list  = QSerialPortInfo::availablePorts();
foreach (const QSerialPortInfo & info, list) {ui->comboBox->addItem(info.portName());
}

(3)界面上放一个下拉列表控件comBaud,添加几种波特率

ui->comBaud->addItem(tr("9600"));
ui->comBaud->addItem(tr("4800"));
ui->comBaud->addItem(tr("19200"));

(4)创建一个串口对象:

QSerialPort port;

(5)界面上放置一个按钮openCom,用于打开串口,转到槽添加如下代码,

void Widget::on_openCom_clicked()
{QString name = ui->openCom->text();//获取按钮上的文字if(name == tr("打开串口")){port.setPortName(ui->comboBox->currentText());//选择串口号      if( !port.open(QIODevice::ReadWrite))//打开串口{QMessageBox::information(this, tr("提示"), tr("打开串口失败,请查看串口是否被占用"), QMessageBox::Ok);return;}ui->openCom->setText(tr("关闭串口"));//改变按钮上的文字    port.setBaudRate(ui->comBaud->currentText().toInt());port.setDataBits(QSerialPort::Data8);//8位数据位port.setParity(QSerialPort::NoParity);//无检验port.setStopBits(QSerialPort::OneStop);//1位停止位port.setFlowControl(QSerialPort::NoFlowControl);//无硬件控制      ui->comboBox->setDisabled(true);//串口号下拉列表变灰ui->comBaud->setDisabled(true);//波特率下接列表变灰}else{port.close();//关闭串口ui->comboBox->setEnabled(true);//串口号下拉列表变亮ui->comBaud->setEnabled(true);//串口号下拉列表变亮ui->openCom->setText(tr("打开串口"));}
}

(6)添加一个按钮detect,用于点击之后开始与RS485设备通信,添加一个列表控件listWidget,用于显示通讯结果,转到槽添加如下代码

void Widget::on_detect_clicked()
{if(!port.isOpen()){QMessageBox::critical(this, tr("错误"), tr("请先打开串口"), QMessageBox::Ok);return;}ui->listWidget->clear();//清空列表内容   MyThread *th = new MyThread(this, &port);//启动线程,在线程内通讯    th->start();
}

(7)添加一个C++类,类名MyThread,继承自QThread,添加一个信号sendResult,用于将通讯结果反馈给主程(暂时这么干吧,毕竟我才刚入门)。

mythread.h文件:
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QThread>
#include <widget.h>
#include <QtSerialPort/QtSerialPort>class MyThread : public QThread
{Q_OBJECT
public:    MyThread(Widget * parent, QSerialPort *port);
protected:void run();
protected:   QSerialPort * port;Widget * parent;
signals:void sendResult(int code, QString str);
};
#endif // MYTHREAD_H
mythread.cpp文件:
#include "mythread.h"MyThread::MyThread(Widget * parent, QSerialPort *port)
{this->parent = parent;//传入父对象,以便给它发信号this->port = port;//传入串口对象,以便在线程中使用
}
//CRC校验码计算
unsigned short CRC16(unsigned char *pBuf,unsigned short len)
{unsigned short uCRC=0xFFFF;int i,j;for(i=0;i<len;i++){uCRC^=(*(pBuf+i));for(j=0;j<8;j++){if((uCRC&0x0001)==0x0001){uCRC=(uCRC>>1);uCRC^=0xA001;}elseuCRC=(uCRC>>1);}}return uCRC;
}void MyThread::run()
{bool bRet = false;unsigned char buf[6];//关联信号与槽,用于给父对象信号,告知通讯结果QObject::connect(this, SIGNAL(sendResult(int, QString)), parent, SLOT(result(int, QString)));//与43个RS485设备通讯,检查设备是否有回应                for(unsigned char addr = 1; addr <= 43 ; ++addr){buf[0] = addr;buf[1] = 0x03;buf[2] = 0xA0;buf[3] = 0x06;unsigned short crc = CRC16(buf, 4);buf[4] = static_cast<unsigned char>(crc);buf[5] = static_cast<unsigned char>(crc >> 8);port->write(reinterpret_cast<const char *>(buf), 6);//发送数据port->flush();//让数据立刻从串口发送出去,不要在缓冲区里墨迹        msleep(200);//等待200ms//检查串口接收到了多少数据int num = static_cast<int>(port->bytesAvailable());if(num != 6){QString str = QString().sprintf("addr=%02d,设备没有响应, 返回数据量为%d",addr, static_cast<int>(num));emit sendResult(0, str);}else{// 接收到了6个字节,表时RS485设备有响应QByteArray array = port->readAll();QString str = QString().sprintf("addr=%02d, 发现设备,返回数据量为%d",addr, static_cast<int>(num));emit sendResult(0, str);}msleep(800);      }
}

(8)测试结果如下:43个设备除41号设备外(该设备RS485通讯线没有连接)都有回应,完成了预期目标。

小结

(1)往串口写数据的时候,write()后面要加一句flush()才能把数据立刻通过串口发送出去。
(2)刚开始用的是异步读取数据方式,感觉怪怪的,因为发送数据和接收数据不在同一个函数里,而且readyRead信号槽中接收到的数据经常不完整,往往要多次拼接数据,这对于RS485通讯来说很不好操作,后来改用同步操作,使用waitForReadyRead()来等待响应数据,结果在waitForReadyRead()函数这里折腾了小半天,根据手册,waitForReadyRead()返回值为true时表明已经接收到数据了,返回false表示接收超时,结果明明有接收到数据,却还是会超时返回false,造成先是readyRead信号槽提示接收到数据,然后是waitForReadyRead()返回false提示接收超时。后来进到QIODevice::waitForReadyRead()中查看,里面说到使用connect关联了readyRead信号,那么waitForReadyRead()将不会接收到readyReady信号返回true,这下明白了,把connect信号的那句注释掉,waitForReadyRead()果然返回了true,于是就有了上面的程序,先发送数据,等等待一个固定时间再读数据,不要用waitForReadyRead()来等待响应数据,因为它接收到的数据不完整。

QT使用串口与RS485设备通讯相关推荐

  1. java 串口 dtr rts_串口(RS232 RS485等)通讯中RTS/CTS,DTR/DSR的含义详解

    串口(RS232 RS485等)通讯中RTS/CTS,DTR/DSR的含义详解 先贴出缩写的含义: DTR – Data Terminal Ready DSR – Data Set Ready RTS ...

  2. 【整理】串口(RS232/RS485等)通讯中RTS/CTS,DTR/DSR的含义详解

    [整理]串口(RS232/RS485等)通讯中RTS/CTS,DTR/DSR的含义详解 RS232 crifan 7年前 (2013-10-17) 14942浏览 0评论 [背景] 之前就折腾过很多关 ...

  3. java 串口 dtr rts_【整理】串口(RS232/RS485等)通讯中RTS/CTS,DTR/DSR的含义详解

    [背景] 之前就折腾过很多关于RTS/CTS,DTR/DSR的内容: 但是至今还是觉得,没有彻底明白,还有有一点点迷惑. 现在重新去整理相关知识. [折腾过程] 1.参考: 先贴出缩写的含义:DTR ...

  4. Modbus RS485 设备的串口调试

    Modbus RS485 设备的串口调试 准备 Modbus功能码 二.使用步骤 1.接线 2.串口调试 温湿度的计算 准备 硬件设备: 电脑 RS485 温湿度传感器 RS485 土壤温湿度传感器 ...

  5. 串口RS232 RS485最本质的区别!-!I2C通讯协议 最简单的总线通讯!-深入理解SPi通讯协议!

    5分钟看懂!串口RS232 RS485最本质的区别!-4分钟看懂!I2C通讯协议 最简单的总线通讯!-深入理解SPi通讯协议,5分钟看懂! 一.5分钟看懂!串口RS232 RS485最本质的区别! 二 ...

  6. 用QT做串口通讯,读取身份证信息

    用QT做串口通讯,自己用的是一所的身份证模块 首先说明QT里面用作串口通讯的是 QSerialPort ,QSerialPortInfo 按照流程 1:检索串口,找到电脑上可以用的串口号 2:打开串口 ...

  7. 安卓设备通过USB串口与STM32单片机通讯之二

    安卓设备通过USB串口与STM32单片机通讯之二 本博文系JGB联合商务组的原创作品,引用请标明出处. 本博文接续上一篇的末尾章节. (二) APP的JAVA代码部分(使用Android Studio ...

  8. rs485与modbus流程图_基于Modbus协议的RS485总线通讯机设计原理及应用

    摘要:该文详细论述了基于Modbus协议的RS485总线通讯机设计原理及应用.物理结构上包括上位机.主站.和多个从站节点.在上位机与各从站节点间增加主站,承担上位机的轮询工作,使上位机从繁忙的通信工作 ...

  9. Qt实现串口通信(C++实现串口通信小助手)---串口收发及串口数据解码、串口通信模拟器。

    Qt实现串口通信示例 前言:以下串口通信示例,参考了现有网上前辈们的资源,最后结合部分个人的思想,所以下述博客会将实现的原理及代码的案例进行公开. 这里我们先上效果图: 一.串口通信简介 串口通信是上 ...

最新文章

  1. java字符串去掉空格
  2. linux 设备管理工具 udev 规则编写
  3. python工作技巧_Python常用小技巧汇总
  4. python3读取excel数据-python3读取Excel表格数据的代码分享
  5. 【STM32】外部中断实验代码详解
  6. 4.2 Tensorflow笔记:池化函数
  7. 史上最经典CAP讲解
  8. 武汉大学2020年大学生程序设计大赛决赛(重现赛)J (oeis or 卡特兰数+可重集排列数)
  9. 思科服务器dns配置文件,cisco设置dns方法
  10. 前端工程师推荐浏览器
  11. 【渝粤教育】电大中专药理学基础作业 题库
  12. element-ui的confirm里面可一写html
  13. html中的超链接打不开,excel里超链接打不开怎么办
  14. matlab画心形线
  15. 描绘新十年智慧生活蓝图,AWE2021圆满闭幕
  16. 【定量分析、量化金融与统计学】R语言方差分析的outliers陷阱
  17. PostgreSQL的json和jsonb比较
  18. 重学Elasticsearch第1章 : Elasticsearch, Kibana概念、Elasticsearch相关术语
  19. good 第一篇、教你学会看电路图轻松修手机
  20. 怎么学习oracle浅析

热门文章

  1. php中字符串去除空格,php怎么去除字符串中的空格
  2. 基于STM32的新西达电调和无刷电机
  3. Python中打开文件的方式(With open)
  4. 使用yum高速部署Oracle安装环境(11g)
  5. 使用minitab18 做单因子方差分析达到算法指标之间多重比较的目的(One way ANOVA, Multiple comparison)
  6. 二进制空间权重矩阵_空间权重矩阵(SWM)
  7. 模拟工作室机架音频插件
  8. 嵌入式linux解决方法 | bin文件烧写不到SD卡中
  9. C#混音同时录制采集声卡和麦克风话筒
  10. C++ | ListNode:链表节点