1 绪论
回顾之前的文章,发现记录自己的学习过程真的挺有用,至少当自己再次遇到相同问题的时候,可以想到自己曾经研究过这些东西,也能很快的进行定位。所以,索性就把当前项目调试中遇到的一些知识也做一个记录吧。
2 CAN卡的使用
本次记录的内容也很简单,就是如何使用已经内置在工控机中的CAN卡。目前手中的工控机内置的是宜鼎科技的CAN卡,当时定制它的目的是因为它支持socketcan,因为之前用过以太网的Socket接口,觉得这个应该使用起来会简单一些吧。
2.1 驱动文件的修改
官方网站可以搜索到驱动程序。在下面这个网址内输入EMUC-B202,就可以搜索到对应的驱动程序。
由于我的系统是ubuntu20.04,官方的这个驱动包在实际使用中会报错。感谢工程师董岗,给了我一个最新版本。其实就是修改driver/main.c文件。至于官方的那个不能用,就是因为linux内核升级以后,有些函数的形参、返回值发生了变化,以至于无法编译通过。
2.2 编译过程
其实,对于编译过程,文件中有一个PDF写的很清楚了,我按照自己的理解,把步骤简单写一写。
Step1:
首先在ubuntu命令行中执行dmesg指令,打印的东西挺多,在里面找如下的记录:
Step2:
linux系统下解压驱动文件,在根目录下执行make指令进行编译。如果不成功,可以考虑安装gcc、g++这些工具链。
make以后生成了两个文件,emuc2socketcan.ko和emucd_32 or emucd_64。这两个文件很关键,其实就是底层的一个驱动文件。从后文创建CAN卡自启动的那个脚本可以看出,需要把这两个文件拷贝到指定目录中,方便系统启动时加载。具体可以看add_2_boot.sh这个脚本。
Step3:
使用如下命令给脚本start.sh增加执行权限,并运行。
chmod +x start.sh
./start.sh
执行完成后,就可以使用ifconfig命令查到设备了。
这里的can0和can1的名称,是在start.sh文件中定义的。设备的波特率也是在这个文件中定义的。
Step4:
创建两个终端。在第一个终端中,输入以下指令:
在第二个终端中,输入以下指令:
这样就证明成功安装了驱动。
Step5:
想要系统上电后自动加载驱动,同样需要配置好start.sh文件中的波特率,名称,还有错误类型后。在boot exec文件夹下,修改add_2_boot.sh的权限并执行。
chmod +x add_2_boot.sh
./add_2_boot.sh
Step6:
重启设备,然后再次执行STEP4中的指令。如果成功,则说明本次的驱动安装成功。
Step7:
下面就是激动人心的编程环节了。
首先是初始化部分:
extern int can_fd;
int can_init(void)
{
/****************通过 ip 指令设置 can0 参数******************/
can_fd = socket(AF_CAN, SOCK_RAW, CAN_RAW);
if (can_fd < 0)
{
perror("socket can creat error!\n");
return -1;
}
const char *ifname = "can0";
struct ifreq ifr;
strcpy(ifr.ifr_name, ifname); // 制定编号为can0的设备,获取设备索引
ioctl(can_fd, SIOCGIFINDEX, &ifr);
struct sockaddr_can addr;
addr.can_family = AF_CAN;
addr.can_ifindex = ifr.ifr_ifindex;
/*将套接字与can0绑定*/
int bind_res = bind(can_fd, (struct sockaddr *)&addr, sizeof(addr));
if (bind_res < 0)
{
perror("can 0 bind error!\n");
return -1;
}
/*过滤规则设置*/
struct can_filter rfilter[3];
rfilter[0].can_id = 1;
rfilter[0].can_mask = CAN_SFF_MASK; // 标准帧
rfilter[1].can_id = 2;
rfilter[1].can_mask = CAN_SFF_MASK; // 标准帧
rfilter[2].can_id = 3;
rfilter[2].can_mask = CAN_SFF_MASK; // 标准帧
return can_fd;
}
测试代码部分:
int can_fd;
int main(int argc, char *argv[])
{
can_init();
while (1)
{
/*测试源码*/
#if 1
struct can_frame tx_frame, rx_frame; // 发送frame和接收frame
ssize_t nbytes = read(can_fd, &rx_frame, sizeof(struct can_frame));
if (nbytes < 0)
{
perror("Error in socket read");
return 1;
}
// 打印接收到的CAN数据
#if 0
printf("Received frame with ID 0x%X, DLC %d, data: ", rx_frame.can_id, rx_frame.can_dlc);
for (int i = 0; i < rx_frame.can_dlc; ++i)
printf("%02X ", rx_frame.data[i]);
printf("\n");
#endif
// 发送CAN数据帧
tx_frame.can_id = 0x123;
tx_frame.can_dlc = 8;
for (int i = 0; i < tx_frame.can_dlc; i++)
{
tx_frame.data[i] = rx_frame.data[i];
}
nbytes = write(can_fd, &rx_frame, sizeof(struct can_frame));
if (nbytes != sizeof(struct can_frame))
{
perror("Error in socket write");
return 1;
}
printf("CAN frame sent successfully.\n");
#endif
}
return 0;
}
3 结束语
最近学了很多东西C++还有CMake的知识,感觉前方道路漫漫啊。。。。加油
先在这里保存一个简单的CMakeLists.txt文件吧。
cmake_minimum_required(VERSION 3.0)
project(CAN_DEMO)
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
aux_source_directory(src SRCLIST)
include_directories(include)
add_executable(can_demo ${SRCLIST} main.cpp)