QT5使用PCAN读取CAN数据
项目中需要在QT框架中获取车辆的CAN信息,主要需要获取车速,方向盘,油门,挡位,刹车等信息。一开始准备用qt去调用PCANBasic.dll的API来实现这个功能,但意外的发现,QT5.8以上的版本,已经有内部的接口可以直接调用PCAN的插件,从而实现QT通过PCAN硬件设备读取CAN信息。PCAN支持的插件有如下,其中PeakCAN就是我们需要使用的插件了。
CAN Bus example
在欢迎模式上,搜索CAN,可以看到官方的例子,CAN Bus example。界面如下,plugin选择peakcan,就可以连上PCAN使用了。由于官方例子功能比较多,在简单应用下不需要这么多的功能,所以我简化了代码。
重点讲解
pro文件
pro文件里需要加serialbus这个库,才能在源文件中使用<QCanBus>
、<QCanBusFrame>
、<QCanBusDevice>
这些头文件。
QT += serialbus
创建CAN bus Device
创建CAN bus Device我们需要用到下面两个方法。
QList QCanBus::availableDevices(const QString &plugin, QString *errorMessage = nullptr) const
QCanBusDevice *QCanBus::createDevice(const QString &plugin, const QString &interfaceName, QString *errorMessage = nullptr) const
availableDevices(const QString &plugin, QString *errorMessage = nullptr)
方法返回选定插件的接口信息,返回值是QList<QCanBusDeviceInfo>
类型的,如果只接一个PCAN的话,list的大小是1。QCanBusDeviceInfo
这个类包含接口的一些信息,我们主要使用其中的name方法,获取指定插件的有效接口。
createDevice(const QString &plugin, const QString &interfaceName, QString *errorMessage = nullptr)
方法根据插件名和接口名,创建CAN bus Device。
QList<QCanBusDeviceInfo> m_interfaces;
QCanBusDevice *m_canDevice=nullptr;
const QString plugin="peakcan";
QString errorString;
m_interfaces = QCanBus::instance()->availableDevices(plugin);
if(!m_interfaces.empty())
{
qDebug()<<m_interfaces.begin()->name();
m_canDevice=QCanBus::instance()->createDevice(plugin, m_interfaces.begin()->name(), &errorString);
}
else
{
qDebug()<<"do not find available interface for pcan";
}
如果创建成功,会返回QCanBusDevice
类型的指针,我们可以用这个设备的指针去做参数配置、设备连接、设备断开连接、readframe等操作。如果创建失败,会返回nullptr
。
设备创建成功才能做参数配置,所以需要做一个判断,创建成功,有一个最重要的参数需要配置,就是波特率,因为一般车子的波特率是500kBit/s。所以我们设定500000。其他的参数可以默认。
connectDevice()
方法用来连接设备,连接成功会返回true
。相应的disconnectDevice()
用来断开设备,释放端口。
const int bitrate=500000;
bool connectflag=false;
if (!m_canDevice)
{
qDebug() <<"createDevice error" <<errorString;
}
else
{
/***peakcan do not support configuration key RawFilterKey***/
// QCanBusDevice::Filter filter;
// QList<QCanBusDevice::Filter> filterList;
// // filter all CAN bus packages with id 0x444 (base) or 0xXXXXX444 (extended)
// filter.frameId = 0x444u;
// qDebug()<<filter.frameId;
// filter.frameIdMask = 0x0u;
// filter.format = QCanBusDevice::Filter::MatchBaseFormat;
// filter.type = QCanBusFrame::DataFrame;
// filterList.append(filter);
// m_canDevice->setConfigurationParameter(QCanBusDevice::RawFilterKey, QVariant::fromValue(filterList));
m_canDevice->setConfigurationParameter(QCanBusDevice::BitRateKey,QVariant(bitrate));
connectflag= m_canDevice->connectDevice();
qDebug()<<"connecflag"<<connectflag;
}
参数设置
void QCanBusDevice::setConfigurationParameter(int key, const QVariant &value)
device参数的配置用上面这个方法,其中key是参数类型(枚举量),value是参数的值,根据需求来设定。
key的枚举量如下:
讲几个key,其他的暂时没用上,可根据官方文档详细了解。
RawFilterKey
设定滤波的,可以滤掉指定id的报文,值的类型是QList<QCanBusDevice::Filter>
类型的,但不是所有的插件都支持这个功能,PCAN就不支持。
BitRateKey
设置波特率的,根据需求设置波特率。
CanFdKey
设置CAN的帧格式,是标准格式还是CANFD格式。值是bool量。
DataBitRateKey
设置数据传输率,当CAN的帧格式时CANFD格式的时候,需要设置。
接收报文
设备连接成功后,就需要关联两组信号和槽,一个是errorOccurred信号,故障发生时发射,一个framesReceived信号,收到报文的时候发射。相应的槽,对该信号发射后,进行处理和操作。
if(connectflag)
{
connect(m_canDevice, &QCanBusDevice::errorOccurred,
this, &MainWindow::processErrors);
connect(m_canDevice, &QCanBusDevice::framesReceived,
this, &MainWindow::processReceivedFrames);
ui->canBT->setEnabled(false);
}
errorOccurred信号会携带QCanBusDevice::CanBusError
(枚举量)类型的参数,我在processErrors这个槽函数没有做任何额外的处理,只是调用QCanBusDevice::errorString()
打印出来了错误信息。有需求的话可以在每个case里,做对该错误的处理。
void MainWindow::processErrors(QCanBusDevice::CanBusError error) const
{
switch (error) {
case QCanBusDevice::ReadError:
case QCanBusDevice::WriteError:
case QCanBusDevice::ConnectionError:
case QCanBusDevice::ConfigurationError:
case QCanBusDevice::UnknownError:
qDebug()<<m_canDevice->errorString();
break;
default:
break;
}
}
framesReceived信号发出代表有报文收到了,framesAvailable()
返回在内部队列的报文条数,readFrame()
从队列里弹出一条报文并返回QCanBusFrame
类型的frame。还有一个readAllFrames()
方法,这个方法是取出对列里的所有报文,返回QVector<QCanBusFrame>
类型的vector。
举个例子,队列里有5条报文,framesAvailable()
会返回5,readFrame()
方法调用后,在没有新报文加入队列的情况下,framesAvailable()
会返回4,因为readFrame()
已经从队列中弹出了一条报文。如果使用readAllFrames()
方法,5条会被全部弹出,vector的大小也为5。
void MainWindow::processReceivedFrames()
{
if (!m_canDevice)
return;
while (m_canDevice->framesAvailable())
{
const QCanBusFrame frame = m_canDevice->readFrame();
//qDebug()<<frame.error();
// qDebug()<<frame.frameId();//frame id,it's decimal
//qDebug()<<frame.frameType();//1 means dataframe
//qDebug()<<m_canDevice->state();
QString view;
// short temp3;
if (frame.frameType() == QCanBusFrame::ErrorFrame)
{
view = m_canDevice->interpretErrorFrame(frame);
}
else if(frame.frameType()==QCanBusFrame::DataFrame)
{
view=frame.toString();
}
qDebug()<<view;
}
}
PCAN支持的QCanBusDevice::ConfigurationKey类型
上传demo
candemo