QT 在Linux下面编写一个串口同步通信的demo(QserialPort)
项目场景:
QT 编写串口同步通信(使用QserialPort)。
主要代码:
由于我这边的设备使用的485通信,有多个从站,使用异步通讯的时候处理数据不是很方便。
#include "testcompro.h"
#include "ui_testcompro.h"
TestComPro::TestComPro(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::TestComPro)
{
ui->setupUi(this);
m_serialPort = new QSerialPort();
//初始化UI
initUI();
//获取设备上的串口
m_portNameList = getPortNameList();
//把串口添加到combox
ui->cb_comlist->addItems(m_portNameList);
connect(ui->btn_Open,&QPushButton::clicked,this,&TestComPro::BtnOpenPort);
connect(ui->m_BtnSendData,&QPushButton::clicked,this,&TestComPro::BtnWriteCurVolData);
connect(ui->m_BtnReadCurVal,&QPushButton::clicked,this,&TestComPro::BtnReadCurValData);
//std::thread tprocess(&TestComPro::Display,this);
//tprocess.detach();
}
TestComPro::~TestComPro()
{
if (m_serialPort->isOpen())
{
m_serialPort->close();
}
delete m_serialPort;
delete ui;
}
void TestComPro::initUI()
{
ui->m_BtnSendData->setText("Send");
ui->Ch1_data->setText("ch1:0.00v 0.00A");
ui->Ch2_data->setText("ch2:0.00v 0.00A");
this->setWindowTitle("Test QSerialPortDemo");
QStringList m_ChName;
m_ChName.append("CH1");
m_ChName.append("CH2");
m_ChName.append("CH3");
m_ChName.append("CH4");
ui->Cbb_CH->addItems(m_ChName);
}
//获取设备上所有的串口
QStringList TestComPro::getPortNameList()
{
QStringList m_serialPortName;
foreach(const QSerialPortInfo &info,QSerialPortInfo::availablePorts())
{
m_serialPortName << info.portName();
qDebug()<<"serialPortName:"<<info.portName();
}
return m_serialPortName;
}
//打开设备上指定的串口
void TestComPro::openPort(QString port)
{
if(m_serialPort->isOpen())//如果串口已经打开了 先给他关闭了
{
m_serialPort->clear();
m_serialPort->close();
}
m_serialPort = new QSerialPort();
m_serialPort->setPortName(port);//当前选择的串口名字
if(!m_serialPort->open(QIODevice::ReadWrite))//用ReadWrite 的模式尝试打开串口
{
qDebug()<<"打开失败!";
return;
}
qDebug()<<"串口打开成功!";
m_serialPort->setBaudRate(QSerialPort::Baud9600,QSerialPort::AllDirections);//设置波特率和读写方向
m_serialPort->setDataBits(QSerialPort::Data8); //数据位为8位
m_serialPort->setFlowControl(QSerialPort::NoFlowControl);//无流控制
m_serialPort->setParity(QSerialPort::NoParity); //无校验位
m_serialPort->setStopBits(QSerialPort::OneStop); //一位停止位
//m_serialPort->setRequestToSend(true); //设置 RTS 为高电平
//m_serialPort->setDataTerminalReady(true); //设置 DTR 为高电平
//连接信号槽 当下位机发送数据QSerialPortInfo 会发送个 readyRead 信号,我们定义个槽void receiveInfo()解析数据
//connect(m_serialPort,SIGNAL(readyRead()),this,SLOT(receiveInfo()));
}
//打开串口的slots函数
void TestComPro::BtnOpenPort()
{
ui->Ch1_data->setText("ch1:0.00v 0.00A");
ui->Ch2_data->setText("ch2:0.00v 0.00A");
openPort(ui->cb_comlist->currentText());
}
//写入电源电压电流的槽函数
void TestComPro::BtnWriteCurVolData()
{
if(isDigitStr( ui->Le_voltage->text())&&isDigitStr( ui->Le_Current->text()))
{
uint8_t ch=uint8_t(ui->Cbb_CH->currentIndex()+1);
WriteCurVolData(ch,int16_t(ui->Le_voltage->text().toInt()),int16_t(ui->Le_Current->text().toInt()));
}
else {
qDebug()<<"non available input";
}
}
//读取电流电压的slots函数
void TestComPro::BtnReadCurValData()
{
double *p=new double[2];
ReadCurValData(1,p);
ui->Ch1_data->setText(QString().sprintf("CH1 Vol:%fV Cur:%fA",p[0],p[1]));
}
//电源电压电流写入
bool TestComPro::WriteCurVolData(uint8_t ch,int16_t vol,int16_t cur)
{
//double value[2] = {0,0};
//newcmdV ="01 10 00 00 00 02 04 13 88 03 e8 77 BF";
QByteArray ba;
ba.resize(12);
ba[0] = ch;//ch
ba[1] = 0x10;//cmd
ba[2] = 0x00;
ba[3] = 0x00;
ba[4] = 0x00;
ba[5] = 0x02;
ba[6] = 0x04;
ba[7] = uint8_t(vol>>8);//vol_h
ba[8] = uint8_t(vol);//vol_l
ba[9] = uint8_t(cur>>8);//cur_h
ba[10] = uint8_t(cur);//cur_l
ba[11] = 0x00;//h
ba[12] = 0x00;//l
uint16_t wcrc = ModbusCRC16(ba,ba.size()-2);
ba[11]=uint8_t(wcrc);ba[12]=uint8_t(wcrc>>8);
qDebug()<<"Read info:"<<SendReadCmd(ba);
return false;
}
//电源电压电流读取
bool TestComPro::ReadCurValData(uint8_t ch,double* const val)
{
double value[2] = {0,0};
QByteArray ba;
ba.resize(8);
ba[0] = ch;//ch
ba[1] = 0x03;//cmd
ba[2] = 0x00;
ba[3] = 0x00;
ba[4] = 0x00;
ba[5] = 0x02;
ba[6] = 0x00;
ba[7] = 0x00;
uint16_t wcrc = ModbusCRC16(ba,ba.size()-2);
ba[6]=uint8_t(wcrc);ba[7]=uint8_t(wcrc>>8);
QByteArray rcev=SendReadCmd(ba);
value[0] = static_cast<double>((rcev[3] * 256 + rcev[4])/1000);
value[1] = static_cast<double>((rcev[5] * 256 + rcev[6])/1000);
qDebug()<<"Read info:"<<rcev;
for(int i=0;i<2;i++) {
val[i]=value[i];
}
return false;
}
//delay函数
void TestComPro::delay(int ms) {
QTime tm;
tm.restart();
while (tm.elapsed() < ms) {
QCoreApplication::processEvents();
std::this_thread::sleep_for(std::chrono::microseconds(2000));
}
}
//光比串口
void TestComPro::CloseCom(void)
{
m_serialPort->clear();
m_serialPort->close();
m_serialPort->deleteLater(); //因为之前new了serial这个对象,所以在关闭串口的时候要销毁这个对象。不然会造成内存泄露
}
//发送命令给串口,并读取数据,检验crc校验数据。
QByteArray TestComPro::SendReadCmd(QByteArray cmd)
{
QByteArray data=nullptr;
QByteArray Tempdata=nullptr;
int i=0;
if (m_serialPort == nullptr || !m_serialPort->isOpen()) {
return data;
}
m_serialPort->clear();
m_serialPort->write(cmd);
m_serialPort->waitForBytesWritten(1000);
m_serialPort->waitForReadyRead(500);
while(true)
{
i++;
delay(10);
Tempdata = m_serialPort->readAll(); //读取串口数据
qDebug()<<"SendReadCmd Read info Tempdata:"<<Tempdata<<"cycle:"<<i;
data=data+Tempdata;
bool bcrc=CrcReadDataVerify(data);
ui->Ch2_data->setText(QString().sprintf("cycle:%d",i));
if(i>10||bcrc==true)
{
//读到数据了,退出循环
return data;
}
}
}
//bytearray转成十六进制字符串。
QString TestComPro::byteArrayToHexStr(const QByteArray &data)
{
QString temp = "";
QString hex = data.toHex();
for (int i = 0; i < hex.length(); i = i + 2) {
temp += hex.mid(i, 2) + " ";
}
return temp.trimmed().toUpper();
}
//传入一个bytearray数组,并对这个数组进行后两位进行校验计算,得出的数据进行比较。校验一样说明串口数据接收完整。
bool TestComPro::CrcReadDataVerify(QByteArray arr)
{
int len=arr.size();
if(len<2)
return false;
uint8_t h=arr[len-2],l=arr[len-1];
uint16_t crcdata=ModbusCRC16(arr,arr.size()-2);
uint16_t crccal=l*256+h;
if(crcdata==crccal)
return true;
else {
return false;
}
}
//modbus16位校验,传入一个一维数组。和需要校验数组的长度
uint16_t TestComPro::ModbusCRC16(QByteArray senddata,int len)
{
//int len=senddata.size();
uint16_t wcrc=0XFFFF;//预置16位crc寄存器,初值全部为1
uint8_t temp;//定义中间变量
int i=0,j=0;//定义计数
for(i=0;i<len;i++)//循环计算每个数据
{
temp=senddata.at(i);
wcrc^=temp;
for(j=0;j<8;j++){
//判断右移出的是不是1,如果是1则与多项式进行异或。
if(wcrc&0X0001){
wcrc>>=1;//先将数据右移一位
wcrc^=0XA001;//与上面的多项式进行异或
}
else//如果不是1,则直接移出
wcrc>>=1;//直接移出
}
}
//temp=wcrc;//crc的值
return wcrc;
}
//字符串转16进制
QByteArray TestComPro::QString2Hex(QString str)
{
QByteArray senddata;
int hexdata,lowhexdata;
int hexdatalen = 0;
int len = str.length();
senddata.resize(len/2);
char lstr,hstr;
for(int i=0; i<len; )
{
hstr=str[i].toLatin1();
if(hstr == ' ')
{
i++;
continue;
}
i++;
if(i >= len)
break;
lstr = str[i].toLatin1();
hexdata = ConvertHexChar(hstr);
lowhexdata = ConvertHexChar(lstr);
if((hexdata == 16) || (lowhexdata == 16))
break;
else
hexdata = hexdata*16+lowhexdata;
i++;
senddata[hexdatalen] =static_cast<char>(hexdata);
hexdatalen++;
}
senddata.resize(hexdatalen);
return senddata;
}
//字符转16进制
char TestComPro::ConvertHexChar(char ch)
{
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 (0);
}
//判断字符串是不是数字
bool TestComPro::isDigitStr(QString src)
{
QByteArray ba = src.toLatin1();//QString 转换为 char*
const char *s = ba.data();
while(*s && *s>='0' && *s<='9')
{
s++;
}
if (*s)
{ //不是纯数字
return false;
}
else
{ //纯数字
return true;
}
}
.H文件
#ifndef TESTCOMPRO_H
#define TESTCOMPRO_H
#include <QMainWindow>
#include <QSerialPort>
#include <QSerialPortInfo>
#include <QPushButton>
#include <QLabel>
#include <thread>
#include <QLayout>
#include <QDebug>
#include <QTime>
#include <QCoreApplication>
using namespace std;
namespace Ui {
class TestComPro;
}
class TestComPro : public QMainWindow
{
Q_OBJECT
public:
explicit TestComPro(QWidget *parent = nullptr);
~TestComPro();
void initUI();
QStringList getPortNameList();//获取所有可用的串口列表
QByteArray QString2Hex(QString str);
char ConvertHexChar(char ch);
void openPort(QString port);//打开串口
void CloseCom(void);
bool WriteCurVolData(uint8_t ch,int16_t vol,int16_t cur);
bool ReadCurValData(uint8_t ch,double *val);
QByteArray SendReadCmd(QByteArray cmd);
uint16_t ModbusCRC16(QByteArray senddata,int len);
bool CrcReadDataVerify(QByteArray arr);
void delay(int ms);
bool isDigitStr(QString src);
QString byteArrayToHexStr(const QByteArray &data);
private:
Ui::TestComPro *ui;
QSerialPort* m_serialPort; //串口类
QStringList m_portNameList;
bool stopflag=true;
public slots:
void BtnWriteCurVolData();//write data
void BtnReadCurValData();
void BtnOpenPort();
};
#endif // TESTCOMPRO_H