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


在这里插入图片描述

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值