三菱PLC与上位机进行通讯

您所在的位置:网站首页 三菱plc串口通讯不上 三菱PLC与上位机进行通讯

三菱PLC与上位机进行通讯

2024-07-08 00:33:25| 来源: 网络整理| 查看: 265

三菱PLC与上位机串口通信 一.三菱Fx系列PLC编程口通讯协议地址算法1.DEVICE READ(读出软设备状态值)2.DEVICE WRITE(向PLC 软设备写入值)3.位设备强制置位/复位4.三菱Fx系列PLC地址对应表 二.源代码1.ui文件2.PlcConnection.h3.PlcConnection.cpp4.main.cpp 参考资料:

一.三菱Fx系列PLC编程口通讯协议地址算法

三菱PLC编程口通讯协议三菱PLC编程口的通讯协议只有四个命令:

命令命令码目标设备DEVICE READ CMD“0”X,Y,M,S,T,C,DDEVICE WRITE CMD“1”X,Y,M,S,T,C,DFORCE ON CMD“7”X,Y,M,S,T,CFORCE OFF CMD“8”X,Y,M,S,T,C

五个标示:

ENQ05H请求ACK06HPLC正确响应NAK15HPLC错误响应STX02H报文开始ETX03H报文结束

使用累加方式的和校验,帧格式如下: STX CMD DATA … DATA ETX SUM(upper) SUM(lower) 和校验: SUM= CMD+……+ETX。 如SUM=73H,SUM=“73”。

1.DEVICE READ(读出软设备状态值)

计算机向PLC发送: 始命令 首地址 位数 终和校验 STX CMD   GROUP ADDRESS   BYTES   ETX   SUM PLC 返回 STX 1ST DATA 2ND DATA … LAST DATA ETX SUM

2.DEVICE WRITE(向PLC 软设备写入值)

计算机向PLC发送: 始命令   首地址  位数   数据   终和校验 PLC 返回 ACK (06H) 接受正确 NAK (15H) 接受错误

3.位设备强制置位/复位

FORCE ON 置位 始命令   地址   终和校验 STX   CMD   ADDRESS   ETX   SUM 02H   37H   ADDRESS   03H   SUM FORCE OFF 复位 始  命令  地址   终 和校验 STX   CMD   ADDRESS   ETX   SUM 02H   38H   ADDRESS   03H   SUM PLC 返回 ACK(06H) 接受正确 NAK(15H) 接受错误

4.三菱Fx系列PLC地址对应表

以上就是这些协议,但是由于没有寄存器类型信息,所以地址的计算十分关键,如D100和M100分别对应哪个地址呢?下面就是三菱Fx系列PLC地址对应表。

Public Const PLC_D_Base_AddRess = 4096 Public Const PLC_D_Special_Base_AddRess = 3584 Public Const PLC_Y_Group_Base_AddRess = 160 Public Const PLC_PY_Group_Base_AddRess = 672 Public Const PLC_T_Group_Base_AddRess = 192 Public Const PLC_OT_Group_Base_AddRess = 704 Public Const PLC_RT_Group_Base_AddRess = 1216 Public Const PLC_M_SINGLE_Base_AddRess = 2048(命令为7或8时) Public Const PLC_M_Group_Base_AddRess = 256 Public Const PLC_PM_Group_Base_AddRess = 768 Public Const PLC_S_Group_Base_AddRess = 0 Public Const PLC_X_Group_Base_AddRess = 128 Public Const PLC_C_Group_Base_AddRess = 448 Public Const PLC_OC_Group_Base_AddRess = 960 Public Const PLC_RC_Group_Base_AddRess = 1472 Public Const PLC_TV_Group_Base_AddRess = 2048 Public Const PLC_CV16_Group_Base_AddRess = 2560 Public Const PLC_CV32_Group_Base_AddRess = 3072

当我们用DEVICE READ命令时,D100地址=100*2+4096;M100地址=100+256;不同的是D类型寄存器存放的是字,M寄存器存放的是位,同样是读两个字节,D100返回的就是PLC中D100地址的值,M类型寄存器返回的是M100到M116的值。所以当我们用FORCE ON 命令时,M100寄存器地址=100+2048; 但三菱公司好像不甘于如此,FORCE ON/Off命令中地址排列与DEVICE READ/WRITE不同,是低位在前高位在后。如Y20,地址是0510H,代码中4个字节地址表示为:1005。(注意:Y寄存器为八进制,如Y20地址=16+1280=0510H)

二.源代码 1.ui文件

