From ef240991f1f6e0d205b1b439416a77ae2ff91299 Mon Sep 17 00:00:00 2001 From: lipengpeng Date: Thu, 24 Jul 2025 14:54:29 +0800 Subject: [PATCH] =?UTF-8?q?CRC=E6=A0=A1=E9=AA=8C=E5=92=8C=E5=AF=B9?= =?UTF-8?q?=E6=8A=A5=E6=96=87=E7=9A=84=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modbus.pro.user | 2 +- widget.cpp | 204 ++++++++++++++++++++++++++++++-- widget.ui | 304 +++++++++++++++++++++++------------------------- 3 files changed, 341 insertions(+), 169 deletions(-) diff --git a/modbus.pro.user b/modbus.pro.user index b047d9f..475d687 100644 --- a/modbus.pro.user +++ b/modbus.pro.user @@ -1,6 +1,6 @@ - + EnvironmentId diff --git a/widget.cpp b/widget.cpp index 1de5a7b..80328e2 100644 --- a/widget.cpp +++ b/widget.cpp @@ -4,6 +4,9 @@ #include #include #include +#include +#include +#include #include "crc.h" Widget::Widget(QWidget *parent) : @@ -69,6 +72,7 @@ void Widget::on_btnConnect_clicked() else { serialPort->close(); + qDebug() << "Serial close"; ui->btnConnect->setText("连接"); } @@ -76,11 +80,106 @@ void Widget::on_btnConnect_clicked() void Widget::on_pushWrite_clicked() { - const char *sendData = ui->lineEdit->text().toStdString().c_str(); - serialPort->write(sendData); + switch (ui->comboBox_gongnengma->currentIndex()) + { + case (2): //写多个线圈 + { + QString sendData = ui->lineEdit->text().trimmed(); + if (sendData.isEmpty()) return; + + for (QChar ch : sendData) { + if (ch != '0' && ch != '1') { + QMessageBox::warning(this, "提示", "只允许输入 0 或 1!"); + return; + } + } + + QVector coils; + for (QChar ch : sendData) + { + coils.append(ch == '1'); + } + + quint16 coilCount = coils.size(); + + QByteArray SendCommand; //要发送的报文 + SendCommand.append(ui->comboBox_stationAddress->currentText().toInt()%256); //加入站地址 + SendCommand.append(0x0f); + SendCommand.append(ui->lineEdit_stratAddress->text().toInt()/256); //加入起始地址 + SendCommand.append(ui->lineEdit_stratAddress->text().toInt()%256); + SendCommand.append(ui->lineEdit_length->text().toInt()/256); //加入长度 + SendCommand.append(ui->lineEdit_length->text().toInt()%256); + + int byteCount = (coilCount + 7) / 8; + + SendCommand.append(byteCount); //字节数 + for (int i = 0; i < byteCount; ++i) + { + quint8 byte = 0; + for (int j = 0; j < 8; ++j) + { + int bitIndex = i * 8 + j; + if (bitIndex < coils.size() && coils[bitIndex]) + byte |= (1 << j); + } + SendCommand.append(static_cast(byte)); + } + + + quint16 temp = calculateCrc(SendCommand); //计算crc + + SendCommand.append(temp%256); //加入计算的crc值 + SendCommand.append(temp/256); + + serialPort->write(SendCommand); qDebug() << "SenOk" <lineEdit->text().trimmed(); + if (sendData.isEmpty()) return; + + QStringList sl = sendData.split(','); + QVector values; + bool ok; + for (const QString &s : sl) + { + quint16 v = s.toUShort(&ok, 16); + if (!ok) + { + QMessageBox::warning(this, "提示", "请输入正确的十六进制值!"); + return; + } + values.append(v); + } + + QByteArray SendCommand; //要发送的报文 + SendCommand.append(ui->comboBox_stationAddress->currentText().toInt()%256); //加入站地址 + SendCommand.append(0x10); + SendCommand.append(ui->lineEdit_stratAddress->text().toInt()/256); //加入起始地址 + SendCommand.append(ui->lineEdit_stratAddress->text().toInt()%256); + SendCommand.append(ui->lineEdit_length->text().toInt()/256); //加入长度 + SendCommand.append(ui->lineEdit_length->text().toInt()%256); + SendCommand.append(static_cast(values.size() * 2)); + for (quint16 v : values) + { + SendCommand.append(static_cast((v >> 8) & 0xFF)); + SendCommand.append(static_cast(v & 0xFF)); + } + + quint16 temp = calculateCrc(SendCommand); //计算crc + + SendCommand.append(temp%256); //加入计算的crc值 + SendCommand.append(temp/256); + + serialPort->write(SendCommand); + qDebug() << "SenOk" <readAll(); @@ -88,30 +187,113 @@ void Widget::on_SerialData_ReadyToRead() qDebug() << hexData; ui->textEdit->append(hexData); + //对接收的报文进行CRC校验 + QByteArray payload = revMessage.left(revMessage.length() - 2); + + //分离接收值的crc校验位 + quint8 receivedCrcLow = static_cast(revMessage.at(revMessage.length() - 2)); + quint8 receivedCrcHigh = static_cast(revMessage.at(revMessage.length() - 1)); + + //计算返回的报文的crc + quint16 crc = calculateCrc(payload); + quint8 calcCrcLow = crc & 0xFF; + quint8 calcCrcHigh = (crc >> 8) & 0xFF; + + //比较计算的crc值和接收到的crc值是否一致 + if(calcCrcLow == receivedCrcLow && calcCrcHigh == receivedCrcHigh) + { + qDebug() << "接收成功"; + switch (ui->comboBox_gongnengma->currentIndex()) + { + //解析读线圈的返回报文 + case 0: + { + quint8 byteCount = static_cast(revMessage[2]); + + for (int byteIndex = 0; byteIndex < byteCount; byteIndex++) + { + quint8 byteValue = static_cast(revMessage[3 + byteIndex]); + + // 解析每个字节的8个位 + for (int bitIndex = 0; bitIndex < 8; bitIndex++) + { + int coilIndex = byteIndex * 8 + bitIndex; + if (coilIndex < ui->lineEdit_length->text().toInt()) + { + bool state = byteValue & (1 << bitIndex); + qDebug() << coilIndex <<" " << state; + ui->textEdit->append("线圈"+QString::number(coilIndex+1)+":"+QString::number(state)); + } + } + } + break; + } + //解析读寄存器的返回报文 + case 1: + { + if (revMessage.size() >= 5 && revMessage.at(1) == 0x03) + { + int byteCount = revMessage.at(2); + QByteArray data = revMessage.mid(3, byteCount); + + QVector registers; + for (int i = 0; i < data.size(); i += 2) + { + quint16 value = (static_cast(data[i]) << 8) | static_cast(data[i+1]); + registers.append(value); + } + + for (int i = 0; i < registers.size(); i++) + { + qDebug() << "Register value:" << registers.at(i); + ui->textEdit->append("寄存器"+QString::number(i)+":"+QString::number(registers.at(i))); + } + } + break; + } + case 2: + { + + if (revMessage.at(1) == '\x8f') + { + quint8 exCode = revMessage.at(2); + qDebug() << "异常响应,异常码 =" << exCode; + return; + } + } + + + } + } + else + { + qDebug() << "接收失败"; + } } +//发送读线圈和寄存器报文 void Widget::on_btn_read_clicked() { - QByteArray SendCommand; - SendCommand.append(ui->comboBox_stationAddress->currentText().toInt()%256); - if (ui->comboBox_gongnengma->currentIndex() == 0) + QByteArray SendCommand; //要发送的报文 + SendCommand.append(ui->comboBox_stationAddress->currentText().toInt()%256); //加入站地址 + if (ui->comboBox_gongnengma->currentIndex() == 0) //读线圈 { SendCommand.append(0x01); } - else if(ui->comboBox_gongnengma->currentIndex() == 1) + else if(ui->comboBox_gongnengma->currentIndex() == 1) //读寄存器 { SendCommand.append(0x03); } - SendCommand.append(ui->lineEdit_stratAddress->text().toInt()/256); + SendCommand.append(ui->lineEdit_stratAddress->text().toInt()/256); //加入起始地址 SendCommand.append(ui->lineEdit_stratAddress->text().toInt()%256); - SendCommand.append(ui->lineEdit_length->text().toInt()/256); + SendCommand.append(ui->lineEdit_length->text().toInt()/256); //加入长度 SendCommand.append(ui->lineEdit_length->text().toInt()%256); - quint16 temp = calculateCrc(SendCommand); + quint16 temp = calculateCrc(SendCommand); //计算crc - SendCommand.append(temp%256); + SendCommand.append(temp%256); //加入计算的crc值 SendCommand.append(temp/256); qDebug() << SendCommand.toHex(); - serialPort->write(SendCommand); + serialPort->write(SendCommand); //发送报文 } diff --git a/widget.ui b/widget.ui index dc01c71..746685e 100644 --- a/widget.ui +++ b/widget.ui @@ -140,8 +140,8 @@ - 480 - 30 + 340 + 60 93 28 @@ -224,200 +224,190 @@ - 420 - 70 - 231 - 21 + 340 + 250 + 301 + 31 - 660 - 60 + 340 + 290 93 28 - 发送 + - - - 420 - 100 - 251 - 151 - - - - - - - 70 - 150 - 21 - 16 - - - - - - - - - - 92 - 120 - 61 - 21 - - - - - 1 - - - - - 2 - - - - - 3 - - - - - 4 - - - - - 5 - - - - - 6 - - - - - 7 - - - - - 8 - - - - - 9 - - - - 40 - 120 - 45 - 16 + 170 + 251 + 271 - - 站地址 - - + 90 - 210 - 51 - 21 - - - - 00 - - - - - - 100 - 180 - 51 - 21 + 460 + 93 + 28 - 00 + - + 40 - 210 - 41 - 16 - - - - 长度 - - - - - - 30 - 180 - 71 - 16 - - - - 起始地址 - - - - - - 90 - 150 - 61 - 21 + 115 + 672 + 23 - - - 线圈 - - - - - 寄存器 - - + + + + + 站地址 + + + + + + + + 1 + + + + + 2 + + + + + 3 + + + + + 4 + + + + + 5 + + + + + 6 + + + + + 7 + + + + + 8 + + + + + 9 + + + + + + + + 对象 + + + + + + + + 读线圈 + + + + + 读寄存器 + + + + + 写线圈 + + + + + 写寄存器 + + + + + + + + 起始地址 + + + + + + + 00 + + + + + + + 长度 + + + + + + + 00 + + + + - + - 40 - 260 - 93 - 28 + 340 + 160 + 311 + 91 - + 写线圈时,使用1、0代表线圈的开关, +从左到右依次输入每个线圈的开关; +写寄存器时,每个寄存器的值用16进制表示, +相邻寄存器的值之间用","分离。