目錄
一、前言
我們常用Modbus協議來讀取儀器的某一或多個寄存器的值,但在現實中,可能讀取的寄存器過多(例如幾百個);當出現問題的時候,不容易分析每個寄存器的值,此Modbus組包小工具,可以很好顯示對應的寄存器。
二、操作說明
1. 寄存器->組幀
輸入開始寄存器、結束寄存器(數字),點擊【顯示寄存器】,則會顯示寄存器的值(默認全0);一個寄存器16位,H和L都是十六進制數。
如果你想修改某一寄存器的值,則點擊相應表格,例如我這把50號寄存器的H改爲5B;點擊Pack,就把50-100寄存器的值,以16進制組幀顯示
2. 組幀->寄存器
例如我收到了一串16進制數據:5A 00 00 33 44 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
我現在知道,寄存器從0開始(可以修改開始寄存器),那我想知道這串數據對應寄存器的所有值,則操作如下:
在輸入框輸入獲取到的數據,點擊UnPack,就可以看到每個寄存器對應的高低位
3. 組包
Modbus最後的組包,例如我加入包頭01 03 22,再添加寄存器的數據,點擊組包,則會在末尾自動添加CRC16
三、源碼簡析
①表格類CModel
CModel.h
#ifndef CMODEL_H
#define CMODEL_H
#include <QAbstractTableModel>
#include <QStringList>
#include <QVector>
class CModel : public QAbstractTableModel
{
Q_OBJECT
public:
explicit CModel();
~CModel();
//行數
virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
//列數
virtual int columnCount(const QModelIndex &parent = QModelIndex()) const;
//顯示的數據
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
//從表格界面修改數據
virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
//顯示行首
virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
//設置表格可選性等
virtual Qt::ItemFlags flags(const QModelIndex &index) const;
//設置開始寄存器、結束寄存器後,顯示錶格,默認寄存器的高低16位都爲0
void setParam(int sIndex, int eIndex);
QString pack();
bool unpack(const QString& str, int sIndex, int &eIndex);
private:
//行首值
QStringList m_horHeard;
//表格數據
QVector<QStringList> m_vecData;
};
#endif // CMODEL_H
CModel.cpp
#include "CModel.h"
#include <QCoreApplication>
#include <QDebug>
#include <QTextCodec>
CModel::CModel()
{
//設置行首值
m_horHeard << tr("Register")
<< tr("H")
<< tr("L");
}
CModel::~CModel()
{
}
int CModel::rowCount(const QModelIndex &parent) const
{
return m_vecData.count();
}
int CModel::columnCount(const QModelIndex &parent) const
{
return m_horHeard.count();
}
QVariant CModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
{
return QVariant();
}
if (role == Qt::DisplayRole || role == Qt::EditRole)
{
//行
int iRow = index.row();
//列
int iColumn = index.column();
switch (iColumn)
{
case 0:
return m_vecData.at(iRow).at(iColumn);
break;
case 1:
return m_vecData.at(iRow).at(iColumn);
break;
case 2:
return m_vecData.at(iRow).at(iColumn);
break;
default:
break;
}
}
else if (role == Qt::TextAlignmentRole)
{
return Qt::AlignCenter;
}
return QVariant();
}
//這裏界面修改, 數據變動的地方
bool CModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (!index.isValid())
return false;
if (role == Qt::DisplayRole ||role == Qt::EditRole)
{
int iRow = index.row();
int iColumn = index.column();
QStringList strList = m_vecData.at(iRow);
switch (iColumn)
{
case 0:
strList.replace(iColumn, value.toString());
m_vecData.replace(iRow, strList);
break;
case 1:
strList.replace(iColumn, value.toString());
m_vecData.replace(iRow, strList);
break;
case 2:
strList.replace(iColumn, value.toString());
m_vecData.replace(iRow, strList);
break;
default:
break;
}
//reset函數可以立即刷新表格數據
reset();
return true;
}
return false;
}
QVariant CModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
{
return m_horHeard.at(section);
}
return QVariant();
}
Qt::ItemFlags CModel::flags(const QModelIndex &index) const
{
Qt::ItemFlags flag = QAbstractTableModel::flags(index);
if (index.column() > 0)
{
flag |= Qt::ItemIsEditable;
}
return flag;
}
void CModel::setParam(int sIndex, int eIndex)
{
m_vecData.clear();
for(int i = sIndex; i <= eIndex; i++)
{
QString sIndex = QString("%1").arg(i);
QStringList strlist;
strlist << sIndex
<< "00"
<< "00";
m_vecData.push_back(strlist);
}
reset();
}
QString CModel::pack()
{
QString str;
for(int i = 0; i < m_vecData.count(); i++)
{
QStringList strlist = m_vecData[i];
//第一列不要
for(int j = 1; j < strlist.count(); j++)
{
str.append(strlist[j]);
str.append(" ");
}
}
return str;
}
bool CModel::unpack(const QString &str, int sIndex, int &eIndex)
{
QString sStr = str;
sStr.remove(" ");
if(sStr.length()%4 != 0)
{
return false;
}
m_vecData.clear();
for(int i=0,j=0; j<sStr.length()/4; i+=4,j++)
{
QStringList sList;
QString strH;
QString strL;
strH.append(sStr.at(i));
strH.append(sStr.at(i+1));
strL.append(sStr.at(i+2));
strL.append(sStr.at(i+3));
QString strIndex = QString("%1").arg(sIndex+j);
sList << strIndex
<< strH
<< strL;
m_vecData.append(sList);
eIndex = sIndex+j;
}
reset();
return true;
}
②界面交互類MainWindow
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
class CModel;
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private:
//CRC16
int crc16_modbus(unsigned char *buf, unsigned int len);
private slots:
//顯示寄存器table
void on_btn_show_clicked();
//寄存器的數據組幀
void on_btn_pack_clicked();
//幀的數據顯示在寄存器table上
void on_btn_unpack_clicked();
//添加包頭,組包
void on_btn_pack2_clicked();
//清空數據
void on_btn_clear1_clicked();
void on_btn_clear2_clicked();
void on_btn_clear3_clicked();
private:
Ui::MainWindow *ui;
CModel *m_pModel;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "CTable/CModel.h"
#include <QDebug>
#include <QMessageBox>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
this->setWindowTitle("PackApp");
m_pModel = new CModel;
ui->tableView->setModel(m_pModel);
//設置表格屬性
ui->tableView->setEditTriggers(QTableView::AllEditTriggers);
ui->tableView->setSelectionBehavior(QTableView::SelectRows);
ui->tableView->setSelectionMode(QTableView::SingleSelection);
ui->tableView->horizontalHeader()->setResizeMode(QHeaderView::Stretch);
}
MainWindow::~MainWindow()
{
delete ui;
if(m_pModel != NULL)
{
delete m_pModel;
m_pModel = NULL;
}
}
void MainWindow::on_btn_show_clicked()
{
int startRrgister = ui->ledit_startReg->text().toInt();
int endRrgister = ui->ledit_endReg->text().toInt();
m_pModel->setParam(startRrgister, endRrgister);
}
void MainWindow::on_btn_pack_clicked()
{
if(ui->ledit_startReg->text().toInt() > ui->ledit_endReg->text().toInt())
{
QMessageBox::warning(this, "warning", "Register input wrong!");
return;
}
ui->textEdit->clear();
QString sValueStr = m_pModel->pack();
ui->textEdit->append(sValueStr);
}
void MainWindow::on_btn_unpack_clicked()
{
int eIndex = 0;
bool bRet = m_pModel->unpack(ui->textEdit->toPlainText(),
ui->ledit_startReg->text().toInt(),
eIndex);
if(bRet)
{
QString sEIndex = QString("%1").arg(eIndex);
ui->ledit_endReg->setText(sEIndex);
}
else
{
QMessageBox::warning(this, "warning", "hex is wrong!");
}
}
void MainWindow::on_btn_pack2_clicked()
{
ui->textEdit3->clear();
QString str = ui->textEdit2->toPlainText();
str.remove(" ");
if(str.length()%2 != 0)
{
QMessageBox::warning(this, "warning", "hex is wrong!");
return;
}
unsigned char buffer[str.length()/2];
for(int i=0,j=0; j<str.length()/2; i+=2,j++)
{
QString strHex;
strHex.append("0x");
strHex.append(str.at(i));
strHex.append(str.at(i+1));
int value = strHex.toInt(0, 16);
buffer[j] = value;
}
int crc = crc16_modbus(buffer, str.length()/2);
int crc16_H = ((crc&0xFF00) >> 8);
int crc16_L = (crc&0xFF);
QString sCRC16 = QString("%1 %2").arg(crc16_H, 2, 16, QChar('0')).arg(crc16_L, 2, 16, QChar('0'));
sCRC16 = sCRC16.toUpper();
ui->ledit_crc16->setText(sCRC16);
ui->textEdit3->append(ui->textEdit2->toPlainText() + sCRC16);
}
void MainWindow::on_btn_clear1_clicked()
{
ui->textEdit->clear();
}
void MainWindow::on_btn_clear2_clicked()
{
ui->textEdit2->clear();
}
void MainWindow::on_btn_clear3_clicked()
{
ui->textEdit3->clear();
}
int MainWindow::crc16_modbus(unsigned char* buf, unsigned int len)
{
unsigned int crc=0xFFFF;
unsigned int i,j;
for (j = 0; j < len; j++)
{
crc=crc^*buf++;
for(i = 0; i<8; i++)
{
if ((crc&0x0001) > 0)
{
crc=crc>>1;
crc=crc^0xa001;
}
else
{
crc = crc>>1;
}
}
}
crc = ((crc>>8) | ((crc&0xFF) << 8));
return crc;
}
四、Demo/小工具
鏈接:https://pan.baidu.com/s/1UMLIf8AhuzqDzak5AS_N9Q
提取碼:m7kl