介绍

Linux自带socket CAN驱动,可以使用VCAN功能来模拟CAN数据的收发,这个在原型开发阶段非常有用,拿一台笔记本直接调试CAN设备

使用

虚拟CAN

加载vcan.ko,这个驱动是操作系统自带的,位置参考:/usr/lib/modules/5.15.0-131-generic/kernel/drivers/net/can

1
sudo modprobe vcan

如果你的Linux操作系统没有vcan驱动,请下载kernel编译一个,VCAN驱动模块配置如下:

img

添加vcan设备:vcan0

1
2
sudo ip link add dev vcan0 type vcan
sudo ip link set up vcan0

vcan卡连接、关闭

1
2
ifconfig vcan0 up
ifconfig vcan0 down

发送随机数据用于开发、调试、测试

1
2
3
4
5
# 用cansend发送8个字节数据格式
while true; do cansend vcan0 123#1122334455667788; sleep 1; done;

# 发送随机数
while true; do cansend vcan0 $(printf "%X#%02X%02X%02X%02X%02X%02X%02X%02X" $((RANDOM%0x800)) $((RANDOM%256)) $((RANDOM%256)) $((RANDOM%256)) $((RANDOM%256)) $((RANDOM%256)) $((RANDOM%256)) $((RANDOM%256)) $((RANDOM%256))); sleep 1; done;

接收数据

1
candump vcan0

can数据发送和接收情况统计

1
ip -details -statistics link show vcan0

数据分析:可以用wireshark工具抓取can帧

img

真实CAN

真实的CAN需要MCU/MPU/SOC端有CAN控制器(内部集成,引脚有RXD、TXD)、匹配的CAN收发器(CANH、CANL)、与之匹配的终端电阻(高速CAN为120Ω),常见的CAN收发器有:

  • TJA1043
  • MCP2515 / MCP2551
  • SN65HVD230

启用can设备:使用前先设置波特率,高速CAN为500k

1
2
sudo ip link set can0 type can bitrate 500000
sudo ip link set up can0

本地测试直接设置数据回环模式,如果不需要记得关掉,否则数据出不去也进不来!

1
2
3
4
# 开启本地回环模式
sudo ip link set can0 type can loopback on
# 关闭本地回环模式
sudo ip link set can0 type can loopback off

编码

Linux C++实现CAN数据收发状态检测独立线程处理

can_bus_interface.h

CanWorker负责处理socket CAN连接状态和数据的收发,CanBusInterface则作为一个单例管理CAN通信(开辟线程)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#ifndef CANBUSINTERFACE_H
#define CANBUSINTERFACE_H

#include <QObject>
#include <QSocketNotifier>
#include <QThread>
#include <QDebug>
#include <linux/can.h>
#include <string>

class CanWorker : public QObject
{
Q_OBJECT
public:
explicit CanWorker(int socket, QObject *parent = nullptr);
~CanWorker();

signals:
void frameReceived(const struct can_frame &frame);
void errorOccurred(const QString &errorString);

private slots:
void onSocketActivated(int socket);

private:
int m_socket;
QSocketNotifier *m_notifier;
};

class CanBusInterface : public QObject
{
Q_OBJECT
public:
static CanBusInterface *getInstance();

protected:
explicit CanBusInterface(QObject *parent = nullptr);

~CanBusInterface();

public:
bool open(const std::string &interfaceName);
void close();
bool isOpen() const;
bool sendFrame(const struct can_frame &frame);

signals:
void frameReceived(const struct can_frame &frame);
void errorOccurred(const QString &errorString);

private:
int m_socket;
bool m_isOpen;
QThread *m_workerThread;
CanWorker *m_worker;
};

#endif // CANBUSINTERFACE_H

can_bus_interface.cpp

CanWorker和CanBusInterface的实现部分,socketCAN的配置:socket(PF_CAN, SOCK_RAW, CAN_RAW)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
#include "can_bus_interface.h"
#include <QDebug>
#include <QSocketNotifier>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <unistd.h>

