飞凌嵌入式
直播中

华仔stm32

3年用户 2984经验值
擅长:嵌入式技术
私信 关注
[技术]

【飞凌RK3568开发板试用体验】QT的CAN调试器

在can设备树的修改成功后,利用QT5的QCanBusDevice模块创建一个can的调试模块(注意这个例程是由野火linux_can摘抄而来,自己做了一点修改)
直接上代码:socketcan.pro:

QT       += core gui serialbus

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

CONFIG += c++11

# The following define makes your compiler emit warnings if you use
# any Qt feature that has been marked deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS

# You can also make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

SOURCES += \
    main.cpp \
    mainwindow.cpp

HEADERS += \
    mainwindow.h

# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target

mainwindow.h:

/******************************************************************
Copyright © Deng Zhimao Co., Ltd. 1990-2021. All rights reserved.
* @projectName   04_socketcan
* [url=home.php?mod=space&uid=2666770]@Brief[/url]         mainwindow.h
* [url=home.php?mod=space&uid=40524]@author[/url]        Deng Zhimao
* @email         1252699831@qq.com
* [url=home.php?mod=space&uid=191254]@net[/url]           www.openedv.com
* @date          2021-03-15
*******************************************************************/
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QCanBusDevice>
#include <QCanBus>
#include <QPushButton>
#include <QTextBrowser>
#include <QLineEdit>
#include <QVBoxLayout>
#include <QLabel>
#include <QComboBox>
#include <QGridLayout>
#include <QMessageBox>
#include <QDebug>

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private:
    /* CAN设备 */
    QCanBusDevice *canDevice;

    /* 用作接收数据 */
    QTextBrowser *textBrowser;

    /* 用作发送数据 */
    QLineEdit *lineEdit;

    /* 按钮 */
    QPushButton *pushButton[2];

    /* 下拉选择盒子 */
    QComboBox *comboBox[3];

    /* 标签 */
    QLabel *label[4];

    /* 垂直布局 */
    QVBoxLayout *vboxLayout;

    /* 网络布局 */
    QGridLayout *gridLayout;

    /* 主布局 */
    QWidget *mainWidget;

    /* 设置功能区域 */
    QWidget *funcWidget;

    /* 布局初始化 */
    void layoutInit();

    /* 插件类型项初始化 */
    void pluginItemInit();

    /* 比特率项初始化 */
    void bitrateItemInit();

private slots:
    /* 发送消息 */
    void sendFrame();

    /* 接收消息 */
    void receivedFrames();

    /* 插件发生改变 */
    void pluginChanged(int);

    /* 处理can错误 */
    void canDeviceErrors(QCanBusDevice::CanBusError) const;

    /* 连接或者断开can */
    void connectDevice();
};
#endif // MAINWINDOW_H

main.cpp:

#include "mainwindow.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}

mainwindow.cpp

/******************************************************************
Copyright © Deng Zhimao Co., Ltd. 1990-2021. All rights reserved.
* @projectName   04_socketcan
* @brief         mainwindow.cpp
* @author        Deng Zhimao
* @email         1252699831@qq.com
* @net           www.openedv.com
* @date          2021-03-15
*******************************************************************/
#include "mainwindow.h"
#include <QGuiApplication>
#include <QScreen>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    /* 使用系统指令比特率初始化CAN,默认为500000bits/s */
    system("ifconfig can0 down");
    system("ip link set up can0 type can bitrate 500000 restart-ms 100");

    /* 布局初始化 */
    layoutInit();

    /* 可用插件初始化 */
    pluginItemInit();

    /* 可用接口项初始化 */
    pluginChanged(comboBox[0]->currentIndex());

    /* 比特率项初始化 */
    bitrateItemInit();
}

MainWindow::~MainWindow()
{
}

static QString frameFlags(const QCanBusFrame &frame)
{
    /* 格式化接收到的消息 */
    QString result = QLatin1String(" --- ");

    if (frame.hasBitrateSwitch())
        result[1] = QLatin1Char('B');
    if (frame.hasErrorStateIndicator())
        result[2] = QLatin1Char('E');
    if (frame.hasLocalEcho())
        result[3] = QLatin1Char('L');

    return result;
}

/* 发送消息 */
void MainWindow::sendFrame()
{
    if (!canDevice)
        return;
    /* 读取QLineEdit的文件 */
    QString str = lineEdit->text();
    QByteArray data = 0;
    QString strTemp = nullptr;
    /* 以空格分隔lineEdit的内容,并存储到字符串链表中 */
    QStringList strlist = str.split(' ');
    for (int i = 1; i < strlist.count(); i++) {
        strTemp = strTemp + strlist[i];
    }
    /* 将字符串的内容转为QByteArray类型 */
    data = QByteArray::fromHex(strTemp.toLatin1());

    bool ok;
    /* 以16进制读取要发送的帧内容里第一个数据,并作为帧ID */
    int framId = strlist[0].toInt(&ok, 16);
    QCanBusFrame frame = QCanBusFrame(framId, data);
    /* 写入帧 */
    canDevice->writeFrame(frame);
}