在这里插入图片描述

2.PlcConnection.h #ifndef MAINWINDOW_H #define MAINWINDOW_H #include "ui_PlcConnection.h" #include #include #include #include #include //#include #include using namespace std; #define STX 0x02 //报文开始 #define ETX 0x03 //文本结束 #define EOT 0x04 //传送结束 #define ENQ 0x05 //查询 #define ACK 0x06 //PLC肯定响应 #define NAK 0x15 //PLC否定响应 #define DEVICE_READ_CMD '0' //读命令,适用软元件X、Y、M、S、T、C、D #define DEVICE_WRITE_CMD '1' //写命令,适用软元件X、Y、M、S、T、C、D #define FORCE_ON_CMD '7' //强制通命令,适用软元件X、Y、M、S、T、C #define FORCE_OFF_CMD '8' //强制断命令,适用软元件X、Y、M、S、T、C namespace Ui { class PlcConnection; } class PlcConnection : public QMainWindow { Q_OBJECT public: PlcConnection(QWidget *parent = Q_NULLPTR); //explicit PlcConnection(QWidget* parent = nullptr); //~PlcConnection(); private: Ui::PlcConnectionClass ui; QSerialPort serial;//声明串口类 //public: private: //QSerialPort serial;//声明串口类 QTcpServer* server; QTcpSocket* client; QUdpSocket* sender; QUdpSocket* receiver; QTcpSocket* clientConnection[10]; quint8 index; QTimer testTimer; quint16 port_old; quint16 flag; QHostAddress serverAddress; QByteArray datagram; QTimer* timer; //public: private: void find_seralport(); char ConvertHexChar(char ch); void StringToHex(QString str, QByteArray& senddata); //字符串转换为十六进制数据0-F void on_time_scan_serial(); uint8_t sum8(uint8_t data[], uint32_t len); //累加和 int buf2value(char* b); private slots: void on_openPortBtn_clicked(); void read_Com(); //手动添加的槽函数声明,用于读出串口缓冲区的内容 void on_sendButton_clicked(); void on_pushButton_2_clicked(); void testFunction(); //void on_BaudBox_currentTextChanged(const QString &arg1); void onTimeOut(); void on_sendButton_2_clicked(); void on_sendButton_3_clicked(); void on_sendButton_4_clicked(); void on_sendButton_5_clicked(); }; #endif // MAINWINDOW_H 3.PlcConnection.cpp #include "PlcConnection.h" #include #include #include "ui_PlcConnection.h" PlcConnection::PlcConnection(QWidget *parent):QMainWindow(parent) { ui.setupUi(this); //关闭发送按钮禁能 ui.sendButton->setEnabled(false); ui.sendButton_2->setEnabled(false); ui.sendButton_3->setEnabled(false); ui.sendButton_4->setEnabled(false); ui.sendButton_5->setEnabled(false); /*查找可用的串口*/ find_seralport(); ui.BaudBox->setCurrentText("9600"); ui.BitNumBox->setCurrentText("7 bit"); ui.ParityBox->setCurrentText("EVEN"); //on_time_scan_serial(); } /* 串口读取回调 */ void PlcConnection::read_Com() { qDebug("aaa"); /* 信号到来,读取所有的字符串 */ QByteArray buf = serial.readAll(); QDataStream out(&buf, QIODevice::ReadWrite); //将字节数组读入 while (!out.atEnd()) { qint8 outChar = 0; out >> outChar; //每字节填充一次,直到结束 //十六进制的转换 QString str = QString("%1").arg(outChar & 0xFF, 2, 16, QLatin1Char('0')); qDebug() insertPlainText(" "); } char* b = buf.data(); int length = buf.length(); if (length != 0) ui.recvTextBrowser->insertPlainText("\n"); printf("receive:%d\n", length); for (int i = 0; i static char bb[500]; static int index; uint8_t sum = sum8((uint8_t*)b + 1, length - 3); unsigned int temp = 0; sscanf(b + length - 2, "%02x", &temp); printf("temp = 0x%x\n", temp); printf("sum = 0x%x\n", sum); if (temp == sum && b[0] == 0x02) { printf("check sum OK\n"); index = 0; QString display; for (int i = 0; i printf("check sum failed\n"); memcpy(bb + index, b, length); index += length; uint8_t sum = sum8((uint8_t*)bb + 1, index - 3); unsigned int temp = 0; printf("index = %d\n", index); sscanf(bb + index - 2, "%02x", &temp); printf("temp = 0x%x\n", temp); printf("sum = 0x%x\n", sum); for (int i = 0; i printf("check sum OK\n"); QString display; for (int i = 0; i printf("check sum failed\n"); buf.clear(); fflush(stdout); return; } fflush(stdout); } } else if (length == 1) { if (b[0] == 0x06) { qDebug() /* 首地址 */ int addr = ui.spinBox->value(); qDebug() 0x02, DEVICE_READ_CMD, }; //起始、命令 int d_addr = 0; if (ui.comboBox->currentText() == "S") d_addr = addr * 2 + 0x0000; else if (ui.comboBox->currentText() == "X") d_addr = addr * 2 + 0x0080; else if (ui.comboBox->currentText() == "Y") d_addr = addr * 2 + 0x00A0; else if (ui.comboBox->currentText() == "T") d_addr = addr * 2 + 0x00C0; else if (ui.comboBox->currentText() == "M") /**/ d_addr = addr * 2 + 0x0100; else if (ui.comboBox->currentText() == "C") d_addr = addr * 2 + 0x01C0; else if (ui.comboBox->currentText() == "D") /**/ d_addr = addr * 2 + 0x1000; char d_addr_str[5] = { 0 }; snprintf(d_addr_str, 5, "%04X", d_addr); /* 地址不需要倒序,值需要倒序 */ for (int i = 0; i 0 }; snprintf(sum_str, 3, "%02X", sum); buf1[9] = sum_str[0]; buf1[10] = sum_str[1]; /* 打印 */ printf("send:\n"); for (int i = 0; i 0 }; sprintf(tmp, "%02x ", buf1[i]); str += tmp; } qDebug() = 8000 && addr 0x02, 'E', '0', '0', }; //起始、命令 int d_addr = (addr - 8000) * 2; d_addr += 0x8000; char d_addr_str[5] = { 0 }; snprintf(d_addr_str, 5, "%04X", d_addr); for (int i = 0; i 0 }; snprintf(sum_str, 3, "%02X", sum); buf1[11] = sum_str[0]; buf1[12] = sum_str[1]; /* 打印 */ printf("send:\n"); for (int i = 0; i 0x02, DEVICE_WRITE_CMD, }; //起始、命令:写 /* 首地址 */ int addr = ui.spinBox->value(); if (addr >= 0 && addr 0 }; snprintf(d_addr_str, 5, "%04X", d_addr); for (int i = 0; i qDebug() 0 }; snprintf(sum_str, 3, "%02X", sum); buf1[9 + count * 4] = sum_str[0]; buf1[10 + count * 4] = sum_str[1]; /* 打印 */ printf("send:\n"); for (int i = 0; i = 8000 && addr 0x02, 'E', '1', '0', }; //起始、命令:写 int d_addr = (addr - 8000) * 2; d_addr += 0x8000; char d_addr_str[5] = { 0 }; snprintf(d_addr_str, 5, "%04X", d_addr); for (int i = 0; i qDebug() 0} }; for (int i = 0; i 0 }; snprintf(sum_str, 3, "%02X", sum); buf1[11 + count * 4] = sum_str[0]; buf1[12 + count * 4] = sum_str[1]; /* 打印 */ printf("send:\n"); for (int i = 0; i sum += data[i]; } return sum; } /* 打开串口 */ void PlcConnection::on_openPortBtn_clicked() { if (ui.openPortBtn->text() == "打开") { ui.openPortBtn->setText("关闭"); //按下“OpenPort”后,按键显示为“ClosePort” // if(ui->openPortBtn->text() == "打开") // ui->PortBox->setDisabled(true); //按下“OpenPort”后,禁止再修改COM口 serial.setPortName(ui.PortBox->currentText()); //设置COM口 QSerialPort::BaudRate baud_rate = QSerialPort::Baud115200; if (ui.BaudBox->currentText() == "1200") baud_rate = QSerialPort::Baud1200; else if (ui.BaudBox->currentText() == "9600") baud_rate = QSerialPort::Baud9600; else if (ui.BaudBox->currentText() == "2400") baud_rate = QSerialPort::Baud2400; else if (ui.BaudBox->currentText() == "4800") baud_rate = QSerialPort::Baud4800; else if (ui.BaudBox->currentText() == "9600") baud_rate = QSerialPort::Baud9600; else if (ui.BaudBox->currentText() == "19200") baud_rate = QSerialPort::Baud19200; else if (ui.BaudBox->currentText() == "38400") baud_rate = QSerialPort::Baud38400; else if (ui.BaudBox->currentText() == "57600") baud_rate = QSerialPort::Baud57600; else if (ui.BaudBox->currentText() == "115200") baud_rate = QSerialPort::Baud115200; serial.setBaudRate(baud_rate); //设置波特率 QSerialPort::DataBits data_bits = QSerialPort::Data8; if (ui.BitNumBox->currentText() == "8 bit") data_bits = QSerialPort::Data8; else if (ui.BitNumBox->currentText() == "7 bit") data_bits = QSerialPort::Data7; else if (ui.BitNumBox->currentText() == "6 bit") data_bits = QSerialPort::Data6; else if (ui.BitNumBox->currentText() == "5 bit") data_bits = QSerialPort::Data5; serial.setDataBits(data_bits); //设置数据位 serial.setFlowControl(QSerialPort::NoFlowControl);//无流控制 QSerialPort::Parity parity = QSerialPort::NoParity; if (ui.ParityBox->currentText() == "NONE") parity = QSerialPort::NoParity; else if (ui.ParityBox->currentText() == "EVEN") parity = QSerialPort::EvenParity; else if (ui.ParityBox->currentText() == "ODD") parity = QSerialPort::OddParity; else if (ui.ParityBox->currentText() == "SPACE") parity = QSerialPort::SpaceParity; else if (ui.ParityBox->currentText() == "MARK") parity = QSerialPort::MarkParity; serial.setParity(parity); //设置校验位 QSerialPort::StopBits stop_bits = QSerialPort::OneStop; if (ui.StopBox->currentText() == "1 bit") stop_bits = QSerialPort::OneStop; else if (ui.StopBox->currentText() == "1.5 bit") stop_bits = QSerialPort::OneAndHalfStop; else if (ui.StopBox->currentText() == "5 bit") stop_bits = QSerialPort::TwoStop; serial.setStopBits(stop_bits); //设置停止位 serial.close(); //先关串口,再打开,可以保证串口不被其它函数占用。 if (serial.open(QIODevice::ReadWrite)) //以可读写的方式打开串口 { connect(&serial, SIGNAL(readyRead()), this, SLOT(read_Com())); //把串口的readyRead()信号绑定到read_Com()这个槽函数上 } ui.sendButton->setEnabled(true); ui.sendButton_2->setEnabled(true); ui.sendButton_3->setEnabled(true); ui.sendButton_4->setEnabled(true); ui.sendButton_5->setEnabled(true); } else { ui.openPortBtn->setText("打开"); //按下“ClosePort”后,按键显示为“OpenPort” ui.PortBox->setEnabled(true); //按下“ClosePort”后,COM口可被修改 serial.close(); //关串口 //关闭发送按钮的使能 ui.sendButton->setEnabled(false); ui.sendButton_2->setEnabled(false); ui.sendButton_3->setEnabled(false); ui.sendButton_4->setEnabled(false); ui.sendButton_5->setEnabled(false); } } /* 清除按钮 */ void PlcConnection::on_pushButton_2_clicked() { ui.recvTextBrowser->clear(); } void PlcConnection::testFunction() { // qDebug() read(1); } int PlcConnection::buf2value(char* b) { char a[4] = { 0 }; a[0] = b[2]; a[1] = b[3]; a[2] = b[0]; a[3] = b[1]; for (int i = 0; i 0x00 }; /* 提取参数 */ QString sand_str(ui.lineEdit_sand1_2->text()); QString temp; int cnt = 0; for (int i = 0; ; i++) { temp = sand_str.left(2); if (temp == "") break; buf1[i] = temp.toInt(nullptr, 16); // qDebug() write(buf1, cnt); // serial.write(ui->textEdit_2->toPlainText().toLatin1()); serial.write(buf1, cnt); } /* 查找可用的串口 */ void PlcConnection::find_seralport() { foreach(const QSerialPortInfo & info, QSerialPortInfo::availablePorts()) { QSerialPort serial; serial.setPort(info); if (serial.open(QIODevice::ReadWrite)) { ui.PortBox->addItem(serial.portName()); serial.close(); } } } void PlcConnection::on_time_scan_serial() { timer = new QTimer; connect(timer, SIGNAL(timeout()), this, SLOT(onTimeOut())); timer->start(1000); } void PlcConnection::onTimeOut() { find_seralport(); qDebug() int hexdata, lowhexdata; int hexdatalen = 0; int len = str.length(); senddata.resize(len / 2); char lstr, hstr; for (int i = 0; i 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] = (char)hexdata; hexdatalen++; } senddata.resize(hexdatalen); } char PlcConnection::ConvertHexChar(char ch) { if ((ch >= '0') && (ch = 'A') && (ch = 'a') && (ch uint8_t buf1[100] = { 0x02, FORCE_ON_CMD, }; //起始、命令:写 int d_addr = 0; if (ui.comboBox_2->currentText() == "S") d_addr = addr * 1 + 0x0000; else if (ui.comboBox_2->currentText() == "X") d_addr = addr * 2 + 0x0400; else if (ui.comboBox_2->currentText() == "Y") d_addr = addr * 1 + 0x0500; else if (ui.comboBox_2->currentText() == "T") d_addr = addr * 1 + 0x0600; else if (ui.comboBox_2->currentText() == "M") d_addr = addr * 1 + 0x0800; else if (ui.comboBox_2->currentText() == "C") d_addr = addr * 1 + 0x0E00; char d_addr_str[5] = { 0 }; snprintf(d_addr_str, 5, "%04X", d_addr); for (int i = 0; i uint8_t buf1[100] = { 0x02, 'E', '7', }; //起始、命令:写 int d_addr = 0; addr -= 8000; d_addr = addr * 1 + 0x6000; char d_addr_str[5] = { 0 }; snprintf(d_addr_str, 5, "%04X", d_addr); for (int i = 0; i /* 首地址 */ int addr = ui.spinBox_3->value(); if (addr >= 0 && addr 0x02, FORCE_OFF_CMD, }; //起始、命令:写 int d_addr = 0; if (ui.comboBox_2->currentText() == "S") d_addr = addr * 1 + 0x0000; else if (ui.comboBox_2->currentText() == "X") d_addr = addr * 2 + 0x0400; else if (ui.comboBox_2->currentText() == "Y") d_addr = addr * 1 + 0x0500; else if (ui.comboBox_2->currentText() == "T") d_addr = addr * 1 + 0x0600; else if (ui.comboBox_2->currentText() == "M") d_addr = addr * 1 + 0x0800; else if (ui.comboBox_2->currentText() == "C") d_addr = addr * 1 + 0x0E00; char d_addr_str[5] = { 0 }; snprintf(d_addr_str, 5, "%04X", d_addr); for (int i = 0; i uint8_t buf1[100] = { 0x02, 'E', '8', }; //起始、命令:写 int d_addr = 0; addr -= 8000; d_addr = addr * 1 + 0x6000; char d_addr_str[5] = { 0 }; snprintf(d_addr_str, 5, "%04X", d_addr); for (int i = 0; i QApplication a(argc, argv); PlcConnection w; w.setWindowTitle("三菱FX3U编程口通信助手"); w.show(); return a.exec(); }

