三菱PLC与上位机串口通信
一.三菱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、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 <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;
else
hexdata = 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();
}
运行结果:
参考资料:
我还下了网上两个别人的实例,若有需要参考的评论区留下邮箱,我把那两个工程文件发给你。。。