/* 接收消息 */
void MainWindow::receivedFrames()
{
    if (!canDevice)
        return;

    /* 读取帧 */
    while (canDevice->framesAvailable()) {
        const QCanBusFrame frame = canDevice->readFrame();
        QString view;
        if (frame.frameType() == QCanBusFrame::ErrorFrame)
            view = canDevice->interpretErrorFrame(frame);
        else
            view = frame.toString();

        const QString time = QString::fromLatin1("%1.%2  ")
                .arg(frame.timeStamp()
                     .seconds(), 10, 10, QLatin1Char(' '))
                .arg(frame.timeStamp()
                     .microSeconds() / 100, 4, 10, QLatin1Char('0'));

        const QString flags = frameFlags(frame);
        /* 接收消息框追加接收到的消息 */
        textBrowser->insertPlainText(time + flags + view + "\n");
        textBrowser->moveCursor(QTextCursor::End);
    }
}

void MainWindow::layoutInit()
{
    /* 获取屏幕的分辨率,Qt官方建议使用这
     * 种方法获取屏幕分辨率,防上多屏设备导致对应不上
     * 注意,这是获取整个桌面系统的分辨率
     */
    QList <QScreen *> list_screen =  QGuiApplication::screens();

    /* 如果是ARM平台,直接设置大小为屏幕的大小 */
#if __arm__
    /* 重设大小 */
    this->resize(list_screen.at(0)->geometry().width(),
                 list_screen.at(0)->geometry().height());
#else
    /* 否则则设置主窗体大小为800x480 */
    this->resize(800, 480);
#endif
    /* 对象初始化 */
    textBrowser = new QTextBrowser();
    lineEdit = new QLineEdit();
    vboxLayout = new QVBoxLayout();
    funcWidget = new QWidget();
    mainWidget = new QWidget();
    gridLayout = new QGridLayout();

    /* QList链表,字符串类型 */
    QList <QString> list1;
    list1<<"插件类型:"<<"可用接口:"<<"比特率bits/sec:";

    for (int i = 0; i < 3; i++) {
        label[i] = new QLabel(list1[i]);
        /* 设置最小宽度与高度 */
        label[i]->setMinimumSize(120, 30);
        label[i]->setMaximumHeight(50);
        /* 自动调整label的大小 */
        label[i]->setSizePolicy(QSizePolicy::Expanding,
                                QSizePolicy::Expanding);
        /* 将label[i]添加至网格的坐标(0, i) */
        gridLayout->addWidget(label[i], 0, i);
    }
    label[3] = new QLabel();
    label[3]->setMaximumHeight(30);

    for (int i = 0; i < 3; i++) {
        comboBox[i] = new QComboBox();
        comboBox[i]->setMinimumSize(120, 30);
        comboBox[i]->setMaximumHeight(50);
        /* 自动调整label的大小 */
        comboBox[i]->setSizePolicy(QSizePolicy::Expanding,
                                   QSizePolicy::Expanding);
        /* 将comboBox[i]添加至网格的坐标(1, i) */
        gridLayout->addWidget(comboBox[i], 1, i);
    }

    /* QList链表,字符串类型 */
    QList <QString> list2;
    list2<<"发送"<<"连接CAN";

    for (int i = 0; i < 2; i++) {
        pushButton[i] = new QPushButton(list2[i]);
        pushButton[i]->setMinimumSize(120, 30);
        pushButton[i]->setMaximumHeight(50);
        /* 自动调整label的大小 */
        pushButton[i]->setSizePolicy(QSizePolicy::Expanding,
                                     QSizePolicy::Expanding);
        /* 将pushButton[0]添加至网格的坐标(i, 3) */
        gridLayout->addWidget(pushButton[i], i, 3);
    }
    pushButton[0]->setEnabled(false);

    /* 布局 */
    vboxLayout->addWidget(textBrowser);
    vboxLayout->addWidget(lineEdit);
    funcWidget->setLayout(gridLayout);
    vboxLayout->addWidget(funcWidget);
    vboxLayout->addWidget(label[3]);
    mainWidget->setLayout(vboxLayout);
    this->setCentralWidget(mainWidget);

    /* 设置文本 */
    textBrowser->setPlaceholderText("系统时间 帧ID 长度 数据");
    lineEdit->setText("123 aa 77 66 55 44 33 22 11");
    label[3]->setText(tr("未连接!"));

    connect(pushButton[1], SIGNAL(clicked()),
            this, SLOT(connectDevice()));
    connect(pushButton[0], SIGNAL(clicked()),
            this, SLOT(sendFrame()));
}

/* 从系统中读取可用的插件,并显示到comboBox[0] */
void MainWindow::pluginItemInit()
{
    comboBox[0]->addItems(QCanBus::instance()->plugins());
    for (int i = 0; i < QCanBus::instance()->plugins().count(); i++) {
        if (QCanBus::instance()->plugins().at(i) == "socketcan")
            comboBox[0]->setCurrentIndex(i);
    }
    connect(comboBox[0], SIGNAL(currentIndexChanged(int)),
            this, SLOT(pluginChanged(int)));
}