运行结果: 在这里插入图片描述

参考资料:

我还下了网上两个别人的实例,若有需要参考的评论区留下邮箱,我把那两个工程文件发给你。。。 在这里插入图片描述 在这里插入图片描述



【本文地址】

公司简介

联系我们

今日新闻


点击排行

实验室常用的仪器、试剂和
说到实验室常用到的东西,主要就分为仪器、试剂和耗
不用再找了,全球10大实验
01、赛默飞世尔科技(热电)Thermo Fisher Scientif
三代水柜的量产巅峰T-72坦
作者:寞寒最近,西边闹腾挺大,本来小寞以为忙完这
通风柜跟实验室通风系统有
说到通风柜跟实验室通风,不少人都纠结二者到底是不
集消毒杀菌、烘干收纳为一
厨房是家里细菌较多的地方,潮湿的环境、没有完全密
实验室设备之全钢实验台如
全钢实验台是实验室家具中较为重要的家具之一,很多

推荐新闻


图片新闻

实验室药品柜的特性有哪些
实验室药品柜是实验室家具的重要组成部分之一,主要
小学科学实验中有哪些教学
计算机 计算器 一般 打孔器 打气筒 仪器车 显微镜
实验室各种仪器原理动图讲
1.紫外分光光谱UV分析原理:吸收紫外光能量,引起分
高中化学常见仪器及实验装
1、可加热仪器:2、计量仪器:(1)仪器A的名称:量
微生物操作主要设备和器具
今天盘点一下微生物操作主要设备和器具,别嫌我啰嗦
浅谈通风柜使用基本常识
 众所周知,通风柜功能中最主要的就是排气功能。在

专题文章

    CopyRight 2018-2019 实验室设备网 版权所有 win10的实时保护怎么永久关闭