三菱PLC与上位机串口通信

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

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

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

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

五个标示

ENQ 05H 请求
ACK 06H PLC正确响应
NAK 15H PLC错误响应
STX 02H 报文开始
ETX 03H 报文结束

使用累加方式的和校验,帧格式如下:
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 <QtWidgets/QMainWindow>
#include <QtSerialPort/QSerialPort>
#include <QtSerialPort/QSerialPortInfo>
#include <QtNetwork/QtNetwork>
#include <QAbstractScrollArea>//#include <QTextCursor>#include <iostream>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、Cnamespace Ui
{class PlcConnection;
}class PlcConnection : public QMainWindow
{Q_OBJECTpublic: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-Fvoid 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 <QTimer>
#include <QMessageBox>
#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() << str;ui.recvTextBrowser->insertPlainText(str);ui.recvTextBrowser->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 < length; i++){printf("0x%02x ", b[i]);}printf("\n");qDebug() << "length:" << length;if (length > 1){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 < (length - 4) / 4; i++){int v = buf2value(b + 1 + i * 4);display += QString::number(v) + ",";}ui.lineEdit->setText(display);}else{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 < index; i++){printf("%02x ", bb[i]);}printf("\n");if (temp == sum){printf("check sum OK\n");QString display;for (int i = 0; i < (index - 4) / 4; i++){int v = buf2value(bb + 1 + i * 4);display += QString::number(v) + ",";}ui.lineEdit->setText(display);index = 0;}else{printf("check sum failed\n");buf.clear();fflush(stdout);return;}fflush(stdout);}}else if (length == 1){if (b[0] == 0x06){qDebug() << "写入成功";}else if (b[0] == 0x15){qDebug() << "写入失败";}}buf.clear();fflush(stdout);
}/**************** 读取 ***************/
void PlcConnection::on_sendButton_2_clicked()
{/* 首地址 */int addr = ui.spinBox->value();qDebug() << addr;if (addr >= 0 && addr < 1024){uint8_t buf1[100] = { 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 < 4; i++)buf1[2 + i] = d_addr_str[i];/* 字节数 */char count_str[3] = { 0 };int count = ui.spinBox_2->value() * 2;snprintf(count_str, 3, "%02X", count);buf1[6] = count_str[0];buf1[7] = count_str[1];/* 结束 */buf1[8] = ETX;/* 累加和 *///从命令开始到结束为止的每一个字节累加和(不包括起始字节)uint8_t sum = sum8(buf1 + 1, 8);char sum_str[3] = { 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 < 11; i++)printf("%02x ", buf1[i]);printf("\n");serial.write((char*)buf1, 11);QString str;for (int i = 0; i < 11; i++){char tmp[4] = { 0 };sprintf(tmp, "%02x ", buf1[i]);str += tmp;}qDebug() << str;ui.lineEdit_sand1_2->setText(str);}else if (addr >= 8000 && addr < 8512){uint8_t buf1[100] = { 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 < 4; i++)buf1[4 + i] = d_addr_str[i];/* 字节数 */char count_str[3] = { 0 };int count = ui.spinBox_2->value() * 2;snprintf(count_str, 3, "%02X", count);buf1[8] = count_str[0];buf1[9] = count_str[1];/* 结束 */buf1[10] = ETX;/* 累加和 *///从命令开始到结束为止的每一个字节累加和(不包括起始字节)uint8_t sum = sum8(buf1 + 1, 10);char sum_str[3] = { 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 < 13; i++)printf("%02x ", buf1[i]);printf("\n");serial.write((char*)buf1, 13);}fflush(stdout);
}/********************** 写入 **********************/
void PlcConnection::on_sendButton_3_clicked()
{uint8_t buf1[100] = { 0x02, DEVICE_WRITE_CMD, };  //起始、命令:写/* 首地址 */int addr = ui.spinBox->value();if (addr >= 0 && addr < 1024){int d_addr = 0;if (ui.comboBox->currentText() == "S")d_addr = addr * 2 + 0x0000;else if (ui.comboBox->currentText() == "Y")d_addr = addr * 2 + 0x00A0;else if (ui.comboBox->currentText() == "X")d_addr = addr * 2 + 0x0080;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 < 4; i++)buf1[2 + i] = d_addr_str[i];/* 字节数 */char count_str[3] = { 0 };int count = ui.spinBox_2->value() * 2;snprintf(count_str, 3, "%02X", count);buf1[6] = count_str[0];buf1[7] = count_str[1];count /= 2;QString str = ui.lineEdit->text();QStringList list = str.split(",", QString::SkipEmptyParts);qDebug() << list.length();if (list.length() < count){qDebug() << "请输入足够多的数据";QMessageBox::critical(nullptr, "提醒", "请输入足够多的数据", QMessageBox::Ok, 0);return;}char v[5][5];for (int i = 0; i < count; i++){qDebug() << list[i];QByteArray ba = list.at(i).toLatin1();char* c_str = ba.data();snprintf(v[i], 5, "%04X", atoi(c_str));qDebug() << v[i];buf1[8 + i * 4 + 2] = v[i][0];buf1[8 + i * 4 + 3] = v[i][1];buf1[8 + i * 4 + 0] = v[i][2];buf1[8 + i * 4 + 1] = v[i][3];}/* 结束 */buf1[8 + count * 4] = ETX;/* 累加和 *///从命令开始到结束为止的每一个字节累加和(不包括起始字节)uint8_t sum = sum8(buf1 + 1, 8 + count * 4);char sum_str[3] = { 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 < 11 + count * 4; i++)printf("%02x ", buf1[i]);printf("\n");serial.write((char*)buf1, 11 + count * 4);fflush(stdout);}else if (addr >= 8000 && addr < 8512){uint8_t buf1[100] = { 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 < 4; i++)buf1[4 + i] = d_addr_str[i];/* 字节数 */char count_str[3] = { 0 };int count = ui.spinBox_2->value() * 2;snprintf(count_str, 3, "%02X", count);buf1[8] = count_str[0];buf1[9] = count_str[1];count /= 2;QString str = ui.lineEdit->text();QStringList list = str.split(",");qDebug() << list.length();if (list.length() < count){qDebug() << "请输入足够多的数据";QMessageBox::critical(0, "提醒", "请输入足够多的数据", QMessageBox::Ok, 0);return;}char v[5][5] = { {0} };for (int i = 0; i < count; i++){qDebug() << list[i];QByteArray ba = list.at(i).toLatin1();char* c_str = ba.data();snprintf(v[i], 5, "%04X", atoi(c_str));qDebug() << v[i];buf1[10 + i * 4 + 2] = v[i][0];buf1[10 + i * 4 + 3] = v[i][1];buf1[10 + i * 4 + 0] = v[i][2];buf1[10 + i * 4 + 1] = v[i][3];}/* 结束 */buf1[10 + count * 4] = ETX;/* 累加和 *///从命令开始到结束为止的每一个字节累加和(不包括起始字节)uint8_t sum = sum8(buf1 + 1, 10 + count * 4);char sum_str[3] = { 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 < 13 + count * 4; i++)printf("%02x ", buf1[i]);printf("\n");serial.write((char*)buf1, 13 + count * 4);fflush(stdout);}}/* 累加和 */
uint8_t PlcConnection::sum8(uint8_t data[], uint32_t len)
{uint8_t sum = 0;for (uint32_t i = 0; i < len; 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() << clientConnection[0]->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 < 4; i++)printf("%c ", a[i]);printf("\n");int v = 0;sscanf(a, "%04x", &v);//    printf("v = %d\n", v);return v;
}void PlcConnection::on_sendButton_clicked()
{/* 串口发送 *///    serial.write(ui->textEdit_2->toPlainText().toLatin1());char buf1[100] = { 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() << buf1[i];sand_str = sand_str.mid(3);qDebug() << temp;cnt++;}//    clientConnection[ui->comboBox_2->currentIndex()]->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() << "aaa";
}//void MainWindow::on_BaudBox_currentTextChanged(const QString &arg1)
//{//    qDebug() << arg1;
//    MainWindow::on_openPortBtn_clicked();
//}/* 转换 */
void PlcConnection::StringToHex(QString str, QByteArray& senddata) //字符串转换为十六进制数据0-F
{int hexdata, lowhexdata;int hexdatalen = 0;int len = str.length();senddata.resize(len / 2);char lstr, hstr;for (int i = 0; i < len; ){//char lstr,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;elsehexdata = hexdata * 16 + lowhexdata;i++;senddata[hexdatalen] = (char)hexdata;hexdatalen++;}senddata.resize(hexdatalen);
}char PlcConnection::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 ch - ch;//不在0-f范围内的会发送成0
}/* 设置 */
void PlcConnection::on_sendButton_4_clicked()
{/* 首地址 */int addr = ui.spinBox_3->value();if (addr >= 0 && addr < 1024){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 < 2; i++)buf1[2 + i + 2] = d_addr_str[i];for (int i = 0; i < 2; i++)buf1[2 + i] = d_addr_str[i + 2];/* 结束 */buf1[6] = ETX;/* 累加和 *///从命令开始到结束为止的每一个字节累加和(不包括起始字节)uint8_t sum = sum8(buf1 + 1, 6);char sum_str[3] = { 0 };snprintf(sum_str, 3, "%02X", sum);buf1[7] = sum_str[0];buf1[8] = sum_str[1];/* 打印 */printf("send:\n");for (int i = 0; i < 9; i++)printf("%02x ", buf1[i]);printf("\n");serial.write((char*)buf1, 9);fflush(stdout);          //清空缓存区}else if (addr >= 8000 && addr < 8512){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 < 2; i++)buf1[3 + i + 2] = d_addr_str[i];for (int i = 0; i < 2; i++)buf1[3 + i] = d_addr_str[i + 2];/* 结束 */buf1[7] = ETX;/* 累加和 *///从命令开始到结束为止的每一个字节累加和(不包括起始字节)uint8_t sum = sum8(buf1 + 1, 7);char sum_str[3] = { 0 };snprintf(sum_str, 3, "%02X", sum);buf1[8] = sum_str[0];buf1[9] = sum_str[1];/* 打印 */printf("send:\n");for (int i = 0; i < 10; i++)printf("%02x ", buf1[i]);printf("\n");serial.write((char*)buf1, 10);fflush(stdout);}}/* 清除 */
void PlcConnection::on_sendButton_5_clicked()
{/* 首地址 */int addr = ui.spinBox_3->value();if (addr >= 0 && addr < 1024){uint8_t buf1[100] = { 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 < 2; i++)buf1[2 + i + 2] = d_addr_str[i];for (int i = 0; i < 2; i++)buf1[2 + i] = d_addr_str[i + 2];/* 结束 */buf1[6] = ETX;/* 累加和 *///从命令开始到结束为止的每一个字节累加和(不包括起始字节)uint8_t sum = sum8(buf1 + 1, 6);char sum_str[3] = { 0 };snprintf(sum_str, 3, "%02X", sum);buf1[7] = sum_str[0];buf1[8] = sum_str[1];/* 打印 */printf("send:\n");for (int i = 0; i < 9; i++)printf("%02x ", buf1[i]);printf("\n");serial.write((char*)buf1, 9);fflush(stdout);}else if (addr >= 8000 && addr < 8512){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 < 2; i++)buf1[3 + i + 2] = d_addr_str[i];for (int i = 0; i < 2; i++)buf1[3 + i] = d_addr_str[i + 2];/* 结束 */buf1[7] = ETX;/* 累加和 *///从命令开始到结束为止的每一个字节累加和(不包括起始字节)uint8_t sum = sum8(buf1 + 1, 7);char sum_str[3] = { 0 };snprintf(sum_str, 3, "%02X", sum);buf1[8] = sum_str[0];buf1[9] = sum_str[1];/* 打印 */printf("send:\n");for (int i = 0; i < 10; i++)printf("%02x ", buf1[i]);printf("\n");serial.write((char*)buf1, 10);fflush(stdout);}
}

4.main.cpp

#pragma execution_character_set("utf-8")
#include "PlcConnection.h"
#include <QtWidgets/QApplication>int main(int argc, char *argv[])
{QApplication a(argc, argv);PlcConnection w;w.setWindowTitle("三菱FX3U编程口通信助手");w.show();return a.exec();
}

运行结果:

参考资料:

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

三菱PLC与上位机进行通讯相关推荐

  1. c#rs232与三菱通讯_C#对三菱PLC的以太网和串口通讯以及台达PLC的以太网通讯

    [实例简介] C#对三菱PLC的以太网和串口通讯以及台达PLC的以太网通讯 [实例截图] [核心代码] Conn └── Conn ├── Conn.sln └── ConnTest ├── bin ...

  2. 三菱PLC通过CC LINK IE通讯控制2个三菱伺服的测试 程序,里面有JOG HOME,定位,适合你入门参考。包合IO规划,伺候参数,PLC程序。

    三菱PLC通过CC LINK IE通讯控制2个三菱伺服的测试 程序,里面有JOG HOME,定位,适合你入门参考.包合IO规划,伺候参数,PLC程序.

  3. 雅马哈与上位机TCP通讯

    雅马哈与上位机TCP通讯 1.0 可以先用调试助手测试,先设定电脑IP以及雅马哈控制器IP,系统--通信设置,设置控制器的IP 1.1 在选项--通用以太网端口,设置GP0(通讯对象),模式伺服,通讯 ...

  4. YAMAHA机器人跟上位机TCP通讯

    工控小知识: YAMAHA机器人跟上位机TCP通讯 机器人当断电后重启后是不会自动执行程序的,这时候的机器人相当于是伺服端,在等待外部客户端给命令,需要上位机发一个@RUN+回车空格的指令,机器人返回 ...

  5. 三菱PLC和PC机通过编程口串行通信实现

    1 系统硬件构成 三菱FX系列PLC自带的编程口是RS-422接口,而PC机的串行通信口则是RS-232C接口,两者之间需要通过SC-09适配电缆才能通信.不同设备上相同类型的通信接口的引脚定义可能存 ...

  6. UR机器人开发(1)-上位机直接通讯控制

    第一次使用UR机器人,并且尝试与上位机直接通讯,进行运动控制.因此本文记录整个安装调试过程,避免后续踩坑.本文采用socket通讯方式,通过python编程,实现机器人乱序抓取(暂且不考虑视觉部分). ...

  7. 工控通讯经历1:(C#)三菱FX5U-32M与上位机通讯(超详细!)

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 前言 本文介绍的是如何在PC端(工控机)上利用MX COMPONENT软件通过直连或者经过交换机的连接方式建立与三菱PLC(FX5U- ...

  8. 基恩士上位机链路通讯_基恩士PLC通讯源码

    基恩士PLC KV7000,8000还是比较好用的,那如何和上位机通讯,我把源码写出来了.采用上位链路通讯,基恩士官方给我们留了8501端口,这个端口有意思刚好是我生日.基恩士的资料我觉得做的特别好, ...

  9. 如何实现台达PLC与上位机的数据通讯?

    台达PLC是国产PLC品牌中性价比较高的一款,具备简单易操作的编程模式.稳定可靠的性能,在工业控制领域中应用广泛.在实际使用过程中,通过台达PLC进行生产设备的管理控制,需要数据采集传输,一般在触摸屏 ...

  10. ABB AC500 系列 PLC 与上位机iFix 的通讯配置

    ABB PLC IP 及 MODBUS TCP/IP 协议设置 通过 IP config tool 配置设备 IP 在 软件中,有 3 种方式可以进入 IP config tool 的配置界面  双 ...

最新文章

  1. mysql优化之sakila测试数据库
  2. [MyBatisPlus]乐观锁和悲观锁
  3. 超级简单的Region对比识别验证码
  4. 两个有序数组求中位数log(m+n)复杂度
  5. python函数详解图_[宜配屋]听图阁
  6. Linux中强大的top命令
  7. warning C4819 解决方案 warning C4819: The file contains a character that cannot be represented in the cu
  8. 获取div相对文档的位置
  9. 全国青少年软件编程(C语言)等级考试试题-2019年9月(一级含答案)
  10. 【Gulimall+】免费白嫖内网穿透:小米球ngrok
  11. Java游戏项目分享
  12. 互联网晚报 | 12月7日 星期二 | 阿里新设两大数字商业板块;B站宣布迈入8K超高清时代;中国物流集团正式成立...
  13. 中国吸尘器产业发展前瞻与市场投资盈利分析报告2021年版
  14. 用笔记本做路由器共享4G流量
  15. java截取固定大小图片_JAVA技巧(长字符串按指定长度截取)
  16. Oracle发布Oracle Enterprise Manager 12c
  17. grub2命令 linux启动盘,Grub2 制作多系统U盘启动
  18. TPC/IP协议中与IP相关的知识点简介
  19. 成立3年亮出成绩单:高博要打造医疗创新高地
  20. vue echarts柱状图 隐藏坐标轴刻度线等 横向显示百分比

热门文章

  1. Spotfire 条形图属性 直线和曲线 格式设置
  2. 物联网服务器Domoticz自带的消息推送到QQ邮箱
  3. ztree树默认根据ID默认选中该条数据
  4. 永远不合规的Android应用隐私政策-Andrid开发者的吐槽
  5. 国标GB28181协议国标平台EasyGBS客户端作为上级平台如何跟下级海康8700平台对接?
  6. python数据分析 活用pandas库_Python数据分析:活用Pandas库
  7. 使用AJAX中的get请求中文乱码的问题
  8. 分享一款超好用的 Web SSH 客户端工具
  9. PSF 点扩展函数 (from WikiPedia)
  10. mysql绿化,Mysql精简与绿化版本