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

五个标示

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();
}

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

参考资料:

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

  • 21
    点赞
  • 91
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 133
    评论
Simulink是一款功能强大的工程仿真软件,可以用于模拟和建模各种系统。Can通信是一种常用的实时通信协议,常用于汽车电子系统和其他实时控制系统中。 要使用Simulink进行CAN通信,首先需要安装Simulink的CAN通信支持包。然后,可以按照以下步骤进行: 1. 打开Simulink并创建一个新的模型。 2. 在模型中选择CAN通信的节点。可以从“Simulink Library Browser”中的“Simscape -> Connectors”类别中找到CAN通信节点。 3. 在模型中添加CAN通信节点,并通过连接线将节点连接起来。 4. 配置CAN节点的参数,例如CAN通信速率、CAN数据帧类型等。可以通过右键单击节点并选择“Block Parameters”进行配置。 5. 在模型中添加发送和接收CAN消息的功能。可以使用“CAN Transmit”和“CAN Receive”节点进行发送和接收数据。 6. 配置发送和接收节点的参数,例如CAN标识符和数据格式。可以通过右键单击节点并选择“Block Parameters”进行配置。 7. 在模型中添加其他所需的功能和控制逻辑。可以使用Simulink的其他库和工具。 8. 转换模型以生成C代码,并进行编译和烧录到目标硬件中。 9. 在目标硬件上运行生成的代码,并连接CAN网络。 10. 监视和分析CAN通信数据,以确保正确的数据传输和通信。 以上是使用Simulink进行CAN通信的基本步骤。当然,具体的操作步骤和配置参数可能因具体的应用场景和硬件平台而有所不同。因此,在实际使用中,建议参考相关的Simulink和硬件文档,并根据具体要求进行配置和调整。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 133
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

boss-dog

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值