三菱PLC与上位机进行通讯

2023-05-16

三菱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

五个标示

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(命令为78) 
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();
}

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

参考资料:

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

三菱PLC与上位机进行通讯 的相关文章

  • plc无法跟计算机通信,无法与PLC通信

    首先 xff0c 先确定这个电缆是否有问题 xff0c 可以拿个之前可以通讯上的CPU试试 xff0c 如果电缆没问题 xff0c 建议把这个软件卸载重新安装 xff0c 不知能否帮到你 回答者 xff1a yf1234 新生 amp nb
  • C# SerialPort 读写三菱FX系列PLC

    1 xff1a 串口初始化 com 61 new SerialPort 34 COM3 34 9600 Parity Even 7 StopBits One 2 xff1a 打开关闭串口 if com IsOpen com Close co
  • PLC中相关量的斜坡控制

    转载于 https www cnblogs com chenpan6227 p 11558750 html
  • 【毕业设计】plc 水果采摘机械臂 -----【论文+二维图+三维图】

    效率太 低 xff0c 8 0年 代后 才开 始研 究和 制造 切割 型采摘 器 果 园 采 摘 野葱 xff0c 人 工使 用剪 刀采 摘 xff0c 发展 到使 用 机 械装 置采 摘 xff0c 例 如一 种人 工财 政才 知采 摘器
  • 串口的偶校验位设置_最基本的PLC串口通讯和基本的通讯接口你都了解吗?

    电力作业人员在使用PLC的时候会接触到很多的通讯协议以及通讯接口 xff0c 最基本的PLC串口通讯和基本的通讯接口你都了解吗 xff1f 1 xff0c 什么是串口通讯 xff1f 串口是计算机上一种非常通用设备通信的协议 不要与通用串行
  • LabVIEW通过以太网(TCP)与S7-1200 PLC通信

    LabVIEW通过以太网 TCP 与S7 1200 PLC通信 laiping 20170901 摘要 本文介绍LabView采用以太网 TCP 方式与西门子S7 1200PLC通讯 关键字 LabView S7 1200 TCP PLC
  • 西门子PLC内部的数据类型大全

    西门子PLC的数据类型种类繁多 本文进行了收集 并指明了适用范围 长度 供需要进行数据采集和分析的朋友们参考 本表格整理自博图V14 不保证更高级版本不会新增数据类型 请使用中注意 类别 数据类型 长度 位 长度 字节 S7 300 400
  • PLC(二)西门子S7-200PLC基础知识

    西门子S7 200PLC基础知识 一 西门子S7 200PLC模块与界线 S7 200系列PLC硬件包括S7 200CPU由6个型号 使用方法基本相同 西门子S7 200CPU模块 S7 200CPU将微处理器 集成电源和多个数字量I O点
  • PLC与旋转编码器的连接

    PLC与旋转编码器的连接 PLC与常用输入设备的连接 旋转编码器是一种光电式旋转测量装置 它将被测的角位移直接转换成数字信号 高速脉冲信号 因此可将旋转编码器的输出脉冲信号直接输入给PLC 利用PLC的高速计数器对其脉冲信号进行计数 以获得
  • C# 三菱FX PLC XYS读写,串口读写

    花了两三天写了一个这个 本来想着自己用的 看到有很多替代品 果断开源了吧 下载地址 https github com t39q MitsubishiFX PLC XYS 以下是原理 后面有帮助类和调用方法 调用方法 private void
  • 西门子博图指令(计数器操作)

    计数器操作 综述 加计数 介绍 程序 程序演示 减计数 介绍 程序 程序演示 加减计数 介绍 程序 程序演示 源程序 综述 主要介绍博图V15中计数器功能块指令的相关操作 仿真PLC为1200系列 1 加计数 介绍 接口参数 声明 数据类型
  • 如何创建你的第一个西门子200PLC程序

    更多关于西门子S7 200PLC内容请查看 西门子200系列PLC学习课程大纲 创建西门子200PLC程序分五步 1 打开Micro WIN软件 2 新建工程 3 打开程序编辑器 4 输入程序指令 5 保存程序 我们以下图程序为例讲解西门子
  • 三菱PLC 闪烁动作 ST语言

    闪烁动作 输入X006为ON时 定时器T2的触点每隔5秒瞬间动作一次 T2的触点 每次ON时都使输出Y007交替ON OFF OUT T X006 AND NOT TS2 TC2 K50 ALT X006 AND TS2 Y007
  • FPGA、PLC、STM32、单片机、计算机等概念间的关系、区别及各自的优缺点

    FPGA PLC STM32 Arduino 单片机 计算机等概念间的关系 区别及各自的优缺点 入门之初 对于标题所列各个概念总是含混不清的 这样一来 不知道自己应该从何学起 或者不知道自己想要实现的IDEA应该使用什么样的硬件系统最合适
  • PLC学习札记

    PLC概念相关 PLC编程 PLC系统 通过了解概念 知道了plc的核心是对继电器编程 什么是继电器 继电器 最后 阅读PLC指导手册 结合之前学习的知识 融会贯通 PLC编程手册 FX系列 pdf 指令表运行机制 仅限于本项目
  • S71200外围设备接线-输入接线

    S71200外围设备接线 输入端子接线 含NPN和PNP 传感器接线 作为一个PLC的初学者 我觉得第一件事请并不是学习什么TIA Portal软件或者编程指令 而是了解PLC的系统参数和外围设备接线 上面的一张文章 我通过图文的方式简单讲
  • 西门子S7-200PLC的自锁

    自锁 百度 交流接触器通过自身的常开辅助触头使线圈总是处于得电状态的现象叫做自锁 在通常的电路中 按下开关 电路通电 松开开关 电路又断开了 一旦按下开关 就能够自动保持持续通电 直到按下其它开关使之断路为止 这样的电路 称为自锁电路 置位
  • 自己写的PLC编程软件,和FANUC PMC功能基本保持一致

    自己写的PLC编程软件 和FANUC PMC功能基本保持一致 下载地址 免积分 链接 pan baidu com s 162 GcF7wh SNT3McATPPmg 提取码 1234 https download csdn net down
  • 从 Android 向 PLC 发送布尔值

    我能够与 PLC 建立连接以从中读取数据 现在有一个问题 那就是我必须编写一种方法来修改PLC中的数据 为了实现这一点 我必须向 PLC 发送两个值 一个 int 值和一个 boolean 值 我通过 net wimpi modbus 包中
  • VBA 中运行时错误 429,但类已注册

    我正在尝试重新创建一个程序 该程序使用 JavaScript 打开与 PLC 的连接 然后在网页上显示各种信息 由于各种原因 我宁愿将其以 MS Access 的形式保存 并且一直在努力寻找合适的 dll 来使用 Jet32X dll 如果

