QT通信-串口通信
- 前言
- 一、串口搜索
- 二、打开串口
- 二、发送或接收数据
- 二、程序如下:
- 总结
前言
QT 串口通信基于QT的QSerialPort类,先在项目文件pro中添加QT += serialport。
我使用的是ubuntu16.04,window下未测试是否能用。
QT的QSerialPort类说明见官网
一、串口搜索
使用QT串口通信类中QSerialPortInfo类的availablePorts()来获取计算机存在的串口。
初始化串口对象
m_serialPort = new QSerialPort();//实例化串口类一个对象
foreach是for的简化式,主要用于循环次数未知,这里获取的串口数目未知,将串口名称存放在QStringList中。
//选择串口
void CommuncationCenter::serial_select()
{
foreach(const QSerialPortInfo &info,QSerialPortInfo::availablePorts())
{
m_serialPortName << info.portName();
QString c= "串口:";
emit(error_log(c+info.portName()));
}
if(m_serialPort->isOpen())//如果串口已经打开了 先给他关闭了
{
m_serialPort->clear();
m_serialPort->close();
}
}
二、打开串口
选择QStringList中的串口名称,通过setPortName(QString)来设置打开那个串口,使用open(QIODevice::ReadWrite)用ReadWrite 的模式尝试打开串口,打开成功后设置串口通信的波特率,校验方式等配置。(打开方式有多种,只读(r/o)、只写(w/o)或读写(r/w)模式)
m_serialPort->setPortName(m_serialPortName[0]);
if(!m_serialPort->open(QIODevice::ReadWrite))//用ReadWrite 的模式尝试打开串口
{
c = "打开失败!:";
emit(error_log(c+m_serialPortName[i]));
return false;
}
else
{
m_serialPort->setBaudRate(my_baud,QSerialPort::AllDirections);//设置波特率和读写方向
m_serialPort->setDataBits(QSerialPort::Data8); //数据位为8位
m_serialPort->setFlowControl(QSerialPort::NoFlowControl);//无流控制
m_serialPort->setParity(QSerialPort::NoParity); //无校验位
m_serialPort->setStopBits(QSerialPort::OneStop); //一位停止位
//连接信号槽 当下位机发送数据QSerialPortInfo 会发送个 readyRead 信号,我们定义个槽void receiveInfo()解析数据
connect(m_serialPort,SIGNAL(readyRead()),this,SLOT(receiveInfo()));
connect(timer, SIGNAL(timeout()), this, SLOT(timeUpdate()));
c = "打开成功!:";
emit(error_log(c+m_serialPortName[i]));
注意:串口始终以独占访问方式打开(即没有其他进程或线程可以访问已打开的串口)。
二、发送或接收数据
使用read () 和write () ,也可以调用readLine () 和readAll () 。如果不是一次读取所有数据,剩余的数据将在以后可用,因为新的传入数据将附加到 QSerialPort 的内部读取缓冲区。也可以使用setReadBufferSize ()限制读取缓冲区的大小。
从串口读取数据:QByteArray info = m_serialPort->readAll();
发送数据给串口:m_serialPort->write(info,len);
二、程序如下:
.h:
#ifndef COMMUNCATIONCENTER_H
#define COMMUNCATIONCENTER_H
#include<QtCore>
#include <QObject>
#include <QtSerialPort/QSerialPort>
#include <QtSerialPort/QSerialPortInfo>
#include <>
class CommuncationCenter : public QThread
{
Q_OBJECT //使用信号与槽函数
public:
explicit CommuncationCenter(QObject *parent = nullptr);
QStringList m_serialPortName;
void run();
void serial_select();//选择串口
bool serial_port(QString my_com, qint32 my_baud);//打开串口
void sendInfo(const QString &info);//发送数据
void sendInfo(char* info,int len);//发送数据
void serial_close();//关闭串口
QString Crc16(const QString &data_str);//CRC校验
private:
QSerialPort *m_serialPort;
QTimer *timer;
QString read_data;
char convertCharToHex(char ch);
void convertStringToHex(const QString &str, QByteArray &byteData);
//声明信号
signals:
void error_log(QString error_port);
void get_data(QString read_data);
void error_data(QString e_data);
private slots:
void receiveInfo();//接受分段数据
void timeUpdate(); //接受全部数据
};
#endif // COMMUNCATIONCENTER_H
.cpp
#include ""
CommuncationCenter::CommuncationCenter(QObject *parent):QThread(parent)
{
m_serialPort = new QSerialPort();//实例化串口类一个对象
timer = new QTimer();
read_data = "";
}
//选择串口
void CommuncationCenter::serial_select()
{
foreach(const QSerialPortInfo &info,QSerialPortInfo::availablePorts())
{
m_serialPortName << info.portName();
QString c= "串口:";
emit(error_log(c+info.portName()));
}
if(m_serialPort->isOpen())//如果串口已经打开了 先给他关闭了
{
m_serialPort->clear();
m_serialPort->close();
}
}
//打开串口
bool CommuncationCenter::serial_port(QString my_com, qint32 my_baud)
{
if(m_serialPort->isOpen())//如果串口已经打开了 先给他关闭了
{
m_serialPort->clear();
m_serialPort->close();
}
if (m_serialPortName[0]!=0)
{
//设置串口名字 假设我们上面已经成功获取到了
for(int i = 0; i < m_serialPortName.size(); i++)
{
if (m_serialPortName[i] == my_com)
{
QString c= "找到匹配的串口::";
emit(error_log(c+m_serialPortName[i]));
m_serialPort->setPortName(m_serialPortName[0]);
if(!m_serialPort->open(QIODevice::ReadWrite))//用ReadWrite 的模式尝试打开串口
{
c = "打开失败!:";
emit(error_log(c+m_serialPortName[i]));
return false;
}
else
{
m_serialPort->setBaudRate(my_baud,QSerialPort::AllDirections);//设置波特率和读写方向
m_serialPort->setDataBits(QSerialPort::Data8); //数据位为8位
m_serialPort->setFlowControl(QSerialPort::NoFlowControl);//无流控制
m_serialPort->setParity(QSerialPort::NoParity); //无校验位
m_serialPort->setStopBits(QSerialPort::OneStop); //一位停止位
//连接信号槽 当下位机发送数据QSerialPortInfo 会发送个 readyRead 信号,我们定义个槽void receiveInfo()解析数据
connect(m_serialPort,SIGNAL(readyRead()),this,SLOT(receiveInfo()));
connect(timer, SIGNAL(timeout()), this, SLOT(timeUpdate()));
c = "打开成功!:";
emit(error_log(c+m_serialPortName[i]));
return true;
}
}
}
}
QString c= "没有找到匹配的串口!";
emit(error_log(c));
return false;
}
void CommuncationCenter::run()
{
}
//串口接收单片机的数据
void CommuncationCenter::receiveInfo()
{
timer->start(100);//启动定时器,接收100毫秒数据(根据情况设定)
QByteArray info = m_serialPort->readAll();
QByteArray heData = info.toHex();
QString hexData(heData);
read_data = read_data+hexData;
//这里面的协议 你们自己定义就行 单片机发什么 代表什么 我们这里简单模拟一下
}
void CommuncationCenter::timeUpdate()
{
timer->stop();
if(read_data.size()!=0)
{
if(read_data == "010000")
{
emit(error_log("do"+read_data));//do something
}
else if(read_data == "100001")
{
emit(error_log("you"+read_data));//do something
}
else{
emit(error_log(read_data));
emit(get_data(read_data));//发送数据给主线程
}
}
read_data = "";
}
//串口向单片机发送数据======================================================
//基本和单片机交互 数据 都是16进制的 我们这里自己写一个 Qstring 转为 16进制的函数
void CommuncationCenter::convertStringToHex(const QString &str, QByteArray &byteData)
{
int hexdata,lowhexdata;
int hexdatalen = 0;
int len = str.length();
byteData.resize(len/2);
char lstr,hstr;
for(int i=0; i<len; )
{
//char lstr,
hstr=str[i].toLatin1();
if(hstr == ' ')
{
i++;
continue;
}
i++;
if(i >= len)
break;
lstr = str[i].toLatin1();
hexdata = convertCharToHex(hstr);
lowhexdata = convertCharToHex(lstr);
if((hexdata == 16) || (lowhexdata == 16))
break;
else
hexdata = hexdata*16+lowhexdata;
i++;
byteData[hexdatalen] = (char)hexdata;
hexdatalen++;
}
byteData.resize(hexdatalen);
}
//另一个 函数 char 转为 16进制
char CommuncationCenter::convertCharToHex(char ch)
{
/*
0x30等于十进制的48,48也是0的ASCII值,,
1-9的ASCII值是49-57,,所以某一个值-0x30,,
就是将字符0-9转换为0-9
*/
if((ch >= '0') && (ch <= '9'))
return ch-0x30;
else if((ch >= 'A') && (ch <= 'F'))
return ch-'A'+10;
else if((ch >= 'a') && (ch <= 'f'))
return ch-'a'+10;
else return (-1);
}
//写两个函数 串口向单片机发送数据
void CommuncationCenter::sendInfo(char* info,int len){
for(int i=0; i<len; ++i)
{
printf("0x%x\n", info[i]);
}
m_serialPort->write(info,len);//这句是真正的给单片机发数据 用到的是QIODevice::write 具体可以看文档
}
void CommuncationCenter::sendInfo(const QString &info){
QByteArray sendBuf;
convertStringToHex(info, sendBuf); //把QString 转换 为 hex
m_serialPort->write(sendBuf);//这句是真正的给单片机发数据 用到的是QIODevice::write 具体可以看文档
emit(error_log("发送数据成功!"+info));
}
//========================================================================
//关闭串口
void CommuncationCenter::serial_close()
{
if (m_serialPort->isOpen())
{
m_serialPort->close();
}
delete m_serialPort;
}
//====================CRC校验====================================
QString CommuncationCenter::Crc16(const QString &data_str)
{
quint8 buf;
quint16 crc16 = 0xFFFF;
QByteArray data;
convertStringToHex(data_str, data); //把QString 转换 为 hex
for ( auto i = 0; i < data.size(); ++i )
{
buf = data.at( i ) ^ crc16;
crc16 >>= 8;
crc16 ^= crc16Table[ buf ];
}
QString str = QString::number(crc16,16).toUpper();
qDebug()<<"CRC校验"<<str;
return str;
}
总结
QT的串口通信程序可能有沉余代码,可以更加简洁,谢谢大家点赞及指导!
希望和大家一起学习,交流!