**
QT使用libmodbus库与施耐德PLC通信
**
1.环境配置
2.modbus简介
3.libmodbus简介
4.施耐德PLC地址映射
5.程序源码
1.环境配置
我当前使用的环境为WIN10 + MSVC2013 X64 + QT 5.9.9 +QT Creator 4.11.0
2.modbus简介
modbus作为一种通信协议,支持RS232,RS485以及以太网等设备。请求建立连接的称为主站master,
作出响应的称为从站slave,主站可以单独与一个从站进行通讯,也可以以广播的方式进行通信。
modbus的数据传输支持两种方式:一种是ASCLL方式,一种是RTU方式。
两种方式的主要区别是;前者以ascll码的方式进行传输,调试起来更加直观,但是传输效率低,因为一个ascll码的二进制是8位,相反,后者以16位数据传输,效率要高得多。以传输数据0xA8为例,ascll传输需要将‘A’和’8’分开传输,也即0x41和0x38,而rtu直接传输0xA8即可。
从站地址从0-247,0是广播地址所有从机都可以识别,所以实际上从机的寻址范围是1-247,主从通信时从站地址设为1即可
modbus的功能码
modbus通信数据格式,只介绍用到的几个:
关于CRC(Modbus)校验,这里贴一个链接,做调试用
CRC在线校验
3.libmodbus简介
关于libmodbus的使用网上很多,我也看了许多,有两个写的还是很全的
libmodbus的编译
libmodbus的使用
4.施耐德PLC地址映射
其他PLC的地址映射见这里
5.程序源码
在.pro文件中链接WS2_32.LIB库,以及头文件路径
LIBS += -L$$PWD/lib/ -lWS2_32
INCLUDEPATH += $$PWD/libmodbus
这是整个工程文件
libmodbus文件夹中有
在.h文件中声明modbus对象
modbus_t *mb;
.c文件构造函数中添加
mb = modbus_new_rtu("COM5", 38400, 'N', 8, 1);
modbus_set_slave(mb, 1); //设置modbus从机地址
modbus_set_response_timeout(mb, 0, 1000000);/* 超时时间1s */
析构函数中添加
MainWindow::~MainWindow()
{
modbus_close(mb);
modbus_free(mb);
delete ui;
}
设备连接
if(modbus_connect(mb) < 0)
{
ui->textEdit->append(QString::fromLocal8Bit("连接失败"));
}
else {
ui->textEdit->append(QString::fromLocal8Bit("连接成功"));
}
0x01码 (读QX输出线圈)
void MainWindow::on_pushButton_01_clicked()
{
qDebug() << "0x01";
uint8_t dest[8] = {0};
if(modbus_read_bits(mb, 0, 8, dest) != 8 )/* 读输出端 */
{
qDebug() << "Read Coils Error";
}else{
qDebug() << "Read Coils Success";
}
}
0x02码 (读取IX离散输入)
void MainWindow::on_pushButton_02_clicked()
{
qDebug() << "0x02";
uint8_t dest_2[8] = {0};
modbus_read_input_bits(mb, 0, 8, dest_2); /* 读输入端 */
qDebug() << dest_2[0] << ":" << dest_2[1] << ":" << dest_2[2] << ":" << dest_2[3];
qDebug() << dest_2[4] << ":" << dest_2[5] << ":" << dest_2[6] << ":" << dest_2[7];
}
0x03码(读取MW寄存器)
void MainWindow::on_pushButton_03_clicked()
{
qDebug() << "0x03";
uint16_t reg[5];
int rc = modbus_read_registers(mb, 0, 5, reg);
for(int i = 0; i < rc; i++) {
QString str = QString::fromLocal8Bit("寄存器reg[%1]的值为:%2").arg(i+300).arg(decTobin(QString::number(reg[i]))); /* 读多个保持寄存器 */
ui->textEdit->append(str);
}
}
0x10码 (写MW寄存器)
void MainWindow::on_pushButton_10_clicked()
{
/* MW0 40001 */
/* MW359 40360*/
qDebug() << "0x10";
uint16_t tab_regs[5] = {206,0,1,0,0};
if(modbus_write_registers(mb, 360, 5, tab_regs) == (-1))
{
qDebug() << "write error";
}
}
以下是我修改后的使用0X01,0X02,0X03,0X10的例子供大家参考
void MainWindow::on_btnConfirm_clicked()
{
byte keyValue = 1 << 5;
qDebug() << keyValue;
byte mode = ui->comboBoxMode->currentIndex(); /* 0 1 2 3 */
qDebug() << mode;
int plateNum = ui->lineEditNum->text().toUInt();
qDebug() << plateNum;
byte cmode = 0;
switch (mode) {
case 0: cmode = 1 << 0; break;
case 1: cmode = 1 << 1; break;
case 2: cmode = 1 << 2; break;
case 3: cmode = 1 << 3; break;
default:cmode = 0; break;
}
qDebug() << cmode;
uint16_t tab_reg[5] = {plateNum, 0, cmode, keyValue, 0};/* 数组实际为如 101,0,1,32,0 */
byte rc = modbus_write_registers(mb, 360, 5, tab_reg); /* 判断写入的数据长度是不是5个,点对点通信时从站是有返回值的 */
if(rc == sizeof (tab_reg)/sizeof (tab_reg[0])){
qDebug() << "write success";
}
}
void MainWindow::on_btnViewPlate_clicked() /* 查看扩展板输入输出状态 */
{
uint16_t reg[2];
int rc = modbus_read_registers(mb, 0, 2, reg);
for(byte i = 0; i < rc; i++){
ui->textEdit->append(QString::fromLocal8Bit("扩展板0%1").arg(i+1));
ui->textEdit->append("----------------------------");
for(byte j = 0; j < 16; j++){
QString str = QString("%1 : %2").arg(j+1).arg( (reg[rc] >> j) & 0x0001);/* 获取位状态 */
ui->textEdit->append(str);
}
}
}
void MainWindow::on_btnViewPlc_clicked() /* 查看PLC输入输出点状态 */
{
uint8_t dest[8] = {0};
byte rc = modbus_read_input_bits(mb, 0, 8, dest); /* 读输入端 */
for(byte i = 0; i < rc; i++){
QString in = QString("IX0.%1 = %2").arg(i).arg(dest[i]);
ui->textEdit->append(in);
}
memset(dest, '\0', sizeof (dest));
ui->textEdit->append("---------------------------");
rc = modbus_read_bits(mb, 0, 8, dest);
for(byte i = 0; i < rc; i++){
QString in = QString("QX0.%1 = %2").arg(i).arg(dest[i]);
ui->textEdit->append(in);
}
memset(dest, '\0', sizeof (dest));
}
输入车位号206,点击确认,打开modscan32查看一下PLC内部值是否改变
可以看到已经写入成功
最后,说一下我调试的过程,首先先写好一个测试用的DEMO,我把常用的几个功能码都使用了用按钮触发,就是上面贴的代码,有几个我没贴,然后使用软件VSPD虚拟两个串口出来,再用软件Modbus Slave来模拟从站,就可以对代码进行调试了。如果有硬件,可以使用两个USB转485并联与PLC相连,打开串口调试软件比如我用的XCOM2.0,和你自己写的软件分别连接一个COM口,这样你发送给PLC的数据就可以显示在串口调试软件上了,这里要注意的是,点对点测试的时候,串口调试软件会收到两条数据,一条是你发送给PLC的数据,一条是PLC返回的数据,一开始我以为是我发送的数据格式有误。好了,今天就写到这里,关于工程文件已经上传CSDN了,积分窘迫的同学可以评论或者私信我,分享快乐。