相机和毫米波雷达数据融合2--SocketCan编写
相机和毫米波雷达数据融合2--SocketCan编写参考文章工控机Can接口激活项目SocketCan编写头文件 can_vehicle_class.h源文件 can_vehicle_class.cpp构造函数Can_Init()CAN_Receive_Msg(parameters)Can_Send_Msg(parameters)参考文章linux下的SocketCan编写Linux socket
相机和毫米波雷达数据融合2--SocketCan编写
参考文章
linux下的SocketCan编写
Linux socket CAN编程示例
工控机Can接口激活
工控机的can卡需要激活,一般厂家会有执行文件,你每次开机然后执行这个文件就好了。也可以把这个文件设置成开机自动执行,这样就不要每次都运行了。
里面的内容没有太大借鉴意义,不同厂家写的不一样。运行成功之后可以通过ifconfig命令查看是否出现了相应的can口。注意这里面的can口命名(can0,can1)很重要,后面的SocketCan代码中的名字和这里的一致
算了还是看看这个**.sh**文件里写了什么吧。
重点就是设置的语句“set up”等等,你会看到参考文章链接中的代码前面会有类似的设置语句,这时你就知道我的**Can_Init()**里面为什么注释掉这几行设置代码了。总结就是别重复激活,一个位置激活了,另外就没必要再次激活了。
Canutils,这是linux下一个查看can报文的软件,你可以在你的linux里下一个,网上教程很多。用candump命令可以很好地查看指定can口传输的报文。
项目SocketCan编写
本人对c++基础不好,在编写的时候对相应类的编写可能不合适,希望大家一起交流,指导。
里面的错误可以忽略,因为这是linux下的代码,但是为了方便写文章我在windows下打开了。
箭头所指的两个文件用来实现SocketCan功能。
头文件 can_vehicle_class.h
#ifndef CAN_VEHICLE_CLASS_H
#define CAN_VEHICLE_CLASS_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/can.h>
#include <linux/can/raw.h>
#include <vector>
using namespace std;
class CanVehicleClass
{
private:
int Can_Init();
public:
CanVehicleClass(char* name);
char* can_name;//绑定的can口
int s;//socketcan套接字
unsigned char CAN_Receive_Msg(can_frame& receive_msg,vector<unsigned int> &receive_id);
int Can_Send_Msg(vector<can_frame>& send_msg_vec);
vector<can_frame> send_msg_vec;//打算发送的报文
vector<unsigned int> receive_id_vec;//接受过滤的报文id
can_frame receive_msg;//接收到的报文
};
#endif
这个头文件声明了一个CanVehicleClass类,类中声明了构造函数,can初始化函数,can报文发送函数,can报文接收函数,指定的can口名称,想要过滤的报文id,想要发送的报文本身,接收到的报文本身。
源文件 can_vehicle_class.cpp
源文件中对四个函数进行定义,最上面的是CanvehicleClass类的构造函数。
下面来看看这几个函数。
构造函数
CanVehicleClass::CanVehicleClass(char* name)
{
can_name=name;
printf("%s","In class constructor of CanVehicleClass.");
Can_Init();
}
构造函数在创建类的时候会自己运行,所以我把can口名字定义,和Can_Init()函数都写在里面了。
Can_Init()
int CanVehicleClass::Can_Init()
{
int ret;
struct sockaddr_can addr;
struct ifreq ifr;
// system("sudo ip link set can0 type vcan bitrate 100000");
// system("sudo ifconfig can0 up");
// printf("this is a can send demo\r\n");
//1.Create socket
s = socket(PF_CAN, SOCK_RAW, CAN_RAW);
if (s < 0)
{
perror("socket PF_CAN failed");
return -1;
}
//2.Specify can0 device
strcpy(ifr.ifr_name, can_name);//这里写入can口名字,我的是can_name
ret = ioctl(s, SIOCGIFINDEX, &ifr);
if (ret < 0)
{
perror("ioctl failed");
return -1;
}
//3.Bind the socket to can0
addr.can_family = AF_CAN;
addr.can_ifindex = ifr.ifr_ifindex;
ret = bind(s, (struct sockaddr *)&addr, sizeof(addr));
if (ret < 0)
{
perror("bind failed");
return -1;
}
//4.Disable filtering rules, do not receive packets, only send
setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, NULL, 0);
//5.return socket
return s;
}
这个代码我也不太懂,但是不需要怎么改,注意在里面指定can口就好了。想要了解相关知识可以自己去查或者看文章开头的参考文章。
CAN_Receive_Msg(parameters)
unsigned char CanVehicleClass::CAN_Receive_Msg(can_frame &receive_msg,vector<unsigned int> &receive_id_vec)
{
//Define receive rules
struct can_filter rfilter[receive_id_vec.size()];
for(int i=0;i<receive_id_vec.size();++i)
{
rfilter[i].can_id = receive_id_vec[i];
rfilter[i].can_mask = CAN_SFF_MASK;
}
setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, &rfilter, sizeof(rfilter));
int nbytes=0;
nbytes = read(s, &receive_msg, sizeof(receive_msg));
if(nbytes > 0)
{
return receive_msg.can_id;
}
if(nbytes == -1)
{
printf("%s","receive error msg");
close(s);
return -1;
}
}
传入的参数:receive_msg用来存储read函数在总线上读到的can报文。receive_id_vec是你打算获取的报文id列表。这个函数运行之后,receive_msg就被赋值了,这是CanVehicleClass类的成员数据。
Can_Send_Msg(parameters)
int CanVehicleClass::Can_Send_Msg(vector<can_frame>& send_msg_vec)
{
int nbytes;
// struct can_frame frame;
// frame.can_id = send_id;
// frame.can_dlc = send_len;
// for(int i=0;i<send_len;i++)
// frame.data[i]=send_msg[i];
//Send message
for(int i = 0; i < send_msg_vec.size(); ++i)
{
nbytes = write(this->s, &send_msg_vec[i], sizeof(send_msg_vec[i]));
if(nbytes != sizeof(send_msg_vec[i]))
{
printf("%s,%s","send error frame ",strerror(errno));
close(this->s);
return -1;
}
}
return nbytes;
}
传入的参数:你想要发送的报文列表,send_msg_vec是一个存储了很多你这个周期想要发送的报文的vector,这个函数的关键在于用write函数将vector中的can_frame们发送出去。
总结
至此,SocketCan的编写内容就讲完了,代码中有一些不太需要的我没有删除,遇到不懂的可以百度,或者查看参考文章链接。这篇文章主要是完成了具有SocketCan功能类的编写,这样可以方便在之后的程序进行调用。 写完这些文件之后你可以在can_vehicle_class.cpp中继续编写一个main()函数来测试编写的代码是否正确,或者是重新写一个文件,但是需要注意头文件的包含,还有多源文件编译的命令。(去百度linux的编译命令行)
大概就是这样的一段代码吧,我没跑过如果编译有错看看是不是多源文件编译没弄好,头文件没弄好之类的。如果生成了可执行文件,你可有如下选择进行测试:
- 利用canutils,这是最简单的测试方式。首先利用candump can0,来监听can0口的报文,然后运行可执行文件,会发现窗口出现相应的报文,说明发送报文没问题。然后用cansend 发送0x17 0x18的报文,代码窗口如果只显示0x17的id说明接收没问题。
- 用usbcan,用usbcan连接你的电脑和工控机的can口,然后用相应的测试软件TSmaster来发送和接收can,与上文类似。
int main()
{
CanVehicleClass test_can("can0");
for (int i = 0; i < 10; ++i)
{
can_frame temp;
temp.can_id=0x16;
temp.can_dlc=8;
for(int j=0;j<temp.can_dlc;++j)
{
temp.data[j]=i;
}
test_can.send_msg_vec.push_back(temp);
}
test_can.send_msg_vec(test_can.send_msg_vec);
while(1)
{
test_can.receive_id_vec = {0x17};
test_can.CAN_Receive_Msg(rec_msg,test_can.receive_id_vec);
cout << test_can.receive_msg.can_id << endl;
}
return 0;
}
如果代码这里有问题,可以在评论区提出来,最好先看参考文章的内容,百度之后再问,最后的测试代码是我写文章的时候写的没有测试,不知道有没有问题,但是你可以看到,写成类之后,这几行代码就完成需要的功能。
更多推荐
所有评论(0)