一、 介绍CAN总线
二、USBCAN总线
2.1 产品介绍
USBCAN 转换器模块实现了将 CAN 总线接口与 USB 接口进行相互转换。可以 简单方便的通过电脑监视 CAN 总线网络,同时可以实现工业现场数据稳定的双 向通信。模块广泛应用与工业控制、汽车电子、安防监控等领域的通信、控制、 数据传输。
本模块是在电脑上将 USB 接口虚拟成串口设备实现通信,用户在电脑上只需 要掌握串口通信相关知识即可使用,具有使用方便、二次开发简单、稳定可靠等特点。
2.2 接口说明
(1)MODE:将该跳线连接上,将强制产品进入设置模式
(2)RES:终端匹配电阻。将跳线连接上,则 CAN 总线接入 120 欧姆终端匹配电阻,跳线断开则没有终端匹配电阻接入。
(3) CAN_H 和 CAN_L:CAN 总线信号,和其他设备连接时 CAN_H 连 CAN_H 信号,CAN_L 连 CAN_L 信号,注意不能接反。
(4)GND:模块内部地信号,非必须信号,如果线缆有屏蔽层,用户可自由选 择是否将该信号连接与屏蔽增连接。
2.3 包模式
包模式下,串口处为固定 16byte 包长,数据不足部分补 0。这种模式下用户可以随意控制 CAN 总线上发出报文。串口处包格式说明:
三、QT调用第三方库环境搭建
首先在QT工程中新建一个项目,并编译运行程序(在项目中把Shadow build去掉)把库函数文件都放在工作目录下。库函数文件总共有三个文件:ControlCAN.h、 ControlCAN.lib、ControlCAN.dll 和一个文件夹 kerneldlls。在.pro文件中加入
LIBS += -L$$PWD/./ ControlCAN.lib ControlCAN.dll
INCLUDEPATH += $$PWD\ControlCAN.h
并在构造函数中添加 #include "ControlCAN.h" #include<QDebug>头文件和测试
1.选择32位
2.去掉Shadow build
3. 编译运行
4. 将库放入把库函数文件都放在工作目录下
5 .pro中添加环境变量
6 添加测试代码编译运行通过
7. 运行成功 (用的双头usbcan口同时插入电脑后编译通过)
四、USBCAN第三方库函数介绍
使用流程图
整体流程分六个步骤:
1.打开设备 VCI_OpenDevice()
VCI_OpenDevice(DWORD DevType, DWORD DevIndex, DWORD Reserved);
2.初始化某一路 VCI_InitCAN()
VCI_InitCAN(DWORD DevType, DWORD DevIndex, DWORD CANIndex, PVCI_INIT_CONFIG pInitConfig);
3.启动某一路 VCI_StartCAN()
VCI_StartCAN(DWORD DevType, DWORD DevIndex, DWORD CANIndex);
4.读取CAN帧 VCI_Receive()
VCI_Receive(DWORD DevType, DWORD DevIndex, DWORD CANIndex, PVCI_CAN_OBJ pReceive, ULONG Len, INT WaitTime=-1);
5.发送CAN帧 VCI_Transmit()
VCI_Transmit(DWORD DevType, DWORD DevIndex, DWORD CANIndex, PVCI_CAN_OBJ pSend, ULONG Len);
6.关闭设备 VCI_CloseDevice()
VCI_CloseDevice(DWORD DevType, DWORD DevIndex);
通用类型:
DevType
设备类型号。对应不同的产品型号。
DevIndex
设备索引号。比如当只有一个 PCIe-9221 时,索引号为 0,这时再插入一个 PCIe-9221,
那么后面插入的这个设备索引号就是 1,以此类推。
CANIndex
第几路 CAN。即对应卡的 CAN 通道号,CAN0 为 0,CAN1 为 1,以此类推。
详细看CAN API使用说明
5 QT下CAN总线代码编写思路
(代码质量很一般仅学习。)
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "ControlCAN.h"
#include <QThread>
#include "recever.h"
#include"send_thread.h"
#include<QMutex>
#define DevType 4 // PCIe-9221
#define DevIndex 0 // 索引号0
#define CANIndex 0 //CAN0通道
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
bool Start();
void Stop();
bool InitSerial();
void setUI();
void Close();
void ResetCAN();
signals:
void receve();
void Write(const QString &data,BYTE ,BYTE ,BYTE );
private slots:
void on_btn_openConsole_clicked();
void on_btn_openConsole_2_clicked();
void on_btn_openConsole_3_clicked();
void on_btn_recev_clicked();
void Readdata(const QString &);
void on_btn_send_clicked();
void on_pushButton_3_clicked();
private:
Ui::MainWindow *ui;
bool IsRunning_ = false;
recever *mrecever;
send_thread *Send_thread;
QThread mThread;
QThread sendThread;
DWORD REL;
VCI_INIT_CONFIG Init;
QMutex mutex;
};
#endif // MAINWINDOW_H
recever.h
#ifndef RECEVER_H
#define RECEVER_H
#include <QObject>
#include "ControlCAN.h"
#define DevType 4 // PCIe-9221
#define DevIndex 0 // 索引号0
#define CANIndex 0 //CAN0通道
class recever: public QObject
{
Q_OBJECT
public:
explicit recever(QObject *parent = nullptr);
void VCI_Rece();
DWORD dwRel=false;
bool CloseREL=false;
public slots:
void readData();
signals:
void SignalRead(const QString &);
private:
DWORD ReceiveNum;
VCI_CAN_OBJ Receive[100];
};
#endif // RECEVER_H
send_thread.h
#ifndef SEND_THREAD_H
#define SEND_THREAD_H
#include <QObject>
#include "ControlCAN.h"
#define DevType 4 // PCIe-9221
#define DevIndex 0 // 索引号0
#define CANIndex 0 //CAN0通道
class send_thread : public QObject
{
Q_OBJECT
public:
explicit send_thread(QObject *parent = nullptr);
QByteArray HexStringToByteArray(QString hex, bool *ok);
signals:
public slots:
void SlotDoWrite(const QString &data,BYTE ,BYTE ,BYTE);
private:
VCI_CAN_OBJ vco;
};
#endif // SEND_THREAD_H
mainwindow.c
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include<QMessageBox>
#include<QDebug>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
setUI();
mrecever = new recever;
Send_thread = new send_thread;
mrecever->moveToThread(&mThread);
Send_thread->moveToThread(&sendThread);
qRegisterMetaType<DWORD>("DWORD");
qDebug()<< "main::SlotSessionDisConnected " << QThread::currentThreadId();
connect(this,&MainWindow::receve,mrecever,&recever::readData);
connect(mrecever,&recever::SignalRead,this,&MainWindow::Readdata,Qt::DirectConnection);
connect(this,&MainWindow::Write,Send_thread,&send_thread::SlotDoWrite);
}
MainWindow::~MainWindow()
{
// disconnect(this,&Widget::receve,mComservice,&ComService::readData);
//disconnect(mComservice,&ComService::SignalRead,this,&Widget::Readdata);
this->Close();
mThread.quit();
mThread.wait();
sendThread.quit();
sendThread.wait();
delete mrecever;
delete Send_thread;
delete ui;
}
void MainWindow::setUI()
{
setFixedHeight(446);
setFixedWidth(544);
ui->btn_recev->setEnabled(false);
ui->btn_send->setEnabled(false);
ui->btn_openConsole_2->setEnabled(false);
ui->btn_openConsole_3->setEnabled(false);
ui->comboBox->addItem("正常发送");
ui->comboBox->addItem("单次发送");
ui->comboBox->addItem("自发自收");
ui->comboBox->addItem("单次自发自收");
ui->comboBox_2->addItem("标准帧");
ui->comboBox_2->addItem("扩展帧");
ui->comboBox_3->addItem("数据帧");
ui->comboBox_3->addItem("远程帧");
}
bool MainWindow::Start()
{
if(IsRunning_)
return true;
mThread.start();
sendThread.start();
IsRunning_ = true;
return true;
}
void MainWindow::Stop()
{
if(!IsRunning_)
return;
mThread.quit();
mThread.wait();
sendThread.quit();
sendThread.wait();
IsRunning_=false;
}
bool MainWindow::InitSerial()
{
mrecever->dwRel=false;
mrecever->CloseREL=false;
REL=VCI_OpenDevice(DevType,DevIndex,0);
if(REL!= 1) return FALSE; //打开设备
Init.Timing0=0;
Init.Timing1=0x1C;
Init.Mode=0;
Init.AccMask=0xFFFFFFFF;
Init.Filter=1;
if(VCI_InitCAN(DevType,DevIndex,CANIndex,&Init)!= 1) FALSE; //初始化设备
if(VCI_StartCAN(DevType,DevIndex,CANIndex)!=1) FALSE ; //运行设备
return true;
}
void MainWindow::on_btn_openConsole_clicked()
{
if(ui->btn_openConsole->text()==tr("打开CAN口"))
{
if(!Start())
return;
if(!InitSerial())
{
QMessageBox::information(this,"打开CAN口","CAN口打开失败");
return;
}
QMessageBox::information(this,"打开CAN口","CAN口打开成功");
if(!mrecever->dwRel||!mrecever->CloseREL)
{
mrecever->CloseREL=0;
mrecever->dwRel=0;
emit receve();
}
ui->btn_recev->setEnabled(false);
ui->btn_send->setEnabled(true);
ui->btn_openConsole_2->setEnabled(true);
ui->btn_openConsole_3->setEnabled(true);
ui->btn_openConsole->setEnabled(false);
}
}
void MainWindow::on_btn_openConsole_2_clicked()
{
// if(VCI_CloseDevice(DevType, DevIndex) != 1) qDebug()<<"error";
Close();
if(mrecever->CloseREL)
{
ui->btn_openConsole->setEnabled(true);
ui->btn_openConsole_2->setEnabled(false);
ui->btn_openConsole_3->setEnabled(false);
ui->btn_send->setEnabled(false);
ui->btn_recev->setEnabled(false);
QMessageBox::information(this,"关闭CAN口","CAN口关闭成功");
Stop();
}
}
void MainWindow::Close()
{
mutex.lock();
qDebug()<< "SerialThreadsClose::SlotSessionDisConnected123 " << QThread::currentThreadId();
if(!mrecever->CloseREL)
{
if(VCI_CloseDevice(DevType, DevIndex)==1)
{
qDebug()<<"CAN口关闭成功";
mrecever->CloseREL=TRUE;
}
}
mutex.unlock();
// mutex.unlock();
}
void MainWindow::Readdata(const QString &temp)
{
ui->textEdit_recv->append(temp.toLocal8Bit());
}
void MainWindow::on_btn_openConsole_3_clicked()
{
ResetCAN();
if(mrecever->dwRel)
{
ui->btn_recev->setEnabled(true);
}
qDebug()<<mrecever->dwRel;
}
void MainWindow::ResetCAN()
{
mutex.lock();
mrecever->dwRel= VCI_ResetCAN(DevType, DevIndex, CANIndex);
if(mrecever->dwRel!=1) qDebug()<<"error";
if(VCI_StartCAN(DevType, DevIndex, CANIndex)!=1) qDebug()<<"error";
else qDebug()<<"复位成功";
mutex.unlock();
}
//接收
void MainWindow::on_btn_recev_clicked()
{
qDebug()<<mrecever->dwRel<<mrecever->CloseREL;
if(!mrecever->dwRel||!mrecever->CloseREL)
{
mrecever->CloseREL=0;
mrecever->dwRel=0;
emit receve();
ui->btn_recev->setEnabled(false);
}
else qDebug()<<"接收中ing";
}
//发送
void MainWindow::on_btn_send_clicked()
{
qRegisterMetaType<BYTE>("BYTE") ;
BYTE SendType=0; BYTE RemoteFlag=0; BYTE ExternFlag=0;
if(ui->comboBox->currentText()=="正常发送") SendType=0;
if(ui->comboBox->currentText()=="单次发送") SendType=1;
if(ui->comboBox->currentText()=="自发自收") SendType=2;
if(ui->comboBox->currentText()=="单次自发自收") SendType=3;
if(ui->comboBox_2->currentText()=="标准帧") RemoteFlag=0;
if(ui->comboBox_2->currentText()=="扩展帧") RemoteFlag=1;
if(ui->comboBox_3->currentText()=="数据帧") ExternFlag=0;
if(ui->comboBox_3->currentText()=="远程帧") ExternFlag=1;
qDebug()<<"扩展帧"<<SendType<<RemoteFlag<<ExternFlag;
QString sendMsg = ui->textEdit_send->toPlainText();
if(sendMsg.isEmpty())
sendMsg="00 00 00 00 00 00 00 00";
qDebug()<<"sendMsg"<<sendMsg;
emit Write(sendMsg,SendType,RemoteFlag,ExternFlag);
}
void MainWindow::on_pushButton_3_clicked()
{
ui->textEdit_recv->clear();
}
recever.c
#include "recever.h"
#include <QDebug>
#include<QThread>
recever::recever(QObject *parent)
: QObject{parent}
{
}
void recever::readData()
{
BYTE num;
qDebug() << "receve::SlotDoWrite threadID:"<< QThread::currentThreadId();
while(1)
{
QThread::msleep(500);
//qDebug()<<num;
num=VCI_GetReceiveNum(DevType,DevIndex,CANIndex);
qDebug()<<num;
if(num)
{
VCI_Rece();
}
qDebug()<<dwRel<<dwRel;
if(dwRel||CloseREL)
{
return;
}
}
}
void recever::VCI_Rece()
{
qDebug()<< "VCI_Rece::SlotSessionDisConnected123 " << QThread::currentThreadId();
QByteArray strData;
strData.resize(8);
ReceiveNum= VCI_Receive(DevType,DevIndex,CANIndex,Receive,10,-1);
qDebug()<<"ReceiveNum:"<<ReceiveNum;
for (int i = 0; i < ReceiveNum; i++)
{
strData[0] = Receive[i].Data[0];
strData[1] = Receive[i].Data[1];
strData[2] = Receive[i].Data[2];
strData[3] = Receive[i].Data[3];
strData[4] = Receive[i].Data[4];
strData[5] = Receive[i].Data[5];
strData[6] = Receive[i].Data[6];
strData[7] = Receive[i].Data[7];
QString string_temp = (QString(strData.toHex(' ').toUpper().append(' ')));
qDebug()<<ReceiveNum<<":data:"<<string_temp;
emit SignalRead(string_temp);
return;
}
}
send_thread.c
#include "send_thread.h"
#include <QDataStream>
#include <QDebug>
#include<QThread>
#include<QtCore>
send_thread::send_thread(QObject *parent)
: QObject{parent}
{
}
void send_thread::SlotDoWrite(const QString &data,BYTE SendType,BYTE RemoteFlag,BYTE ExternFlag)
{
qDebug() << "write::SlotDoWrite threadID:"<< QThread::currentThreadId();
vco.SendType = SendType; // 正常发送
vco.RemoteFlag = RemoteFlag; // 数据帧
vco.ExternFlag = ExternFlag; // 标准帧
vco.DataLen = 8; // 数据长度1个字节
bool ok;
QByteArray buff=HexStringToByteArray(data, &ok);
// QByteArray buff=HexStringToByteArray(data, &ok);
qDebug()<<buff.toHex().toUpper();
//QDataStream out(buff);
vco.Data[0] = static_cast<unsigned char>(buff.at(0));
vco.Data[1] = static_cast<unsigned char>(buff.at(1));
vco.Data[2] = static_cast<unsigned char>(buff.at(2));
vco.Data[3] = static_cast<unsigned char>(buff.at(3));
vco.Data[4] = static_cast<unsigned char>(buff.at(4));
vco.Data[5] = static_cast<unsigned char>(buff.at(5));
vco.Data[6] = static_cast<unsigned char>(buff.at(6));
vco.Data[7] = static_cast<unsigned char>(buff.at(7));
if(VCI_Transmit(DevType, DevIndex, CANIndex,&vco , 1)<0) qDebug()<<"发送失败" ;
}
QByteArray send_thread:: HexStringToByteArray(QString hex, bool *ok)
{
int p;
QByteArray ret;
QStringList lst = hex.simplified().split(' ');//转化为字符串数组
ret.resize(lst.count());
for(int i = 0; i < lst.count(); i++)
{
p = lst[i].toInt(ok, 16);
if(!(*ok) || p > 255 )
{
return 0;
}
ret[i] = p;
}
return ret;
}
最终效果图