项目上需要上位机和底层板子进行can fd通讯,上位机采集板子的数据并检测其工作情况,底层板子数据通过can fd进行传输。前提条件是安装了PeakOemDrv.exe这个驱动(供应商提供,网上应该也能下载到),然后上位机通过USB线连接板子硬件,硬件连接成功后,在设备管理器中可以查看设备信息,如下:
编写的peakcan接口主要参考了这篇文章:QT5使用PCAN读取CAN数据_qcanbusdevice-优快云博客。
1、环境:Qt5.15.2 +VS2019。
2、前提条件:.pro中需要添加QT += serialbus serialport。安装了PeakOemDrv.exe驱动。
3、编写的PeakCAN的代码:
peakcan.h
#ifndef PEAKCAN_H
#define PEAKCAN_H
#include <QCanBus>
#include <QObject>
#include <QTimer>
class PeakCAN : public QObject
{
Q_OBJECT
public:
PeakCAN(QString deviceName = "", QObject *parent = nullptr);
~PeakCAN();
bool Connect();
void Disconnect();
void writeFrame(const quint32 id, const QByteArray &frame);
private slots:
void processErrors(QCanBusDevice::CanBusError);
void onReceiveFrames();
void onStateChangeHandle(QCanBusDevice::CanBusDeviceState state);
bool initDevice();
void detectPcan();
signals:
void connectedDone();
void disconnectedDone();
void framesReadReady(const QVector<QCanBusFrame> &);
private:
QCanBusDevice * m_canDevice = nullptr;
QList<QCanBusDeviceInfo> m_interfaces;
QCanBusDevice::CanBusDeviceState m_connectState;
bool m_connectFalg;
bool m_connected = false;
QString m_devcieName;
QTimer * m_PcanDetectTimer = nullptr; //检测插入定时器
int m_PcanDetectInterval = 5000;
QVector<QCanBusFrame> m_frames;
};
#endif // PEAKCAN_H
peakcan.cpp
4、使用说明: new PeakCAN后,调用Connect()接口连接,由于实际的板子有2路can,而目前只用到了channel 0,所以在Connect()接口中,只连接到了通道0,如果需要连接多路信号,可能需要new 多个PeakCan,并指定设备名。PeakCAN收到消息后,会通过framesReadReady信号发出。向设备发送can时,调用writeFrame接口。
#include "peakcan.h"
#include <QCanBus>
#include <QCanBusFrame>
#include <QDateTime>
#include <QDebug>
#include <QThread>
PeakCAN::PeakCAN(QString deviceName, QObject *parent) : QObject(parent), m_devcieName(deviceName)
{
m_connectFalg = false;
qRegisterMetaType<QCanBusDevice::CanBusDeviceState>("QCanBusDevice::CanBusDeviceState");
qRegisterMetaType<QVector<QCanBusFrame>>("QVector<QCanBusFrame>");
m_PcanDetectTimer = new QTimer(this);
connect(m_PcanDetectTimer, &QTimer::timeout, this, &PeakCAN::detectPcan);
}
PeakCAN::~PeakCAN()
{
Disconnect();
qDebug() << "PeakCAN destructiong...";
}
bool PeakCAN::Connect()
{
m_connectFalg = true;
if (m_canDevice == nullptr)
{
initDevice();
}
if (m_canDevice == nullptr)
{
return false;
}
if (!m_canDevice->connectDevice())
{
qDebug() << "PEAKCan connect fail!";
m_canDevice->disconnectDevice();
return false;
}
return true;
}
void PeakCAN::Disconnect()
{
m_connectFalg = false;
if (m_canDevice)
{
disconnect(m_canDevice, &QCanBusDevice::errorOccurred, this, &PeakCAN::processErrors);
disconnect(m_canDevice, &QCanBusDevice::framesReceived, this, &PeakCAN::onReceiveFrames);
disconnect(m_canDevice, &QCanBusDevice::stateChanged, this, &PeakCAN::onStateChangeHandle);
m_canDevice->disconnectDevice();
}
m_PcanDetectTimer->stop();
}
void PeakCAN::processErrors(QCanBusDevice::CanBusError error)
{
switch (error)
{
case QCanBusDevice::ReadError:
case QCanBusDevice::WriteError:
case QCanBusDevice::ConnectionError:
qDebug() << m_canDevice->errorString();
m_canDevice->resetController();
break;
case QCanBusDevice::ConfigurationError:
case QCanBusDevice::UnknownError:
qDebug() << m_canDevice->errorString();
// m_canDevice->resetController();
break;
default:
break;
}
}
void PeakCAN::writeFrame(const quint32 ID, const QByteArray &data)
{
if (!m_connectFalg)
{
return;
}
QCanBusFrame frame;
frame.setFrameType(QCanBusFrame::DataFrame);
// frame.setExtendedFrameFormat(ID > 0x7FF ? true : false); /* 是否为扩展帧 */ setFrameId时内部会自行设置扩展标志
frame.setTimeStamp(QDateTime::currentMSecsSinceEpoch()); //时间戳
if (data.size() > 8)
{
frame.setBitrateSwitch(true); // BRS 位
// frame.setFlexibleDataRateFormat(true);//FDF 位
}
frame.setFrameId(ID);
frame.setPayload(data);
if (m_canDevice)
{
m_canDevice->writeFrame(frame);
}
}
bool PeakCAN::initDevice()
{
if (!QCanBus::instance()->plugins().contains(QStringLiteral("peakcan")))
{
qDebug("peakcan plugin not available");
return false;
}
const QString plugin = "peakcan";
m_interfaces = QCanBus::instance()->availableDevices(plugin);
if (m_interfaces.empty())
{
qDebug() << "PEAKCan do not find available interface for pcan";
m_PcanDetectTimer->start(m_PcanDetectInterval);
return false;
}
m_PcanDetectTimer->stop();
foreach (QCanBusDeviceInfo deviceInfo, m_interfaces)
{
if (deviceInfo.channel() == 0) //只连接通道0
{
QString errorString;
if (m_devcieName.isEmpty())
{
m_canDevice = QCanBus::instance()->createDevice(plugin, deviceInfo.name(), &errorString);
}
else if (deviceInfo.name() == m_devcieName)
{
m_canDevice = QCanBus::instance()->createDevice(plugin, deviceInfo.name(), &errorString);
if (!m_canDevice)
{
qDebug() << "PEAKCan createDevice error" << errorString;
qDebug() << "PEAKCan deviceName:" << deviceInfo.name() << " description:" << deviceInfo.description()
<< " channel:" << deviceInfo.channel();
}
break;
}
}
}
if (!m_canDevice)
{
return false;
}
const int bitrate = 500000;
const int datarate = 2000000;
m_canDevice->setConfigurationParameter(QCanBusDevice::CanFdKey, QVariant(true)); // canFD设置为true
m_canDevice->setConfigurationParameter(QCanBusDevice::BitRateKey, QVariant(bitrate)); //波特率
m_canDevice->setConfigurationParameter(QCanBusDevice::DataBitRateKey, QVariant(datarate)); //传输速率
connect(m_canDevice, &QCanBusDevice::errorOccurred, this, &PeakCAN::processErrors);
connect(m_canDevice, &QCanBusDevice::framesReceived, this, &PeakCAN::onReceiveFrames);
connect(m_canDevice, &QCanBusDevice::stateChanged, this, &PeakCAN::onStateChangeHandle);
return true;
}
void PeakCAN::detectPcan()
{
initDevice();
if (m_connectFalg && m_canDevice)
{
m_canDevice->connectDevice();
}
}
//收到can消息处理
void PeakCAN::onReceiveFrames()
{
if (m_canDevice)
{
m_canDevice->readAllFrames().swap(m_frames);
emit framesReadReady(m_frames);
}
}
void PeakCAN::onStateChangeHandle(QCanBusDevice::CanBusDeviceState state)
{
m_connectState = state;
if (state == QCanBusDevice::UnconnectedState)
{
emit disconnectedDone();
m_connected = false;
}
else if (state == QCanBusDevice::ConnectedState)
{
m_connected = true;
emit connectedDone();
}
}