/* 插件类型改变 */
void MainWindow::pluginChanged(int)
{
    QList<QCanBusDeviceInfo> interfaces;
    comboBox[1]->clear();
    /* 当我们改变插件时,我们同时需要将可用接口,从插件类型中读取出来 */
    interfaces = QCanBus::instance()
            ->availableDevices(comboBox[0]->currentText());
    for (const QCanBusDeviceInfo &info : qAsConst(interfaces)) {
        comboBox[1]->addItem(info.name());
    }
}

/* 初始化一些常用的比特率,can的比特率不是随便设置的,有相应的计算公式 */
void MainWindow::bitrateItemInit()
{
    const QList<int> rates = {
        10000, 20000, 50000, 100000, 125000,
        250000, 500000, 800000, 1000000
    };

    for (int rate : rates)
        comboBox[2]->addItem(QString::number(rate), rate);

    /* 默认初始化以1000000比特率 */
    comboBox[2]->setCurrentIndex(6);
}

/* 连接或断开CAN */
void MainWindow::connectDevice()
{
    if (pushButton[1]->text() == "连接CAN") {
        /* Qt中的QCanBusDevice::BitRateKey不能设置比特率 */
        QString cmd1 = tr("ifconfig %1 down")
                .arg(comboBox[1]->currentText());
        QString cmd2 =
                tr("ip link set up %1 type can bitrate %2 restart-ms 100")
                .arg(comboBox[1]->currentText())
                .arg(comboBox[2]->currentText());
        /* 使用系统指令以设置的比特率初始化CAN */
        system(cmd1.toStdString().c_str());
        system(cmd2.toStdString().c_str());

        QString errorString;
        /* 以设置的插件名与接口实例化canDevice */
        canDevice = QCanBus::instance()->
                createDevice(comboBox[0]->currentText(),
                comboBox[1]->currentText(),
                &errorString);

        if (!canDevice) {
            label[3]->setText(
                        tr("Error creating device '%1', reason: '%2'")
                        .arg(comboBox[0]->currentText())
                    .arg(errorString));
            return;
        }

        /* 连接CAN */
        if (!canDevice->connectDevice()) {
            label[3]->setText(tr("Connection error: %1")
                              .arg(canDevice->errorString()));
            delete canDevice;
            canDevice = nullptr;

            return;
        }

        connect(canDevice, SIGNAL(framesReceived()),
                this, SLOT(receivedFrames()));
        connect(canDevice,
                SIGNAL(errorOccurred(QCanBusDevice::CanBusError)),
                this,
                SLOT(canDeviceErrors(QCanBusDevice::CanBusError)));
        /* 将连接信息插入到label */
        label[3]->setText(
                    tr("插件类型为: %1, 已连接到 %2, 比特率为 %3 kBit/s")
                    .arg(comboBox[0]->currentText())
                .arg(comboBox[1]->currentText())
                .arg(comboBox[2]->currentText().toInt() / 1000));
        pushButton[1]->setText("断开CAN");
        /* 使能/失能 */
        pushButton[0]->setEnabled(true);
        comboBox[0]->setEnabled(false);
        comboBox[1]->setEnabled(false);
        comboBox[2]->setEnabled(false);
    } else {
        if (!canDevice)
            return;

        /* 断开连接 */
        canDevice->disconnectDevice();
        delete canDevice;
        canDevice = nullptr;
        pushButton[1]->setText("连接CAN");
        pushButton[0]->setEnabled(false);
        label[3]->setText(tr("未连接!"));
        comboBox[0]->setEnabled(true);
        comboBox[1]->setEnabled(true);
        comboBox[2]->setEnabled(true);
    }
}

void MainWindow::canDeviceErrors(QCanBusDevice::CanBusError error) const
{
    /* 错误处理 */
    switch (error) {
    case QCanBusDevice::ReadError:
    case QCanBusDevice::WriteError:
    case QCanBusDevice::ConnectionError:
    case QCanBusDevice::ConfigurationError:
    case QCanBusDevice::UnknownError:
        label[3]->setText(canDevice->errorString());
        break;
    default:
        break;
    }
}

编译后生成socketcan上传给开发板,运行程序:
[root@ok3568:/test]# ./socket_can
QStandardPaths: wrong permissions on runtime directory /var/run, 7755 instead of 7700
[ 956.007316] IPv6: ADDRCONF(NETDEV_CHANGE): can0: link becomes ready
qt.qpa.wayland: No shell integration named "xdg-shell" found

baaff9118361b20ebcb3d9139ad916d.jpg

到此,我们在OK3568上用python跟QT、C、shell都实现了对CAN总线的操作。可以说这款芯片,对can的支持非常好,特别适合在汽车领域,或者是工程机领域。

QTcan

更多回帖

发帖
×
20
完善资料,
赚取积分