三菱PLC与上位机进行通讯
三菱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与上位机进行通讯相关推荐
- c#rs232与三菱通讯_C#对三菱PLC的以太网和串口通讯以及台达PLC的以太网通讯
[实例简介] C#对三菱PLC的以太网和串口通讯以及台达PLC的以太网通讯 [实例截图] [核心代码] Conn └── Conn ├── Conn.sln └── ConnTest ├── bin ...
- 三菱PLC通过CC LINK IE通讯控制2个三菱伺服的测试 程序,里面有JOG HOME,定位,适合你入门参考。包合IO规划,伺候参数,PLC程序。
三菱PLC通过CC LINK IE通讯控制2个三菱伺服的测试 程序,里面有JOG HOME,定位,适合你入门参考.包合IO规划,伺候参数,PLC程序.
- 雅马哈与上位机TCP通讯
雅马哈与上位机TCP通讯 1.0 可以先用调试助手测试,先设定电脑IP以及雅马哈控制器IP,系统--通信设置,设置控制器的IP 1.1 在选项--通用以太网端口,设置GP0(通讯对象),模式伺服,通讯 ...
- YAMAHA机器人跟上位机TCP通讯
工控小知识: YAMAHA机器人跟上位机TCP通讯 机器人当断电后重启后是不会自动执行程序的,这时候的机器人相当于是伺服端,在等待外部客户端给命令,需要上位机发一个@RUN+回车空格的指令,机器人返回 ...
- 三菱PLC和PC机通过编程口串行通信实现
1 系统硬件构成 三菱FX系列PLC自带的编程口是RS-422接口,而PC机的串行通信口则是RS-232C接口,两者之间需要通过SC-09适配电缆才能通信.不同设备上相同类型的通信接口的引脚定义可能存 ...
- UR机器人开发(1)-上位机直接通讯控制
第一次使用UR机器人,并且尝试与上位机直接通讯,进行运动控制.因此本文记录整个安装调试过程,避免后续踩坑.本文采用socket通讯方式,通过python编程,实现机器人乱序抓取(暂且不考虑视觉部分). ...
- 工控通讯经历1:(C#)三菱FX5U-32M与上位机通讯(超详细!)
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 前言 本文介绍的是如何在PC端(工控机)上利用MX COMPONENT软件通过直连或者经过交换机的连接方式建立与三菱PLC(FX5U- ...
- 基恩士上位机链路通讯_基恩士PLC通讯源码
基恩士PLC KV7000,8000还是比较好用的,那如何和上位机通讯,我把源码写出来了.采用上位链路通讯,基恩士官方给我们留了8501端口,这个端口有意思刚好是我生日.基恩士的资料我觉得做的特别好, ...
- 如何实现台达PLC与上位机的数据通讯?
台达PLC是国产PLC品牌中性价比较高的一款,具备简单易操作的编程模式.稳定可靠的性能,在工业控制领域中应用广泛.在实际使用过程中,通过台达PLC进行生产设备的管理控制,需要数据采集传输,一般在触摸屏 ...
- ABB AC500 系列 PLC 与上位机iFix 的通讯配置
ABB PLC IP 及 MODBUS TCP/IP 协议设置 通过 IP config tool 配置设备 IP 在 软件中,有 3 种方式可以进入 IP config tool 的配置界面 双 ...
最新文章
- mysql优化之sakila测试数据库
- [MyBatisPlus]乐观锁和悲观锁
- 超级简单的Region对比识别验证码
- 两个有序数组求中位数log(m+n)复杂度
- python函数详解图_[宜配屋]听图阁
- Linux中强大的top命令
- warning C4819 解决方案 warning C4819: The file contains a character that cannot be represented in the cu
- 获取div相对文档的位置
- 全国青少年软件编程(C语言)等级考试试题-2019年9月(一级含答案)
- 【Gulimall+】免费白嫖内网穿透:小米球ngrok
- Java游戏项目分享
- 互联网晚报 | 12月7日 星期二 | 阿里新设两大数字商业板块;B站宣布迈入8K超高清时代;中国物流集团正式成立...
- 中国吸尘器产业发展前瞻与市场投资盈利分析报告2021年版
- 用笔记本做路由器共享4G流量
- java截取固定大小图片_JAVA技巧(长字符串按指定长度截取)
- Oracle发布Oracle Enterprise Manager 12c
- grub2命令 linux启动盘,Grub2 制作多系统U盘启动
- TPC/IP协议中与IP相关的知识点简介
- 成立3年亮出成绩单:高博要打造医疗创新高地
- vue echarts柱状图 隐藏坐标轴刻度线等 横向显示百分比
热门文章
- Spotfire 条形图属性 直线和曲线 格式设置
- 物联网服务器Domoticz自带的消息推送到QQ邮箱
- ztree树默认根据ID默认选中该条数据
- 永远不合规的Android应用隐私政策-Andrid开发者的吐槽
- 国标GB28181协议国标平台EasyGBS客户端作为上级平台如何跟下级海康8700平台对接?
- python数据分析 活用pandas库_Python数据分析:活用Pandas库
- 使用AJAX中的get请求中文乱码的问题
- 分享一款超好用的 Web SSH 客户端工具
- PSF 点扩展函数 (from WikiPedia)
- mysql绿化,Mysql精简与绿化版本