// CanWorker 实现
CanWorker::CanWorker(int socket, QObject *parent)
: QObject(parent), m_socket(socket), m_notifier(nullptr)
{
// 创建socket通知器
m_notifier = new QSocketNotifier(m_socket, QSocketNotifier::Read, this);
connect(m_notifier, &QSocketNotifier::activated, this, &CanWorker::onSocketActivated);

qRegisterMetaType<can_frame>("can_frame");
}

CanWorker::~CanWorker()
{
if (m_notifier) {
m_notifier->setEnabled(false);
delete m_notifier;
}
}

void CanWorker::onSocketActivated(int socket)
{
Q_UNUSED(socket);

struct can_frame frame;
int bytesRead = read(m_socket, &frame, sizeof(frame));

if (bytesRead == sizeof(frame)) {
emit frameReceived(frame);
} else if (bytesRead < 0) {
emit errorOccurred("Failed to read CAN frame");
}
}

// CanBusInterface 实现
CanBusInterface *CanBusInterface::getInstance()
{
static CanBusInterface instance;
return &instance;
}

CanBusInterface::CanBusInterface(QObject *parent)
: QObject(parent), m_socket(-1), m_isOpen(false),
m_workerThread(nullptr), m_worker(nullptr)
{
}

CanBusInterface::~CanBusInterface()
{
close();
}

bool CanBusInterface::open(const std::string &interfaceName)
{
if (m_isOpen) {
qWarning() << "CAN interface is already open";
return true;
}

// 创建socket
m_socket = socket(PF_CAN, SOCK_RAW, CAN_RAW);
if (m_socket < 0) {
emit errorOccurred("Failed to create socket");
return false;
}

// 获取接口索引
struct ifreq ifr;
strncpy(ifr.ifr_name, interfaceName.c_str(), IFNAMSIZ - 1);
ifr.ifr_name[IFNAMSIZ - 1] = '\0';
if (ioctl(m_socket, SIOCGIFINDEX, &ifr) < 0) {
emit errorOccurred("Failed to get interface index");
::close(m_socket);
m_socket = -1;
return false;
}

// 绑定socket到CAN接口
struct sockaddr_can addr;
addr.can_family = AF_CAN;
addr.can_ifindex = ifr.ifr_ifindex;
if (bind(m_socket, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
emit errorOccurred("Failed to bind socket to interface");
::close(m_socket);
m_socket = -1;
return false;
}

// 创建并启动工作线程
m_workerThread = new QThread();
m_worker = new CanWorker(m_socket);
m_worker->moveToThread(m_workerThread);

// 连接信号槽
connect(m_workerThread, &QThread::finished, m_worker, &CanWorker::deleteLater);
connect(m_workerThread, &QThread::finished, m_workerThread, &QThread::deleteLater);

connect(m_worker, &CanWorker::frameReceived, this, &CanBusInterface::frameReceived);
connect(m_worker, &CanWorker::errorOccurred, this, &CanBusInterface::errorOccurred);

m_workerThread->start();

m_isOpen = true;
return true;
}

void CanBusInterface::close()
{
if (!m_isOpen) return;

m_isOpen = false;

if (m_workerThread) {
m_workerThread->quit();
m_workerThread->wait();
m_workerThread = nullptr;
m_worker = nullptr;
}

if (m_socket >= 0) {
::close(m_socket);
m_socket = -1;
}
}

bool CanBusInterface::isOpen() const
{
return m_isOpen;
}

bool CanBusInterface::sendFrame(const struct can_frame &frame)
{
if (!m_isOpen) {
emit errorOccurred("CAN interface is not open");
return false;
}

int bytesWritten = write(m_socket, &frame, sizeof(frame));
if (bytesWritten != sizeof(frame)) {
emit errorOccurred("Failed to write CAN frame");
return false;
}

return true;
}

注意点1:这里需要把can_frame类型注册为Qt能够识别的元数据类型

1
qRegisterMetaType<can_frame>("can_frame");

注意点2:只有独立的QObject才能使用moveToThread

1
2
3
4
5
m_workerThread = new QThread();
m_worker = new CanWorker(m_socket);
m_worker->moveToThread(m_workerThread);

m_workerThread->start();

© 2025 hywing 使用 Stellar 创建
总访问 113701 次 | 本页访问 326