随机推荐

  • 《FPGA学习》->多个按键控制LED灯

    x1f34e 与其担心未来 xff0c 不如现在好好努力 在这条路上 xff0c 只有奋斗才能给你安全感 你若努力 xff0c 全世界都会为你让路 本次项目任务 xff0c 利用开发板上的4个按键KEY1 xff0c KEY2 xff0c
  • 《FPGA学习》->呼吸灯

    x1f34e 与其担心未来 xff0c 不如现在好好努力 在这条路上 xff0c 只有奋斗才能给你安全感 你若努力 xff0c 全世界都会为你让路 呼吸灯 xff0c 简而言之就像人类呼吸一样 xff0c 有节奏的让LED灯从 xff1a
  • 《FPGA学习》->蜂鸣器播放

    x1f34e 与其担心未来 xff0c 不如现在好好努力 在这条路上 xff0c 只有奋斗才能给你安全感 你若努力 xff0c 全世界都会为你让路 蜂鸣器的发声原理由振动装置和谐振装置组成 xff0c 而蜂鸣器又分为无源他激型与有源自激型
  • 基于STM32的语音控制电机

    号外号外 单片机项目实战课程又更新了 炸弹 大家好 我是 朽木自雕i 一个闲暇时间用来敲敲代码 画画板子 焊焊电路 玩玩单片机 搞搞Linux 写写Blog记录成长的技术人er 很高兴再次见
  • ESP32单片机入门篇

    目录 一 ESP32单片机的基本概念 1 双核架构 2 Wi Fi和蓝牙功能 3 集成多种外设 4 支持多种操作系统 二 开发环境 1 Arduino IDE 2 ESP IDF 三 开发语言 四 注意事项 五 代码例程 xff08 1 x
  • 什么是死锁,产生死锁的原因及必要条件

    什么是死锁 xff1f 所谓死锁 xff0c 是指多个进程在运行过程中因争夺资源而造成的一种僵局 xff0c 当进程处于这种僵持状态时 xff0c 若无外力作用 xff0c 它们都将无法再向前推进 因此我们举个例子来描述 xff0c 如果此
  • ESP32红外控制舵机

    目录 一 ESP32红外解码 二 ESP32舵机控制 三 ESP32红外控制舵机 结语 ESP32作为一款功能强大的单片机 xff0c 常被应用于物联网 智能家居 智能硬件等领域 与其他单片机相比 xff0c ESP32具有更高的运行速度和
  • ESP32 OTA升级

    目录 一 ESP32 OTA升级原理 1 ESP32固件编译 2 固件的远程传输 二 基于ESP32HTTPUpdate库的OTA升级 1 硬件准备 2 软件实现 三 注意事项 1 升级文件大小限制 2 WiFi稳定性 3 固件版本号 结语
  • 基于ESP32的温湿度环境监测

    目录 一 传感器介绍 二 设计思路 三 电路连接 四 项目代码 五 注意事项 一 传感器介绍 SCH30 是一款温湿度一体化数字传感器 xff0c 采用CMOSens 技术 xff0c 提供出色的性能 可靠性和稳定性 它还具有超低能耗 xf
  • 小觅相机SDK安装与报错解决

    目录 安装小觅相机SDK1 下载SDK2 准备依赖3 编译代码出现问题 xff1a 报错 xff1a 原因 xff1a 解决方案 xff1a 4 编译安装ROS版本ROS的安装 5 配置 bashrc文件6 运行相机 最后顺利完成SDK安装
  • 基于51单片机和物联网的智能家居系统(ESP8266物联网模块)

    前言 该智能家居系统以 STC89C52单片机为控制核心 xff0c 结合 LCD1602 液晶显示屏 L298N电机驱动模块 光敏电阻 xff0c ESP8266WiFi模块 xff0c DS18B20温度计设计并实现了自动感光窗帘与居室
  • aarch64-linux-gnu-gcc交叉编译链工具

    aarch64 linux gnu gcc交叉编译工具链 安装编译可执行文件交叉编译 自己记录学习所用 安装 按下 CTRL 43 ALT 43 T打开控制台 xff0c 输入如下指令安装 span class token comment
  • CMakeLists.txt的创建和基本使用

    文章目录 1 简单介绍2 一个简单的例子3 将主函数及库函数一起编译4 带上外部库 学习记录所用 1 简单介绍 CMakeLists txt文件的编写比MakeFile文件的编写更加简单和容易理解 CMakeLists txt通过cmake
  • linux线程切换怎么实现

    Linux线程切换的实现涉及到操作系统的调度 和线程上下文 的切换 线程上下文包括程序计数器 xff08 PC xff09 和寄存器值 xff0c 以及线程的堆栈和堆栈指针等 操作系统通过调度器决定哪个线程将获得CPU时间片来执行 当一个线
  • PID实时无线调参

    今天实现了PID参数的实时无线整定 xff0c 记录一下历程 1 将CRC h CRC c usart2 c usart2 h等文件添加到STM32工程中 如下图 xff1a 2 其中 xff0c CRC h CRC c用于数据包的校验 x
  • 海康威视工业相机SDK二次开发(VS+Opencv+QT+海康SDK+C++)(一)

    最近在做一个项目 xff0c 涉及到工业相机 xff0c 需要对其进行二次开发 相机方面选择了海康威视 xff0c 网上关于海康威视工业相机SDK的开发资料很少 xff0c 官方文档里面虽然写的是支持C 43 43 开发的 xff0c 但其
  • FFmpeg源码分析:写音视频帧av_write_frame()

    FFmpeg在libavformat模块提供音视频的muxer封装与demuxer解封装 其中muxer封装文件包括avformat write header av write frame 和av write trailer 本文主要探讨a
  • 海康威视工业相机SDK二次开发(VS+Opencv+QT+海康SDK+C++)(二)

    本文接上次的博客海康威视工业相机SDK二次开发 xff08 VS 43 Opencv 43 QT 43 海康SDK 43 C 43 43 xff09 xff08 一 xff09 xff0c 上个博客中并未用到QT xff0c 本文介绍项目内
  • 单目相机标定(使用Matlab)

    内容 一 单目视觉成像原理1 理想情况下相机成像模型1 1 世界坐标系 gt 相机坐标系1 2 相机坐标系 gt 图像坐标系1 3 图像坐标系 gt 像素坐标系1 4 总结 xff1a 世界坐标系 gt 像素坐标系 二 考虑畸变情况下相机成
  • 三菱PLC与上位机进行通讯

    三菱PLC与上位机串口通信 一 三菱Fx系列PLC编程口通讯协议地址算法1 DEVICE READ xff08 读出软设备状态值 xff09 2 DEVICE WRITE xff08 向PLC 软设备写入值 xff09 3 位设备强制置位