一、基本步骤
1、打开并绑定到 CAN 套接字
在执行任何操作之前,第一步是创建一个套接字。此函数接受三个参数 – 域/协议系列 (PF_CAN)、套接字类型(原始或数据报)和套接字协议。如果成功,该函数将返回文件描述符。
int s;
if ((s = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0) {
perror("Socket");
return 1;
}
接下来,我们必须检索要使用的接口名称(can0、can1、vcan0 等)的接口索引。为此,我们发送一个 I/O 控制调用,并传递一个包含接口名称的 ifreq 结构:
struct ifreq ifr;
strcpy(ifr.ifr_name, "vcan0" );
ioctl(s, SIOCGIFINDEX, &ifr);
有了接口索引,我们现在可以将套接字绑定到 CAN 接口:
struct sockaddr_can addr;
memset(&addr, 0, sizeof(addr));
addr.can_family = AF_CAN;
addr.can_ifindex = ifr.ifr_ifindex;
if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
perror("Bind");
return 1;
}
2、发送帧
要发送CAN帧,必须初始化can_frame结构并用数据填充它。基本的can_frame结构在 include/linux/can.h 中定义,如下所示:
struct can_frame {
canid_t can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */
canid_t can_id; /* 32 位 CAN_ID + EFF/RTR/ERR 标志 */
__u8 can_dlc; /* 以字节为单位的帧有效负载长度 (0 .. 8) */
__u8 __pad; /*填充*/
__u8 __res0; /* 保留 / 填充 */
__u8 __res1; /* 保留 / 填充 */
__u8 data[8] __attribute__((aligned(8)));
};
下面我们初始化一个 ID 为 0x555 的 CAN 帧,一个包含 “hello” 的 5 个字节的有效负载,并使用 write() 系统调用发送它:
struct can_frame frame;
frame.can_id = 0x555;
frame.can_dlc = 5;
sprintf(frame.data, "Hello");
if (write(s, &frame, sizeof(struct can_frame)) != sizeof(struct can_frame)) {
perror("Write");
return 1;
}
3、读取帧
要读取帧,请初始化can_frame并调用 read() 系统调用。这将阻塞,直到帧可用:
int nbytes;
struct can_frame frame;
nbytes = read(s, &frame, sizeof(struct can_frame));
if (nbytes < 0) {
perror("Read");
return 1;
}
printf("0x%03X [%d] ",frame.can_id, frame.can_dlc);
for (i = 0; i < frame.can_dlc; i++)
printf("%02X ",frame.data[i]);
printf("\r\n");
在上面的示例中,我们显示 ID、数据长度代码 (DLC) 和有效负载。
4、设置过滤器
除了读取之外,您可能还希望过滤掉不相关的CAN帧。这发生在驱动程序级别,这比在用户模式应用程序中读取每一帧更有效。
若要设置筛选器,请初始化单个can_filter结构或结构数组,然后填充can_id和can_mask。调用 setsockopt():
struct can_filter rfilter[1];
rfilter[0].can_id = 0x550;
rfilter[0].can_mask = 0xFF0;
//rfilter[1].can_id = 0x200;
//rfilter[1].can_mask = 0x700;
setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, &rfilter, sizeof(rfilter));
5、关闭套接字
最后,如果不再需要套接字,关闭:
if (close(s) < 0) {
perror("Close");
return 1;
}
完整代码:
#include <iostream>
#include <thread>
#include <cstring>
#include <unistd.h>
#include <net/if.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <linux/can.h>
#include <linux/can/raw.h>
#include <iomanip>
// // CAN配置函数,设置波特率等参数
// void configureCan(int sock, const char *interface_name) {
// // 设置波特率
// std::string set_bitrate_command = "ip link set " + std::string(interface_name) + " type can bitrate 500000";
// system(set_bitrate_command.c_str());
// // 再次打开CAN接口
// std::string up_command = "ifconfig " + std::string(interface_name) + " up";
// system(up_command.c_str());
// }
// CAN初始化函数,创建套接字并绑定
int initCanSocket(const char *interface_name) {
int sock = socket(PF_CAN, SOCK_RAW, CAN_RAW);
if (sock < 0) {
perror("Socket creation failed");
return -1;
}
struct sockaddr_can addr;
struct ifreq ifr;
std::strcpy(ifr.ifr_name, interface_name);
if (ioctl(sock, SIOCGIFINDEX, &ifr) < 0) {
perror("IOCTL failed");
close(sock);
return -1;
}
addr.can_family = AF_CAN;
addr.can_ifindex = ifr.ifr_ifindex;
if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
perror("Binding failed");
close(sock);
return -1;
}
return sock;
}
// 处理发送CAN帧的函数
void sendCanFrames(int sock, const char *interface_name) {
struct sockaddr_can addr;
struct ifreq ifr;
struct can_frame frame;
std::memset(&ifr, 0, sizeof(ifr));
std::strcpy(ifr.ifr_name, interface_name);
ioctl(sock, SIOCGIFINDEX, &ifr);
addr.can_family = AF_CAN;
addr.can_ifindex = ifr.ifr_ifindex;
while (true) {
std::string input;
// 提示用户输入并等待
std::cout << "Enter CAN frame data (8 bytes in hexadecimal): ";
// 清空输入流
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
std::getline(std::cin, input);
// 检查输入是否为空
if (input.empty()) {
std::cerr << "Error: Input cannot be empty.\n";
continue; // 如果输入为空,则重新提示用户输入
}
// Convert user input to CAN frame data
int num_bytes = input.length() / 2; // 每个字节由2个字符表示
if (num_bytes > 8) {
std::cerr << "Error: Input data exceeds maximum length of 8 bytes.\n";
continue; // 如果数据超过8个字节,则重新提示用户输入
}
frame.can_dlc = num_bytes;
for (int i = 0; i < num_bytes; ++i) {
std::string byte_string = input.substr(2*i, 2);
frame.data[i] = std::stoi(byte_string, 0, 16); // Convert hexadecimal string to integer
}
// 示例CAN ID
frame.can_id = 0x123;
// 发送CAN帧
ssize_t nbytes = write(sock, &frame, sizeof(struct can_frame));
if (nbytes != sizeof(struct can_frame)) {
perror("write");
break;
}
printf("Sent CAN frame on interface %s\n", interface_name);
// 发送下一帧前睡眠一段时间,可以根据需求调整
usleep(1000000); // 1秒
}
}
// CAN接收函数,以线程方式接收CAN帧
void receiveCanFrames(int sock, const char *interface_name) {
struct can_frame frame;
while (true) {
ssize_t nbytes = read(sock, &frame, sizeof(struct can_frame));
if (nbytes > 0) {
std::cout << "Received CAN frame on " << interface_name << std::endl;
std::cout << "Data: ";
for (int i = 0; i < frame.can_dlc; ++i) {
std::cout << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(frame.data[i]) << " ";
}
std::cout << std::endl;
// 假设CAN数据在frame.data中,长度为frame.can_dlc
// // 解析CAN帧数据,示例中假设前两个字节是命令
// if (frame.can_dlc >= 2) {
// uint8_t command_byte1 = frame.data[0];
// uint8_t command_byte2 = frame.data[1];
// // 根据接收到的命令字节执行不同的命令行操作
// if (command_byte1 == 0x00 && command_byte2 == 0x11) {
// // 执行命令 dvr_test -c 4 0 4 8 12
// char *args[] = { (char*)"dvr_test", (char*)"-c", (char*)"4",(char*)"0", (char*)"4", (char*)"8", (char*)"12", NULL };
// execvp(args[0], args);
// } else if (command_byte1 == 0x22 && command_byte2 == 0x33) {
// // 执行命令 dvr_test -c 4 12 4 8 0
// char *args[] = { (char*)"dvr_test", (char*)"-c", (char*)"4",(char*)"12", (char*)"4", (char*)"8", (char*)"0", NULL };
// execvp(args[0], args);
// } else {
// std::cout << "Unhandled command bytes: " << std::hex << (int)command_byte1 << " " << (int)command_byte2 << std::endl;
// // 如果有其他命令需要处理,可以继续添加判断和执行操作
// }
// } else {
// std::cout << "Invalid CAN frame data length" << std::endl;
// }
}
}
}
int main() {
int sock_awlink0, sock_awlink1;
const char *can_interface_awlink0 = "awlink0";
const char *can_interface_awlink1 = "awlink1";
// 初始化CAN接口套接字
sock_awlink0 = initCanSocket(can_interface_awlink0);
sock_awlink1 = initCanSocket(can_interface_awlink1);
if (sock_awlink0 < 0 || sock_awlink1 < 0) {
std::cerr << "Failed to initialize CAN sockets." << std::endl;
return 1;
}
std::string user_choice;
std::cout << "Select CAN interface (0 for awlink0, 1 for awlink1): ";
std::cin >> user_choice;
// 配置CAN接口
// configureCan(sock_awlink0, can_interface_awlink0);
// configureCan(sock_awlink1, can_interface_awlink1);
if (user_choice == "0") {
// 创建并启动发送线程和接收线程
std::thread send_thread_awlink0(sendCanFrames, sock_awlink0, can_interface_awlink0);
std::thread receive_thread_awlink0(receiveCanFrames, sock_awlink0, can_interface_awlink0);
// 主线程等待发送线程和接收线程结束
send_thread_awlink0.join();
receive_thread_awlink0.join();
} else if (user_choice == "1") {
// 创建并启动发送线程和接收线程
std::thread send_thread_awlink1(sendCanFrames, sock_awlink1, can_interface_awlink1);
std::thread receive_thread_awlink1(receiveCanFrames, sock_awlink1, can_interface_awlink1);
// 主线程等待发送线程和接收线程结束
send_thread_awlink1.join();
receive_thread_awlink1.join();
}
// 关闭套接字
close(sock_awlink0);
close(sock_awlink1);
return 0;
}
二、测试
1、按照正常步骤开启CAN
2、编译后拷贝到开发板,修改执行权限,运行
发送数据:
然后可以通过can分析仪工具TCANLINPro查看发来的数据
接收数据:
通过TCANLINPro发送数据
开发板接受相应数据: